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 "CXCursor.h"
     12 #include "CXSourceLocation.h"
     13 #include "CXTranslationUnit.h"
     14 
     15 #include "clang/Frontend/ASTUnit.h"
     16 #include "clang/AST/DeclObjC.h"
     17 
     18 using namespace clang;
     19 using namespace cxcursor;
     20 
     21 static void getTopOverriddenMethods(CXTranslationUnit TU,
     22                                     Decl *D,
     23                                     SmallVectorImpl<Decl *> &Methods) {
     24   if (!D)
     25     return;
     26   if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
     27     return;
     28 
     29   SmallVector<CXCursor, 8> Overridden;
     30   cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
     31 
     32   if (Overridden.empty()) {
     33     Methods.push_back(D->getCanonicalDecl());
     34     return;
     35   }
     36 
     37   for (SmallVector<CXCursor, 8>::iterator
     38          I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
     39     getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
     40 }
     41 
     42 namespace {
     43 
     44 struct FindFileIdRefVisitData {
     45   CXTranslationUnit TU;
     46   FileID FID;
     47   Decl *Dcl;
     48   int SelectorIdIdx;
     49   CXCursorAndRangeVisitor visitor;
     50 
     51   typedef SmallVector<Decl *, 8> TopMethodsTy;
     52   TopMethodsTy TopMethods;
     53 
     54   FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
     55                          Decl *D, int selectorIdIdx,
     56                          CXCursorAndRangeVisitor visitor)
     57     : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
     58     Dcl = getCanonical(D);
     59     getTopOverriddenMethods(TU, Dcl, TopMethods);
     60   }
     61 
     62   ASTContext &getASTContext() const {
     63     return static_cast<ASTUnit *>(TU->TUData)->getASTContext();
     64   }
     65 
     66   /// \brief We are looking to find all semantically relevant identifiers,
     67   /// so the definition of "canonical" here is different than in the AST, e.g.
     68   ///
     69   /// \code
     70   ///   class C {
     71   ///     C() {}
     72   ///   };
     73   /// \endcode
     74   ///
     75   /// we consider the canonical decl of the constructor decl to be the class
     76   /// itself, so both 'C' can be highlighted.
     77   Decl *getCanonical(Decl *D) const {
     78     if (!D)
     79       return 0;
     80 
     81     D = D->getCanonicalDecl();
     82 
     83     if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
     84       if (ImplD->getClassInterface())
     85         return getCanonical(ImplD->getClassInterface());
     86 
     87     } else if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D)) {
     88       return getCanonical(CXXCtorD->getParent());
     89     }
     90 
     91     return D;
     92   }
     93 
     94   bool isHit(Decl *D) const {
     95     if (!D)
     96       return false;
     97 
     98     D = getCanonical(D);
     99     if (D == Dcl)
    100       return true;
    101 
    102     if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
    103       return isOverriddingMethod(D);
    104 
    105     return false;
    106   }
    107 
    108 private:
    109   bool isOverriddingMethod(Decl *D) const {
    110     if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
    111           TopMethods.end())
    112       return true;
    113 
    114     TopMethodsTy methods;
    115     getTopOverriddenMethods(TU, D, methods);
    116     for (TopMethodsTy::iterator
    117            I = methods.begin(), E = methods.end(); I != E; ++I) {
    118       if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
    119             TopMethods.end())
    120         return true;
    121     }
    122 
    123     return false;
    124   }
    125 };
    126 
    127 } // end anonymous namespace.
    128 
    129 /// \brief 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   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     data->visitor.visit(data->visitor.context, cursor,
    206                         cxloc::translateSourceRange(Ctx, Loc));
    207   }
    208   return CXChildVisit_Recurse;
    209 }
    210 
    211 static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
    212                            const FileEntry *File,
    213                            CXCursorAndRangeVisitor Visitor) {
    214   assert(clang_isDeclaration(declCursor.kind));
    215   ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
    216   SourceManager &SM = Unit->getSourceManager();
    217 
    218   FileID FID = SM.translateFile(File);
    219   Decl *Dcl = cxcursor::getCursorDecl(declCursor);
    220   if (!Dcl)
    221     return;
    222 
    223   FindFileIdRefVisitData data(TU, FID, Dcl,
    224                               cxcursor::getSelectorIdentifierIndex(declCursor),
    225                               Visitor);
    226 
    227   if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
    228     clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
    229                         findFileIdRefVisit, &data);
    230     return;
    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   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 
    251   FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
    252                             const IdentifierInfo *Macro,
    253                             CXCursorAndRangeVisitor visitor)
    254     : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
    255 
    256   ASTContext &getASTContext() const {
    257     return Unit.getASTContext();
    258   }
    259 };
    260 
    261 } // anonymous namespace
    262 
    263 static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
    264                                                      CXCursor parent,
    265                                                      CXClientData client_data) {
    266   const IdentifierInfo *Macro = 0;
    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   data->visitor.visit(data->visitor.context, cursor,
    302                       cxloc::translateSourceRange(Ctx, Loc));
    303   return CXChildVisit_Continue;
    304 }
    305 
    306 static void 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;
    312 
    313   ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
    314   SourceManager &SM = Unit->getSourceManager();
    315 
    316   FileID FID = SM.translateFile(File);
    317   const IdentifierInfo *Macro = 0;
    318   if (Cursor.kind == CXCursor_MacroDefinition)
    319     Macro = getCursorMacroDefinition(Cursor)->getName();
    320   else
    321     Macro = getCursorMacroExpansion(Cursor)->getName();
    322   if (!Macro)
    323     return;
    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   FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
    334 }
    335 
    336 
    337 //===----------------------------------------------------------------------===//
    338 // libclang public APIs.
    339 //===----------------------------------------------------------------------===//
    340 
    341 extern "C" {
    342 
    343 void clang_findReferencesInFile(CXCursor cursor, CXFile file,
    344                                 CXCursorAndRangeVisitor visitor) {
    345   bool Logging = ::getenv("LIBCLANG_LOGGING");
    346 
    347   if (clang_Cursor_isNull(cursor)) {
    348     if (Logging)
    349       llvm::errs() << "clang_findReferencesInFile: Null cursor\n";
    350     return;
    351   }
    352   if (cursor.kind == CXCursor_NoDeclFound) {
    353     if (Logging)
    354       llvm::errs() << "clang_findReferencesInFile: Got CXCursor_NoDeclFound\n";
    355     return;
    356   }
    357   if (!file) {
    358     if (Logging)
    359       llvm::errs() << "clang_findReferencesInFile: Null file\n";
    360     return;
    361   }
    362   if (!visitor.visit) {
    363     if (Logging)
    364       llvm::errs() << "clang_findReferencesInFile: Null visitor\n";
    365     return;
    366   }
    367 
    368   ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
    369   if (!CXXUnit)
    370     return;
    371 
    372   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
    373 
    374   if (cursor.kind == CXCursor_MacroDefinition ||
    375       cursor.kind == CXCursor_MacroExpansion) {
    376     findMacroRefsInFile(cxcursor::getCursorTU(cursor),
    377                         cursor,
    378                         static_cast<const FileEntry *>(file),
    379                         visitor);
    380     return;
    381   }
    382 
    383   // We are interested in semantics of identifiers so for C++ constructor exprs
    384   // prefer type references, e.g.:
    385   //
    386   //  return MyStruct();
    387   //
    388   // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
    389   // we are actually interested in the type declaration.
    390   cursor = cxcursor::getTypeRefCursor(cursor);
    391 
    392   CXCursor refCursor = clang_getCursorReferenced(cursor);
    393 
    394   if (!clang_isDeclaration(refCursor.kind)) {
    395     if (Logging)
    396       llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a "
    397                       "declaration\n";
    398     return;
    399   }
    400 
    401   findIdRefsInFile(cxcursor::getCursorTU(cursor),
    402                    refCursor,
    403                    static_cast<const FileEntry *>(file),
    404                    visitor);
    405 }
    406 
    407 static enum CXVisitorResult _visitCursorAndRange(void *context,
    408                                                  CXCursor cursor,
    409                                                  CXSourceRange range) {
    410   CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
    411   return INVOKE_BLOCK2(block, cursor, range);
    412 }
    413 
    414 void clang_findReferencesInFileWithBlock(CXCursor cursor,
    415                                          CXFile file,
    416                                          CXCursorAndRangeVisitorBlock block) {
    417   CXCursorAndRangeVisitor visitor = { block,
    418                                       block ? _visitCursorAndRange : 0 };
    419   return clang_findReferencesInFile(cursor, file, visitor);
    420 }
    421 
    422 } // end: extern "C"
    423 
    424