Home | History | Annotate | Line # | Download | only in libclang
      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 
     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 
     55   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 
     63   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.
     78   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 
     96   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:
    111   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.
    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 
    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 
    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 
    250   FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
    251                             const IdentifierInfo *Macro,
    252                             CXCursorAndRangeVisitor visitor)
    253     : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
    254 
    255   ASTContext &getASTContext() const {
    256     return Unit.getASTContext();
    257   }
    258 };
    259 
    260 } // anonymous namespace
    261 
    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 
    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 
    343   FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File,
    344                           CXCursorAndRangeVisitor visitor)
    345     : Unit(Unit), File(File), visitor(visitor) { }
    346 
    347   ASTContext &getASTContext() const {
    348     return Unit.getASTContext();
    349   }
    350 
    351   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 
    372   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 
    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 
    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 
    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 
    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 
    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 
    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