Home | History | Annotate | Download | only in libclang
      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 
     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 
     56   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 
     64   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.
     79   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 
     97   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:
    112   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.
    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 
    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 
    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 
    253   FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
    254                             const IdentifierInfo *Macro,
    255                             CXCursorAndRangeVisitor visitor)
    256     : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
    257 
    258   ASTContext &getASTContext() const {
    259     return Unit.getASTContext();
    260   }
    261 };
    262 
    263 } // anonymous namespace
    264 
    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 
    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 
    346   FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File,
    347                           CXCursorAndRangeVisitor visitor)
    348     : Unit(Unit), File(File), visitor(visitor) { }
    349 
    350   ASTContext &getASTContext() const {
    351     return Unit.getASTContext();
    352   }
    353 
    354   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 
    375   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 
    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 
    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 
    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 
    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 
    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 
    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