blob: 4eabefb9259c29d5bb33df6f6a21fa4bc8e06240 [file] [log] [blame]
Argyrios Kyrtzidiscddafd32011-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
Argyrios Kyrtzidisc504eb32011-11-16 08:58:54 +000010#include "CursorVisitor.h"
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +000011#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;
Argyrios Kyrtzidisc504eb32011-11-16 08:58:54 +000019using namespace cxcursor;
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +000020
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 {
Argyrios Kyrtzidis831411f2011-12-08 01:56:07 +000076 if (!D)
77 return 0;
78
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +000079 D = D->getCanonicalDecl();
80
Argyrios Kyrtzidis831411f2011-12-08 01:56:07 +000081 if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
82 if (ImplD->getClassInterface())
83 return getCanonical(ImplD->getClassInterface());
84
85 } else if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D)) {
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +000086 return getCanonical(CXXCtorD->getParent());
Argyrios Kyrtzidis831411f2011-12-08 01:56:07 +000087 }
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +000088
89 return D;
90 }
91
92 bool isHit(Decl *D) const {
Argyrios Kyrtzidis831411f2011-12-08 01:56:07 +000093 if (!D)
94 return false;
95
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +000096 D = getCanonical(D);
97 if (D == Dcl)
98 return true;
99
100 if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
101 return isOverriddingMethod(D);
102
103 return false;
104 }
105
106private:
107 bool isOverriddingMethod(Decl *D) const {
108 if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
109 TopMethods.end())
110 return true;
111
112 TopMethodsTy methods;
113 getTopOverriddenMethods(TU, D, methods);
114 for (TopMethodsTy::iterator
115 I = methods.begin(), E = methods.end(); I != E; ++I) {
116 if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
117 TopMethods.end())
118 return true;
119 }
120
121 return false;
122 }
123};
124
125} // end anonymous namespace.
126
127/// \brief For a macro \arg Loc, returns the file spelling location and sets
128/// to \arg isMacroArg whether the spelling resides inside a macro definition or
129/// a macro argument.
130static SourceLocation getFileSpellingLoc(SourceManager &SM,
131 SourceLocation Loc,
132 bool &isMacroArg) {
133 assert(Loc.isMacroID());
134 SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
135 if (SpellLoc.isMacroID())
136 return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
137
138 isMacroArg = SM.isMacroArgExpansion(Loc);
139 return SpellLoc;
140}
141
142static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
143 CXCursor parent,
144 CXClientData client_data) {
145 CXCursor declCursor = clang_getCursorReferenced(cursor);
146 if (!clang_isDeclaration(declCursor.kind))
147 return CXChildVisit_Recurse;
148
149 Decl *D = cxcursor::getCursorDecl(declCursor);
150 FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
151 if (data->isHit(D)) {
152 cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
153
154 // We are looking for identifiers to highlight so for objc methods (and
155 // not a parameter) we can only highlight the selector identifiers.
156 if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
157 cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
158 cxcursor::getSelectorIdentifierIndex(cursor) == -1)
159 return CXChildVisit_Recurse;
160
161 if (clang_isExpression(cursor.kind)) {
162 if (cursor.kind == CXCursor_DeclRefExpr ||
163 cursor.kind == CXCursor_MemberRefExpr) {
164 // continue..
165
166 } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
167 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
168 // continue..
169
170 } else
171 return CXChildVisit_Recurse;
172 }
173
174 SourceLocation
175 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
176 SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
177 if (SelIdLoc.isValid())
178 Loc = SelIdLoc;
179
Argyrios Kyrtzidis1ddb97ec2011-11-29 03:14:11 +0000180 ASTContext &Ctx = data->getASTContext();
181 SourceManager &SM = Ctx.getSourceManager();
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +0000182 bool isInMacroDef = false;
183 if (Loc.isMacroID()) {
184 bool isMacroArg;
185 Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
186 isInMacroDef = !isMacroArg;
187 }
188
189 // We are looking for identifiers in a specific file.
190 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
191 if (LocInfo.first != data->FID)
192 return CXChildVisit_Recurse;
193
194 if (isInMacroDef) {
195 // FIXME: For a macro definition make sure that all expansions
196 // of it expand to the same reference before allowing to point to it.
Argyrios Kyrtzidis1ddb97ec2011-11-29 03:14:11 +0000197 return CXChildVisit_Recurse;
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +0000198 }
199
200 data->visitor.visit(data->visitor.context, cursor,
Argyrios Kyrtzidis1ddb97ec2011-11-29 03:14:11 +0000201 cxloc::translateSourceRange(Ctx, Loc));
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +0000202 }
203 return CXChildVisit_Recurse;
204}
205
206static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
207 const FileEntry *File,
208 CXCursorAndRangeVisitor Visitor) {
209 assert(clang_isDeclaration(declCursor.kind));
210 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +0000211 SourceManager &SM = Unit->getSourceManager();
212
213 FileID FID = SM.translateFile(File);
214 Decl *Dcl = cxcursor::getCursorDecl(declCursor);
Argyrios Kyrtzidis831411f2011-12-08 01:56:07 +0000215 if (!Dcl)
216 return;
217
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +0000218 FindFileIdRefVisitData data(TU, FID, Dcl,
219 cxcursor::getSelectorIdentifierIndex(declCursor),
220 Visitor);
221
222 if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
223 clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
224 findFileIdRefVisit, &data);
225 return;
226 }
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +0000227
Argyrios Kyrtzidisc504eb32011-11-16 08:58:54 +0000228 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
229 CursorVisitor FindIdRefsVisitor(TU,
230 findFileIdRefVisit, &data,
231 /*VisitPreprocessorLast=*/true,
232 /*VisitIncludedEntities=*/false,
Argyrios Kyrtzidis1ddb97ec2011-11-29 03:14:11 +0000233 Range,
234 /*VisitDeclsOnly=*/true);
Argyrios Kyrtzidisc504eb32011-11-16 08:58:54 +0000235 FindIdRefsVisitor.visitFileRegion();
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +0000236}
237
Argyrios Kyrtzidis1ddb97ec2011-11-29 03:14:11 +0000238namespace {
239
240struct FindFileMacroRefVisitData {
241 ASTUnit &Unit;
242 const FileEntry *File;
243 const IdentifierInfo *Macro;
244 CXCursorAndRangeVisitor visitor;
245
246 FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
247 const IdentifierInfo *Macro,
248 CXCursorAndRangeVisitor visitor)
249 : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
250
251 ASTContext &getASTContext() const {
252 return Unit.getASTContext();
253 }
254};
255
256} // anonymous namespace
257
258static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
259 CXCursor parent,
260 CXClientData client_data) {
261 const IdentifierInfo *Macro = 0;
262 if (cursor.kind == CXCursor_MacroDefinition)
263 Macro = getCursorMacroDefinition(cursor)->getName();
264 else if (cursor.kind == CXCursor_MacroExpansion)
265 Macro = getCursorMacroExpansion(cursor)->getName();
266 if (!Macro)
267 return CXChildVisit_Continue;
268
269 FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
270 if (data->Macro != Macro)
271 return CXChildVisit_Continue;
272
273 SourceLocation
274 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
275
276 ASTContext &Ctx = data->getASTContext();
277 SourceManager &SM = Ctx.getSourceManager();
278 bool isInMacroDef = false;
279 if (Loc.isMacroID()) {
280 bool isMacroArg;
281 Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
282 isInMacroDef = !isMacroArg;
283 }
284
285 // We are looking for identifiers in a specific file.
286 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
287 if (SM.getFileEntryForID(LocInfo.first) != data->File)
288 return CXChildVisit_Continue;
289
290 if (isInMacroDef) {
291 // FIXME: For a macro definition make sure that all expansions
292 // of it expand to the same reference before allowing to point to it.
293 return CXChildVisit_Continue;
294 }
295
296 data->visitor.visit(data->visitor.context, cursor,
297 cxloc::translateSourceRange(Ctx, Loc));
298 return CXChildVisit_Continue;
299}
300
301static void findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
302 const FileEntry *File,
303 CXCursorAndRangeVisitor Visitor) {
304 if (Cursor.kind != CXCursor_MacroDefinition &&
305 Cursor.kind != CXCursor_MacroExpansion)
306 return;
307
308 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
309 SourceManager &SM = Unit->getSourceManager();
310
311 FileID FID = SM.translateFile(File);
312 const IdentifierInfo *Macro = 0;
313 if (Cursor.kind == CXCursor_MacroDefinition)
314 Macro = getCursorMacroDefinition(Cursor)->getName();
315 else
316 Macro = getCursorMacroExpansion(Cursor)->getName();
317 if (!Macro)
318 return;
319
320 FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
321
322 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
323 CursorVisitor FindMacroRefsVisitor(TU,
324 findFileMacroRefVisit, &data,
325 /*VisitPreprocessorLast=*/false,
326 /*VisitIncludedEntities=*/false,
327 Range);
328 FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
329}
330
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +0000331
332//===----------------------------------------------------------------------===//
333// libclang public APIs.
334//===----------------------------------------------------------------------===//
335
336extern "C" {
337
338void clang_findReferencesInFile(CXCursor cursor, CXFile file,
339 CXCursorAndRangeVisitor visitor) {
340 bool Logging = ::getenv("LIBCLANG_LOGGING");
341
342 if (clang_Cursor_isNull(cursor)) {
343 if (Logging)
344 llvm::errs() << "clang_findReferencesInFile: Null cursor\n";
345 return;
346 }
Argyrios Kyrtzidisfa469d02011-12-09 00:17:49 +0000347 if (cursor.kind == CXCursor_NoDeclFound) {
348 if (Logging)
349 llvm::errs() << "clang_findReferencesInFile: Got CXCursor_NoDeclFound\n";
350 return;
351 }
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +0000352 if (!file) {
353 if (Logging)
354 llvm::errs() << "clang_findReferencesInFile: Null file\n";
355 return;
356 }
357 if (!visitor.visit) {
358 if (Logging)
359 llvm::errs() << "clang_findReferencesInFile: Null visitor\n";
360 return;
361 }
362
Argyrios Kyrtzidisebbb2062011-11-29 23:21:50 +0000363 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
364 if (!CXXUnit)
365 return;
366
367 ASTUnit::ConcurrencyCheck Check(*CXXUnit);
368
Argyrios Kyrtzidis1ddb97ec2011-11-29 03:14:11 +0000369 if (cursor.kind == CXCursor_MacroDefinition ||
370 cursor.kind == CXCursor_MacroExpansion) {
371 findMacroRefsInFile(cxcursor::getCursorTU(cursor),
372 cursor,
373 static_cast<const FileEntry *>(file),
374 visitor);
375 return;
376 }
377
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +0000378 // We are interested in semantics of identifiers so for C++ constructor exprs
379 // prefer type references, e.g.:
380 //
381 // return MyStruct();
382 //
383 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
384 // we are actually interested in the type declaration.
385 cursor = cxcursor::getTypeRefCursor(cursor);
386
387 CXCursor refCursor = clang_getCursorReferenced(cursor);
388
389 if (!clang_isDeclaration(refCursor.kind)) {
390 if (Logging)
391 llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a "
392 "declaration\n";
393 return;
394 }
395
Argyrios Kyrtzidiscddafd32011-10-06 07:00:54 +0000396 findIdRefsInFile(cxcursor::getCursorTU(cursor),
397 refCursor,
398 static_cast<const FileEntry *>(file),
399 visitor);
400}
401
402static enum CXVisitorResult _visitCursorAndRange(void *context,
403 CXCursor cursor,
404 CXSourceRange range) {
405 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
406 return INVOKE_BLOCK2(block, cursor, range);
407}
408
409void clang_findReferencesInFileWithBlock(CXCursor cursor,
410 CXFile file,
411 CXCursorAndRangeVisitorBlock block) {
412 CXCursorAndRangeVisitor visitor = { block,
413 block ? _visitCursorAndRange : 0 };
414 return clang_findReferencesInFile(cursor, file, visitor);
415}
416
417} // end: extern "C"
418