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