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 "Index_Internal.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 18 using namespace clang; 19 20 static void getTopOverriddenMethods(CXTranslationUnit TU, 21 Decl *D, 22 SmallVectorImpl<Decl *> &Methods) { 23 if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D)) 24 return; 25 26 SmallVector<CXCursor, 8> Overridden; 27 cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden); 28 29 if (Overridden.empty()) { 30 Methods.push_back(D->getCanonicalDecl()); 31 return; 32 } 33 34 for (SmallVector<CXCursor, 8>::iterator 35 I = Overridden.begin(), E = Overridden.end(); I != E; ++I) 36 getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods); 37 } 38 39 namespace { 40 41 struct FindFileIdRefVisitData { 42 CXTranslationUnit TU; 43 FileID FID; 44 Decl *Dcl; 45 int SelectorIdIdx; 46 CXCursorAndRangeVisitor visitor; 47 48 typedef SmallVector<Decl *, 8> TopMethodsTy; 49 TopMethodsTy TopMethods; 50 51 FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID, 52 Decl *D, int selectorIdIdx, 53 CXCursorAndRangeVisitor visitor) 54 : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) { 55 Dcl = getCanonical(D); 56 getTopOverriddenMethods(TU, Dcl, TopMethods); 57 } 58 59 ASTContext &getASTContext() const { 60 return static_cast<ASTUnit *>(TU->TUData)->getASTContext(); 61 } 62 63 /// \brief We are looking to find all semantically relevant identifiers, 64 /// so the definition of "canonical" here is different than in the AST, e.g. 65 /// 66 /// \code 67 /// class C { 68 /// C() {} 69 /// }; 70 /// \endcode 71 /// 72 /// we consider the canonical decl of the constructor decl to be the class 73 /// itself, so both 'C' can be highlighted. 74 Decl *getCanonical(Decl *D) const { 75 D = D->getCanonicalDecl(); 76 77 if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) 78 return getCanonical(ImplD->getClassInterface()); 79 if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D)) 80 return getCanonical(CXXCtorD->getParent()); 81 82 return D; 83 } 84 85 bool isHit(Decl *D) const { 86 D = getCanonical(D); 87 if (D == Dcl) 88 return true; 89 90 if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D)) 91 return isOverriddingMethod(D); 92 93 return false; 94 } 95 96 private: 97 bool isOverriddingMethod(Decl *D) const { 98 if (std::find(TopMethods.begin(), TopMethods.end(), D) != 99 TopMethods.end()) 100 return true; 101 102 TopMethodsTy methods; 103 getTopOverriddenMethods(TU, D, methods); 104 for (TopMethodsTy::iterator 105 I = methods.begin(), E = methods.end(); I != E; ++I) { 106 if (std::find(TopMethods.begin(), TopMethods.end(), *I) != 107 TopMethods.end()) 108 return true; 109 } 110 111 return false; 112 } 113 }; 114 115 } // end anonymous namespace. 116 117 /// \brief For a macro \arg Loc, returns the file spelling location and sets 118 /// to \arg isMacroArg whether the spelling resides inside a macro definition or 119 /// a macro argument. 120 static SourceLocation getFileSpellingLoc(SourceManager &SM, 121 SourceLocation Loc, 122 bool &isMacroArg) { 123 assert(Loc.isMacroID()); 124 SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc); 125 if (SpellLoc.isMacroID()) 126 return getFileSpellingLoc(SM, SpellLoc, isMacroArg); 127 128 isMacroArg = SM.isMacroArgExpansion(Loc); 129 return SpellLoc; 130 } 131 132 static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor, 133 CXCursor parent, 134 CXClientData client_data) { 135 CXCursor declCursor = clang_getCursorReferenced(cursor); 136 if (!clang_isDeclaration(declCursor.kind)) 137 return CXChildVisit_Recurse; 138 139 Decl *D = cxcursor::getCursorDecl(declCursor); 140 FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data; 141 if (data->isHit(D)) { 142 cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor); 143 144 // We are looking for identifiers to highlight so for objc methods (and 145 // not a parameter) we can only highlight the selector identifiers. 146 if ((cursor.kind == CXCursor_ObjCClassMethodDecl || 147 cursor.kind == CXCursor_ObjCInstanceMethodDecl) && 148 cxcursor::getSelectorIdentifierIndex(cursor) == -1) 149 return CXChildVisit_Recurse; 150 151 if (clang_isExpression(cursor.kind)) { 152 if (cursor.kind == CXCursor_DeclRefExpr || 153 cursor.kind == CXCursor_MemberRefExpr) { 154 // continue.. 155 156 } else if (cursor.kind == CXCursor_ObjCMessageExpr && 157 cxcursor::getSelectorIdentifierIndex(cursor) != -1) { 158 // continue.. 159 160 } else 161 return CXChildVisit_Recurse; 162 } 163 164 SourceLocation 165 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); 166 SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor); 167 if (SelIdLoc.isValid()) 168 Loc = SelIdLoc; 169 170 SourceManager &SM = data->getASTContext().getSourceManager(); 171 bool isInMacroDef = false; 172 if (Loc.isMacroID()) { 173 bool isMacroArg; 174 Loc = getFileSpellingLoc(SM, Loc, isMacroArg); 175 isInMacroDef = !isMacroArg; 176 } 177 178 // We are looking for identifiers in a specific file. 179 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 180 if (LocInfo.first != data->FID) 181 return CXChildVisit_Recurse; 182 183 if (isInMacroDef) { 184 // FIXME: For a macro definition make sure that all expansions 185 // of it expand to the same reference before allowing to point to it. 186 Loc = SourceLocation(); 187 } 188 189 data->visitor.visit(data->visitor.context, cursor, 190 cxloc::translateSourceRange(D->getASTContext(), Loc)); 191 } 192 return CXChildVisit_Recurse; 193 } 194 195 static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor, 196 const FileEntry *File, 197 CXCursorAndRangeVisitor Visitor) { 198 assert(clang_isDeclaration(declCursor.kind)); 199 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData); 200 ASTContext &Ctx = Unit->getASTContext(); 201 SourceManager &SM = Unit->getSourceManager(); 202 203 FileID FID = SM.translateFile(File); 204 Decl *Dcl = cxcursor::getCursorDecl(declCursor); 205 FindFileIdRefVisitData data(TU, FID, Dcl, 206 cxcursor::getSelectorIdentifierIndex(declCursor), 207 Visitor); 208 209 if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) { 210 clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU), 211 findFileIdRefVisit, &data); 212 return; 213 } 214 215 if (FID == SM.getMainFileID() && !Unit->isMainFileAST()) { 216 SourceLocation FileLoc = SM.getLocForStartOfFile(FID); 217 TranslationUnitDecl *TUD = Ctx.getTranslationUnitDecl(); 218 CXCursor TUCursor = clang_getTranslationUnitCursor(TU); 219 for (DeclContext::decl_iterator 220 I = TUD->noload_decls_begin(), E = TUD->noload_decls_end(); 221 I != E; ++I) { 222 Decl *D = *I; 223 224 SourceRange R = D->getSourceRange(); 225 if (R.isInvalid()) 226 continue; 227 if (SM.isBeforeInTranslationUnit(R.getEnd(), FileLoc)) 228 continue; 229 230 if (TagDecl *TD = dyn_cast<TagDecl>(D)) 231 if (!TD->isFreeStanding()) 232 continue; 233 234 CXCursor CurCursor = cxcursor::MakeCXCursor(D, TU); 235 findFileIdRefVisit(CurCursor, TUCursor, &data); 236 clang_visitChildren(CurCursor, findFileIdRefVisit, &data); 237 } 238 return; 239 } 240 241 clang_visitChildren(clang_getTranslationUnitCursor(TU), 242 findFileIdRefVisit, &data); 243 } 244 245 246 //===----------------------------------------------------------------------===// 247 // libclang public APIs. 248 //===----------------------------------------------------------------------===// 249 250 extern "C" { 251 252 void clang_findReferencesInFile(CXCursor cursor, CXFile file, 253 CXCursorAndRangeVisitor visitor) { 254 bool Logging = ::getenv("LIBCLANG_LOGGING"); 255 256 if (clang_Cursor_isNull(cursor)) { 257 if (Logging) 258 llvm::errs() << "clang_findReferencesInFile: Null cursor\n"; 259 return; 260 } 261 if (!file) { 262 if (Logging) 263 llvm::errs() << "clang_findReferencesInFile: Null file\n"; 264 return; 265 } 266 if (!visitor.visit) { 267 if (Logging) 268 llvm::errs() << "clang_findReferencesInFile: Null visitor\n"; 269 return; 270 } 271 272 // We are interested in semantics of identifiers so for C++ constructor exprs 273 // prefer type references, e.g.: 274 // 275 // return MyStruct(); 276 // 277 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but 278 // we are actually interested in the type declaration. 279 cursor = cxcursor::getTypeRefCursor(cursor); 280 281 CXCursor refCursor = clang_getCursorReferenced(cursor); 282 283 if (!clang_isDeclaration(refCursor.kind)) { 284 if (Logging) 285 llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a " 286 "declaration\n"; 287 return; 288 } 289 290 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor); 291 ASTUnit::ConcurrencyCheck Check(*CXXUnit); 292 293 findIdRefsInFile(cxcursor::getCursorTU(cursor), 294 refCursor, 295 static_cast<const FileEntry *>(file), 296 visitor); 297 } 298 299 static enum CXVisitorResult _visitCursorAndRange(void *context, 300 CXCursor cursor, 301 CXSourceRange range) { 302 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context; 303 return INVOKE_BLOCK2(block, cursor, range); 304 } 305 306 void clang_findReferencesInFileWithBlock(CXCursor cursor, 307 CXFile file, 308 CXCursorAndRangeVisitorBlock block) { 309 CXCursorAndRangeVisitor visitor = { block, 310 block ? _visitCursorAndRange : 0 }; 311 return clang_findReferencesInFile(cursor, file, visitor); 312 } 313 314 } // end: extern "C" 315 316