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 15#include "clang/Frontend/ASTUnit.h" 16#include "clang/AST/DeclObjC.h" 17 18using namespace clang; 19using namespace cxcursor; 20 21static void getTopOverriddenMethods(CXTranslationUnit TU, 22 Decl *D, 23 SmallVectorImpl<Decl *> &Methods) { 24 if (!D) 25 return; 26 if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D)) 27 return; 28 29 SmallVector<CXCursor, 8> Overridden; 30 cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden); 31 32 if (Overridden.empty()) { 33 Methods.push_back(D->getCanonicalDecl()); 34 return; 35 } 36 37 for (SmallVector<CXCursor, 8>::iterator 38 I = Overridden.begin(), E = Overridden.end(); I != E; ++I) 39 getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods); 40} 41 42namespace { 43 44struct FindFileIdRefVisitData { 45 CXTranslationUnit TU; 46 FileID FID; 47 Decl *Dcl; 48 int SelectorIdIdx; 49 CXCursorAndRangeVisitor visitor; 50 51 typedef SmallVector<Decl *, 8> TopMethodsTy; 52 TopMethodsTy TopMethods; 53 54 FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID, 55 Decl *D, int selectorIdIdx, 56 CXCursorAndRangeVisitor visitor) 57 : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) { 58 Dcl = getCanonical(D); 59 getTopOverriddenMethods(TU, Dcl, TopMethods); 60 } 61 62 ASTContext &getASTContext() const { 63 return static_cast<ASTUnit *>(TU->TUData)->getASTContext(); 64 } 65 66 /// \brief We are looking to find all semantically relevant identifiers, 67 /// so the definition of "canonical" here is different than in the AST, e.g. 68 /// 69 /// \code 70 /// class C { 71 /// C() {} 72 /// }; 73 /// \endcode 74 /// 75 /// we consider the canonical decl of the constructor decl to be the class 76 /// itself, so both 'C' can be highlighted. 77 Decl *getCanonical(Decl *D) const { 78 if (!D) 79 return 0; 80 81 D = D->getCanonicalDecl(); 82 83 if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) { 84 if (ImplD->getClassInterface()) 85 return getCanonical(ImplD->getClassInterface()); 86 87 } else if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D)) { 88 return getCanonical(CXXCtorD->getParent()); 89 } 90 91 return D; 92 } 93 94 bool isHit(Decl *D) const { 95 if (!D) 96 return false; 97 98 D = getCanonical(D); 99 if (D == Dcl) 100 return true; 101 102 if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D)) 103 return isOverriddingMethod(D); 104 105 return false; 106 } 107 108private: 109 bool isOverriddingMethod(Decl *D) const { 110 if (std::find(TopMethods.begin(), TopMethods.end(), D) != 111 TopMethods.end()) 112 return true; 113 114 TopMethodsTy methods; 115 getTopOverriddenMethods(TU, D, methods); 116 for (TopMethodsTy::iterator 117 I = methods.begin(), E = methods.end(); I != E; ++I) { 118 if (std::find(TopMethods.begin(), TopMethods.end(), *I) != 119 TopMethods.end()) 120 return true; 121 } 122 123 return false; 124 } 125}; 126 127} // end anonymous namespace. 128 129/// \brief 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. 132static 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 144static 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 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 data->visitor.visit(data->visitor.context, cursor, 206 cxloc::translateSourceRange(Ctx, Loc)); 207 } 208 return CXChildVisit_Recurse; 209} 210 211static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor, 212 const FileEntry *File, 213 CXCursorAndRangeVisitor Visitor) { 214 assert(clang_isDeclaration(declCursor.kind)); 215 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData); 216 SourceManager &SM = Unit->getSourceManager(); 217 218 FileID FID = SM.translateFile(File); 219 Decl *Dcl = cxcursor::getCursorDecl(declCursor); 220 if (!Dcl) 221 return; 222 223 FindFileIdRefVisitData data(TU, FID, Dcl, 224 cxcursor::getSelectorIdentifierIndex(declCursor), 225 Visitor); 226 227 if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) { 228 clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU), 229 findFileIdRefVisit, &data); 230 return; 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 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 namespace 262 263static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor, 264 CXCursor parent, 265 CXClientData client_data) { 266 const IdentifierInfo *Macro = 0; 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 SourceLocation 279 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 std::pair<FileID, unsigned> 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 expansions 297 // of it expand to the same reference before allowing to point to it. 298 return CXChildVisit_Continue; 299 } 300 301 data->visitor.visit(data->visitor.context, cursor, 302 cxloc::translateSourceRange(Ctx, Loc)); 303 return CXChildVisit_Continue; 304} 305 306static void 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; 312 313 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData); 314 SourceManager &SM = Unit->getSourceManager(); 315 316 FileID FID = SM.translateFile(File); 317 const IdentifierInfo *Macro = 0; 318 if (Cursor.kind == CXCursor_MacroDefinition) 319 Macro = getCursorMacroDefinition(Cursor)->getName(); 320 else 321 Macro = getCursorMacroExpansion(Cursor)->getName(); 322 if (!Macro) 323 return; 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 FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion(); 334} 335 336 337//===----------------------------------------------------------------------===// 338// libclang public APIs. 339//===----------------------------------------------------------------------===// 340 341extern "C" { 342 343void clang_findReferencesInFile(CXCursor cursor, CXFile file, 344 CXCursorAndRangeVisitor visitor) { 345 bool Logging = ::getenv("LIBCLANG_LOGGING"); 346 347 if (clang_Cursor_isNull(cursor)) { 348 if (Logging) 349 llvm::errs() << "clang_findReferencesInFile: Null cursor\n"; 350 return; 351 } 352 if (cursor.kind == CXCursor_NoDeclFound) { 353 if (Logging) 354 llvm::errs() << "clang_findReferencesInFile: Got CXCursor_NoDeclFound\n"; 355 return; 356 } 357 if (!file) { 358 if (Logging) 359 llvm::errs() << "clang_findReferencesInFile: Null file\n"; 360 return; 361 } 362 if (!visitor.visit) { 363 if (Logging) 364 llvm::errs() << "clang_findReferencesInFile: Null visitor\n"; 365 return; 366 } 367 368 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor); 369 if (!CXXUnit) 370 return; 371 372 ASTUnit::ConcurrencyCheck Check(*CXXUnit); 373 374 if (cursor.kind == CXCursor_MacroDefinition || 375 cursor.kind == CXCursor_MacroExpansion) { 376 findMacroRefsInFile(cxcursor::getCursorTU(cursor), 377 cursor, 378 static_cast<const FileEntry *>(file), 379 visitor); 380 return; 381 } 382 383 // We are interested in semantics of identifiers so for C++ constructor exprs 384 // prefer type references, e.g.: 385 // 386 // return MyStruct(); 387 // 388 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but 389 // we are actually interested in the type declaration. 390 cursor = cxcursor::getTypeRefCursor(cursor); 391 392 CXCursor refCursor = clang_getCursorReferenced(cursor); 393 394 if (!clang_isDeclaration(refCursor.kind)) { 395 if (Logging) 396 llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a " 397 "declaration\n"; 398 return; 399 } 400 401 findIdRefsInFile(cxcursor::getCursorTU(cursor), 402 refCursor, 403 static_cast<const FileEntry *>(file), 404 visitor); 405} 406 407static enum CXVisitorResult _visitCursorAndRange(void *context, 408 CXCursor cursor, 409 CXSourceRange range) { 410 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context; 411 return INVOKE_BLOCK2(block, cursor, range); 412} 413 414void clang_findReferencesInFileWithBlock(CXCursor cursor, 415 CXFile file, 416 CXCursorAndRangeVisitorBlock block) { 417 CXCursorAndRangeVisitor visitor = { block, 418 block ? _visitCursorAndRange : 0 }; 419 return clang_findReferencesInFile(cursor, file, visitor); 420} 421 422} // end: extern "C" 423 424