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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "CursorVisitor.h" 10 #include "CLog.h" 11 #include "CXCursor.h" 12 #include "CXSourceLocation.h" 13 #include "CXTranslationUnit.h" 14 #include "clang/AST/DeclObjC.h" 15 #include "clang/Frontend/ASTUnit.h" 16 #include "llvm/Support/Compiler.h" 17 18 using namespace clang; 19 using namespace cxcursor; 20 using namespace cxindex; 21 22 static void getTopOverriddenMethods(CXTranslationUnit TU, 23 const Decl *D, 24 SmallVectorImpl<const Decl *> &Methods) { 25 if (!D) 26 return; 27 if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D)) 28 return; 29 30 SmallVector<CXCursor, 8> Overridden; 31 cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden); 32 33 if (Overridden.empty()) { 34 Methods.push_back(D->getCanonicalDecl()); 35 return; 36 } 37 38 for (SmallVectorImpl<CXCursor>::iterator 39 I = Overridden.begin(), E = Overridden.end(); I != E; ++I) 40 getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods); 41 } 42 43 namespace { 44 45 struct FindFileIdRefVisitData { 46 CXTranslationUnit TU; 47 FileID FID; 48 const Decl *Dcl; 49 int SelectorIdIdx; 50 CXCursorAndRangeVisitor visitor; 51 52 typedef SmallVector<const Decl *, 8> TopMethodsTy; 53 TopMethodsTy TopMethods; 54 55 FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID, 56 const Decl *D, int selectorIdIdx, 57 CXCursorAndRangeVisitor visitor) 58 : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) { 59 Dcl = getCanonical(D); 60 getTopOverriddenMethods(TU, Dcl, TopMethods); 61 } 62 63 ASTContext &getASTContext() const { 64 return cxtu::getASTUnit(TU)->getASTContext(); 65 } 66 67 /// We are looking to find all semantically relevant identifiers, 68 /// so the definition of "canonical" here is different than in the AST, e.g. 69 /// 70 /// \code 71 /// class C { 72 /// C() {} 73 /// }; 74 /// \endcode 75 /// 76 /// we consider the canonical decl of the constructor decl to be the class 77 /// itself, so both 'C' can be highlighted. 78 const Decl *getCanonical(const Decl *D) const { 79 if (!D) 80 return nullptr; 81 82 D = D->getCanonicalDecl(); 83 84 if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) { 85 if (ImplD->getClassInterface()) 86 return getCanonical(ImplD->getClassInterface()); 87 88 } else if (const CXXConstructorDecl *CXXCtorD = 89 dyn_cast<CXXConstructorDecl>(D)) { 90 return getCanonical(CXXCtorD->getParent()); 91 } 92 93 return D; 94 } 95 96 bool isHit(const Decl *D) const { 97 if (!D) 98 return false; 99 100 D = getCanonical(D); 101 if (D == Dcl) 102 return true; 103 104 if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D)) 105 return isOverriddingMethod(D); 106 107 return false; 108 } 109 110 private: 111 bool isOverriddingMethod(const Decl *D) const { 112 if (llvm::find(TopMethods, D) != TopMethods.end()) 113 return true; 114 115 TopMethodsTy methods; 116 getTopOverriddenMethods(TU, D, methods); 117 for (TopMethodsTy::iterator 118 I = methods.begin(), E = methods.end(); I != E; ++I) { 119 if (llvm::find(TopMethods, *I) != TopMethods.end()) 120 return true; 121 } 122 123 return false; 124 } 125 }; 126 127 } // end anonymous namespace. 128 129 /// 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 const 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 if (data->visitor.visit(data->visitor.context, cursor, 206 cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break) 207 return CXChildVisit_Break; 208 } 209 return CXChildVisit_Recurse; 210 } 211 212 static bool findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor, 213 const FileEntry *File, 214 CXCursorAndRangeVisitor Visitor) { 215 assert(clang_isDeclaration(declCursor.kind)); 216 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); 217 218 FileID FID = SM.translateFile(File); 219 const Decl *Dcl = cxcursor::getCursorDecl(declCursor); 220 if (!Dcl) 221 return false; 222 223 FindFileIdRefVisitData data(TU, FID, Dcl, 224 cxcursor::getSelectorIdentifierIndex(declCursor), 225 Visitor); 226 227 if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) { 228 return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU), 229 findFileIdRefVisit, &data); 230 } 231 232 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); 233 CursorVisitor FindIdRefsVisitor(TU, 234 findFileIdRefVisit, &data, 235 /*VisitPreprocessorLast=*/true, 236 /*VisitIncludedEntities=*/false, 237 Range, 238 /*VisitDeclsOnly=*/true); 239 return FindIdRefsVisitor.visitFileRegion(); 240 } 241 242 namespace { 243 244 struct FindFileMacroRefVisitData { 245 ASTUnit &Unit; 246 const FileEntry *File; 247 const IdentifierInfo *Macro; 248 CXCursorAndRangeVisitor visitor; 249 250 FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File, 251 const IdentifierInfo *Macro, 252 CXCursorAndRangeVisitor visitor) 253 : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { } 254 255 ASTContext &getASTContext() const { 256 return Unit.getASTContext(); 257 } 258 }; 259 260 } // anonymous namespace 261 262 static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor, 263 CXCursor parent, 264 CXClientData client_data) { 265 const IdentifierInfo *Macro = nullptr; 266 if (cursor.kind == CXCursor_MacroDefinition) 267 Macro = getCursorMacroDefinition(cursor)->getName(); 268 else if (cursor.kind == CXCursor_MacroExpansion) 269 Macro = getCursorMacroExpansion(cursor).getName(); 270 if (!Macro) 271 return CXChildVisit_Continue; 272 273 FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data; 274 if (data->Macro != Macro) 275 return CXChildVisit_Continue; 276 277 SourceLocation 278 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); 279 280 ASTContext &Ctx = data->getASTContext(); 281 SourceManager &SM = Ctx.getSourceManager(); 282 bool isInMacroDef = false; 283 if (Loc.isMacroID()) { 284 bool isMacroArg; 285 Loc = getFileSpellingLoc(SM, Loc, isMacroArg); 286 isInMacroDef = !isMacroArg; 287 } 288 289 // We are looking for identifiers in a specific file. 290 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 291 if (SM.getFileEntryForID(LocInfo.first) != data->File) 292 return CXChildVisit_Continue; 293 294 if (isInMacroDef) { 295 // FIXME: For a macro definition make sure that all expansions 296 // of it expand to the same reference before allowing to point to it. 297 return CXChildVisit_Continue; 298 } 299 300 if (data->visitor.visit(data->visitor.context, cursor, 301 cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break) 302 return CXChildVisit_Break; 303 return CXChildVisit_Continue; 304 } 305 306 static bool 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 false; 312 313 ASTUnit *Unit = cxtu::getASTUnit(TU); 314 SourceManager &SM = Unit->getSourceManager(); 315 316 FileID FID = SM.translateFile(File); 317 const IdentifierInfo *Macro = nullptr; 318 if (Cursor.kind == CXCursor_MacroDefinition) 319 Macro = getCursorMacroDefinition(Cursor)->getName(); 320 else 321 Macro = getCursorMacroExpansion(Cursor).getName(); 322 if (!Macro) 323 return false; 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 return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion(); 334 } 335 336 namespace { 337 338 struct FindFileIncludesVisitor { 339 ASTUnit &Unit; 340 const FileEntry *File; 341 CXCursorAndRangeVisitor visitor; 342 343 FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File, 344 CXCursorAndRangeVisitor visitor) 345 : Unit(Unit), File(File), visitor(visitor) { } 346 347 ASTContext &getASTContext() const { 348 return Unit.getASTContext(); 349 } 350 351 enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) { 352 if (cursor.kind != CXCursor_InclusionDirective) 353 return CXChildVisit_Continue; 354 355 SourceLocation 356 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); 357 358 ASTContext &Ctx = getASTContext(); 359 SourceManager &SM = Ctx.getSourceManager(); 360 361 // We are looking for includes in a specific file. 362 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 363 if (SM.getFileEntryForID(LocInfo.first) != File) 364 return CXChildVisit_Continue; 365 366 if (visitor.visit(visitor.context, cursor, 367 cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break) 368 return CXChildVisit_Break; 369 return CXChildVisit_Continue; 370 } 371 372 static enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent, 373 CXClientData client_data) { 374 return static_cast<FindFileIncludesVisitor*>(client_data)-> 375 visit(cursor, parent); 376 } 377 }; 378 379 } // anonymous namespace 380 381 static bool findIncludesInFile(CXTranslationUnit TU, const FileEntry *File, 382 CXCursorAndRangeVisitor Visitor) { 383 assert(TU && File && Visitor.visit); 384 385 ASTUnit *Unit = cxtu::getASTUnit(TU); 386 SourceManager &SM = Unit->getSourceManager(); 387 388 FileID FID = SM.translateFile(File); 389 390 FindFileIncludesVisitor IncludesVisitor(*Unit, File, Visitor); 391 392 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); 393 CursorVisitor InclusionCursorsVisitor(TU, 394 FindFileIncludesVisitor::visit, 395 &IncludesVisitor, 396 /*VisitPreprocessorLast=*/false, 397 /*VisitIncludedEntities=*/false, 398 Range); 399 return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion(); 400 } 401 402 403 //===----------------------------------------------------------------------===// 404 // libclang public APIs. 405 //===----------------------------------------------------------------------===// 406 407 extern "C" { 408 409 CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file, 410 CXCursorAndRangeVisitor visitor) { 411 LogRef Log = Logger::make(__func__); 412 413 if (clang_Cursor_isNull(cursor)) { 414 if (Log) 415 *Log << "Null cursor"; 416 return CXResult_Invalid; 417 } 418 if (cursor.kind == CXCursor_NoDeclFound) { 419 if (Log) 420 *Log << "Got CXCursor_NoDeclFound"; 421 return CXResult_Invalid; 422 } 423 if (!file) { 424 if (Log) 425 *Log << "Null file"; 426 return CXResult_Invalid; 427 } 428 if (!visitor.visit) { 429 if (Log) 430 *Log << "Null visitor"; 431 return CXResult_Invalid; 432 } 433 434 if (Log) 435 *Log << cursor << " @" << static_cast<const FileEntry *>(file); 436 437 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor); 438 if (!CXXUnit) 439 return CXResult_Invalid; 440 441 ASTUnit::ConcurrencyCheck Check(*CXXUnit); 442 443 if (cursor.kind == CXCursor_MacroDefinition || 444 cursor.kind == CXCursor_MacroExpansion) { 445 if (findMacroRefsInFile(cxcursor::getCursorTU(cursor), 446 cursor, 447 static_cast<const FileEntry *>(file), 448 visitor)) 449 return CXResult_VisitBreak; 450 return CXResult_Success; 451 } 452 453 // We are interested in semantics of identifiers so for C++ constructor exprs 454 // prefer type references, e.g.: 455 // 456 // return MyStruct(); 457 // 458 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but 459 // we are actually interested in the type declaration. 460 cursor = cxcursor::getTypeRefCursor(cursor); 461 462 CXCursor refCursor = clang_getCursorReferenced(cursor); 463 464 if (!clang_isDeclaration(refCursor.kind)) { 465 if (Log) 466 *Log << "cursor is not referencing a declaration"; 467 return CXResult_Invalid; 468 } 469 470 if (findIdRefsInFile(cxcursor::getCursorTU(cursor), 471 refCursor, 472 static_cast<const FileEntry *>(file), 473 visitor)) 474 return CXResult_VisitBreak; 475 return CXResult_Success; 476 } 477 478 CXResult clang_findIncludesInFile(CXTranslationUnit TU, CXFile file, 479 CXCursorAndRangeVisitor visitor) { 480 if (cxtu::isNotUsableTU(TU)) { 481 LOG_BAD_TU(TU); 482 return CXResult_Invalid; 483 } 484 485 LogRef Log = Logger::make(__func__); 486 if (!file) { 487 if (Log) 488 *Log << "Null file"; 489 return CXResult_Invalid; 490 } 491 if (!visitor.visit) { 492 if (Log) 493 *Log << "Null visitor"; 494 return CXResult_Invalid; 495 } 496 497 if (Log) 498 *Log << TU << " @" << static_cast<const FileEntry *>(file); 499 500 ASTUnit *CXXUnit = cxtu::getASTUnit(TU); 501 if (!CXXUnit) 502 return CXResult_Invalid; 503 504 ASTUnit::ConcurrencyCheck Check(*CXXUnit); 505 506 if (findIncludesInFile(TU, static_cast<const FileEntry *>(file), visitor)) 507 return CXResult_VisitBreak; 508 return CXResult_Success; 509 } 510 511 static enum CXVisitorResult _visitCursorAndRange(void *context, 512 CXCursor cursor, 513 CXSourceRange range) { 514 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context; 515 return INVOKE_BLOCK2(block, cursor, range); 516 } 517 518 CXResult clang_findReferencesInFileWithBlock(CXCursor cursor, 519 CXFile file, 520 CXCursorAndRangeVisitorBlock block) { 521 CXCursorAndRangeVisitor visitor = { block, 522 block ? _visitCursorAndRange : nullptr }; 523 return clang_findReferencesInFile(cursor, file, visitor); 524 } 525 526 CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU, 527 CXFile file, 528 CXCursorAndRangeVisitorBlock block) { 529 CXCursorAndRangeVisitor visitor = { block, 530 block ? _visitCursorAndRange : nullptr }; 531 return clang_findIncludesInFile(TU, file, visitor); 532 } 533 534 } // end: extern "C" 535