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 "CLog.h"
12 #include "CXCursor.h"
13 #include "CXSourceLocation.h"
14 #include "CXTranslationUnit.h"
15 #include "clang/AST/DeclObjC.h"
16 #include "clang/Frontend/ASTUnit.h"
17 #include "llvm/Support/Compiler.h"
18 
19 using namespace clang;
20 using namespace cxcursor;
21 using namespace cxindex;
22 
getTopOverriddenMethods(CXTranslationUnit TU,const Decl * D,SmallVectorImpl<const Decl * > & Methods)23 static void getTopOverriddenMethods(CXTranslationUnit TU,
24                                     const Decl *D,
25                                     SmallVectorImpl<const Decl *> &Methods) {
26   if (!D)
27     return;
28   if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
29     return;
30 
31   SmallVector<CXCursor, 8> Overridden;
32   cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
33 
34   if (Overridden.empty()) {
35     Methods.push_back(D->getCanonicalDecl());
36     return;
37   }
38 
39   for (SmallVectorImpl<CXCursor>::iterator
40          I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
41     getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
42 }
43 
44 namespace {
45 
46 struct FindFileIdRefVisitData {
47   CXTranslationUnit TU;
48   FileID FID;
49   const Decl *Dcl;
50   int SelectorIdIdx;
51   CXCursorAndRangeVisitor visitor;
52 
53   typedef SmallVector<const Decl *, 8> TopMethodsTy;
54   TopMethodsTy TopMethods;
55 
FindFileIdRefVisitData__anon71fde9250111::FindFileIdRefVisitData56   FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
57                          const Decl *D, int selectorIdIdx,
58                          CXCursorAndRangeVisitor visitor)
59     : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
60     Dcl = getCanonical(D);
61     getTopOverriddenMethods(TU, Dcl, TopMethods);
62   }
63 
getASTContext__anon71fde9250111::FindFileIdRefVisitData64   ASTContext &getASTContext() const {
65     return cxtu::getASTUnit(TU)->getASTContext();
66   }
67 
68   /// \brief We are looking to find all semantically relevant identifiers,
69   /// so the definition of "canonical" here is different than in the AST, e.g.
70   ///
71   /// \code
72   ///   class C {
73   ///     C() {}
74   ///   };
75   /// \endcode
76   ///
77   /// we consider the canonical decl of the constructor decl to be the class
78   /// itself, so both 'C' can be highlighted.
getCanonical__anon71fde9250111::FindFileIdRefVisitData79   const Decl *getCanonical(const Decl *D) const {
80     if (!D)
81       return nullptr;
82 
83     D = D->getCanonicalDecl();
84 
85     if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
86       if (ImplD->getClassInterface())
87         return getCanonical(ImplD->getClassInterface());
88 
89     } else if (const CXXConstructorDecl *CXXCtorD =
90                    dyn_cast<CXXConstructorDecl>(D)) {
91       return getCanonical(CXXCtorD->getParent());
92     }
93 
94     return D;
95   }
96 
isHit__anon71fde9250111::FindFileIdRefVisitData97   bool isHit(const Decl *D) const {
98     if (!D)
99       return false;
100 
101     D = getCanonical(D);
102     if (D == Dcl)
103       return true;
104 
105     if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
106       return isOverriddingMethod(D);
107 
108     return false;
109   }
110 
111 private:
isOverriddingMethod__anon71fde9250111::FindFileIdRefVisitData112   bool isOverriddingMethod(const Decl *D) const {
113     if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
114           TopMethods.end())
115       return true;
116 
117     TopMethodsTy methods;
118     getTopOverriddenMethods(TU, D, methods);
119     for (TopMethodsTy::iterator
120            I = methods.begin(), E = methods.end(); I != E; ++I) {
121       if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
122             TopMethods.end())
123         return true;
124     }
125 
126     return false;
127   }
128 };
129 
130 } // end anonymous namespace.
131 
132 /// \brief For a macro \arg Loc, returns the file spelling location and sets
133 /// to \arg isMacroArg whether the spelling resides inside a macro definition or
134 /// a macro argument.
getFileSpellingLoc(SourceManager & SM,SourceLocation Loc,bool & isMacroArg)135 static SourceLocation getFileSpellingLoc(SourceManager &SM,
136                                          SourceLocation Loc,
137                                          bool &isMacroArg) {
138   assert(Loc.isMacroID());
139   SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
140   if (SpellLoc.isMacroID())
141     return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
142 
143   isMacroArg = SM.isMacroArgExpansion(Loc);
144   return SpellLoc;
145 }
146 
findFileIdRefVisit(CXCursor cursor,CXCursor parent,CXClientData client_data)147 static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
148                                                   CXCursor parent,
149                                                   CXClientData client_data) {
150   CXCursor declCursor = clang_getCursorReferenced(cursor);
151   if (!clang_isDeclaration(declCursor.kind))
152     return CXChildVisit_Recurse;
153 
154   const Decl *D = cxcursor::getCursorDecl(declCursor);
155   if (!D)
156     return CXChildVisit_Continue;
157 
158   FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
159   if (data->isHit(D)) {
160     cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
161 
162     // We are looking for identifiers to highlight so for objc methods (and
163     // not a parameter) we can only highlight the selector identifiers.
164     if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
165          cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
166          cxcursor::getSelectorIdentifierIndex(cursor) == -1)
167       return CXChildVisit_Recurse;
168 
169     if (clang_isExpression(cursor.kind)) {
170       if (cursor.kind == CXCursor_DeclRefExpr ||
171           cursor.kind == CXCursor_MemberRefExpr) {
172         // continue..
173 
174       } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
175                  cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
176         // continue..
177 
178       } else
179         return CXChildVisit_Recurse;
180     }
181 
182     SourceLocation
183       Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
184     SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
185     if (SelIdLoc.isValid())
186       Loc = SelIdLoc;
187 
188     ASTContext &Ctx = data->getASTContext();
189     SourceManager &SM = Ctx.getSourceManager();
190     bool isInMacroDef = false;
191     if (Loc.isMacroID()) {
192       bool isMacroArg;
193       Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
194       isInMacroDef = !isMacroArg;
195     }
196 
197     // We are looking for identifiers in a specific file.
198     std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
199     if (LocInfo.first != data->FID)
200       return CXChildVisit_Recurse;
201 
202     if (isInMacroDef) {
203       // FIXME: For a macro definition make sure that all expansions
204       // of it expand to the same reference before allowing to point to it.
205       return CXChildVisit_Recurse;
206     }
207 
208     if (data->visitor.visit(data->visitor.context, cursor,
209                         cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
210       return CXChildVisit_Break;
211   }
212   return CXChildVisit_Recurse;
213 }
214 
findIdRefsInFile(CXTranslationUnit TU,CXCursor declCursor,const FileEntry * File,CXCursorAndRangeVisitor Visitor)215 static bool findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
216                              const FileEntry *File,
217                              CXCursorAndRangeVisitor Visitor) {
218   assert(clang_isDeclaration(declCursor.kind));
219   SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
220 
221   FileID FID = SM.translateFile(File);
222   const Decl *Dcl = cxcursor::getCursorDecl(declCursor);
223   if (!Dcl)
224     return false;
225 
226   FindFileIdRefVisitData data(TU, FID, Dcl,
227                               cxcursor::getSelectorIdentifierIndex(declCursor),
228                               Visitor);
229 
230   if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
231     return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
232                                findFileIdRefVisit, &data);
233   }
234 
235   SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
236   CursorVisitor FindIdRefsVisitor(TU,
237                                   findFileIdRefVisit, &data,
238                                   /*VisitPreprocessorLast=*/true,
239                                   /*VisitIncludedEntities=*/false,
240                                   Range,
241                                   /*VisitDeclsOnly=*/true);
242   return FindIdRefsVisitor.visitFileRegion();
243 }
244 
245 namespace {
246 
247 struct FindFileMacroRefVisitData {
248   ASTUnit &Unit;
249   const FileEntry *File;
250   const IdentifierInfo *Macro;
251   CXCursorAndRangeVisitor visitor;
252 
FindFileMacroRefVisitData__anon71fde9250211::FindFileMacroRefVisitData253   FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
254                             const IdentifierInfo *Macro,
255                             CXCursorAndRangeVisitor visitor)
256     : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
257 
getASTContext__anon71fde9250211::FindFileMacroRefVisitData258   ASTContext &getASTContext() const {
259     return Unit.getASTContext();
260   }
261 };
262 
263 } // anonymous namespace
264 
findFileMacroRefVisit(CXCursor cursor,CXCursor parent,CXClientData client_data)265 static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
266                                                      CXCursor parent,
267                                                      CXClientData client_data) {
268   const IdentifierInfo *Macro = nullptr;
269   if (cursor.kind == CXCursor_MacroDefinition)
270     Macro = getCursorMacroDefinition(cursor)->getName();
271   else if (cursor.kind == CXCursor_MacroExpansion)
272     Macro = getCursorMacroExpansion(cursor).getName();
273   if (!Macro)
274     return CXChildVisit_Continue;
275 
276   FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
277   if (data->Macro != Macro)
278     return CXChildVisit_Continue;
279 
280   SourceLocation
281     Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
282 
283   ASTContext &Ctx = data->getASTContext();
284   SourceManager &SM = Ctx.getSourceManager();
285   bool isInMacroDef = false;
286   if (Loc.isMacroID()) {
287     bool isMacroArg;
288     Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
289     isInMacroDef = !isMacroArg;
290   }
291 
292   // We are looking for identifiers in a specific file.
293   std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
294   if (SM.getFileEntryForID(LocInfo.first) != data->File)
295     return CXChildVisit_Continue;
296 
297   if (isInMacroDef) {
298     // FIXME: For a macro definition make sure that all expansions
299     // of it expand to the same reference before allowing to point to it.
300     return CXChildVisit_Continue;
301   }
302 
303   if (data->visitor.visit(data->visitor.context, cursor,
304                         cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
305     return CXChildVisit_Break;
306   return CXChildVisit_Continue;
307 }
308 
findMacroRefsInFile(CXTranslationUnit TU,CXCursor Cursor,const FileEntry * File,CXCursorAndRangeVisitor Visitor)309 static bool findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
310                                 const FileEntry *File,
311                                 CXCursorAndRangeVisitor Visitor) {
312   if (Cursor.kind != CXCursor_MacroDefinition &&
313       Cursor.kind != CXCursor_MacroExpansion)
314     return false;
315 
316   ASTUnit *Unit = cxtu::getASTUnit(TU);
317   SourceManager &SM = Unit->getSourceManager();
318 
319   FileID FID = SM.translateFile(File);
320   const IdentifierInfo *Macro = nullptr;
321   if (Cursor.kind == CXCursor_MacroDefinition)
322     Macro = getCursorMacroDefinition(Cursor)->getName();
323   else
324     Macro = getCursorMacroExpansion(Cursor).getName();
325   if (!Macro)
326     return false;
327 
328   FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
329 
330   SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
331   CursorVisitor FindMacroRefsVisitor(TU,
332                                   findFileMacroRefVisit, &data,
333                                   /*VisitPreprocessorLast=*/false,
334                                   /*VisitIncludedEntities=*/false,
335                                   Range);
336   return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
337 }
338 
339 namespace {
340 
341 struct FindFileIncludesVisitor {
342   ASTUnit &Unit;
343   const FileEntry *File;
344   CXCursorAndRangeVisitor visitor;
345 
FindFileIncludesVisitor__anon71fde9250311::FindFileIncludesVisitor346   FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File,
347                           CXCursorAndRangeVisitor visitor)
348     : Unit(Unit), File(File), visitor(visitor) { }
349 
getASTContext__anon71fde9250311::FindFileIncludesVisitor350   ASTContext &getASTContext() const {
351     return Unit.getASTContext();
352   }
353 
visit__anon71fde9250311::FindFileIncludesVisitor354   enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) {
355     if (cursor.kind != CXCursor_InclusionDirective)
356       return CXChildVisit_Continue;
357 
358     SourceLocation
359       Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
360 
361     ASTContext &Ctx = getASTContext();
362     SourceManager &SM = Ctx.getSourceManager();
363 
364     // We are looking for includes in a specific file.
365     std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
366     if (SM.getFileEntryForID(LocInfo.first) != File)
367       return CXChildVisit_Continue;
368 
369     if (visitor.visit(visitor.context, cursor,
370                       cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
371       return CXChildVisit_Break;
372     return CXChildVisit_Continue;
373   }
374 
visit__anon71fde9250311::FindFileIncludesVisitor375   static enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent,
376                                        CXClientData client_data) {
377     return static_cast<FindFileIncludesVisitor*>(client_data)->
378                                                           visit(cursor, parent);
379   }
380 };
381 
382 } // anonymous namespace
383 
findIncludesInFile(CXTranslationUnit TU,const FileEntry * File,CXCursorAndRangeVisitor Visitor)384 static bool findIncludesInFile(CXTranslationUnit TU, const FileEntry *File,
385                                CXCursorAndRangeVisitor Visitor) {
386   assert(TU && File && Visitor.visit);
387 
388   ASTUnit *Unit = cxtu::getASTUnit(TU);
389   SourceManager &SM = Unit->getSourceManager();
390 
391   FileID FID = SM.translateFile(File);
392 
393   FindFileIncludesVisitor IncludesVisitor(*Unit, File, Visitor);
394 
395   SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
396   CursorVisitor InclusionCursorsVisitor(TU,
397                                         FindFileIncludesVisitor::visit,
398                                         &IncludesVisitor,
399                                         /*VisitPreprocessorLast=*/false,
400                                         /*VisitIncludedEntities=*/false,
401                                         Range);
402   return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion();
403 }
404 
405 
406 //===----------------------------------------------------------------------===//
407 // libclang public APIs.
408 //===----------------------------------------------------------------------===//
409 
410 extern "C" {
411 
clang_findReferencesInFile(CXCursor cursor,CXFile file,CXCursorAndRangeVisitor visitor)412 CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file,
413                                     CXCursorAndRangeVisitor visitor) {
414   LogRef Log = Logger::make(LLVM_FUNCTION_NAME);
415 
416   if (clang_Cursor_isNull(cursor)) {
417     if (Log)
418       *Log << "Null cursor";
419     return CXResult_Invalid;
420   }
421   if (cursor.kind == CXCursor_NoDeclFound) {
422     if (Log)
423       *Log << "Got CXCursor_NoDeclFound";
424     return CXResult_Invalid;
425   }
426   if (!file) {
427     if (Log)
428       *Log << "Null file";
429     return CXResult_Invalid;
430   }
431   if (!visitor.visit) {
432     if (Log)
433       *Log << "Null visitor";
434     return CXResult_Invalid;
435   }
436 
437   if (Log)
438     *Log << cursor << " @" << static_cast<const FileEntry *>(file);
439 
440   ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
441   if (!CXXUnit)
442     return CXResult_Invalid;
443 
444   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
445 
446   if (cursor.kind == CXCursor_MacroDefinition ||
447       cursor.kind == CXCursor_MacroExpansion) {
448     if (findMacroRefsInFile(cxcursor::getCursorTU(cursor),
449                             cursor,
450                             static_cast<const FileEntry *>(file),
451                             visitor))
452       return CXResult_VisitBreak;
453     return CXResult_Success;
454   }
455 
456   // We are interested in semantics of identifiers so for C++ constructor exprs
457   // prefer type references, e.g.:
458   //
459   //  return MyStruct();
460   //
461   // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
462   // we are actually interested in the type declaration.
463   cursor = cxcursor::getTypeRefCursor(cursor);
464 
465   CXCursor refCursor = clang_getCursorReferenced(cursor);
466 
467   if (!clang_isDeclaration(refCursor.kind)) {
468     if (Log)
469       *Log << "cursor is not referencing a declaration";
470     return CXResult_Invalid;
471   }
472 
473   if (findIdRefsInFile(cxcursor::getCursorTU(cursor),
474                        refCursor,
475                        static_cast<const FileEntry *>(file),
476                        visitor))
477     return CXResult_VisitBreak;
478   return CXResult_Success;
479 }
480 
clang_findIncludesInFile(CXTranslationUnit TU,CXFile file,CXCursorAndRangeVisitor visitor)481 CXResult clang_findIncludesInFile(CXTranslationUnit TU, CXFile file,
482                              CXCursorAndRangeVisitor visitor) {
483   if (cxtu::isNotUsableTU(TU)) {
484     LOG_BAD_TU(TU);
485     return CXResult_Invalid;
486   }
487 
488   LogRef Log = Logger::make(LLVM_FUNCTION_NAME);
489   if (!file) {
490     if (Log)
491       *Log << "Null file";
492     return CXResult_Invalid;
493   }
494   if (!visitor.visit) {
495     if (Log)
496       *Log << "Null visitor";
497     return CXResult_Invalid;
498   }
499 
500   if (Log)
501     *Log << TU << " @" << static_cast<const FileEntry *>(file);
502 
503   ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
504   if (!CXXUnit)
505     return CXResult_Invalid;
506 
507   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
508 
509   if (findIncludesInFile(TU, static_cast<const FileEntry *>(file), visitor))
510     return CXResult_VisitBreak;
511   return CXResult_Success;
512 }
513 
_visitCursorAndRange(void * context,CXCursor cursor,CXSourceRange range)514 static enum CXVisitorResult _visitCursorAndRange(void *context,
515                                                  CXCursor cursor,
516                                                  CXSourceRange range) {
517   CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
518   return INVOKE_BLOCK2(block, cursor, range);
519 }
520 
clang_findReferencesInFileWithBlock(CXCursor cursor,CXFile file,CXCursorAndRangeVisitorBlock block)521 CXResult clang_findReferencesInFileWithBlock(CXCursor cursor,
522                                              CXFile file,
523                                            CXCursorAndRangeVisitorBlock block) {
524   CXCursorAndRangeVisitor visitor = { block,
525                                       block ? _visitCursorAndRange : nullptr };
526   return clang_findReferencesInFile(cursor, file, visitor);
527 }
528 
clang_findIncludesInFileWithBlock(CXTranslationUnit TU,CXFile file,CXCursorAndRangeVisitorBlock block)529 CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU,
530                                            CXFile file,
531                                            CXCursorAndRangeVisitorBlock block) {
532   CXCursorAndRangeVisitor visitor = { block,
533                                       block ? _visitCursorAndRange : nullptr };
534   return clang_findIncludesInFile(TU, file, visitor);
535 }
536 
537 } // end: extern "C"
538 
539