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