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 "CLog.h"
12 #include "CXCursor.h"
13 #include "CXSourceLocation.h"
14 #include "CXTranslationUnit.h"
15 #include "clang/AST/DeclObjC.h"
16 #include "clang/Frontend/ASTUnit.h"
17 #include "llvm/Support/Compiler.h"
18
19 using namespace clang;
20 using namespace cxcursor;
21 using namespace cxindex;
22
getTopOverriddenMethods(CXTranslationUnit TU,const Decl * D,SmallVectorImpl<const Decl * > & Methods)23 static void getTopOverriddenMethods(CXTranslationUnit TU,
24 const Decl *D,
25 SmallVectorImpl<const Decl *> &Methods) {
26 if (!D)
27 return;
28 if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
29 return;
30
31 SmallVector<CXCursor, 8> Overridden;
32 cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
33
34 if (Overridden.empty()) {
35 Methods.push_back(D->getCanonicalDecl());
36 return;
37 }
38
39 for (SmallVectorImpl<CXCursor>::iterator
40 I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
41 getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
42 }
43
44 namespace {
45
46 struct FindFileIdRefVisitData {
47 CXTranslationUnit TU;
48 FileID FID;
49 const Decl *Dcl;
50 int SelectorIdIdx;
51 CXCursorAndRangeVisitor visitor;
52
53 typedef SmallVector<const Decl *, 8> TopMethodsTy;
54 TopMethodsTy TopMethods;
55
FindFileIdRefVisitData__anon419fd7e00111::FindFileIdRefVisitData56 FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
57 const Decl *D, int selectorIdIdx,
58 CXCursorAndRangeVisitor visitor)
59 : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
60 Dcl = getCanonical(D);
61 getTopOverriddenMethods(TU, Dcl, TopMethods);
62 }
63
getASTContext__anon419fd7e00111::FindFileIdRefVisitData64 ASTContext &getASTContext() const {
65 return cxtu::getASTUnit(TU)->getASTContext();
66 }
67
68 /// \brief We are looking to find all semantically relevant identifiers,
69 /// so the definition of "canonical" here is different than in the AST, e.g.
70 ///
71 /// \code
72 /// class C {
73 /// C() {}
74 /// };
75 /// \endcode
76 ///
77 /// we consider the canonical decl of the constructor decl to be the class
78 /// itself, so both 'C' can be highlighted.
getCanonical__anon419fd7e00111::FindFileIdRefVisitData79 const Decl *getCanonical(const Decl *D) const {
80 if (!D)
81 return nullptr;
82
83 D = D->getCanonicalDecl();
84
85 if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
86 if (ImplD->getClassInterface())
87 return getCanonical(ImplD->getClassInterface());
88
89 } else if (const CXXConstructorDecl *CXXCtorD =
90 dyn_cast<CXXConstructorDecl>(D)) {
91 return getCanonical(CXXCtorD->getParent());
92 }
93
94 return D;
95 }
96
isHit__anon419fd7e00111::FindFileIdRefVisitData97 bool isHit(const Decl *D) const {
98 if (!D)
99 return false;
100
101 D = getCanonical(D);
102 if (D == Dcl)
103 return true;
104
105 if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
106 return isOverriddingMethod(D);
107
108 return false;
109 }
110
111 private:
isOverriddingMethod__anon419fd7e00111::FindFileIdRefVisitData112 bool isOverriddingMethod(const Decl *D) const {
113 if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
114 TopMethods.end())
115 return true;
116
117 TopMethodsTy methods;
118 getTopOverriddenMethods(TU, D, methods);
119 for (TopMethodsTy::iterator
120 I = methods.begin(), E = methods.end(); I != E; ++I) {
121 if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
122 TopMethods.end())
123 return true;
124 }
125
126 return false;
127 }
128 };
129
130 } // end anonymous namespace.
131
132 /// \brief For a macro \arg Loc, returns the file spelling location and sets
133 /// to \arg isMacroArg whether the spelling resides inside a macro definition or
134 /// a macro argument.
getFileSpellingLoc(SourceManager & SM,SourceLocation Loc,bool & isMacroArg)135 static SourceLocation getFileSpellingLoc(SourceManager &SM,
136 SourceLocation Loc,
137 bool &isMacroArg) {
138 assert(Loc.isMacroID());
139 SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
140 if (SpellLoc.isMacroID())
141 return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
142
143 isMacroArg = SM.isMacroArgExpansion(Loc);
144 return SpellLoc;
145 }
146
findFileIdRefVisit(CXCursor cursor,CXCursor parent,CXClientData client_data)147 static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
148 CXCursor parent,
149 CXClientData client_data) {
150 CXCursor declCursor = clang_getCursorReferenced(cursor);
151 if (!clang_isDeclaration(declCursor.kind))
152 return CXChildVisit_Recurse;
153
154 const Decl *D = cxcursor::getCursorDecl(declCursor);
155 if (!D)
156 return CXChildVisit_Continue;
157
158 FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
159 if (data->isHit(D)) {
160 cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
161
162 // We are looking for identifiers to highlight so for objc methods (and
163 // not a parameter) we can only highlight the selector identifiers.
164 if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
165 cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
166 cxcursor::getSelectorIdentifierIndex(cursor) == -1)
167 return CXChildVisit_Recurse;
168
169 if (clang_isExpression(cursor.kind)) {
170 if (cursor.kind == CXCursor_DeclRefExpr ||
171 cursor.kind == CXCursor_MemberRefExpr) {
172 // continue..
173
174 } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
175 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
176 // continue..
177
178 } else
179 return CXChildVisit_Recurse;
180 }
181
182 SourceLocation
183 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
184 SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
185 if (SelIdLoc.isValid())
186 Loc = SelIdLoc;
187
188 ASTContext &Ctx = data->getASTContext();
189 SourceManager &SM = Ctx.getSourceManager();
190 bool isInMacroDef = false;
191 if (Loc.isMacroID()) {
192 bool isMacroArg;
193 Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
194 isInMacroDef = !isMacroArg;
195 }
196
197 // We are looking for identifiers in a specific file.
198 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
199 if (LocInfo.first != data->FID)
200 return CXChildVisit_Recurse;
201
202 if (isInMacroDef) {
203 // FIXME: For a macro definition make sure that all expansions
204 // of it expand to the same reference before allowing to point to it.
205 return CXChildVisit_Recurse;
206 }
207
208 if (data->visitor.visit(data->visitor.context, cursor,
209 cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
210 return CXChildVisit_Break;
211 }
212 return CXChildVisit_Recurse;
213 }
214
findIdRefsInFile(CXTranslationUnit TU,CXCursor declCursor,const FileEntry * File,CXCursorAndRangeVisitor Visitor)215 static bool findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
216 const FileEntry *File,
217 CXCursorAndRangeVisitor Visitor) {
218 assert(clang_isDeclaration(declCursor.kind));
219 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
220
221 FileID FID = SM.translateFile(File);
222 const Decl *Dcl = cxcursor::getCursorDecl(declCursor);
223 if (!Dcl)
224 return false;
225
226 FindFileIdRefVisitData data(TU, FID, Dcl,
227 cxcursor::getSelectorIdentifierIndex(declCursor),
228 Visitor);
229
230 if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
231 return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
232 findFileIdRefVisit, &data);
233 }
234
235 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
236 CursorVisitor FindIdRefsVisitor(TU,
237 findFileIdRefVisit, &data,
238 /*VisitPreprocessorLast=*/true,
239 /*VisitIncludedEntities=*/false,
240 Range,
241 /*VisitDeclsOnly=*/true);
242 return FindIdRefsVisitor.visitFileRegion();
243 }
244
245 namespace {
246
247 struct FindFileMacroRefVisitData {
248 ASTUnit &Unit;
249 const FileEntry *File;
250 const IdentifierInfo *Macro;
251 CXCursorAndRangeVisitor visitor;
252
FindFileMacroRefVisitData__anon419fd7e00211::FindFileMacroRefVisitData253 FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
254 const IdentifierInfo *Macro,
255 CXCursorAndRangeVisitor visitor)
256 : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
257
getASTContext__anon419fd7e00211::FindFileMacroRefVisitData258 ASTContext &getASTContext() const {
259 return Unit.getASTContext();
260 }
261 };
262
263 } // anonymous namespace
264
findFileMacroRefVisit(CXCursor cursor,CXCursor parent,CXClientData client_data)265 static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
266 CXCursor parent,
267 CXClientData client_data) {
268 const IdentifierInfo *Macro = nullptr;
269 if (cursor.kind == CXCursor_MacroDefinition)
270 Macro = getCursorMacroDefinition(cursor)->getName();
271 else if (cursor.kind == CXCursor_MacroExpansion)
272 Macro = getCursorMacroExpansion(cursor).getName();
273 if (!Macro)
274 return CXChildVisit_Continue;
275
276 FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
277 if (data->Macro != Macro)
278 return CXChildVisit_Continue;
279
280 SourceLocation
281 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
282
283 ASTContext &Ctx = data->getASTContext();
284 SourceManager &SM = Ctx.getSourceManager();
285 bool isInMacroDef = false;
286 if (Loc.isMacroID()) {
287 bool isMacroArg;
288 Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
289 isInMacroDef = !isMacroArg;
290 }
291
292 // We are looking for identifiers in a specific file.
293 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
294 if (SM.getFileEntryForID(LocInfo.first) != data->File)
295 return CXChildVisit_Continue;
296
297 if (isInMacroDef) {
298 // FIXME: For a macro definition make sure that all expansions
299 // of it expand to the same reference before allowing to point to it.
300 return CXChildVisit_Continue;
301 }
302
303 if (data->visitor.visit(data->visitor.context, cursor,
304 cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
305 return CXChildVisit_Break;
306 return CXChildVisit_Continue;
307 }
308
findMacroRefsInFile(CXTranslationUnit TU,CXCursor Cursor,const FileEntry * File,CXCursorAndRangeVisitor Visitor)309 static bool findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
310 const FileEntry *File,
311 CXCursorAndRangeVisitor Visitor) {
312 if (Cursor.kind != CXCursor_MacroDefinition &&
313 Cursor.kind != CXCursor_MacroExpansion)
314 return false;
315
316 ASTUnit *Unit = cxtu::getASTUnit(TU);
317 SourceManager &SM = Unit->getSourceManager();
318
319 FileID FID = SM.translateFile(File);
320 const IdentifierInfo *Macro = nullptr;
321 if (Cursor.kind == CXCursor_MacroDefinition)
322 Macro = getCursorMacroDefinition(Cursor)->getName();
323 else
324 Macro = getCursorMacroExpansion(Cursor).getName();
325 if (!Macro)
326 return false;
327
328 FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
329
330 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
331 CursorVisitor FindMacroRefsVisitor(TU,
332 findFileMacroRefVisit, &data,
333 /*VisitPreprocessorLast=*/false,
334 /*VisitIncludedEntities=*/false,
335 Range);
336 return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
337 }
338
339 namespace {
340
341 struct FindFileIncludesVisitor {
342 ASTUnit &Unit;
343 const FileEntry *File;
344 CXCursorAndRangeVisitor visitor;
345
FindFileIncludesVisitor__anon419fd7e00311::FindFileIncludesVisitor346 FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File,
347 CXCursorAndRangeVisitor visitor)
348 : Unit(Unit), File(File), visitor(visitor) { }
349
getASTContext__anon419fd7e00311::FindFileIncludesVisitor350 ASTContext &getASTContext() const {
351 return Unit.getASTContext();
352 }
353
visit__anon419fd7e00311::FindFileIncludesVisitor354 enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) {
355 if (cursor.kind != CXCursor_InclusionDirective)
356 return CXChildVisit_Continue;
357
358 SourceLocation
359 Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
360
361 ASTContext &Ctx = getASTContext();
362 SourceManager &SM = Ctx.getSourceManager();
363
364 // We are looking for includes in a specific file.
365 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
366 if (SM.getFileEntryForID(LocInfo.first) != File)
367 return CXChildVisit_Continue;
368
369 if (visitor.visit(visitor.context, cursor,
370 cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
371 return CXChildVisit_Break;
372 return CXChildVisit_Continue;
373 }
374
visit__anon419fd7e00311::FindFileIncludesVisitor375 static enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent,
376 CXClientData client_data) {
377 return static_cast<FindFileIncludesVisitor*>(client_data)->
378 visit(cursor, parent);
379 }
380 };
381
382 } // anonymous namespace
383
findIncludesInFile(CXTranslationUnit TU,const FileEntry * File,CXCursorAndRangeVisitor Visitor)384 static bool findIncludesInFile(CXTranslationUnit TU, const FileEntry *File,
385 CXCursorAndRangeVisitor Visitor) {
386 assert(TU && File && Visitor.visit);
387
388 ASTUnit *Unit = cxtu::getASTUnit(TU);
389 SourceManager &SM = Unit->getSourceManager();
390
391 FileID FID = SM.translateFile(File);
392
393 FindFileIncludesVisitor IncludesVisitor(*Unit, File, Visitor);
394
395 SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
396 CursorVisitor InclusionCursorsVisitor(TU,
397 FindFileIncludesVisitor::visit,
398 &IncludesVisitor,
399 /*VisitPreprocessorLast=*/false,
400 /*VisitIncludedEntities=*/false,
401 Range);
402 return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion();
403 }
404
405
406 //===----------------------------------------------------------------------===//
407 // libclang public APIs.
408 //===----------------------------------------------------------------------===//
409
410 extern "C" {
411
clang_findReferencesInFile(CXCursor cursor,CXFile file,CXCursorAndRangeVisitor visitor)412 CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file,
413 CXCursorAndRangeVisitor visitor) {
414 LogRef Log = Logger::make(LLVM_FUNCTION_NAME);
415
416 if (clang_Cursor_isNull(cursor)) {
417 if (Log)
418 *Log << "Null cursor";
419 return CXResult_Invalid;
420 }
421 if (cursor.kind == CXCursor_NoDeclFound) {
422 if (Log)
423 *Log << "Got CXCursor_NoDeclFound";
424 return CXResult_Invalid;
425 }
426 if (!file) {
427 if (Log)
428 *Log << "Null file";
429 return CXResult_Invalid;
430 }
431 if (!visitor.visit) {
432 if (Log)
433 *Log << "Null visitor";
434 return CXResult_Invalid;
435 }
436
437 if (Log)
438 *Log << cursor << " @" << static_cast<const FileEntry *>(file);
439
440 ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
441 if (!CXXUnit)
442 return CXResult_Invalid;
443
444 ASTUnit::ConcurrencyCheck Check(*CXXUnit);
445
446 if (cursor.kind == CXCursor_MacroDefinition ||
447 cursor.kind == CXCursor_MacroExpansion) {
448 if (findMacroRefsInFile(cxcursor::getCursorTU(cursor),
449 cursor,
450 static_cast<const FileEntry *>(file),
451 visitor))
452 return CXResult_VisitBreak;
453 return CXResult_Success;
454 }
455
456 // We are interested in semantics of identifiers so for C++ constructor exprs
457 // prefer type references, e.g.:
458 //
459 // return MyStruct();
460 //
461 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
462 // we are actually interested in the type declaration.
463 cursor = cxcursor::getTypeRefCursor(cursor);
464
465 CXCursor refCursor = clang_getCursorReferenced(cursor);
466
467 if (!clang_isDeclaration(refCursor.kind)) {
468 if (Log)
469 *Log << "cursor is not referencing a declaration";
470 return CXResult_Invalid;
471 }
472
473 if (findIdRefsInFile(cxcursor::getCursorTU(cursor),
474 refCursor,
475 static_cast<const FileEntry *>(file),
476 visitor))
477 return CXResult_VisitBreak;
478 return CXResult_Success;
479 }
480
clang_findIncludesInFile(CXTranslationUnit TU,CXFile file,CXCursorAndRangeVisitor visitor)481 CXResult clang_findIncludesInFile(CXTranslationUnit TU, CXFile file,
482 CXCursorAndRangeVisitor visitor) {
483 if (cxtu::isNotUsableTU(TU)) {
484 LOG_BAD_TU(TU);
485 return CXResult_Invalid;
486 }
487
488 LogRef Log = Logger::make(LLVM_FUNCTION_NAME);
489 if (!file) {
490 if (Log)
491 *Log << "Null file";
492 return CXResult_Invalid;
493 }
494 if (!visitor.visit) {
495 if (Log)
496 *Log << "Null visitor";
497 return CXResult_Invalid;
498 }
499
500 if (Log)
501 *Log << TU << " @" << static_cast<const FileEntry *>(file);
502
503 ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
504 if (!CXXUnit)
505 return CXResult_Invalid;
506
507 ASTUnit::ConcurrencyCheck Check(*CXXUnit);
508
509 if (findIncludesInFile(TU, static_cast<const FileEntry *>(file), visitor))
510 return CXResult_VisitBreak;
511 return CXResult_Success;
512 }
513
_visitCursorAndRange(void * context,CXCursor cursor,CXSourceRange range)514 static enum CXVisitorResult _visitCursorAndRange(void *context,
515 CXCursor cursor,
516 CXSourceRange range) {
517 CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
518 return INVOKE_BLOCK2(block, cursor, range);
519 }
520
clang_findReferencesInFileWithBlock(CXCursor cursor,CXFile file,CXCursorAndRangeVisitorBlock block)521 CXResult clang_findReferencesInFileWithBlock(CXCursor cursor,
522 CXFile file,
523 CXCursorAndRangeVisitorBlock block) {
524 CXCursorAndRangeVisitor visitor = { block,
525 block ? _visitCursorAndRange : nullptr };
526 return clang_findReferencesInFile(cursor, file, visitor);
527 }
528
clang_findIncludesInFileWithBlock(CXTranslationUnit TU,CXFile file,CXCursorAndRangeVisitorBlock block)529 CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU,
530 CXFile file,
531 CXCursorAndRangeVisitorBlock block) {
532 CXCursorAndRangeVisitor visitor = { block,
533 block ? _visitCursorAndRange : nullptr };
534 return clang_findIncludesInFile(TU, file, visitor);
535 }
536
537 } // end: extern "C"
538
539