CIndexHigh.cpp revision f59edb96b2d0bfe612b732f19519ab84bb995bd4
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 "CXCursor.h" 12#include "CXSourceLocation.h" 13#include "CXTranslationUnit.h" 14#include "clang/AST/DeclObjC.h" 15#include "clang/Frontend/ASTUnit.h" 16 17using namespace clang; 18using namespace cxcursor; 19 20static void getTopOverriddenMethods(CXTranslationUnit TU, 21 Decl *D, 22 SmallVectorImpl<Decl *> &Methods) { 23 if (!D) 24 return; 25 if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D)) 26 return; 27 28 SmallVector<CXCursor, 8> Overridden; 29 cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden); 30 31 if (Overridden.empty()) { 32 Methods.push_back(D->getCanonicalDecl()); 33 return; 34 } 35 36 for (SmallVector<CXCursor, 8>::iterator 37 I = Overridden.begin(), E = Overridden.end(); I != E; ++I) 38 getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods); 39} 40 41namespace { 42 43struct FindFileIdRefVisitData { 44 CXTranslationUnit TU; 45 FileID FID; 46 Decl *Dcl; 47 int SelectorIdIdx; 48 CXCursorAndRangeVisitor visitor; 49 50 typedef SmallVector<Decl *, 8> TopMethodsTy; 51 TopMethodsTy TopMethods; 52 53 FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID, 54 Decl *D, int selectorIdIdx, 55 CXCursorAndRangeVisitor visitor) 56 : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) { 57 Dcl = getCanonical(D); 58 getTopOverriddenMethods(TU, Dcl, TopMethods); 59 } 60 61 ASTContext &getASTContext() const { 62 return static_cast<ASTUnit *>(TU->TUData)->getASTContext(); 63 } 64 65 /// \brief We are looking to find all semantically relevant identifiers, 66 /// so the definition of "canonical" here is different than in the AST, e.g. 67 /// 68 /// \code 69 /// class C { 70 /// C() {} 71 /// }; 72 /// \endcode 73 /// 74 /// we consider the canonical decl of the constructor decl to be the class 75 /// itself, so both 'C' can be highlighted. 76 Decl *getCanonical(Decl *D) const { 77 if (!D) 78 return 0; 79 80 D = D->getCanonicalDecl(); 81 82 if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) { 83 if (ImplD->getClassInterface()) 84 return getCanonical(ImplD->getClassInterface()); 85 86 } else if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D)) { 87 return getCanonical(CXXCtorD->getParent()); 88 } 89 90 return D; 91 } 92 93 bool isHit(Decl *D) const { 94 if (!D) 95 return false; 96 97 D = getCanonical(D); 98 if (D == Dcl) 99 return true; 100 101 if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D)) 102 return isOverriddingMethod(D); 103 104 return false; 105 } 106 107private: 108 bool isOverriddingMethod(Decl *D) const { 109 if (std::find(TopMethods.begin(), TopMethods.end(), D) != 110 TopMethods.end()) 111 return true; 112 113 TopMethodsTy methods; 114 getTopOverriddenMethods(TU, D, methods); 115 for (TopMethodsTy::iterator 116 I = methods.begin(), E = methods.end(); I != E; ++I) { 117 if (std::find(TopMethods.begin(), TopMethods.end(), *I) != 118 TopMethods.end()) 119 return true; 120 } 121 122 return false; 123 } 124}; 125 126} // end anonymous namespace. 127 128/// \brief For a macro \arg Loc, returns the file spelling location and sets 129/// to \arg isMacroArg whether the spelling resides inside a macro definition or 130/// a macro argument. 131static SourceLocation getFileSpellingLoc(SourceManager &SM, 132 SourceLocation Loc, 133 bool &isMacroArg) { 134 assert(Loc.isMacroID()); 135 SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc); 136 if (SpellLoc.isMacroID()) 137 return getFileSpellingLoc(SM, SpellLoc, isMacroArg); 138 139 isMacroArg = SM.isMacroArgExpansion(Loc); 140 return SpellLoc; 141} 142 143static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor, 144 CXCursor parent, 145 CXClientData client_data) { 146 CXCursor declCursor = clang_getCursorReferenced(cursor); 147 if (!clang_isDeclaration(declCursor.kind)) 148 return CXChildVisit_Recurse; 149 150 Decl *D = cxcursor::getCursorDecl(declCursor); 151 if (!D) 152 return CXChildVisit_Continue; 153 154 FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data; 155 if (data->isHit(D)) { 156 cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor); 157 158 // We are looking for identifiers to highlight so for objc methods (and 159 // not a parameter) we can only highlight the selector identifiers. 160 if ((cursor.kind == CXCursor_ObjCClassMethodDecl || 161 cursor.kind == CXCursor_ObjCInstanceMethodDecl) && 162 cxcursor::getSelectorIdentifierIndex(cursor) == -1) 163 return CXChildVisit_Recurse; 164 165 if (clang_isExpression(cursor.kind)) { 166 if (cursor.kind == CXCursor_DeclRefExpr || 167 cursor.kind == CXCursor_MemberRefExpr) { 168 // continue.. 169 170 } else if (cursor.kind == CXCursor_ObjCMessageExpr && 171 cxcursor::getSelectorIdentifierIndex(cursor) != -1) { 172 // continue.. 173 174 } else 175 return CXChildVisit_Recurse; 176 } 177 178 SourceLocation 179 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); 180 SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor); 181 if (SelIdLoc.isValid()) 182 Loc = SelIdLoc; 183 184 ASTContext &Ctx = data->getASTContext(); 185 SourceManager &SM = Ctx.getSourceManager(); 186 bool isInMacroDef = false; 187 if (Loc.isMacroID()) { 188 bool isMacroArg; 189 Loc = getFileSpellingLoc(SM, Loc, isMacroArg); 190 isInMacroDef = !isMacroArg; 191 } 192 193 // We are looking for identifiers in a specific file. 194 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 195 if (LocInfo.first != data->FID) 196 return CXChildVisit_Recurse; 197 198 if (isInMacroDef) { 199 // FIXME: For a macro definition make sure that all expansions 200 // of it expand to the same reference before allowing to point to it. 201 return CXChildVisit_Recurse; 202 } 203 204 data->visitor.visit(data->visitor.context, cursor, 205 cxloc::translateSourceRange(Ctx, Loc)); 206 } 207 return CXChildVisit_Recurse; 208} 209 210static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor, 211 const FileEntry *File, 212 CXCursorAndRangeVisitor Visitor) { 213 assert(clang_isDeclaration(declCursor.kind)); 214 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData); 215 SourceManager &SM = Unit->getSourceManager(); 216 217 FileID FID = SM.translateFile(File); 218 Decl *Dcl = cxcursor::getCursorDecl(declCursor); 219 if (!Dcl) 220 return; 221 222 FindFileIdRefVisitData data(TU, FID, Dcl, 223 cxcursor::getSelectorIdentifierIndex(declCursor), 224 Visitor); 225 226 if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) { 227 clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU), 228 findFileIdRefVisit, &data); 229 return; 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 FindIdRefsVisitor.visitFileRegion(); 240} 241 242namespace { 243 244struct 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 262static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor, 263 CXCursor parent, 264 CXClientData client_data) { 265 const IdentifierInfo *Macro = 0; 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 data->visitor.visit(data->visitor.context, cursor, 301 cxloc::translateSourceRange(Ctx, Loc)); 302 return CXChildVisit_Continue; 303} 304 305static void findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor, 306 const FileEntry *File, 307 CXCursorAndRangeVisitor Visitor) { 308 if (Cursor.kind != CXCursor_MacroDefinition && 309 Cursor.kind != CXCursor_MacroExpansion) 310 return; 311 312 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData); 313 SourceManager &SM = Unit->getSourceManager(); 314 315 FileID FID = SM.translateFile(File); 316 const IdentifierInfo *Macro = 0; 317 if (Cursor.kind == CXCursor_MacroDefinition) 318 Macro = getCursorMacroDefinition(Cursor)->getName(); 319 else 320 Macro = getCursorMacroExpansion(Cursor)->getName(); 321 if (!Macro) 322 return; 323 324 FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor); 325 326 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); 327 CursorVisitor FindMacroRefsVisitor(TU, 328 findFileMacroRefVisit, &data, 329 /*VisitPreprocessorLast=*/false, 330 /*VisitIncludedEntities=*/false, 331 Range); 332 FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion(); 333} 334 335 336//===----------------------------------------------------------------------===// 337// libclang public APIs. 338//===----------------------------------------------------------------------===// 339 340extern "C" { 341 342void clang_findReferencesInFile(CXCursor cursor, CXFile file, 343 CXCursorAndRangeVisitor visitor) { 344 bool Logging = ::getenv("LIBCLANG_LOGGING"); 345 346 if (clang_Cursor_isNull(cursor)) { 347 if (Logging) 348 llvm::errs() << "clang_findReferencesInFile: Null cursor\n"; 349 return; 350 } 351 if (cursor.kind == CXCursor_NoDeclFound) { 352 if (Logging) 353 llvm::errs() << "clang_findReferencesInFile: Got CXCursor_NoDeclFound\n"; 354 return; 355 } 356 if (!file) { 357 if (Logging) 358 llvm::errs() << "clang_findReferencesInFile: Null file\n"; 359 return; 360 } 361 if (!visitor.visit) { 362 if (Logging) 363 llvm::errs() << "clang_findReferencesInFile: Null visitor\n"; 364 return; 365 } 366 367 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor); 368 if (!CXXUnit) 369 return; 370 371 ASTUnit::ConcurrencyCheck Check(*CXXUnit); 372 373 if (cursor.kind == CXCursor_MacroDefinition || 374 cursor.kind == CXCursor_MacroExpansion) { 375 findMacroRefsInFile(cxcursor::getCursorTU(cursor), 376 cursor, 377 static_cast<const FileEntry *>(file), 378 visitor); 379 return; 380 } 381 382 // We are interested in semantics of identifiers so for C++ constructor exprs 383 // prefer type references, e.g.: 384 // 385 // return MyStruct(); 386 // 387 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but 388 // we are actually interested in the type declaration. 389 cursor = cxcursor::getTypeRefCursor(cursor); 390 391 CXCursor refCursor = clang_getCursorReferenced(cursor); 392 393 if (!clang_isDeclaration(refCursor.kind)) { 394 if (Logging) 395 llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a " 396 "declaration\n"; 397 return; 398 } 399 400 findIdRefsInFile(cxcursor::getCursorTU(cursor), 401 refCursor, 402 static_cast<const FileEntry *>(file), 403 visitor); 404} 405 406static enum CXVisitorResult _visitCursorAndRange(void *context, 407 CXCursor cursor, 408 CXSourceRange range) { 409 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context; 410 return INVOKE_BLOCK2(block, cursor, range); 411} 412 413void clang_findReferencesInFileWithBlock(CXCursor cursor, 414 CXFile file, 415 CXCursorAndRangeVisitorBlock block) { 416 CXCursorAndRangeVisitor visitor = { block, 417 block ? _visitCursorAndRange : 0 }; 418 return clang_findReferencesInFile(cursor, file, visitor); 419} 420 421} // end: extern "C" 422 423