CIndexHigh.cpp revision 029c8f37e32b08bad4c757848a695d199fe5c813
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 19using namespace clang; 20using namespace cxcursor; 21using namespace cxindex; 22 23static 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 (SmallVector<CXCursor, 8>::iterator 40 I = Overridden.begin(), E = Overridden.end(); I != E; ++I) 41 getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods); 42} 43 44namespace { 45 46struct 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 0; 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 111private: 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. 135static 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 147static 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 215static void 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; 225 226 FindFileIdRefVisitData data(TU, FID, Dcl, 227 cxcursor::getSelectorIdentifierIndex(declCursor), 228 Visitor); 229 230 if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) { 231 clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU), 232 findFileIdRefVisit, &data); 233 return; 234 } 235 236 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); 237 CursorVisitor FindIdRefsVisitor(TU, 238 findFileIdRefVisit, &data, 239 /*VisitPreprocessorLast=*/true, 240 /*VisitIncludedEntities=*/false, 241 Range, 242 /*VisitDeclsOnly=*/true); 243 FindIdRefsVisitor.visitFileRegion(); 244} 245 246namespace { 247 248struct FindFileMacroRefVisitData { 249 ASTUnit &Unit; 250 const FileEntry *File; 251 const IdentifierInfo *Macro; 252 CXCursorAndRangeVisitor visitor; 253 254 FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File, 255 const IdentifierInfo *Macro, 256 CXCursorAndRangeVisitor visitor) 257 : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { } 258 259 ASTContext &getASTContext() const { 260 return Unit.getASTContext(); 261 } 262}; 263 264} // anonymous namespace 265 266static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor, 267 CXCursor parent, 268 CXClientData client_data) { 269 const IdentifierInfo *Macro = 0; 270 if (cursor.kind == CXCursor_MacroDefinition) 271 Macro = getCursorMacroDefinition(cursor)->getName(); 272 else if (cursor.kind == CXCursor_MacroExpansion) 273 Macro = getCursorMacroExpansion(cursor).getName(); 274 if (!Macro) 275 return CXChildVisit_Continue; 276 277 FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data; 278 if (data->Macro != Macro) 279 return CXChildVisit_Continue; 280 281 SourceLocation 282 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); 283 284 ASTContext &Ctx = data->getASTContext(); 285 SourceManager &SM = Ctx.getSourceManager(); 286 bool isInMacroDef = false; 287 if (Loc.isMacroID()) { 288 bool isMacroArg; 289 Loc = getFileSpellingLoc(SM, Loc, isMacroArg); 290 isInMacroDef = !isMacroArg; 291 } 292 293 // We are looking for identifiers in a specific file. 294 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 295 if (SM.getFileEntryForID(LocInfo.first) != data->File) 296 return CXChildVisit_Continue; 297 298 if (isInMacroDef) { 299 // FIXME: For a macro definition make sure that all expansions 300 // of it expand to the same reference before allowing to point to it. 301 return CXChildVisit_Continue; 302 } 303 304 if (data->visitor.visit(data->visitor.context, cursor, 305 cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break) 306 return CXChildVisit_Break; 307 return CXChildVisit_Continue; 308} 309 310static void findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor, 311 const FileEntry *File, 312 CXCursorAndRangeVisitor Visitor) { 313 if (Cursor.kind != CXCursor_MacroDefinition && 314 Cursor.kind != CXCursor_MacroExpansion) 315 return; 316 317 ASTUnit *Unit = cxtu::getASTUnit(TU); 318 SourceManager &SM = Unit->getSourceManager(); 319 320 FileID FID = SM.translateFile(File); 321 const IdentifierInfo *Macro = 0; 322 if (Cursor.kind == CXCursor_MacroDefinition) 323 Macro = getCursorMacroDefinition(Cursor)->getName(); 324 else 325 Macro = getCursorMacroExpansion(Cursor).getName(); 326 if (!Macro) 327 return; 328 329 FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor); 330 331 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); 332 CursorVisitor FindMacroRefsVisitor(TU, 333 findFileMacroRefVisit, &data, 334 /*VisitPreprocessorLast=*/false, 335 /*VisitIncludedEntities=*/false, 336 Range); 337 FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion(); 338} 339 340 341//===----------------------------------------------------------------------===// 342// libclang public APIs. 343//===----------------------------------------------------------------------===// 344 345extern "C" { 346 347void clang_findReferencesInFile(CXCursor cursor, CXFile file, 348 CXCursorAndRangeVisitor visitor) { 349 LogRef Log = Logger::make(LLVM_FUNCTION_NAME); 350 351 if (clang_Cursor_isNull(cursor)) { 352 if (Log) 353 *Log << "Null cursor"; 354 return; 355 } 356 if (cursor.kind == CXCursor_NoDeclFound) { 357 if (Log) 358 *Log << "Got CXCursor_NoDeclFound"; 359 return; 360 } 361 if (!file) { 362 if (Log) 363 *Log << "Null file"; 364 return; 365 } 366 if (!visitor.visit) { 367 if (Log) 368 *Log << "Null visitor"; 369 return; 370 } 371 372 if (Log) 373 *Log << cursor << " @" << static_cast<const FileEntry *>(file); 374 375 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor); 376 if (!CXXUnit) 377 return; 378 379 ASTUnit::ConcurrencyCheck Check(*CXXUnit); 380 381 if (cursor.kind == CXCursor_MacroDefinition || 382 cursor.kind == CXCursor_MacroExpansion) { 383 findMacroRefsInFile(cxcursor::getCursorTU(cursor), 384 cursor, 385 static_cast<const FileEntry *>(file), 386 visitor); 387 return; 388 } 389 390 // We are interested in semantics of identifiers so for C++ constructor exprs 391 // prefer type references, e.g.: 392 // 393 // return MyStruct(); 394 // 395 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but 396 // we are actually interested in the type declaration. 397 cursor = cxcursor::getTypeRefCursor(cursor); 398 399 CXCursor refCursor = clang_getCursorReferenced(cursor); 400 401 if (!clang_isDeclaration(refCursor.kind)) { 402 if (Log) 403 *Log << "cursor is not referencing a declaration"; 404 return; 405 } 406 407 findIdRefsInFile(cxcursor::getCursorTU(cursor), 408 refCursor, 409 static_cast<const FileEntry *>(file), 410 visitor); 411} 412 413static enum CXVisitorResult _visitCursorAndRange(void *context, 414 CXCursor cursor, 415 CXSourceRange range) { 416 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context; 417 return INVOKE_BLOCK2(block, cursor, range); 418} 419 420void clang_findReferencesInFileWithBlock(CXCursor cursor, 421 CXFile file, 422 CXCursorAndRangeVisitorBlock block) { 423 CXCursorAndRangeVisitor visitor = { block, 424 block ? _visitCursorAndRange : 0 }; 425 return clang_findReferencesInFile(cursor, file, visitor); 426} 427 428} // end: extern "C" 429 430