1 //===- CIndexHigh.cpp - Higher level API functions ------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "CursorVisitor.h"
10 #include "CLog.h"
11 #include "CXCursor.h"
12 #include "CXSourceLocation.h"
13 #include "CXTranslationUnit.h"
14 #include "clang/AST/DeclObjC.h"
15 #include "clang/Frontend/ASTUnit.h"
16 #include "llvm/Support/Compiler.h"
17 
18 using namespace clang;
19 using namespace cxcursor;
20 using namespace cxindex;
21 
getTopOverriddenMethods(CXTranslationUnit TU,const Decl * D,SmallVectorImpl<const Decl * > & Methods)22 static void getTopOverriddenMethods(CXTranslationUnit TU,
23                                     const Decl *D,
24                                     SmallVectorImpl<const Decl *> &Methods) {
25   if (!D)
26     return;
27   if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
28     return;
29 
30   SmallVector<CXCursor, 8> Overridden;
31   cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
32 
33   if (Overridden.empty()) {
34     Methods.push_back(D->getCanonicalDecl());
35     return;
36   }
37 
38   for (SmallVectorImpl<CXCursor>::iterator
39          I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
40     getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
41 }
42 
43 namespace {
44 
45 struct FindFileIdRefVisitData {
46   CXTranslationUnit TU;
47   FileID FID;
48   const Decl *Dcl;
49   int SelectorIdIdx;
50   CXCursorAndRangeVisitor visitor;
51 
52   typedef SmallVector<const Decl *, 8> TopMethodsTy;
53   TopMethodsTy TopMethods;
54 
FindFileIdRefVisitData__anonaed0e45b0111::FindFileIdRefVisitData55   FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
56                          const Decl *D, int selectorIdIdx,
57                          CXCursorAndRangeVisitor visitor)
58     : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
59     Dcl = getCanonical(D);
60     getTopOverriddenMethods(TU, Dcl, TopMethods);
61   }
62 
getASTContext__anonaed0e45b0111::FindFileIdRefVisitData63   ASTContext &getASTContext() const {
64     return cxtu::getASTUnit(TU)->getASTContext();
65   }
66 
67   /// We are looking to find all semantically relevant identifiers,
68   /// so the definition of "canonical" here is different than in the AST, e.g.
69   ///
70   /// \code
71   ///   class C {
72   ///     C() {}
73   ///   };
74   /// \endcode
75   ///
76   /// we consider the canonical decl of the constructor decl to be the class
77   /// itself, so both 'C' can be highlighted.
getCanonical__anonaed0e45b0111::FindFileIdRefVisitData78   const Decl *getCanonical(const Decl *D) const {
79     if (!D)
80       return nullptr;
81 
82     D = D->getCanonicalDecl();
83 
84     if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
85       if (ImplD->getClassInterface())
86         return getCanonical(ImplD->getClassInterface());
87 
88     } else if (const CXXConstructorDecl *CXXCtorD =
89                    dyn_cast<CXXConstructorDecl>(D)) {
90       return getCanonical(CXXCtorD->getParent());
91     }
92 
93     return D;
94   }
95 
isHit__anonaed0e45b0111::FindFileIdRefVisitData96   bool isHit(const Decl *D) const {
97     if (!D)
98       return false;
99 
100     D = getCanonical(D);
101     if (D == Dcl)
102       return true;
103 
104     if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
105       return isOverriddingMethod(D);
106 
107     return false;
108   }
109 
110 private:
isOverriddingMethod__anonaed0e45b0111::FindFileIdRefVisitData111   bool isOverriddingMethod(const Decl *D) const {
112     if (llvm::find(TopMethods, D) != TopMethods.end())
113       return true;
114 
115     TopMethodsTy methods;
116     getTopOverriddenMethods(TU, D, methods);
117     for (TopMethodsTy::iterator
118            I = methods.begin(), E = methods.end(); I != E; ++I) {
119       if (llvm::find(TopMethods, *I) != TopMethods.end())
120         return true;
121     }
122 
123     return false;
124   }
125 };
126 
127 } // end anonymous namespace.
128 
129 /// 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   const 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     if (data->visitor.visit(data->visitor.context, cursor,
206                         cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
207       return CXChildVisit_Break;
208   }
209   return CXChildVisit_Recurse;
210 }
211 
findIdRefsInFile(CXTranslationUnit TU,CXCursor declCursor,const FileEntry * File,CXCursorAndRangeVisitor Visitor)212 static bool findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
213                              const FileEntry *File,
214                              CXCursorAndRangeVisitor Visitor) {
215   assert(clang_isDeclaration(declCursor.kind));
216   SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
217 
218   FileID FID = SM.translateFile(File);
219   const Decl *Dcl = cxcursor::getCursorDecl(declCursor);
220   if (!Dcl)
221     return false;
222 
223   FindFileIdRefVisitData data(TU, FID, Dcl,
224                               cxcursor::getSelectorIdentifierIndex(declCursor),
225                               Visitor);
226 
227   if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
228     return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
229                                findFileIdRefVisit, &data);
230   }
231 
232   SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
233   CursorVisitor FindIdRefsVisitor(TU,
234                                   findFileIdRefVisit, &data,
235                                   /*VisitPreprocessorLast=*/true,
236                                   /*VisitIncludedEntities=*/false,
237                                   Range,
238                                   /*VisitDeclsOnly=*/true);
239   return FindIdRefsVisitor.visitFileRegion();
240 }
241 
242 namespace {
243 
244 struct FindFileMacroRefVisitData {
245   ASTUnit &Unit;
246   const FileEntry *File;
247   const IdentifierInfo *Macro;
248   CXCursorAndRangeVisitor visitor;
249 
FindFileMacroRefVisitData__anonaed0e45b0211::FindFileMacroRefVisitData250   FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
251                             const IdentifierInfo *Macro,
252                             CXCursorAndRangeVisitor visitor)
253     : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
254 
getASTContext__anonaed0e45b0211::FindFileMacroRefVisitData255   ASTContext &getASTContext() const {
256     return Unit.getASTContext();
257   }
258 };
259 
260 } // anonymous namespace
261 
findFileMacroRefVisit(CXCursor cursor,CXCursor parent,CXClientData client_data)262 static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
263                                                      CXCursor parent,
264                                                      CXClientData client_data) {
265   const IdentifierInfo *Macro = nullptr;
266   if (cursor.kind == CXCursor_MacroDefinition)
267     Macro = getCursorMacroDefinition(cursor)->getName();
268   else if (cursor.kind == CXCursor_MacroExpansion)
269     Macro = getCursorMacroExpansion(cursor).getName();
270   if (!Macro)
271     return CXChildVisit_Continue;
272 
273   FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
274   if (data->Macro != Macro)
275     return CXChildVisit_Continue;
276 
277   SourceLocation
278     Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
279 
280   ASTContext &Ctx = data->getASTContext();
281   SourceManager &SM = Ctx.getSourceManager();
282   bool isInMacroDef = false;
283   if (Loc.isMacroID()) {
284     bool isMacroArg;
285     Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
286     isInMacroDef = !isMacroArg;
287   }
288 
289   // We are looking for identifiers in a specific file.
290   std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
291   if (SM.getFileEntryForID(LocInfo.first) != data->File)
292     return CXChildVisit_Continue;
293 
294   if (isInMacroDef) {
295     // FIXME: For a macro definition make sure that all expansions
296     // of it expand to the same reference before allowing to point to it.
297     return CXChildVisit_Continue;
298   }
299 
300   if (data->visitor.visit(data->visitor.context, cursor,
301                         cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
302     return CXChildVisit_Break;
303   return CXChildVisit_Continue;
304 }
305 
findMacroRefsInFile(CXTranslationUnit TU,CXCursor Cursor,const FileEntry * File,CXCursorAndRangeVisitor Visitor)306 static bool 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 false;
312 
313   ASTUnit *Unit = cxtu::getASTUnit(TU);
314   SourceManager &SM = Unit->getSourceManager();
315 
316   FileID FID = SM.translateFile(File);
317   const IdentifierInfo *Macro = nullptr;
318   if (Cursor.kind == CXCursor_MacroDefinition)
319     Macro = getCursorMacroDefinition(Cursor)->getName();
320   else
321     Macro = getCursorMacroExpansion(Cursor).getName();
322   if (!Macro)
323     return false;
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   return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
334 }
335 
336 namespace {
337 
338 struct FindFileIncludesVisitor {
339   ASTUnit &Unit;
340   const FileEntry *File;
341   CXCursorAndRangeVisitor visitor;
342 
FindFileIncludesVisitor__anonaed0e45b0311::FindFileIncludesVisitor343   FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File,
344                           CXCursorAndRangeVisitor visitor)
345     : Unit(Unit), File(File), visitor(visitor) { }
346 
getASTContext__anonaed0e45b0311::FindFileIncludesVisitor347   ASTContext &getASTContext() const {
348     return Unit.getASTContext();
349   }
350 
visit__anonaed0e45b0311::FindFileIncludesVisitor351   enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) {
352     if (cursor.kind != CXCursor_InclusionDirective)
353       return CXChildVisit_Continue;
354 
355     SourceLocation
356       Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
357 
358     ASTContext &Ctx = getASTContext();
359     SourceManager &SM = Ctx.getSourceManager();
360 
361     // We are looking for includes in a specific file.
362     std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
363     if (SM.getFileEntryForID(LocInfo.first) != File)
364       return CXChildVisit_Continue;
365 
366     if (visitor.visit(visitor.context, cursor,
367                       cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
368       return CXChildVisit_Break;
369     return CXChildVisit_Continue;
370   }
371 
visit__anonaed0e45b0311::FindFileIncludesVisitor372   static enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent,
373                                        CXClientData client_data) {
374     return static_cast<FindFileIncludesVisitor*>(client_data)->
375                                                           visit(cursor, parent);
376   }
377 };
378 
379 } // anonymous namespace
380 
findIncludesInFile(CXTranslationUnit TU,const FileEntry * File,CXCursorAndRangeVisitor Visitor)381 static bool findIncludesInFile(CXTranslationUnit TU, const FileEntry *File,
382                                CXCursorAndRangeVisitor Visitor) {
383   assert(TU && File && Visitor.visit);
384 
385   ASTUnit *Unit = cxtu::getASTUnit(TU);
386   SourceManager &SM = Unit->getSourceManager();
387 
388   FileID FID = SM.translateFile(File);
389 
390   FindFileIncludesVisitor IncludesVisitor(*Unit, File, Visitor);
391 
392   SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
393   CursorVisitor InclusionCursorsVisitor(TU,
394                                         FindFileIncludesVisitor::visit,
395                                         &IncludesVisitor,
396                                         /*VisitPreprocessorLast=*/false,
397                                         /*VisitIncludedEntities=*/false,
398                                         Range);
399   return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion();
400 }
401 
402 
403 //===----------------------------------------------------------------------===//
404 // libclang public APIs.
405 //===----------------------------------------------------------------------===//
406 
407 extern "C" {
408 
clang_findReferencesInFile(CXCursor cursor,CXFile file,CXCursorAndRangeVisitor visitor)409 CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file,
410                                     CXCursorAndRangeVisitor visitor) {
411   LogRef Log = Logger::make(__func__);
412 
413   if (clang_Cursor_isNull(cursor)) {
414     if (Log)
415       *Log << "Null cursor";
416     return CXResult_Invalid;
417   }
418   if (cursor.kind == CXCursor_NoDeclFound) {
419     if (Log)
420       *Log << "Got CXCursor_NoDeclFound";
421     return CXResult_Invalid;
422   }
423   if (!file) {
424     if (Log)
425       *Log << "Null file";
426     return CXResult_Invalid;
427   }
428   if (!visitor.visit) {
429     if (Log)
430       *Log << "Null visitor";
431     return CXResult_Invalid;
432   }
433 
434   if (Log)
435     *Log << cursor << " @" << static_cast<const FileEntry *>(file);
436 
437   ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
438   if (!CXXUnit)
439     return CXResult_Invalid;
440 
441   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
442 
443   if (cursor.kind == CXCursor_MacroDefinition ||
444       cursor.kind == CXCursor_MacroExpansion) {
445     if (findMacroRefsInFile(cxcursor::getCursorTU(cursor),
446                             cursor,
447                             static_cast<const FileEntry *>(file),
448                             visitor))
449       return CXResult_VisitBreak;
450     return CXResult_Success;
451   }
452 
453   // We are interested in semantics of identifiers so for C++ constructor exprs
454   // prefer type references, e.g.:
455   //
456   //  return MyStruct();
457   //
458   // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
459   // we are actually interested in the type declaration.
460   cursor = cxcursor::getTypeRefCursor(cursor);
461 
462   CXCursor refCursor = clang_getCursorReferenced(cursor);
463 
464   if (!clang_isDeclaration(refCursor.kind)) {
465     if (Log)
466       *Log << "cursor is not referencing a declaration";
467     return CXResult_Invalid;
468   }
469 
470   if (findIdRefsInFile(cxcursor::getCursorTU(cursor),
471                        refCursor,
472                        static_cast<const FileEntry *>(file),
473                        visitor))
474     return CXResult_VisitBreak;
475   return CXResult_Success;
476 }
477 
clang_findIncludesInFile(CXTranslationUnit TU,CXFile file,CXCursorAndRangeVisitor visitor)478 CXResult clang_findIncludesInFile(CXTranslationUnit TU, CXFile file,
479                              CXCursorAndRangeVisitor visitor) {
480   if (cxtu::isNotUsableTU(TU)) {
481     LOG_BAD_TU(TU);
482     return CXResult_Invalid;
483   }
484 
485   LogRef Log = Logger::make(__func__);
486   if (!file) {
487     if (Log)
488       *Log << "Null file";
489     return CXResult_Invalid;
490   }
491   if (!visitor.visit) {
492     if (Log)
493       *Log << "Null visitor";
494     return CXResult_Invalid;
495   }
496 
497   if (Log)
498     *Log << TU << " @" << static_cast<const FileEntry *>(file);
499 
500   ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
501   if (!CXXUnit)
502     return CXResult_Invalid;
503 
504   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
505 
506   if (findIncludesInFile(TU, static_cast<const FileEntry *>(file), visitor))
507     return CXResult_VisitBreak;
508   return CXResult_Success;
509 }
510 
_visitCursorAndRange(void * context,CXCursor cursor,CXSourceRange range)511 static enum CXVisitorResult _visitCursorAndRange(void *context,
512                                                  CXCursor cursor,
513                                                  CXSourceRange range) {
514   CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
515   return INVOKE_BLOCK2(block, cursor, range);
516 }
517 
clang_findReferencesInFileWithBlock(CXCursor cursor,CXFile file,CXCursorAndRangeVisitorBlock block)518 CXResult clang_findReferencesInFileWithBlock(CXCursor cursor,
519                                              CXFile file,
520                                            CXCursorAndRangeVisitorBlock block) {
521   CXCursorAndRangeVisitor visitor = { block,
522                                       block ? _visitCursorAndRange : nullptr };
523   return clang_findReferencesInFile(cursor, file, visitor);
524 }
525 
clang_findIncludesInFileWithBlock(CXTranslationUnit TU,CXFile file,CXCursorAndRangeVisitorBlock block)526 CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU,
527                                            CXFile file,
528                                            CXCursorAndRangeVisitorBlock block) {
529   CXCursorAndRangeVisitor visitor = { block,
530                                       block ? _visitCursorAndRange : nullptr };
531   return clang_findIncludesInFile(TU, file, visitor);
532 }
533 
534 } // end: extern "C"
535