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