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