blob: ec76898cc83b7a939aa78a6b4ec1dc8500e62f4a [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
Argyrios Kyrtzidise2079cf2011-11-16 08:58:54 +000010#include "CursorVisitor.h"
Argyrios Kyrtzidisaed123e2011-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 Kyrtzidise2079cf2011-11-16 08:58:54 +000019using namespace cxcursor;
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +000020
21static void getTopOverriddenMethods(CXTranslationUnit TU,
22 Decl *D,
23 SmallVectorImpl<Decl *> &Methods) {
Argyrios Kyrtzidis16ed0e62011-12-10 02:36:25 +000024 if (!D)
25 return;
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +000026 if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
27 return;
28
29 SmallVector<CXCursor, 8> Overridden;
30 cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
31
32 if (Overridden.empty()) {
33 Methods.push_back(D->getCanonicalDecl());
34 return;
35 }
36
37 for (SmallVector<CXCursor, 8>::iterator
38 I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
39 getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
40}
41
42namespace {
43
44struct FindFileIdRefVisitData {
45 CXTranslationUnit TU;
46 FileID FID;
47 Decl *Dcl;
48 int SelectorIdIdx;
49 CXCursorAndRangeVisitor visitor;
50
51 typedef SmallVector<Decl *, 8> TopMethodsTy;
52 TopMethodsTy TopMethods;
53
54 FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
55 Decl *D, int selectorIdIdx,
56 CXCursorAndRangeVisitor visitor)
57 : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
58 Dcl = getCanonical(D);
59 getTopOverriddenMethods(TU, Dcl, TopMethods);
60 }
61
62 ASTContext &getASTContext() const {
63 return static_cast<ASTUnit *>(TU->TUData)->getASTContext();
64 }
65
66 /// \brief We are looking to find all semantically relevant identifiers,
67 /// so the definition of "canonical" here is different than in the AST, e.g.
68 ///
69 /// \code
70 /// class C {
71 /// C() {}
72 /// };
73 /// \endcode
74 ///
75 /// we consider the canonical decl of the constructor decl to be the class
76 /// itself, so both 'C' can be highlighted.
77 Decl *getCanonical(Decl *D) const {
Argyrios Kyrtzidise368a642011-12-08 01:56:07 +000078 if (!D)
79 return 0;
80
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +000081 D = D->getCanonicalDecl();
82
Argyrios Kyrtzidise368a642011-12-08 01:56:07 +000083 if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
84 if (ImplD->getClassInterface())
85 return getCanonical(ImplD->getClassInterface());
86
87 } else if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D)) {
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +000088 return getCanonical(CXXCtorD->getParent());
Argyrios Kyrtzidise368a642011-12-08 01:56:07 +000089 }
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +000090
91 return D;
92 }
93
94 bool isHit(Decl *D) const {
Argyrios Kyrtzidise368a642011-12-08 01:56:07 +000095 if (!D)
96 return false;
97
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +000098 D = getCanonical(D);
99 if (D == Dcl)
100 return true;
101
102 if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
103 return isOverriddingMethod(D);
104
105 return false;
106 }
107
108private:
109 bool isOverriddingMethod(Decl *D) const {
110 if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
111 TopMethods.end())
112 return true;
113
114 TopMethodsTy methods;
115 getTopOverriddenMethods(TU, D, methods);
116 for (TopMethodsTy::iterator
117 I = methods.begin(), E = methods.end(); I != E; ++I) {
118 if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
119 TopMethods.end())
120 return true;
121 }
122
123 return false;
124 }
125};
126
127} // end anonymous namespace.
128
129/// \brief For a macro \arg Loc, returns the file spelling location and sets
130/// to \arg isMacroArg whether the spelling resides inside a macro definition or
131/// a macro argument.
132static SourceLocation getFileSpellingLoc(SourceManager &SM,
133 SourceLocation Loc,
134 bool &isMacroArg) {
135 assert(Loc.isMacroID());
136 SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
137 if (SpellLoc.isMacroID())
138 return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
139
140 isMacroArg = SM.isMacroArgExpansion(Loc);
141 return SpellLoc;
142}
143
144static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
145 CXCursor parent,
146 CXClientData client_data) {
147 CXCursor declCursor = clang_getCursorReferenced(cursor);
148 if (!clang_isDeclaration(declCursor.kind))
149 return CXChildVisit_Recurse;
150
151 Decl *D = cxcursor::getCursorDecl(declCursor);
Argyrios Kyrtzidis16ed0e62011-12-10 02:36:25 +0000152 if (!D)
153 return CXChildVisit_Continue;
154
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000155 FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
156 if (data->isHit(D)) {
157 cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
158
159 // We are looking for identifiers to highlight so for objc methods (and
160 // not a parameter) we can only highlight the selector identifiers.
161 if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
162 cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
163 cxcursor::getSelectorIdentifierIndex(cursor) == -1)
164 return CXChildVisit_Recurse;
165
166 if (clang_isExpression(cursor.kind)) {
167 if (cursor.kind == CXCursor_DeclRefExpr ||
168 cursor.kind == CXCursor_MemberRefExpr) {
169 // continue..
170
171 } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
172 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
173 // continue..
174
175 } else
176 return CXChildVisit_Recurse;
177 }
178
179 SourceLocation
180 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
181 SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
182 if (SelIdLoc.isValid())
183 Loc = SelIdLoc;
184
Argyrios Kyrtzidisb49e7282011-11-29 03:14:11 +0000185 ASTContext &Ctx = data->getASTContext();
186 SourceManager &SM = Ctx.getSourceManager();
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000187 bool isInMacroDef = false;
188 if (Loc.isMacroID()) {
189 bool isMacroArg;
190 Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
191 isInMacroDef = !isMacroArg;
192 }
193
194 // We are looking for identifiers in a specific file.
195 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
196 if (LocInfo.first != data->FID)
197 return CXChildVisit_Recurse;
198
199 if (isInMacroDef) {
200 // FIXME: For a macro definition make sure that all expansions
201 // of it expand to the same reference before allowing to point to it.
Argyrios Kyrtzidisb49e7282011-11-29 03:14:11 +0000202 return CXChildVisit_Recurse;
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000203 }
204
205 data->visitor.visit(data->visitor.context, cursor,
Argyrios Kyrtzidisb49e7282011-11-29 03:14:11 +0000206 cxloc::translateSourceRange(Ctx, Loc));
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000207 }
208 return CXChildVisit_Recurse;
209}
210
211static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
212 const FileEntry *File,
213 CXCursorAndRangeVisitor Visitor) {
214 assert(clang_isDeclaration(declCursor.kind));
215 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000216 SourceManager &SM = Unit->getSourceManager();
217
218 FileID FID = SM.translateFile(File);
219 Decl *Dcl = cxcursor::getCursorDecl(declCursor);
Argyrios Kyrtzidise368a642011-12-08 01:56:07 +0000220 if (!Dcl)
221 return;
222
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000223 FindFileIdRefVisitData data(TU, FID, Dcl,
224 cxcursor::getSelectorIdentifierIndex(declCursor),
225 Visitor);
226
227 if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
228 clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
229 findFileIdRefVisit, &data);
230 return;
231 }
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000232
Argyrios Kyrtzidise2079cf2011-11-16 08:58:54 +0000233 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
234 CursorVisitor FindIdRefsVisitor(TU,
235 findFileIdRefVisit, &data,
236 /*VisitPreprocessorLast=*/true,
237 /*VisitIncludedEntities=*/false,
Argyrios Kyrtzidisb49e7282011-11-29 03:14:11 +0000238 Range,
239 /*VisitDeclsOnly=*/true);
Argyrios Kyrtzidise2079cf2011-11-16 08:58:54 +0000240 FindIdRefsVisitor.visitFileRegion();
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000241}
242
Argyrios Kyrtzidisb49e7282011-11-29 03:14:11 +0000243namespace {
244
245struct FindFileMacroRefVisitData {
246 ASTUnit &Unit;
247 const FileEntry *File;
248 const IdentifierInfo *Macro;
249 CXCursorAndRangeVisitor visitor;
250
251 FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
252 const IdentifierInfo *Macro,
253 CXCursorAndRangeVisitor visitor)
254 : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
255
256 ASTContext &getASTContext() const {
257 return Unit.getASTContext();
258 }
259};
260
261} // anonymous namespace
262
263static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
264 CXCursor parent,
265 CXClientData client_data) {
266 const IdentifierInfo *Macro = 0;
267 if (cursor.kind == CXCursor_MacroDefinition)
268 Macro = getCursorMacroDefinition(cursor)->getName();
269 else if (cursor.kind == CXCursor_MacroExpansion)
270 Macro = getCursorMacroExpansion(cursor)->getName();
271 if (!Macro)
272 return CXChildVisit_Continue;
273
274 FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
275 if (data->Macro != Macro)
276 return CXChildVisit_Continue;
277
278 SourceLocation
279 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
280
281 ASTContext &Ctx = data->getASTContext();
282 SourceManager &SM = Ctx.getSourceManager();
283 bool isInMacroDef = false;
284 if (Loc.isMacroID()) {
285 bool isMacroArg;
286 Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
287 isInMacroDef = !isMacroArg;
288 }
289
290 // We are looking for identifiers in a specific file.
291 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
292 if (SM.getFileEntryForID(LocInfo.first) != data->File)
293 return CXChildVisit_Continue;
294
295 if (isInMacroDef) {
296 // FIXME: For a macro definition make sure that all expansions
297 // of it expand to the same reference before allowing to point to it.
298 return CXChildVisit_Continue;
299 }
300
301 data->visitor.visit(data->visitor.context, cursor,
302 cxloc::translateSourceRange(Ctx, Loc));
303 return CXChildVisit_Continue;
304}
305
306static void findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
307 const FileEntry *File,
308 CXCursorAndRangeVisitor Visitor) {
309 if (Cursor.kind != CXCursor_MacroDefinition &&
310 Cursor.kind != CXCursor_MacroExpansion)
311 return;
312
313 ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
314 SourceManager &SM = Unit->getSourceManager();
315
316 FileID FID = SM.translateFile(File);
317 const IdentifierInfo *Macro = 0;
318 if (Cursor.kind == CXCursor_MacroDefinition)
319 Macro = getCursorMacroDefinition(Cursor)->getName();
320 else
321 Macro = getCursorMacroExpansion(Cursor)->getName();
322 if (!Macro)
323 return;
324
325 FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
326
327 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
328 CursorVisitor FindMacroRefsVisitor(TU,
329 findFileMacroRefVisit, &data,
330 /*VisitPreprocessorLast=*/false,
331 /*VisitIncludedEntities=*/false,
332 Range);
333 FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
334}
335
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000336
337//===----------------------------------------------------------------------===//
338// libclang public APIs.
339//===----------------------------------------------------------------------===//
340
341extern "C" {
342
343void clang_findReferencesInFile(CXCursor cursor, CXFile file,
344 CXCursorAndRangeVisitor visitor) {
345 bool Logging = ::getenv("LIBCLANG_LOGGING");
346
347 if (clang_Cursor_isNull(cursor)) {
348 if (Logging)
349 llvm::errs() << "clang_findReferencesInFile: Null cursor\n";
350 return;
351 }
Argyrios Kyrtzidis44517462011-12-09 00:17:49 +0000352 if (cursor.kind == CXCursor_NoDeclFound) {
353 if (Logging)
354 llvm::errs() << "clang_findReferencesInFile: Got CXCursor_NoDeclFound\n";
355 return;
356 }
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000357 if (!file) {
358 if (Logging)
359 llvm::errs() << "clang_findReferencesInFile: Null file\n";
360 return;
361 }
362 if (!visitor.visit) {
363 if (Logging)
364 llvm::errs() << "clang_findReferencesInFile: Null visitor\n";
365 return;
366 }
367
Argyrios Kyrtzidis5c2a1f72011-11-29 23:21:50 +0000368 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
369 if (!CXXUnit)
370 return;
371
372 ASTUnit::ConcurrencyCheck Check(*CXXUnit);
373
Argyrios Kyrtzidisb49e7282011-11-29 03:14:11 +0000374 if (cursor.kind == CXCursor_MacroDefinition ||
375 cursor.kind == CXCursor_MacroExpansion) {
376 findMacroRefsInFile(cxcursor::getCursorTU(cursor),
377 cursor,
378 static_cast<const FileEntry *>(file),
379 visitor);
380 return;
381 }
382
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000383 // We are interested in semantics of identifiers so for C++ constructor exprs
384 // prefer type references, e.g.:
385 //
386 // return MyStruct();
387 //
388 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
389 // we are actually interested in the type declaration.
390 cursor = cxcursor::getTypeRefCursor(cursor);
391
392 CXCursor refCursor = clang_getCursorReferenced(cursor);
393
394 if (!clang_isDeclaration(refCursor.kind)) {
395 if (Logging)
396 llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a "
397 "declaration\n";
398 return;
399 }
400
Argyrios Kyrtzidisaed123e2011-10-06 07:00:54 +0000401 findIdRefsInFile(cxcursor::getCursorTU(cursor),
402 refCursor,
403 static_cast<const FileEntry *>(file),
404 visitor);
405}
406
407static enum CXVisitorResult _visitCursorAndRange(void *context,
408 CXCursor cursor,
409 CXSourceRange range) {
410 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
411 return INVOKE_BLOCK2(block, cursor, range);
412}
413
414void clang_findReferencesInFileWithBlock(CXCursor cursor,
415 CXFile file,
416 CXCursorAndRangeVisitorBlock block) {
417 CXCursorAndRangeVisitor visitor = { block,
418 block ? _visitCursorAndRange : 0 };
419 return clang_findReferencesInFile(cursor, file, visitor);
420}
421
422} // end: extern "C"
423