• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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