blob: b5a05eaafc0ba2b39e51742c8b8aab5219fd0ca3 [file] [log] [blame]
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +00001//===- 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
18using namespace clang;
19
20static 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
39namespace {
40
41struct 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
96private:
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.
120static 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
132static 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
195static 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
250extern "C" {
251
252void 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
299static 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
306void 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