1 //===- CIndexHigh.cpp - Higher level API functions ------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "CursorVisitor.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"
19 using namespace clang
;
20 using namespace cxcursor
;
21 using namespace cxindex
;
23 static void getTopOverriddenMethods(CXTranslationUnit TU
,
25 SmallVectorImpl
<const Decl
*> &Methods
) {
28 if (!isa
<ObjCMethodDecl
>(D
) && !isa
<CXXMethodDecl
>(D
))
31 SmallVector
<CXCursor
, 8> Overridden
;
32 cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D
, TU
), Overridden
);
34 if (Overridden
.empty()) {
35 Methods
.push_back(D
->getCanonicalDecl());
39 for (SmallVectorImpl
<CXCursor
>::iterator
40 I
= Overridden
.begin(), E
= Overridden
.end(); I
!= E
; ++I
)
41 getTopOverriddenMethods(TU
, cxcursor::getCursorDecl(*I
), Methods
);
46 struct FindFileIdRefVisitData
{
51 CXCursorAndRangeVisitor visitor
;
53 typedef SmallVector
<const Decl
*, 8> TopMethodsTy
;
54 TopMethodsTy TopMethods
;
56 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
);
64 ASTContext
&getASTContext() const {
65 return cxtu::getASTUnit(TU
)->getASTContext();
68 /// We are looking to find all semantically relevant identifiers,
69 /// so the definition of "canonical" here is different than in the AST, e.g.
77 /// we consider the canonical decl of the constructor decl to be the class
78 /// itself, so both 'C' can be highlighted.
79 const Decl
*getCanonical(const Decl
*D
) const {
83 D
= D
->getCanonicalDecl();
85 if (const ObjCImplDecl
*ImplD
= dyn_cast
<ObjCImplDecl
>(D
)) {
86 if (ImplD
->getClassInterface())
87 return getCanonical(ImplD
->getClassInterface());
89 } else if (const CXXConstructorDecl
*CXXCtorD
=
90 dyn_cast
<CXXConstructorDecl
>(D
)) {
91 return getCanonical(CXXCtorD
->getParent());
97 bool isHit(const Decl
*D
) const {
105 if (isa
<ObjCMethodDecl
>(D
) || isa
<CXXMethodDecl
>(D
))
106 return isOverriddingMethod(D
);
112 bool isOverriddingMethod(const Decl
*D
) const {
113 if (llvm::is_contained(TopMethods
, D
))
116 TopMethodsTy methods
;
117 getTopOverriddenMethods(TU
, D
, methods
);
118 for (TopMethodsTy::iterator
119 I
= methods
.begin(), E
= methods
.end(); I
!= E
; ++I
) {
120 if (llvm::is_contained(TopMethods
, *I
))
128 } // end anonymous namespace.
130 /// For a macro \arg Loc, returns the file spelling location and sets
131 /// to \arg isMacroArg whether the spelling resides inside a macro definition or
132 /// a macro argument.
133 static SourceLocation
getFileSpellingLoc(SourceManager
&SM
,
136 assert(Loc
.isMacroID());
137 SourceLocation SpellLoc
= SM
.getImmediateSpellingLoc(Loc
);
138 if (SpellLoc
.isMacroID())
139 return getFileSpellingLoc(SM
, SpellLoc
, isMacroArg
);
141 isMacroArg
= SM
.isMacroArgExpansion(Loc
);
145 static enum CXChildVisitResult
findFileIdRefVisit(CXCursor cursor
,
147 CXClientData client_data
) {
148 CXCursor declCursor
= clang_getCursorReferenced(cursor
);
149 if (!clang_isDeclaration(declCursor
.kind
))
150 return CXChildVisit_Recurse
;
152 const Decl
*D
= cxcursor::getCursorDecl(declCursor
);
154 return CXChildVisit_Continue
;
156 FindFileIdRefVisitData
*data
= (FindFileIdRefVisitData
*)client_data
;
157 if (data
->isHit(D
)) {
158 cursor
= cxcursor::getSelectorIdentifierCursor(data
->SelectorIdIdx
, cursor
);
160 // We are looking for identifiers to highlight so for objc methods (and
161 // not a parameter) we can only highlight the selector identifiers.
162 if ((cursor
.kind
== CXCursor_ObjCClassMethodDecl
||
163 cursor
.kind
== CXCursor_ObjCInstanceMethodDecl
) &&
164 cxcursor::getSelectorIdentifierIndex(cursor
) == -1)
165 return CXChildVisit_Recurse
;
167 if (clang_isExpression(cursor
.kind
)) {
168 if (cursor
.kind
== CXCursor_DeclRefExpr
||
169 cursor
.kind
== CXCursor_MemberRefExpr
) {
172 } else if (cursor
.kind
== CXCursor_ObjCMessageExpr
&&
173 cxcursor::getSelectorIdentifierIndex(cursor
) != -1) {
177 return CXChildVisit_Recurse
;
181 Loc
= cxloc::translateSourceLocation(clang_getCursorLocation(cursor
));
182 SourceLocation SelIdLoc
= cxcursor::getSelectorIdentifierLoc(cursor
);
183 if (SelIdLoc
.isValid())
186 ASTContext
&Ctx
= data
->getASTContext();
187 SourceManager
&SM
= Ctx
.getSourceManager();
188 bool isInMacroDef
= false;
189 if (Loc
.isMacroID()) {
191 Loc
= getFileSpellingLoc(SM
, Loc
, isMacroArg
);
192 isInMacroDef
= !isMacroArg
;
195 // We are looking for identifiers in a specific file.
196 std::pair
<FileID
, unsigned> LocInfo
= SM
.getDecomposedLoc(Loc
);
197 if (LocInfo
.first
!= data
->FID
)
198 return CXChildVisit_Recurse
;
201 // FIXME: For a macro definition make sure that all expansions
202 // of it expand to the same reference before allowing to point to it.
203 return CXChildVisit_Recurse
;
206 if (data
->visitor
.visit(data
->visitor
.context
, cursor
,
207 cxloc::translateSourceRange(Ctx
, Loc
)) == CXVisit_Break
)
208 return CXChildVisit_Break
;
210 return CXChildVisit_Recurse
;
213 static bool findIdRefsInFile(CXTranslationUnit TU
, CXCursor declCursor
,
214 const FileEntry
*File
,
215 CXCursorAndRangeVisitor Visitor
) {
216 assert(clang_isDeclaration(declCursor
.kind
));
217 SourceManager
&SM
= cxtu::getASTUnit(TU
)->getSourceManager();
219 FileID FID
= SM
.translateFile(File
);
220 const Decl
*Dcl
= cxcursor::getCursorDecl(declCursor
);
224 FindFileIdRefVisitData
data(TU
, FID
, Dcl
,
225 cxcursor::getSelectorIdentifierIndex(declCursor
),
228 if (const DeclContext
*DC
= Dcl
->getParentFunctionOrMethod()) {
229 return clang_visitChildren(cxcursor::MakeCXCursor(cast
<Decl
>(DC
), TU
),
230 findFileIdRefVisit
, &data
);
233 SourceRange
Range(SM
.getLocForStartOfFile(FID
), SM
.getLocForEndOfFile(FID
));
234 CursorVisitor
FindIdRefsVisitor(TU
,
235 findFileIdRefVisit
, &data
,
236 /*VisitPreprocessorLast=*/true,
237 /*VisitIncludedEntities=*/false,
239 /*VisitDeclsOnly=*/true);
240 return FindIdRefsVisitor
.visitFileRegion();
245 struct FindFileMacroRefVisitData
{
247 const FileEntry
*File
;
248 const IdentifierInfo
*Macro
;
249 CXCursorAndRangeVisitor visitor
;
251 FindFileMacroRefVisitData(ASTUnit
&Unit
, const FileEntry
*File
,
252 const IdentifierInfo
*Macro
,
253 CXCursorAndRangeVisitor visitor
)
254 : Unit(Unit
), File(File
), Macro(Macro
), visitor(visitor
) { }
256 ASTContext
&getASTContext() const {
257 return Unit
.getASTContext();
261 } // anonymous namespace
263 static enum CXChildVisitResult
findFileMacroRefVisit(CXCursor cursor
,
265 CXClientData client_data
) {
266 const IdentifierInfo
*Macro
= nullptr;
267 if (cursor
.kind
== CXCursor_MacroDefinition
)
268 Macro
= getCursorMacroDefinition(cursor
)->getName();
269 else if (cursor
.kind
== CXCursor_MacroExpansion
)
270 Macro
= getCursorMacroExpansion(cursor
).getName();
272 return CXChildVisit_Continue
;
274 FindFileMacroRefVisitData
*data
= (FindFileMacroRefVisitData
*)client_data
;
275 if (data
->Macro
!= Macro
)
276 return CXChildVisit_Continue
;
279 Loc
= cxloc::translateSourceLocation(clang_getCursorLocation(cursor
));
281 ASTContext
&Ctx
= data
->getASTContext();
282 SourceManager
&SM
= Ctx
.getSourceManager();
283 bool isInMacroDef
= false;
284 if (Loc
.isMacroID()) {
286 Loc
= getFileSpellingLoc(SM
, Loc
, isMacroArg
);
287 isInMacroDef
= !isMacroArg
;
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
;
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
;
301 if (data
->visitor
.visit(data
->visitor
.context
, cursor
,
302 cxloc::translateSourceRange(Ctx
, Loc
)) == CXVisit_Break
)
303 return CXChildVisit_Break
;
304 return CXChildVisit_Continue
;
307 static bool findMacroRefsInFile(CXTranslationUnit TU
, CXCursor Cursor
,
308 const FileEntry
*File
,
309 CXCursorAndRangeVisitor Visitor
) {
310 if (Cursor
.kind
!= CXCursor_MacroDefinition
&&
311 Cursor
.kind
!= CXCursor_MacroExpansion
)
314 ASTUnit
*Unit
= cxtu::getASTUnit(TU
);
315 SourceManager
&SM
= Unit
->getSourceManager();
317 FileID FID
= SM
.translateFile(File
);
318 const IdentifierInfo
*Macro
= nullptr;
319 if (Cursor
.kind
== CXCursor_MacroDefinition
)
320 Macro
= getCursorMacroDefinition(Cursor
)->getName();
322 Macro
= getCursorMacroExpansion(Cursor
).getName();
326 FindFileMacroRefVisitData
data(*Unit
, File
, Macro
, Visitor
);
328 SourceRange
Range(SM
.getLocForStartOfFile(FID
), SM
.getLocForEndOfFile(FID
));
329 CursorVisitor
FindMacroRefsVisitor(TU
,
330 findFileMacroRefVisit
, &data
,
331 /*VisitPreprocessorLast=*/false,
332 /*VisitIncludedEntities=*/false,
334 return FindMacroRefsVisitor
.visitPreprocessedEntitiesInRegion();
339 struct FindFileIncludesVisitor
{
341 const FileEntry
*File
;
342 CXCursorAndRangeVisitor visitor
;
344 FindFileIncludesVisitor(ASTUnit
&Unit
, const FileEntry
*File
,
345 CXCursorAndRangeVisitor visitor
)
346 : Unit(Unit
), File(File
), visitor(visitor
) { }
348 ASTContext
&getASTContext() const {
349 return Unit
.getASTContext();
352 enum CXChildVisitResult
visit(CXCursor cursor
, CXCursor parent
) {
353 if (cursor
.kind
!= CXCursor_InclusionDirective
)
354 return CXChildVisit_Continue
;
357 Loc
= cxloc::translateSourceLocation(clang_getCursorLocation(cursor
));
359 ASTContext
&Ctx
= getASTContext();
360 SourceManager
&SM
= Ctx
.getSourceManager();
362 // We are looking for includes in a specific file.
363 std::pair
<FileID
, unsigned> LocInfo
= SM
.getDecomposedLoc(Loc
);
364 if (SM
.getFileEntryForID(LocInfo
.first
) != File
)
365 return CXChildVisit_Continue
;
367 if (visitor
.visit(visitor
.context
, cursor
,
368 cxloc::translateSourceRange(Ctx
, Loc
)) == CXVisit_Break
)
369 return CXChildVisit_Break
;
370 return CXChildVisit_Continue
;
373 static enum CXChildVisitResult
visit(CXCursor cursor
, CXCursor parent
,
374 CXClientData client_data
) {
375 return static_cast<FindFileIncludesVisitor
*>(client_data
)->
376 visit(cursor
, parent
);
380 } // anonymous namespace
382 static bool findIncludesInFile(CXTranslationUnit TU
, const FileEntry
*File
,
383 CXCursorAndRangeVisitor Visitor
) {
384 assert(TU
&& File
&& Visitor
.visit
);
386 ASTUnit
*Unit
= cxtu::getASTUnit(TU
);
387 SourceManager
&SM
= Unit
->getSourceManager();
389 FileID FID
= SM
.translateFile(File
);
391 FindFileIncludesVisitor
IncludesVisitor(*Unit
, File
, Visitor
);
393 SourceRange
Range(SM
.getLocForStartOfFile(FID
), SM
.getLocForEndOfFile(FID
));
394 CursorVisitor
InclusionCursorsVisitor(TU
,
395 FindFileIncludesVisitor::visit
,
397 /*VisitPreprocessorLast=*/false,
398 /*VisitIncludedEntities=*/false,
400 return InclusionCursorsVisitor
.visitPreprocessedEntitiesInRegion();
404 //===----------------------------------------------------------------------===//
405 // libclang public APIs.
406 //===----------------------------------------------------------------------===//
410 CXResult
clang_findReferencesInFile(CXCursor cursor
, CXFile file
,
411 CXCursorAndRangeVisitor visitor
) {
412 LogRef Log
= Logger::make(__func__
);
414 if (clang_Cursor_isNull(cursor
)) {
416 *Log
<< "Null cursor";
417 return CXResult_Invalid
;
419 if (cursor
.kind
== CXCursor_NoDeclFound
) {
421 *Log
<< "Got CXCursor_NoDeclFound";
422 return CXResult_Invalid
;
427 return CXResult_Invalid
;
429 if (!visitor
.visit
) {
431 *Log
<< "Null visitor";
432 return CXResult_Invalid
;
436 *Log
<< cursor
<< " @" << *cxfile::getFileEntryRef(file
);
438 ASTUnit
*CXXUnit
= cxcursor::getCursorASTUnit(cursor
);
440 return CXResult_Invalid
;
442 ASTUnit::ConcurrencyCheck
Check(*CXXUnit
);
444 if (cursor
.kind
== CXCursor_MacroDefinition
||
445 cursor
.kind
== CXCursor_MacroExpansion
) {
446 if (findMacroRefsInFile(cxcursor::getCursorTU(cursor
),
448 *cxfile::getFileEntryRef(file
),
450 return CXResult_VisitBreak
;
451 return CXResult_Success
;
454 // We are interested in semantics of identifiers so for C++ constructor exprs
455 // prefer type references, e.g.:
457 // return MyStruct();
459 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
460 // we are actually interested in the type declaration.
461 cursor
= cxcursor::getTypeRefCursor(cursor
);
463 CXCursor refCursor
= clang_getCursorReferenced(cursor
);
465 if (!clang_isDeclaration(refCursor
.kind
)) {
467 *Log
<< "cursor is not referencing a declaration";
468 return CXResult_Invalid
;
471 if (findIdRefsInFile(cxcursor::getCursorTU(cursor
),
473 *cxfile::getFileEntryRef(file
),
475 return CXResult_VisitBreak
;
476 return CXResult_Success
;
479 CXResult
clang_findIncludesInFile(CXTranslationUnit TU
, CXFile file
,
480 CXCursorAndRangeVisitor visitor
) {
481 if (cxtu::isNotUsableTU(TU
)) {
483 return CXResult_Invalid
;
486 LogRef Log
= Logger::make(__func__
);
490 return CXResult_Invalid
;
492 if (!visitor
.visit
) {
494 *Log
<< "Null visitor";
495 return CXResult_Invalid
;
499 *Log
<< TU
<< " @" << *cxfile::getFileEntryRef(file
);
501 ASTUnit
*CXXUnit
= cxtu::getASTUnit(TU
);
503 return CXResult_Invalid
;
505 ASTUnit::ConcurrencyCheck
Check(*CXXUnit
);
507 if (findIncludesInFile(TU
, *cxfile::getFileEntryRef(file
), visitor
))
508 return CXResult_VisitBreak
;
509 return CXResult_Success
;
512 static enum CXVisitorResult
_visitCursorAndRange(void *context
,
514 CXSourceRange range
) {
515 CXCursorAndRangeVisitorBlock block
= (CXCursorAndRangeVisitorBlock
)context
;
516 return INVOKE_BLOCK2(block
, cursor
, range
);
519 CXResult
clang_findReferencesInFileWithBlock(CXCursor cursor
,
521 CXCursorAndRangeVisitorBlock block
) {
522 CXCursorAndRangeVisitor visitor
= { block
,
523 block
? _visitCursorAndRange
: nullptr };
524 return clang_findReferencesInFile(cursor
, file
, visitor
);
527 CXResult
clang_findIncludesInFileWithBlock(CXTranslationUnit TU
,
529 CXCursorAndRangeVisitorBlock block
) {
530 CXCursorAndRangeVisitor visitor
= { block
,
531 block
? _visitCursorAndRange
: nullptr };
532 return clang_findIncludesInFile(TU
, file
, visitor
);