CIndexHigh.cpp revision e2079cf54ded1eda9e35d215aef6628373368276
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 SourceManager &SM = data->getASTContext().getSourceManager(); 172 bool isInMacroDef = false; 173 if (Loc.isMacroID()) { 174 bool isMacroArg; 175 Loc = getFileSpellingLoc(SM, Loc, isMacroArg); 176 isInMacroDef = !isMacroArg; 177 } 178 179 // We are looking for identifiers in a specific file. 180 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 181 if (LocInfo.first != data->FID) 182 return CXChildVisit_Recurse; 183 184 if (isInMacroDef) { 185 // FIXME: For a macro definition make sure that all expansions 186 // of it expand to the same reference before allowing to point to it. 187 Loc = SourceLocation(); 188 } 189 190 data->visitor.visit(data->visitor.context, cursor, 191 cxloc::translateSourceRange(D->getASTContext(), Loc)); 192 } 193 return CXChildVisit_Recurse; 194} 195 196static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor, 197 const FileEntry *File, 198 CXCursorAndRangeVisitor Visitor) { 199 assert(clang_isDeclaration(declCursor.kind)); 200 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData); 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 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); 216 CursorVisitor FindIdRefsVisitor(TU, 217 findFileIdRefVisit, &data, 218 /*VisitPreprocessorLast=*/true, 219 /*VisitIncludedEntities=*/false, 220 Range); 221 FindIdRefsVisitor.visitFileRegion(); 222} 223 224 225//===----------------------------------------------------------------------===// 226// libclang public APIs. 227//===----------------------------------------------------------------------===// 228 229extern "C" { 230 231void clang_findReferencesInFile(CXCursor cursor, CXFile file, 232 CXCursorAndRangeVisitor visitor) { 233 bool Logging = ::getenv("LIBCLANG_LOGGING"); 234 235 if (clang_Cursor_isNull(cursor)) { 236 if (Logging) 237 llvm::errs() << "clang_findReferencesInFile: Null cursor\n"; 238 return; 239 } 240 if (!file) { 241 if (Logging) 242 llvm::errs() << "clang_findReferencesInFile: Null file\n"; 243 return; 244 } 245 if (!visitor.visit) { 246 if (Logging) 247 llvm::errs() << "clang_findReferencesInFile: Null visitor\n"; 248 return; 249 } 250 251 // We are interested in semantics of identifiers so for C++ constructor exprs 252 // prefer type references, e.g.: 253 // 254 // return MyStruct(); 255 // 256 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but 257 // we are actually interested in the type declaration. 258 cursor = cxcursor::getTypeRefCursor(cursor); 259 260 CXCursor refCursor = clang_getCursorReferenced(cursor); 261 262 if (!clang_isDeclaration(refCursor.kind)) { 263 if (Logging) 264 llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a " 265 "declaration\n"; 266 return; 267 } 268 269 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor); 270 ASTUnit::ConcurrencyCheck Check(*CXXUnit); 271 272 findIdRefsInFile(cxcursor::getCursorTU(cursor), 273 refCursor, 274 static_cast<const FileEntry *>(file), 275 visitor); 276} 277 278static enum CXVisitorResult _visitCursorAndRange(void *context, 279 CXCursor cursor, 280 CXSourceRange range) { 281 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context; 282 return INVOKE_BLOCK2(block, cursor, range); 283} 284 285void clang_findReferencesInFileWithBlock(CXCursor cursor, 286 CXFile file, 287 CXCursorAndRangeVisitorBlock block) { 288 CXCursorAndRangeVisitor visitor = { block, 289 block ? _visitCursorAndRange : 0 }; 290 return clang_findReferencesInFile(cursor, file, visitor); 291} 292 293} // end: extern "C" 294 295