brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.2 KiB · 1f6d3b9 Raw
536 lines · cpp
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-exception6//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 19using namespace clang;20using namespace cxcursor;21using namespace cxindex;22 23static 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>::iterator40         I = Overridden.begin(), E = Overridden.end(); I != E; ++I)41    getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);42}43 44namespace {45 46struct 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  /// 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  /// \code72  ///   class C {73  ///     C() {}74  ///   };75  /// \endcode76  ///77  /// we consider the canonical decl of the constructor decl to be the class78  /// 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 111private:112  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::iterator119           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 sets131/// to \arg isMacroArg whether the spelling resides inside a macro definition or132/// a macro argument.133static 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 145static 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 (and161    // 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      } else177        return CXChildVisit_Recurse;178    }179 180    SourceLocation181      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    FileIDAndOffset 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 expansions202      // 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 213static 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 243namespace {244 245struct 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 namespace262 263static 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  SourceLocation279    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  FileIDAndOffset 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 expansions297    // 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 307static 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  else322    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 337namespace {338 339struct FindFileIncludesVisitor {340  ASTUnit &Unit;341  const FileEntry *File;342  CXCursorAndRangeVisitor visitor;343 344  FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File,345                          CXCursorAndRangeVisitor visitor)346    : Unit(Unit), File(File), visitor(visitor) { }347 348  ASTContext &getASTContext() const {349    return Unit.getASTContext();350  }351 352  enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) {353    if (cursor.kind != CXCursor_InclusionDirective)354      return CXChildVisit_Continue;355 356    SourceLocation357      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    FileIDAndOffset 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 373  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 namespace381 382static 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 408extern "C" {409 410CXResult 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 exprs455  // prefer type references, e.g.:456  //457  //  return MyStruct();458  //459  // for 'MyStruct' we'll have a cursor pointing at the constructor decl but460  // 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 479CXResult 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 512static 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 519CXResult 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 527CXResult 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