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 "CLog.h" 12 #include "CXCursor.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 19 using namespace clang; 20 using namespace cxcursor; 21 using namespace cxindex; 22 23 static 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>::iterator 40 I = Overridden.begin(), E = Overridden.end(); I != E; ++I) 41 getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods); 42 } 43 44 namespace { 45 46 struct 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 /// \brief 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 /// \code 72 /// class C { 73 /// C() {} 74 /// }; 75 /// \endcode 76 /// 77 /// we consider the canonical decl of the constructor decl to be the class 78 /// 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 111 private: 112 bool isOverriddingMethod(const Decl *D) const { 113 if (std::find(TopMethods.begin(), TopMethods.end(), D) != 114 TopMethods.end()) 115 return true; 116 117 TopMethodsTy methods; 118 getTopOverriddenMethods(TU, D, methods); 119 for (TopMethodsTy::iterator 120 I = methods.begin(), E = methods.end(); I != E; ++I) { 121 if (std::find(TopMethods.begin(), TopMethods.end(), *I) != 122 TopMethods.end()) 123 return true; 124 } 125 126 return false; 127 } 128 }; 129 130 } // end anonymous namespace. 131 132 /// \brief For a macro \arg Loc, returns the file spelling location and sets 133 /// to \arg isMacroArg whether the spelling resides inside a macro definition or 134 /// a macro argument. 135 static SourceLocation getFileSpellingLoc(SourceManager &SM, 136 SourceLocation Loc, 137 bool &isMacroArg) { 138 assert(Loc.isMacroID()); 139 SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc); 140 if (SpellLoc.isMacroID()) 141 return getFileSpellingLoc(SM, SpellLoc, isMacroArg); 142 143 isMacroArg = SM.isMacroArgExpansion(Loc); 144 return SpellLoc; 145 } 146 147 static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor, 148 CXCursor parent, 149 CXClientData client_data) { 150 CXCursor declCursor = clang_getCursorReferenced(cursor); 151 if (!clang_isDeclaration(declCursor.kind)) 152 return CXChildVisit_Recurse; 153 154 const Decl *D = cxcursor::getCursorDecl(declCursor); 155 if (!D) 156 return CXChildVisit_Continue; 157 158 FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data; 159 if (data->isHit(D)) { 160 cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor); 161 162 // We are looking for identifiers to highlight so for objc methods (and 163 // not a parameter) we can only highlight the selector identifiers. 164 if ((cursor.kind == CXCursor_ObjCClassMethodDecl || 165 cursor.kind == CXCursor_ObjCInstanceMethodDecl) && 166 cxcursor::getSelectorIdentifierIndex(cursor) == -1) 167 return CXChildVisit_Recurse; 168 169 if (clang_isExpression(cursor.kind)) { 170 if (cursor.kind == CXCursor_DeclRefExpr || 171 cursor.kind == CXCursor_MemberRefExpr) { 172 // continue.. 173 174 } else if (cursor.kind == CXCursor_ObjCMessageExpr && 175 cxcursor::getSelectorIdentifierIndex(cursor) != -1) { 176 // continue.. 177 178 } else 179 return CXChildVisit_Recurse; 180 } 181 182 SourceLocation 183 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); 184 SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor); 185 if (SelIdLoc.isValid()) 186 Loc = SelIdLoc; 187 188 ASTContext &Ctx = data->getASTContext(); 189 SourceManager &SM = Ctx.getSourceManager(); 190 bool isInMacroDef = false; 191 if (Loc.isMacroID()) { 192 bool isMacroArg; 193 Loc = getFileSpellingLoc(SM, Loc, isMacroArg); 194 isInMacroDef = !isMacroArg; 195 } 196 197 // We are looking for identifiers in a specific file. 198 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 199 if (LocInfo.first != data->FID) 200 return CXChildVisit_Recurse; 201 202 if (isInMacroDef) { 203 // FIXME: For a macro definition make sure that all expansions 204 // of it expand to the same reference before allowing to point to it. 205 return CXChildVisit_Recurse; 206 } 207 208 if (data->visitor.visit(data->visitor.context, cursor, 209 cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break) 210 return CXChildVisit_Break; 211 } 212 return CXChildVisit_Recurse; 213 } 214 215 static bool findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor, 216 const FileEntry *File, 217 CXCursorAndRangeVisitor Visitor) { 218 assert(clang_isDeclaration(declCursor.kind)); 219 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); 220 221 FileID FID = SM.translateFile(File); 222 const Decl *Dcl = cxcursor::getCursorDecl(declCursor); 223 if (!Dcl) 224 return false; 225 226 FindFileIdRefVisitData data(TU, FID, Dcl, 227 cxcursor::getSelectorIdentifierIndex(declCursor), 228 Visitor); 229 230 if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) { 231 return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU), 232 findFileIdRefVisit, &data); 233 } 234 235 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); 236 CursorVisitor FindIdRefsVisitor(TU, 237 findFileIdRefVisit, &data, 238 /*VisitPreprocessorLast=*/true, 239 /*VisitIncludedEntities=*/false, 240 Range, 241 /*VisitDeclsOnly=*/true); 242 return FindIdRefsVisitor.visitFileRegion(); 243 } 244 245 namespace { 246 247 struct FindFileMacroRefVisitData { 248 ASTUnit &Unit; 249 const FileEntry *File; 250 const IdentifierInfo *Macro; 251 CXCursorAndRangeVisitor visitor; 252 253 FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File, 254 const IdentifierInfo *Macro, 255 CXCursorAndRangeVisitor visitor) 256 : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { } 257 258 ASTContext &getASTContext() const { 259 return Unit.getASTContext(); 260 } 261 }; 262 263 } // anonymous namespace 264 265 static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor, 266 CXCursor parent, 267 CXClientData client_data) { 268 const IdentifierInfo *Macro = nullptr; 269 if (cursor.kind == CXCursor_MacroDefinition) 270 Macro = getCursorMacroDefinition(cursor)->getName(); 271 else if (cursor.kind == CXCursor_MacroExpansion) 272 Macro = getCursorMacroExpansion(cursor).getName(); 273 if (!Macro) 274 return CXChildVisit_Continue; 275 276 FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data; 277 if (data->Macro != Macro) 278 return CXChildVisit_Continue; 279 280 SourceLocation 281 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); 282 283 ASTContext &Ctx = data->getASTContext(); 284 SourceManager &SM = Ctx.getSourceManager(); 285 bool isInMacroDef = false; 286 if (Loc.isMacroID()) { 287 bool isMacroArg; 288 Loc = getFileSpellingLoc(SM, Loc, isMacroArg); 289 isInMacroDef = !isMacroArg; 290 } 291 292 // We are looking for identifiers in a specific file. 293 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 294 if (SM.getFileEntryForID(LocInfo.first) != data->File) 295 return CXChildVisit_Continue; 296 297 if (isInMacroDef) { 298 // FIXME: For a macro definition make sure that all expansions 299 // of it expand to the same reference before allowing to point to it. 300 return CXChildVisit_Continue; 301 } 302 303 if (data->visitor.visit(data->visitor.context, cursor, 304 cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break) 305 return CXChildVisit_Break; 306 return CXChildVisit_Continue; 307 } 308 309 static bool findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor, 310 const FileEntry *File, 311 CXCursorAndRangeVisitor Visitor) { 312 if (Cursor.kind != CXCursor_MacroDefinition && 313 Cursor.kind != CXCursor_MacroExpansion) 314 return false; 315 316 ASTUnit *Unit = cxtu::getASTUnit(TU); 317 SourceManager &SM = Unit->getSourceManager(); 318 319 FileID FID = SM.translateFile(File); 320 const IdentifierInfo *Macro = nullptr; 321 if (Cursor.kind == CXCursor_MacroDefinition) 322 Macro = getCursorMacroDefinition(Cursor)->getName(); 323 else 324 Macro = getCursorMacroExpansion(Cursor).getName(); 325 if (!Macro) 326 return false; 327 328 FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor); 329 330 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); 331 CursorVisitor FindMacroRefsVisitor(TU, 332 findFileMacroRefVisit, &data, 333 /*VisitPreprocessorLast=*/false, 334 /*VisitIncludedEntities=*/false, 335 Range); 336 return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion(); 337 } 338 339 namespace { 340 341 struct FindFileIncludesVisitor { 342 ASTUnit &Unit; 343 const FileEntry *File; 344 CXCursorAndRangeVisitor visitor; 345 346 FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File, 347 CXCursorAndRangeVisitor visitor) 348 : Unit(Unit), File(File), visitor(visitor) { } 349 350 ASTContext &getASTContext() const { 351 return Unit.getASTContext(); 352 } 353 354 enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) { 355 if (cursor.kind != CXCursor_InclusionDirective) 356 return CXChildVisit_Continue; 357 358 SourceLocation 359 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); 360 361 ASTContext &Ctx = getASTContext(); 362 SourceManager &SM = Ctx.getSourceManager(); 363 364 // We are looking for includes in a specific file. 365 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 366 if (SM.getFileEntryForID(LocInfo.first) != File) 367 return CXChildVisit_Continue; 368 369 if (visitor.visit(visitor.context, cursor, 370 cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break) 371 return CXChildVisit_Break; 372 return CXChildVisit_Continue; 373 } 374 375 static enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent, 376 CXClientData client_data) { 377 return static_cast<FindFileIncludesVisitor*>(client_data)-> 378 visit(cursor, parent); 379 } 380 }; 381 382 } // anonymous namespace 383 384 static bool findIncludesInFile(CXTranslationUnit TU, const FileEntry *File, 385 CXCursorAndRangeVisitor Visitor) { 386 assert(TU && File && Visitor.visit); 387 388 ASTUnit *Unit = cxtu::getASTUnit(TU); 389 SourceManager &SM = Unit->getSourceManager(); 390 391 FileID FID = SM.translateFile(File); 392 393 FindFileIncludesVisitor IncludesVisitor(*Unit, File, Visitor); 394 395 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); 396 CursorVisitor InclusionCursorsVisitor(TU, 397 FindFileIncludesVisitor::visit, 398 &IncludesVisitor, 399 /*VisitPreprocessorLast=*/false, 400 /*VisitIncludedEntities=*/false, 401 Range); 402 return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion(); 403 } 404 405 406 //===----------------------------------------------------------------------===// 407 // libclang public APIs. 408 //===----------------------------------------------------------------------===// 409 410 extern "C" { 411 412 CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file, 413 CXCursorAndRangeVisitor visitor) { 414 LogRef Log = Logger::make(LLVM_FUNCTION_NAME); 415 416 if (clang_Cursor_isNull(cursor)) { 417 if (Log) 418 *Log << "Null cursor"; 419 return CXResult_Invalid; 420 } 421 if (cursor.kind == CXCursor_NoDeclFound) { 422 if (Log) 423 *Log << "Got CXCursor_NoDeclFound"; 424 return CXResult_Invalid; 425 } 426 if (!file) { 427 if (Log) 428 *Log << "Null file"; 429 return CXResult_Invalid; 430 } 431 if (!visitor.visit) { 432 if (Log) 433 *Log << "Null visitor"; 434 return CXResult_Invalid; 435 } 436 437 if (Log) 438 *Log << cursor << " @" << static_cast<const FileEntry *>(file); 439 440 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor); 441 if (!CXXUnit) 442 return CXResult_Invalid; 443 444 ASTUnit::ConcurrencyCheck Check(*CXXUnit); 445 446 if (cursor.kind == CXCursor_MacroDefinition || 447 cursor.kind == CXCursor_MacroExpansion) { 448 if (findMacroRefsInFile(cxcursor::getCursorTU(cursor), 449 cursor, 450 static_cast<const FileEntry *>(file), 451 visitor)) 452 return CXResult_VisitBreak; 453 return CXResult_Success; 454 } 455 456 // We are interested in semantics of identifiers so for C++ constructor exprs 457 // prefer type references, e.g.: 458 // 459 // return MyStruct(); 460 // 461 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but 462 // we are actually interested in the type declaration. 463 cursor = cxcursor::getTypeRefCursor(cursor); 464 465 CXCursor refCursor = clang_getCursorReferenced(cursor); 466 467 if (!clang_isDeclaration(refCursor.kind)) { 468 if (Log) 469 *Log << "cursor is not referencing a declaration"; 470 return CXResult_Invalid; 471 } 472 473 if (findIdRefsInFile(cxcursor::getCursorTU(cursor), 474 refCursor, 475 static_cast<const FileEntry *>(file), 476 visitor)) 477 return CXResult_VisitBreak; 478 return CXResult_Success; 479 } 480 481 CXResult clang_findIncludesInFile(CXTranslationUnit TU, CXFile file, 482 CXCursorAndRangeVisitor visitor) { 483 if (cxtu::isNotUsableTU(TU)) { 484 LOG_BAD_TU(TU); 485 return CXResult_Invalid; 486 } 487 488 LogRef Log = Logger::make(LLVM_FUNCTION_NAME); 489 if (!file) { 490 if (Log) 491 *Log << "Null file"; 492 return CXResult_Invalid; 493 } 494 if (!visitor.visit) { 495 if (Log) 496 *Log << "Null visitor"; 497 return CXResult_Invalid; 498 } 499 500 if (Log) 501 *Log << TU << " @" << static_cast<const FileEntry *>(file); 502 503 ASTUnit *CXXUnit = cxtu::getASTUnit(TU); 504 if (!CXXUnit) 505 return CXResult_Invalid; 506 507 ASTUnit::ConcurrencyCheck Check(*CXXUnit); 508 509 if (findIncludesInFile(TU, static_cast<const FileEntry *>(file), visitor)) 510 return CXResult_VisitBreak; 511 return CXResult_Success; 512 } 513 514 static enum CXVisitorResult _visitCursorAndRange(void *context, 515 CXCursor cursor, 516 CXSourceRange range) { 517 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context; 518 return INVOKE_BLOCK2(block, cursor, range); 519 } 520 521 CXResult clang_findReferencesInFileWithBlock(CXCursor cursor, 522 CXFile file, 523 CXCursorAndRangeVisitorBlock block) { 524 CXCursorAndRangeVisitor visitor = { block, 525 block ? _visitCursorAndRange : nullptr }; 526 return clang_findReferencesInFile(cursor, file, visitor); 527 } 528 529 CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU, 530 CXFile file, 531 CXCursorAndRangeVisitorBlock block) { 532 CXCursorAndRangeVisitor visitor = { block, 533 block ? _visitCursorAndRange : nullptr }; 534 return clang_findIncludesInFile(TU, file, visitor); 535 } 536 537 } // end: extern "C" 538 539