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 "Index_Internal.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 
     20 static void getTopOverriddenMethods(CXTranslationUnit TU,
     21                                     Decl *D,
     22                                     SmallVectorImpl<Decl *> &Methods) {
     23   if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
     24     return;
     25 
     26   SmallVector<CXCursor, 8> Overridden;
     27   cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
     28 
     29   if (Overridden.empty()) {
     30     Methods.push_back(D->getCanonicalDecl());
     31     return;
     32   }
     33 
     34   for (SmallVector<CXCursor, 8>::iterator
     35          I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
     36     getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
     37 }
     38 
     39 namespace {
     40 
     41 struct FindFileIdRefVisitData {
     42   CXTranslationUnit TU;
     43   FileID FID;
     44   Decl *Dcl;
     45   int SelectorIdIdx;
     46   CXCursorAndRangeVisitor visitor;
     47 
     48   typedef SmallVector<Decl *, 8> TopMethodsTy;
     49   TopMethodsTy TopMethods;
     50 
     51   FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
     52                          Decl *D, int selectorIdIdx,
     53                          CXCursorAndRangeVisitor visitor)
     54     : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
     55     Dcl = getCanonical(D);
     56     getTopOverriddenMethods(TU, Dcl, TopMethods);
     57   }
     58 
     59   ASTContext &getASTContext() const {
     60     return static_cast<ASTUnit *>(TU->TUData)->getASTContext();
     61   }
     62 
     63   /// \brief We are looking to find all semantically relevant identifiers,
     64   /// so the definition of "canonical" here is different than in the AST, e.g.
     65   ///
     66   /// \code
     67   ///   class C {
     68   ///     C() {}
     69   ///   };
     70   /// \endcode
     71   ///
     72   /// we consider the canonical decl of the constructor decl to be the class
     73   /// itself, so both 'C' can be highlighted.
     74   Decl *getCanonical(Decl *D) const {
     75     D = D->getCanonicalDecl();
     76 
     77     if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D))
     78       return getCanonical(ImplD->getClassInterface());
     79     if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D))
     80       return getCanonical(CXXCtorD->getParent());
     81 
     82     return D;
     83   }
     84 
     85   bool isHit(Decl *D) const {
     86     D = getCanonical(D);
     87     if (D == Dcl)
     88       return true;
     89 
     90     if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
     91       return isOverriddingMethod(D);
     92 
     93     return false;
     94   }
     95 
     96 private:
     97   bool isOverriddingMethod(Decl *D) const {
     98     if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
     99           TopMethods.end())
    100       return true;
    101 
    102     TopMethodsTy methods;
    103     getTopOverriddenMethods(TU, D, methods);
    104     for (TopMethodsTy::iterator
    105            I = methods.begin(), E = methods.end(); I != E; ++I) {
    106       if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
    107             TopMethods.end())
    108         return true;
    109     }
    110 
    111     return false;
    112   }
    113 };
    114 
    115 } // end anonymous namespace.
    116 
    117 /// \brief For a macro \arg Loc, returns the file spelling location and sets
    118 /// to \arg isMacroArg whether the spelling resides inside a macro definition or
    119 /// a macro argument.
    120 static SourceLocation getFileSpellingLoc(SourceManager &SM,
    121                                          SourceLocation Loc,
    122                                          bool &isMacroArg) {
    123   assert(Loc.isMacroID());
    124   SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
    125   if (SpellLoc.isMacroID())
    126     return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
    127 
    128   isMacroArg = SM.isMacroArgExpansion(Loc);
    129   return SpellLoc;
    130 }
    131 
    132 static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
    133                                                   CXCursor parent,
    134                                                   CXClientData client_data) {
    135   CXCursor declCursor = clang_getCursorReferenced(cursor);
    136   if (!clang_isDeclaration(declCursor.kind))
    137     return CXChildVisit_Recurse;
    138 
    139   Decl *D = cxcursor::getCursorDecl(declCursor);
    140   FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
    141   if (data->isHit(D)) {
    142     cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
    143 
    144     // We are looking for identifiers to highlight so for objc methods (and
    145     // not a parameter) we can only highlight the selector identifiers.
    146     if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
    147          cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
    148          cxcursor::getSelectorIdentifierIndex(cursor) == -1)
    149       return CXChildVisit_Recurse;
    150 
    151     if (clang_isExpression(cursor.kind)) {
    152       if (cursor.kind == CXCursor_DeclRefExpr ||
    153           cursor.kind == CXCursor_MemberRefExpr) {
    154         // continue..
    155 
    156       } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
    157                  cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
    158         // continue..
    159 
    160       } else
    161         return CXChildVisit_Recurse;
    162     }
    163 
    164     SourceLocation
    165       Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
    166     SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
    167     if (SelIdLoc.isValid())
    168       Loc = SelIdLoc;
    169 
    170     SourceManager &SM = data->getASTContext().getSourceManager();
    171     bool isInMacroDef = false;
    172     if (Loc.isMacroID()) {
    173       bool isMacroArg;
    174       Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
    175       isInMacroDef = !isMacroArg;
    176     }
    177 
    178     // We are looking for identifiers in a specific file.
    179     std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
    180     if (LocInfo.first != data->FID)
    181       return CXChildVisit_Recurse;
    182 
    183     if (isInMacroDef) {
    184       // FIXME: For a macro definition make sure that all expansions
    185       // of it expand to the same reference before allowing to point to it.
    186       Loc = SourceLocation();
    187     }
    188 
    189     data->visitor.visit(data->visitor.context, cursor,
    190                         cxloc::translateSourceRange(D->getASTContext(), Loc));
    191   }
    192   return CXChildVisit_Recurse;
    193 }
    194 
    195 static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
    196                            const FileEntry *File,
    197                            CXCursorAndRangeVisitor Visitor) {
    198   assert(clang_isDeclaration(declCursor.kind));
    199   ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
    200   ASTContext &Ctx = Unit->getASTContext();
    201   SourceManager &SM = Unit->getSourceManager();
    202 
    203   FileID FID = SM.translateFile(File);
    204   Decl *Dcl = cxcursor::getCursorDecl(declCursor);
    205   FindFileIdRefVisitData data(TU, FID, Dcl,
    206                               cxcursor::getSelectorIdentifierIndex(declCursor),
    207                               Visitor);
    208 
    209   if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
    210     clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
    211                         findFileIdRefVisit, &data);
    212     return;
    213   }
    214 
    215   if (FID == SM.getMainFileID() && !Unit->isMainFileAST()) {
    216     SourceLocation FileLoc = SM.getLocForStartOfFile(FID);
    217     TranslationUnitDecl *TUD = Ctx.getTranslationUnitDecl();
    218     CXCursor TUCursor = clang_getTranslationUnitCursor(TU);
    219     for (DeclContext::decl_iterator
    220            I = TUD->noload_decls_begin(), E = TUD->noload_decls_end();
    221          I != E; ++I) {
    222       Decl *D = *I;
    223 
    224       SourceRange R = D->getSourceRange();
    225       if (R.isInvalid())
    226         continue;
    227       if (SM.isBeforeInTranslationUnit(R.getEnd(), FileLoc))
    228         continue;
    229 
    230       if (TagDecl *TD = dyn_cast<TagDecl>(D))
    231         if (!TD->isFreeStanding())
    232           continue;
    233 
    234       CXCursor CurCursor = cxcursor::MakeCXCursor(D, TU);
    235       findFileIdRefVisit(CurCursor, TUCursor, &data);
    236       clang_visitChildren(CurCursor, findFileIdRefVisit, &data);
    237     }
    238     return;
    239   }
    240 
    241   clang_visitChildren(clang_getTranslationUnitCursor(TU),
    242                       findFileIdRefVisit, &data);
    243 }
    244 
    245 
    246 //===----------------------------------------------------------------------===//
    247 // libclang public APIs.
    248 //===----------------------------------------------------------------------===//
    249 
    250 extern "C" {
    251 
    252 void clang_findReferencesInFile(CXCursor cursor, CXFile file,
    253                                 CXCursorAndRangeVisitor visitor) {
    254   bool Logging = ::getenv("LIBCLANG_LOGGING");
    255 
    256   if (clang_Cursor_isNull(cursor)) {
    257     if (Logging)
    258       llvm::errs() << "clang_findReferencesInFile: Null cursor\n";
    259     return;
    260   }
    261   if (!file) {
    262     if (Logging)
    263       llvm::errs() << "clang_findReferencesInFile: Null file\n";
    264     return;
    265   }
    266   if (!visitor.visit) {
    267     if (Logging)
    268       llvm::errs() << "clang_findReferencesInFile: Null visitor\n";
    269     return;
    270   }
    271 
    272   // We are interested in semantics of identifiers so for C++ constructor exprs
    273   // prefer type references, e.g.:
    274   //
    275   //  return MyStruct();
    276   //
    277   // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
    278   // we are actually interested in the type declaration.
    279   cursor = cxcursor::getTypeRefCursor(cursor);
    280 
    281   CXCursor refCursor = clang_getCursorReferenced(cursor);
    282 
    283   if (!clang_isDeclaration(refCursor.kind)) {
    284     if (Logging)
    285       llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a "
    286                       "declaration\n";
    287     return;
    288   }
    289 
    290   ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
    291   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
    292 
    293   findIdRefsInFile(cxcursor::getCursorTU(cursor),
    294                    refCursor,
    295                    static_cast<const FileEntry *>(file),
    296                    visitor);
    297 }
    298 
    299 static enum CXVisitorResult _visitCursorAndRange(void *context,
    300                                                  CXCursor cursor,
    301                                                  CXSourceRange range) {
    302   CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
    303   return INVOKE_BLOCK2(block, cursor, range);
    304 }
    305 
    306 void clang_findReferencesInFileWithBlock(CXCursor cursor,
    307                                          CXFile file,
    308                                          CXCursorAndRangeVisitorBlock block) {
    309   CXCursorAndRangeVisitor visitor = { block,
    310                                       block ? _visitCursorAndRange : 0 };
    311   return clang_findReferencesInFile(cursor, file, visitor);
    312 }
    313 
    314 } // end: extern "C"
    315 
    316