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