xref: /llvm-project/clang/tools/libclang/CIndexHigh.cpp (revision fa5788ff8dc10f36e0947757e335cd180a1a63c9)
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 "CXFile.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__anone9f3382d0111::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__anone9f3382d0111::FindFileIdRefVisitData64   ASTContext &getASTContext() const {
65     return cxtu::getASTUnit(TU)->getASTContext();
66   }
67 
68   /// 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__anone9f3382d0111::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__anone9f3382d0111::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__anone9f3382d0111::FindFileIdRefVisitData112   bool isOverriddingMethod(const Decl *D) const {
113     if (llvm::is_contained(TopMethods, D))
114       return true;
115 
116     TopMethodsTy methods;
117     getTopOverriddenMethods(TU, D, methods);
118     for (TopMethodsTy::iterator
119            I = methods.begin(), E = methods.end(); I != E; ++I) {
120       if (llvm::is_contained(TopMethods, *I))
121         return true;
122     }
123 
124     return false;
125   }
126 };
127 
128 } // end anonymous namespace.
129 
130 /// For a macro \arg Loc, returns the file spelling location and sets
131 /// to \arg isMacroArg whether the spelling resides inside a macro definition or
132 /// a macro argument.
getFileSpellingLoc(SourceManager & SM,SourceLocation Loc,bool & isMacroArg)133 static SourceLocation getFileSpellingLoc(SourceManager &SM,
134                                          SourceLocation Loc,
135                                          bool &isMacroArg) {
136   assert(Loc.isMacroID());
137   SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
138   if (SpellLoc.isMacroID())
139     return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
140 
141   isMacroArg = SM.isMacroArgExpansion(Loc);
142   return SpellLoc;
143 }
144 
findFileIdRefVisit(CXCursor cursor,CXCursor parent,CXClientData client_data)145 static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
146                                                   CXCursor parent,
147                                                   CXClientData client_data) {
148   CXCursor declCursor = clang_getCursorReferenced(cursor);
149   if (!clang_isDeclaration(declCursor.kind))
150     return CXChildVisit_Recurse;
151 
152   const Decl *D = cxcursor::getCursorDecl(declCursor);
153   if (!D)
154     return CXChildVisit_Continue;
155 
156   FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
157   if (data->isHit(D)) {
158     cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
159 
160     // We are looking for identifiers to highlight so for objc methods (and
161     // not a parameter) we can only highlight the selector identifiers.
162     if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
163          cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
164          cxcursor::getSelectorIdentifierIndex(cursor) == -1)
165       return CXChildVisit_Recurse;
166 
167     if (clang_isExpression(cursor.kind)) {
168       if (cursor.kind == CXCursor_DeclRefExpr ||
169           cursor.kind == CXCursor_MemberRefExpr) {
170         // continue..
171 
172       } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
173                  cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
174         // continue..
175 
176       } else
177         return CXChildVisit_Recurse;
178     }
179 
180     SourceLocation
181       Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
182     SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
183     if (SelIdLoc.isValid())
184       Loc = SelIdLoc;
185 
186     ASTContext &Ctx = data->getASTContext();
187     SourceManager &SM = Ctx.getSourceManager();
188     bool isInMacroDef = false;
189     if (Loc.isMacroID()) {
190       bool isMacroArg;
191       Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
192       isInMacroDef = !isMacroArg;
193     }
194 
195     // We are looking for identifiers in a specific file.
196     std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
197     if (LocInfo.first != data->FID)
198       return CXChildVisit_Recurse;
199 
200     if (isInMacroDef) {
201       // FIXME: For a macro definition make sure that all expansions
202       // of it expand to the same reference before allowing to point to it.
203       return CXChildVisit_Recurse;
204     }
205 
206     if (data->visitor.visit(data->visitor.context, cursor,
207                         cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
208       return CXChildVisit_Break;
209   }
210   return CXChildVisit_Recurse;
211 }
212 
findIdRefsInFile(CXTranslationUnit TU,CXCursor declCursor,const FileEntry * File,CXCursorAndRangeVisitor Visitor)213 static bool findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
214                              const FileEntry *File,
215                              CXCursorAndRangeVisitor Visitor) {
216   assert(clang_isDeclaration(declCursor.kind));
217   SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
218 
219   FileID FID = SM.translateFile(File);
220   const Decl *Dcl = cxcursor::getCursorDecl(declCursor);
221   if (!Dcl)
222     return false;
223 
224   FindFileIdRefVisitData data(TU, FID, Dcl,
225                               cxcursor::getSelectorIdentifierIndex(declCursor),
226                               Visitor);
227 
228   if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
229     return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
230                                findFileIdRefVisit, &data);
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   return 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__anone9f3382d0211::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__anone9f3382d0211::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 = nullptr;
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   if (data->visitor.visit(data->visitor.context, cursor,
302                         cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
303     return CXChildVisit_Break;
304   return CXChildVisit_Continue;
305 }
306 
findMacroRefsInFile(CXTranslationUnit TU,CXCursor Cursor,const FileEntry * File,CXCursorAndRangeVisitor Visitor)307 static bool findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
308                                 const FileEntry *File,
309                                 CXCursorAndRangeVisitor Visitor) {
310   if (Cursor.kind != CXCursor_MacroDefinition &&
311       Cursor.kind != CXCursor_MacroExpansion)
312     return false;
313 
314   ASTUnit *Unit = cxtu::getASTUnit(TU);
315   SourceManager &SM = Unit->getSourceManager();
316 
317   FileID FID = SM.translateFile(File);
318   const IdentifierInfo *Macro = nullptr;
319   if (Cursor.kind == CXCursor_MacroDefinition)
320     Macro = getCursorMacroDefinition(Cursor)->getName();
321   else
322     Macro = getCursorMacroExpansion(Cursor).getName();
323   if (!Macro)
324     return false;
325 
326   FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
327 
328   SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
329   CursorVisitor FindMacroRefsVisitor(TU,
330                                   findFileMacroRefVisit, &data,
331                                   /*VisitPreprocessorLast=*/false,
332                                   /*VisitIncludedEntities=*/false,
333                                   Range);
334   return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
335 }
336 
337 namespace {
338 
339 struct FindFileIncludesVisitor {
340   ASTUnit &Unit;
341   const FileEntry *File;
342   CXCursorAndRangeVisitor visitor;
343 
FindFileIncludesVisitor__anone9f3382d0311::FindFileIncludesVisitor344   FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File,
345                           CXCursorAndRangeVisitor visitor)
346     : Unit(Unit), File(File), visitor(visitor) { }
347 
getASTContext__anone9f3382d0311::FindFileIncludesVisitor348   ASTContext &getASTContext() const {
349     return Unit.getASTContext();
350   }
351 
visit__anone9f3382d0311::FindFileIncludesVisitor352   enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) {
353     if (cursor.kind != CXCursor_InclusionDirective)
354       return CXChildVisit_Continue;
355 
356     SourceLocation
357       Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
358 
359     ASTContext &Ctx = getASTContext();
360     SourceManager &SM = Ctx.getSourceManager();
361 
362     // We are looking for includes in a specific file.
363     std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
364     if (SM.getFileEntryForID(LocInfo.first) != File)
365       return CXChildVisit_Continue;
366 
367     if (visitor.visit(visitor.context, cursor,
368                       cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
369       return CXChildVisit_Break;
370     return CXChildVisit_Continue;
371   }
372 
visit__anone9f3382d0311::FindFileIncludesVisitor373   static enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent,
374                                        CXClientData client_data) {
375     return static_cast<FindFileIncludesVisitor*>(client_data)->
376                                                           visit(cursor, parent);
377   }
378 };
379 
380 } // anonymous namespace
381 
findIncludesInFile(CXTranslationUnit TU,const FileEntry * File,CXCursorAndRangeVisitor Visitor)382 static bool findIncludesInFile(CXTranslationUnit TU, const FileEntry *File,
383                                CXCursorAndRangeVisitor Visitor) {
384   assert(TU && File && Visitor.visit);
385 
386   ASTUnit *Unit = cxtu::getASTUnit(TU);
387   SourceManager &SM = Unit->getSourceManager();
388 
389   FileID FID = SM.translateFile(File);
390 
391   FindFileIncludesVisitor IncludesVisitor(*Unit, File, Visitor);
392 
393   SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
394   CursorVisitor InclusionCursorsVisitor(TU,
395                                         FindFileIncludesVisitor::visit,
396                                         &IncludesVisitor,
397                                         /*VisitPreprocessorLast=*/false,
398                                         /*VisitIncludedEntities=*/false,
399                                         Range);
400   return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion();
401 }
402 
403 
404 //===----------------------------------------------------------------------===//
405 // libclang public APIs.
406 //===----------------------------------------------------------------------===//
407 
408 extern "C" {
409 
clang_findReferencesInFile(CXCursor cursor,CXFile file,CXCursorAndRangeVisitor visitor)410 CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file,
411                                     CXCursorAndRangeVisitor visitor) {
412   LogRef Log = Logger::make(__func__);
413 
414   if (clang_Cursor_isNull(cursor)) {
415     if (Log)
416       *Log << "Null cursor";
417     return CXResult_Invalid;
418   }
419   if (cursor.kind == CXCursor_NoDeclFound) {
420     if (Log)
421       *Log << "Got CXCursor_NoDeclFound";
422     return CXResult_Invalid;
423   }
424   if (!file) {
425     if (Log)
426       *Log << "Null file";
427     return CXResult_Invalid;
428   }
429   if (!visitor.visit) {
430     if (Log)
431       *Log << "Null visitor";
432     return CXResult_Invalid;
433   }
434 
435   if (Log)
436     *Log << cursor << " @" << *cxfile::getFileEntryRef(file);
437 
438   ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
439   if (!CXXUnit)
440     return CXResult_Invalid;
441 
442   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
443 
444   if (cursor.kind == CXCursor_MacroDefinition ||
445       cursor.kind == CXCursor_MacroExpansion) {
446     if (findMacroRefsInFile(cxcursor::getCursorTU(cursor),
447                             cursor,
448                             *cxfile::getFileEntryRef(file),
449                             visitor))
450       return CXResult_VisitBreak;
451     return CXResult_Success;
452   }
453 
454   // We are interested in semantics of identifiers so for C++ constructor exprs
455   // prefer type references, e.g.:
456   //
457   //  return MyStruct();
458   //
459   // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
460   // we are actually interested in the type declaration.
461   cursor = cxcursor::getTypeRefCursor(cursor);
462 
463   CXCursor refCursor = clang_getCursorReferenced(cursor);
464 
465   if (!clang_isDeclaration(refCursor.kind)) {
466     if (Log)
467       *Log << "cursor is not referencing a declaration";
468     return CXResult_Invalid;
469   }
470 
471   if (findIdRefsInFile(cxcursor::getCursorTU(cursor),
472                        refCursor,
473                        *cxfile::getFileEntryRef(file),
474                        visitor))
475     return CXResult_VisitBreak;
476   return CXResult_Success;
477 }
478 
clang_findIncludesInFile(CXTranslationUnit TU,CXFile file,CXCursorAndRangeVisitor visitor)479 CXResult clang_findIncludesInFile(CXTranslationUnit TU, CXFile file,
480                              CXCursorAndRangeVisitor visitor) {
481   if (cxtu::isNotUsableTU(TU)) {
482     LOG_BAD_TU(TU);
483     return CXResult_Invalid;
484   }
485 
486   LogRef Log = Logger::make(__func__);
487   if (!file) {
488     if (Log)
489       *Log << "Null file";
490     return CXResult_Invalid;
491   }
492   if (!visitor.visit) {
493     if (Log)
494       *Log << "Null visitor";
495     return CXResult_Invalid;
496   }
497 
498   if (Log)
499     *Log << TU << " @" << *cxfile::getFileEntryRef(file);
500 
501   ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
502   if (!CXXUnit)
503     return CXResult_Invalid;
504 
505   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
506 
507   if (findIncludesInFile(TU, *cxfile::getFileEntryRef(file), visitor))
508     return CXResult_VisitBreak;
509   return CXResult_Success;
510 }
511 
_visitCursorAndRange(void * context,CXCursor cursor,CXSourceRange range)512 static enum CXVisitorResult _visitCursorAndRange(void *context,
513                                                  CXCursor cursor,
514                                                  CXSourceRange range) {
515   CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
516   return INVOKE_BLOCK2(block, cursor, range);
517 }
518 
clang_findReferencesInFileWithBlock(CXCursor cursor,CXFile file,CXCursorAndRangeVisitorBlock block)519 CXResult clang_findReferencesInFileWithBlock(CXCursor cursor,
520                                              CXFile file,
521                                            CXCursorAndRangeVisitorBlock block) {
522   CXCursorAndRangeVisitor visitor = { block,
523                                       block ? _visitCursorAndRange : nullptr };
524   return clang_findReferencesInFile(cursor, file, visitor);
525 }
526 
clang_findIncludesInFileWithBlock(CXTranslationUnit TU,CXFile file,CXCursorAndRangeVisitorBlock block)527 CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU,
528                                            CXFile file,
529                                            CXCursorAndRangeVisitorBlock block) {
530   CXCursorAndRangeVisitor visitor = { block,
531                                       block ? _visitCursorAndRange : nullptr };
532   return clang_findIncludesInFile(TU, file, visitor);
533 }
534 
535 } // end: extern "C"
536