1 //===- 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
10 #include "CursorVisitor.h"
11 #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
18 using namespace clang;
19 using namespace cxcursor;
20
getTopOverriddenMethods(CXTranslationUnit TU,Decl * D,SmallVectorImpl<Decl * > & Methods)21 static void getTopOverriddenMethods(CXTranslationUnit TU,
22 Decl *D,
23 SmallVectorImpl<Decl *> &Methods) {
24 if (!D)
25 return;
26 if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
27 return;
28
29 SmallVector<CXCursor, 8> Overridden;
30 cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
31
32 if (Overridden.empty()) {
33 Methods.push_back(D->getCanonicalDecl());
34 return;
35 }
36
37 for (SmallVector<CXCursor, 8>::iterator
38 I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
39 getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
40 }
41
42 namespace {
43
44 struct FindFileIdRefVisitData {
45 CXTranslationUnit TU;
46 FileID FID;
47 Decl *Dcl;
48 int SelectorIdIdx;
49 CXCursorAndRangeVisitor visitor;
50
51 typedef SmallVector<Decl *, 8> TopMethodsTy;
52 TopMethodsTy TopMethods;
53
FindFileIdRefVisitData__anon016113e20111::FindFileIdRefVisitData54 FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
55 Decl *D, int selectorIdIdx,
56 CXCursorAndRangeVisitor visitor)
57 : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
58 Dcl = getCanonical(D);
59 getTopOverriddenMethods(TU, Dcl, TopMethods);
60 }
61
getASTContext__anon016113e20111::FindFileIdRefVisitData62 ASTContext &getASTContext() const {
63 return static_cast<ASTUnit *>(TU->TUData)->getASTContext();
64 }
65
66 /// \brief We are looking to find all semantically relevant identifiers,
67 /// so the definition of "canonical" here is different than in the AST, e.g.
68 ///
69 /// \code
70 /// class C {
71 /// C() {}
72 /// };
73 /// \endcode
74 ///
75 /// we consider the canonical decl of the constructor decl to be the class
76 /// itself, so both 'C' can be highlighted.
getCanonical__anon016113e20111::FindFileIdRefVisitData77 Decl *getCanonical(Decl *D) const {
78 if (!D)
79 return 0;
80
81 D = D->getCanonicalDecl();
82
83 if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
84 if (ImplD->getClassInterface())
85 return getCanonical(ImplD->getClassInterface());
86
87 } else if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D)) {
88 return getCanonical(CXXCtorD->getParent());
89 }
90
91 return D;
92 }
93
isHit__anon016113e20111::FindFileIdRefVisitData94 bool isHit(Decl *D) const {
95 if (!D)
96 return false;
97
98 D = getCanonical(D);
99 if (D == Dcl)
100 return true;
101
102 if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
103 return isOverriddingMethod(D);
104
105 return false;
106 }
107
108 private:
isOverriddingMethod__anon016113e20111::FindFileIdRefVisitData109 bool isOverriddingMethod(Decl *D) const {
110 if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
111 TopMethods.end())
112 return true;
113
114 TopMethodsTy methods;
115 getTopOverriddenMethods(TU, D, methods);
116 for (TopMethodsTy::iterator
117 I = methods.begin(), E = methods.end(); I != E; ++I) {
118 if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
119 TopMethods.end())
120 return true;
121 }
122
123 return false;
124 }
125 };
126
127 } // end anonymous namespace.
128
129 /// \brief For a macro \arg Loc, returns the file spelling location and sets
130 /// to \arg isMacroArg whether the spelling resides inside a macro definition or
131 /// a macro argument.
getFileSpellingLoc(SourceManager & SM,SourceLocation Loc,bool & isMacroArg)132 static SourceLocation getFileSpellingLoc(SourceManager &SM,
133 SourceLocation Loc,
134 bool &isMacroArg) {
135 assert(Loc.isMacroID());
136 SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
137 if (SpellLoc.isMacroID())
138 return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
139
140 isMacroArg = SM.isMacroArgExpansion(Loc);
141 return SpellLoc;
142 }
143
findFileIdRefVisit(CXCursor cursor,CXCursor parent,CXClientData client_data)144 static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
145 CXCursor parent,
146 CXClientData client_data) {
147 CXCursor declCursor = clang_getCursorReferenced(cursor);
148 if (!clang_isDeclaration(declCursor.kind))
149 return CXChildVisit_Recurse;
150
151 Decl *D = cxcursor::getCursorDecl(declCursor);
152 if (!D)
153 return CXChildVisit_Continue;
154
155 FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
156 if (data->isHit(D)) {
157 cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
158
159 // We are looking for identifiers to highlight so for objc methods (and
160 // not a parameter) we can only highlight the selector identifiers.
161 if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
162 cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
163 cxcursor::getSelectorIdentifierIndex(cursor) == -1)
164 return CXChildVisit_Recurse;
165
166 if (clang_isExpression(cursor.kind)) {
167 if (cursor.kind == CXCursor_DeclRefExpr ||
168 cursor.kind == CXCursor_MemberRefExpr) {
169 // continue..
170
171 } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
172 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
173 // continue..
174
175 } else
176 return CXChildVisit_Recurse;
177 }
178
179 SourceLocation
180 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
181 SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
182 if (SelIdLoc.isValid())
183 Loc = SelIdLoc;
184
185 ASTContext &Ctx = data->getASTContext();
186 SourceManager &SM = Ctx.getSourceManager();
187 bool isInMacroDef = false;
188 if (Loc.isMacroID()) {
189 bool isMacroArg;
190 Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
191 isInMacroDef = !isMacroArg;
192 }
193
194 // We are looking for identifiers in a specific file.
195 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
196 if (LocInfo.first != data->FID)
197 return CXChildVisit_Recurse;
198
199 if (isInMacroDef) {
200 // FIXME: For a macro definition make sure that all expansions
201 // of it expand to the same reference before allowing to point to it.
202 return CXChildVisit_Recurse;
203 }
204
205 data->visitor.visit(data->visitor.context, cursor,
206 cxloc::translateSourceRange(Ctx, Loc));
207 }
208 return CXChildVisit_Recurse;
209 }
210
findIdRefsInFile(CXTranslationUnit TU,CXCursor declCursor,const FileEntry * File,CXCursorAndRangeVisitor Visitor)211 static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
212 const FileEntry *File,
213 CXCursorAndRangeVisitor Visitor) {
214 assert(clang_isDeclaration(declCursor.kind));
215 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
216 SourceManager &SM = Unit->getSourceManager();
217
218 FileID FID = SM.translateFile(File);
219 Decl *Dcl = cxcursor::getCursorDecl(declCursor);
220 if (!Dcl)
221 return;
222
223 FindFileIdRefVisitData data(TU, FID, Dcl,
224 cxcursor::getSelectorIdentifierIndex(declCursor),
225 Visitor);
226
227 if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
228 clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
229 findFileIdRefVisit, &data);
230 return;
231 }
232
233 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
234 CursorVisitor FindIdRefsVisitor(TU,
235 findFileIdRefVisit, &data,
236 /*VisitPreprocessorLast=*/true,
237 /*VisitIncludedEntities=*/false,
238 Range,
239 /*VisitDeclsOnly=*/true);
240 FindIdRefsVisitor.visitFileRegion();
241 }
242
243 namespace {
244
245 struct FindFileMacroRefVisitData {
246 ASTUnit &Unit;
247 const FileEntry *File;
248 const IdentifierInfo *Macro;
249 CXCursorAndRangeVisitor visitor;
250
FindFileMacroRefVisitData__anon016113e20211::FindFileMacroRefVisitData251 FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
252 const IdentifierInfo *Macro,
253 CXCursorAndRangeVisitor visitor)
254 : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
255
getASTContext__anon016113e20211::FindFileMacroRefVisitData256 ASTContext &getASTContext() const {
257 return Unit.getASTContext();
258 }
259 };
260
261 } // anonymous namespace
262
findFileMacroRefVisit(CXCursor cursor,CXCursor parent,CXClientData client_data)263 static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
264 CXCursor parent,
265 CXClientData client_data) {
266 const IdentifierInfo *Macro = 0;
267 if (cursor.kind == CXCursor_MacroDefinition)
268 Macro = getCursorMacroDefinition(cursor)->getName();
269 else if (cursor.kind == CXCursor_MacroExpansion)
270 Macro = getCursorMacroExpansion(cursor)->getName();
271 if (!Macro)
272 return CXChildVisit_Continue;
273
274 FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
275 if (data->Macro != Macro)
276 return CXChildVisit_Continue;
277
278 SourceLocation
279 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
280
281 ASTContext &Ctx = data->getASTContext();
282 SourceManager &SM = Ctx.getSourceManager();
283 bool isInMacroDef = false;
284 if (Loc.isMacroID()) {
285 bool isMacroArg;
286 Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
287 isInMacroDef = !isMacroArg;
288 }
289
290 // We are looking for identifiers in a specific file.
291 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
292 if (SM.getFileEntryForID(LocInfo.first) != data->File)
293 return CXChildVisit_Continue;
294
295 if (isInMacroDef) {
296 // FIXME: For a macro definition make sure that all expansions
297 // of it expand to the same reference before allowing to point to it.
298 return CXChildVisit_Continue;
299 }
300
301 data->visitor.visit(data->visitor.context, cursor,
302 cxloc::translateSourceRange(Ctx, Loc));
303 return CXChildVisit_Continue;
304 }
305
findMacroRefsInFile(CXTranslationUnit TU,CXCursor Cursor,const FileEntry * File,CXCursorAndRangeVisitor Visitor)306 static void findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
307 const FileEntry *File,
308 CXCursorAndRangeVisitor Visitor) {
309 if (Cursor.kind != CXCursor_MacroDefinition &&
310 Cursor.kind != CXCursor_MacroExpansion)
311 return;
312
313 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
314 SourceManager &SM = Unit->getSourceManager();
315
316 FileID FID = SM.translateFile(File);
317 const IdentifierInfo *Macro = 0;
318 if (Cursor.kind == CXCursor_MacroDefinition)
319 Macro = getCursorMacroDefinition(Cursor)->getName();
320 else
321 Macro = getCursorMacroExpansion(Cursor)->getName();
322 if (!Macro)
323 return;
324
325 FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
326
327 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
328 CursorVisitor FindMacroRefsVisitor(TU,
329 findFileMacroRefVisit, &data,
330 /*VisitPreprocessorLast=*/false,
331 /*VisitIncludedEntities=*/false,
332 Range);
333 FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
334 }
335
336
337 //===----------------------------------------------------------------------===//
338 // libclang public APIs.
339 //===----------------------------------------------------------------------===//
340
341 extern "C" {
342
clang_findReferencesInFile(CXCursor cursor,CXFile file,CXCursorAndRangeVisitor visitor)343 void clang_findReferencesInFile(CXCursor cursor, CXFile file,
344 CXCursorAndRangeVisitor visitor) {
345 bool Logging = ::getenv("LIBCLANG_LOGGING");
346
347 if (clang_Cursor_isNull(cursor)) {
348 if (Logging)
349 llvm::errs() << "clang_findReferencesInFile: Null cursor\n";
350 return;
351 }
352 if (cursor.kind == CXCursor_NoDeclFound) {
353 if (Logging)
354 llvm::errs() << "clang_findReferencesInFile: Got CXCursor_NoDeclFound\n";
355 return;
356 }
357 if (!file) {
358 if (Logging)
359 llvm::errs() << "clang_findReferencesInFile: Null file\n";
360 return;
361 }
362 if (!visitor.visit) {
363 if (Logging)
364 llvm::errs() << "clang_findReferencesInFile: Null visitor\n";
365 return;
366 }
367
368 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
369 if (!CXXUnit)
370 return;
371
372 ASTUnit::ConcurrencyCheck Check(*CXXUnit);
373
374 if (cursor.kind == CXCursor_MacroDefinition ||
375 cursor.kind == CXCursor_MacroExpansion) {
376 findMacroRefsInFile(cxcursor::getCursorTU(cursor),
377 cursor,
378 static_cast<const FileEntry *>(file),
379 visitor);
380 return;
381 }
382
383 // We are interested in semantics of identifiers so for C++ constructor exprs
384 // prefer type references, e.g.:
385 //
386 // return MyStruct();
387 //
388 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
389 // we are actually interested in the type declaration.
390 cursor = cxcursor::getTypeRefCursor(cursor);
391
392 CXCursor refCursor = clang_getCursorReferenced(cursor);
393
394 if (!clang_isDeclaration(refCursor.kind)) {
395 if (Logging)
396 llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a "
397 "declaration\n";
398 return;
399 }
400
401 findIdRefsInFile(cxcursor::getCursorTU(cursor),
402 refCursor,
403 static_cast<const FileEntry *>(file),
404 visitor);
405 }
406
_visitCursorAndRange(void * context,CXCursor cursor,CXSourceRange range)407 static enum CXVisitorResult _visitCursorAndRange(void *context,
408 CXCursor cursor,
409 CXSourceRange range) {
410 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
411 return INVOKE_BLOCK2(block, cursor, range);
412 }
413
clang_findReferencesInFileWithBlock(CXCursor cursor,CXFile file,CXCursorAndRangeVisitorBlock block)414 void clang_findReferencesInFileWithBlock(CXCursor cursor,
415 CXFile file,
416 CXCursorAndRangeVisitorBlock block) {
417 CXCursorAndRangeVisitor visitor = { block,
418 block ? _visitCursorAndRange : 0 };
419 return clang_findReferencesInFile(cursor, file, visitor);
420 }
421
422 } // end: extern "C"
423
424