blob: 0b36af3c95bae7bdd5b2224a49c6d5f3accbec95 [file] [log] [blame]
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +00001//===- CIndexHigh.cpp - Higher level API functions ------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Argyrios Kyrtzidise2079cf2011-11-16 08:58:54 +000010#include "CursorVisitor.h"
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +000011#include "CXCursor.h"
12#include "CXSourceLocation.h"
13#include "CXTranslationUnit.h"
14
15#include "clang/Frontend/ASTUnit.h"
16#include "clang/AST/DeclObjC.h"
17
18using namespace clang;
Argyrios Kyrtzidise2079cf2011-11-16 08:58:54 +000019using namespace cxcursor;
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +000020
21static void getTopOverriddenMethods(CXTranslationUnit TU,
22 Decl *D,
23 SmallVectorImpl<Decl *> &Methods) {
24 if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
25 return;
26
27 SmallVector<CXCursor, 8> Overridden;
28 cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
29
30 if (Overridden.empty()) {
31 Methods.push_back(D->getCanonicalDecl());
32 return;
33 }
34
35 for (SmallVector<CXCursor, 8>::iterator
36 I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
37 getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
38}
39
40namespace {
41
42struct FindFileIdRefVisitData {
43 CXTranslationUnit TU;
44 FileID FID;
45 Decl *Dcl;
46 int SelectorIdIdx;
47 CXCursorAndRangeVisitor visitor;
48
49 typedef SmallVector<Decl *, 8> TopMethodsTy;
50 TopMethodsTy TopMethods;
51
52 FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
53 Decl *D, int selectorIdIdx,
54 CXCursorAndRangeVisitor visitor)
55 : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
56 Dcl = getCanonical(D);
57 getTopOverriddenMethods(TU, Dcl, TopMethods);
58 }
59
60 ASTContext &getASTContext() const {
61 return static_cast<ASTUnit *>(TU->TUData)->getASTContext();
62 }
63
64 /// \brief We are looking to find all semantically relevant identifiers,
65 /// so the definition of "canonical" here is different than in the AST, e.g.
66 ///
67 /// \code
68 /// class C {
69 /// C() {}
70 /// };
71 /// \endcode
72 ///
73 /// we consider the canonical decl of the constructor decl to be the class
74 /// itself, so both 'C' can be highlighted.
75 Decl *getCanonical(Decl *D) const {
76 D = D->getCanonicalDecl();
77
78 if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D))
79 return getCanonical(ImplD->getClassInterface());
80 if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D))
81 return getCanonical(CXXCtorD->getParent());
82
83 return D;
84 }
85
86 bool isHit(Decl *D) const {
87 D = getCanonical(D);
88 if (D == Dcl)
89 return true;
90
91 if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
92 return isOverriddingMethod(D);
93
94 return false;
95 }
96
97private:
98 bool isOverriddingMethod(Decl *D) const {
99 if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
100 TopMethods.end())
101 return true;
102
103 TopMethodsTy methods;
104 getTopOverriddenMethods(TU, D, methods);
105 for (TopMethodsTy::iterator
106 I = methods.begin(), E = methods.end(); I != E; ++I) {
107 if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
108 TopMethods.end())
109 return true;
110 }
111
112 return false;
113 }
114};
115
116} // end anonymous namespace.
117
118/// \brief For a macro \arg Loc, returns the file spelling location and sets
119/// to \arg isMacroArg whether the spelling resides inside a macro definition or
120/// a macro argument.
121static SourceLocation getFileSpellingLoc(SourceManager &SM,
122 SourceLocation Loc,
123 bool &isMacroArg) {
124 assert(Loc.isMacroID());
125 SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
126 if (SpellLoc.isMacroID())
127 return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
128
129 isMacroArg = SM.isMacroArgExpansion(Loc);
130 return SpellLoc;
131}
132
133static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
134 CXCursor parent,
135 CXClientData client_data) {
136 CXCursor declCursor = clang_getCursorReferenced(cursor);
137 if (!clang_isDeclaration(declCursor.kind))
138 return CXChildVisit_Recurse;
139
140 Decl *D = cxcursor::getCursorDecl(declCursor);
141 FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
142 if (data->isHit(D)) {
143 cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
144
145 // We are looking for identifiers to highlight so for objc methods (and
146 // not a parameter) we can only highlight the selector identifiers.
147 if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
148 cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
149 cxcursor::getSelectorIdentifierIndex(cursor) == -1)
150 return CXChildVisit_Recurse;
151
152 if (clang_isExpression(cursor.kind)) {
153 if (cursor.kind == CXCursor_DeclRefExpr ||
154 cursor.kind == CXCursor_MemberRefExpr) {
155 // continue..
156
157 } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
158 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
159 // continue..
160
161 } else
162 return CXChildVisit_Recurse;
163 }
164
165 SourceLocation
166 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
167 SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
168 if (SelIdLoc.isValid())
169 Loc = SelIdLoc;
170
171 SourceManager &SM = data->getASTContext().getSourceManager();
172 bool isInMacroDef = false;
173 if (Loc.isMacroID()) {
174 bool isMacroArg;
175 Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
176 isInMacroDef = !isMacroArg;
177 }
178
179 // We are looking for identifiers in a specific file.
180 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
181 if (LocInfo.first != data->FID)
182 return CXChildVisit_Recurse;
183
184 if (isInMacroDef) {
185 // FIXME: For a macro definition make sure that all expansions
186 // of it expand to the same reference before allowing to point to it.
187 Loc = SourceLocation();
188 }
189
190 data->visitor.visit(data->visitor.context, cursor,
191 cxloc::translateSourceRange(D->getASTContext(), Loc));
192 }
193 return CXChildVisit_Recurse;
194}
195
196static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
197 const FileEntry *File,
198 CXCursorAndRangeVisitor Visitor) {
199 assert(clang_isDeclaration(declCursor.kind));
200 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000201 SourceManager &SM = Unit->getSourceManager();
202
203 FileID FID = SM.translateFile(File);
204 Decl *Dcl = cxcursor::getCursorDecl(declCursor);
205 FindFileIdRefVisitData data(TU, FID, Dcl,
206 cxcursor::getSelectorIdentifierIndex(declCursor),
207 Visitor);
208
209 if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
210 clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
211 findFileIdRefVisit, &data);
212 return;
213 }
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000214
Argyrios Kyrtzidise2079cf2011-11-16 08:58:54 +0000215 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
216 CursorVisitor FindIdRefsVisitor(TU,
217 findFileIdRefVisit, &data,
218 /*VisitPreprocessorLast=*/true,
219 /*VisitIncludedEntities=*/false,
220 Range);
221 FindIdRefsVisitor.visitFileRegion();
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000222}
223
224
225//===----------------------------------------------------------------------===//
226// libclang public APIs.
227//===----------------------------------------------------------------------===//
228
229extern "C" {
230
231void clang_findReferencesInFile(CXCursor cursor, CXFile file,
232 CXCursorAndRangeVisitor visitor) {
233 bool Logging = ::getenv("LIBCLANG_LOGGING");
234
235 if (clang_Cursor_isNull(cursor)) {
236 if (Logging)
237 llvm::errs() << "clang_findReferencesInFile: Null cursor\n";
238 return;
239 }
240 if (!file) {
241 if (Logging)
242 llvm::errs() << "clang_findReferencesInFile: Null file\n";
243 return;
244 }
245 if (!visitor.visit) {
246 if (Logging)
247 llvm::errs() << "clang_findReferencesInFile: Null visitor\n";
248 return;
249 }
250
251 // We are interested in semantics of identifiers so for C++ constructor exprs
252 // prefer type references, e.g.:
253 //
254 // return MyStruct();
255 //
256 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
257 // we are actually interested in the type declaration.
258 cursor = cxcursor::getTypeRefCursor(cursor);
259
260 CXCursor refCursor = clang_getCursorReferenced(cursor);
261
262 if (!clang_isDeclaration(refCursor.kind)) {
263 if (Logging)
264 llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a "
265 "declaration\n";
266 return;
267 }
268
269 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
270 ASTUnit::ConcurrencyCheck Check(*CXXUnit);
271
272 findIdRefsInFile(cxcursor::getCursorTU(cursor),
273 refCursor,
274 static_cast<const FileEntry *>(file),
275 visitor);
276}
277
278static enum CXVisitorResult _visitCursorAndRange(void *context,
279 CXCursor cursor,
280 CXSourceRange range) {
281 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
282 return INVOKE_BLOCK2(block, cursor, range);
283}
284
285void clang_findReferencesInFileWithBlock(CXCursor cursor,
286 CXFile file,
287 CXCursorAndRangeVisitorBlock block) {
288 CXCursorAndRangeVisitor visitor = { block,
289 block ? _visitCursorAndRange : 0 };
290 return clang_findReferencesInFile(cursor, file, visitor);
291}
292
293} // end: extern "C"
294