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"
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"
18 using namespace clang
;
19 using namespace cxcursor
;
20 using namespace cxindex
;
22 static void getTopOverriddenMethods(CXTranslationUnit TU
,
24 SmallVectorImpl
<const Decl
*> &Methods
) {
27 if (!isa
<ObjCMethodDecl
>(D
) && !isa
<CXXMethodDecl
>(D
))
30 SmallVector
<CXCursor
, 8> Overridden
;
31 cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D
, TU
), Overridden
);
33 if (Overridden
.empty()) {
34 Methods
.push_back(D
->getCanonicalDecl());
38 for (SmallVectorImpl
<CXCursor
>::iterator
39 I
= Overridden
.begin(), E
= Overridden
.end(); I
!= E
; ++I
)
40 getTopOverriddenMethods(TU
, cxcursor::getCursorDecl(*I
), Methods
);
45 struct FindFileIdRefVisitData
{
50 CXCursorAndRangeVisitor visitor
;
52 typedef SmallVector
<const Decl
*, 8> TopMethodsTy
;
53 TopMethodsTy TopMethods
;
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
);
63 ASTContext
&getASTContext() const {
64 return cxtu::getASTUnit(TU
)->getASTContext();
67 /// We are looking to find all semantically relevant identifiers,
68 /// so the definition of "canonical" here is different than in the AST, e.g.
76 /// we consider the canonical decl of the constructor decl to be the class
77 /// itself, so both 'C' can be highlighted.
78 const Decl
*getCanonical(const Decl
*D
) const {
82 D
= D
->getCanonicalDecl();
84 if (const ObjCImplDecl
*ImplD
= dyn_cast
<ObjCImplDecl
>(D
)) {
85 if (ImplD
->getClassInterface())
86 return getCanonical(ImplD
->getClassInterface());
88 } else if (const CXXConstructorDecl
*CXXCtorD
=
89 dyn_cast
<CXXConstructorDecl
>(D
)) {
90 return getCanonical(CXXCtorD
->getParent());
96 bool isHit(const Decl
*D
) const {
104 if (isa
<ObjCMethodDecl
>(D
) || isa
<CXXMethodDecl
>(D
))
105 return isOverriddingMethod(D
);
111 bool isOverriddingMethod(const Decl
*D
) const {
112 if (llvm::is_contained(TopMethods
, D
))
115 TopMethodsTy methods
;
116 getTopOverriddenMethods(TU
, D
, methods
);
117 for (TopMethodsTy::iterator
118 I
= methods
.begin(), E
= methods
.end(); I
!= E
; ++I
) {
119 if (llvm::is_contained(TopMethods
, *I
))
127 } // end anonymous namespace.
129 /// 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.
132 static SourceLocation
getFileSpellingLoc(SourceManager
&SM
,
135 assert(Loc
.isMacroID());
136 SourceLocation SpellLoc
= SM
.getImmediateSpellingLoc(Loc
);
137 if (SpellLoc
.isMacroID())
138 return getFileSpellingLoc(SM
, SpellLoc
, isMacroArg
);
140 isMacroArg
= SM
.isMacroArgExpansion(Loc
);
144 static enum CXChildVisitResult
findFileIdRefVisit(CXCursor cursor
,
146 CXClientData client_data
) {
147 CXCursor declCursor
= clang_getCursorReferenced(cursor
);
148 if (!clang_isDeclaration(declCursor
.kind
))
149 return CXChildVisit_Recurse
;
151 const Decl
*D
= cxcursor::getCursorDecl(declCursor
);
153 return CXChildVisit_Continue
;
155 FindFileIdRefVisitData
*data
= (FindFileIdRefVisitData
*)client_data
;
156 if (data
->isHit(D
)) {
157 cursor
= cxcursor::getSelectorIdentifierCursor(data
->SelectorIdIdx
, cursor
);
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
;
166 if (clang_isExpression(cursor
.kind
)) {
167 if (cursor
.kind
== CXCursor_DeclRefExpr
||
168 cursor
.kind
== CXCursor_MemberRefExpr
) {
171 } else if (cursor
.kind
== CXCursor_ObjCMessageExpr
&&
172 cxcursor::getSelectorIdentifierIndex(cursor
) != -1) {
176 return CXChildVisit_Recurse
;
180 Loc
= cxloc::translateSourceLocation(clang_getCursorLocation(cursor
));
181 SourceLocation SelIdLoc
= cxcursor::getSelectorIdentifierLoc(cursor
);
182 if (SelIdLoc
.isValid())
185 ASTContext
&Ctx
= data
->getASTContext();
186 SourceManager
&SM
= Ctx
.getSourceManager();
187 bool isInMacroDef
= false;
188 if (Loc
.isMacroID()) {
190 Loc
= getFileSpellingLoc(SM
, Loc
, isMacroArg
);
191 isInMacroDef
= !isMacroArg
;
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
;
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.
202 return CXChildVisit_Recurse
;
205 if (data
->visitor
.visit(data
->visitor
.context
, cursor
,
206 cxloc::translateSourceRange(Ctx
, Loc
)) == CXVisit_Break
)
207 return CXChildVisit_Break
;
209 return CXChildVisit_Recurse
;
212 static bool findIdRefsInFile(CXTranslationUnit TU
, CXCursor declCursor
,
213 const FileEntry
*File
,
214 CXCursorAndRangeVisitor Visitor
) {
215 assert(clang_isDeclaration(declCursor
.kind
));
216 SourceManager
&SM
= cxtu::getASTUnit(TU
)->getSourceManager();
218 FileID FID
= SM
.translateFile(File
);
219 const Decl
*Dcl
= cxcursor::getCursorDecl(declCursor
);
223 FindFileIdRefVisitData
data(TU
, FID
, Dcl
,
224 cxcursor::getSelectorIdentifierIndex(declCursor
),
227 if (const DeclContext
*DC
= Dcl
->getParentFunctionOrMethod()) {
228 return clang_visitChildren(cxcursor::MakeCXCursor(cast
<Decl
>(DC
), TU
),
229 findFileIdRefVisit
, &data
);
232 SourceRange
Range(SM
.getLocForStartOfFile(FID
), SM
.getLocForEndOfFile(FID
));
233 CursorVisitor
FindIdRefsVisitor(TU
,
234 findFileIdRefVisit
, &data
,
235 /*VisitPreprocessorLast=*/true,
236 /*VisitIncludedEntities=*/false,
238 /*VisitDeclsOnly=*/true);
239 return FindIdRefsVisitor
.visitFileRegion();
244 struct FindFileMacroRefVisitData
{
246 const FileEntry
*File
;
247 const IdentifierInfo
*Macro
;
248 CXCursorAndRangeVisitor visitor
;
250 FindFileMacroRefVisitData(ASTUnit
&Unit
, const FileEntry
*File
,
251 const IdentifierInfo
*Macro
,
252 CXCursorAndRangeVisitor visitor
)
253 : Unit(Unit
), File(File
), Macro(Macro
), visitor(visitor
) { }
255 ASTContext
&getASTContext() const {
256 return Unit
.getASTContext();
260 } // anonymous namespace
262 static enum CXChildVisitResult
findFileMacroRefVisit(CXCursor cursor
,
264 CXClientData client_data
) {
265 const IdentifierInfo
*Macro
= nullptr;
266 if (cursor
.kind
== CXCursor_MacroDefinition
)
267 Macro
= getCursorMacroDefinition(cursor
)->getName();
268 else if (cursor
.kind
== CXCursor_MacroExpansion
)
269 Macro
= getCursorMacroExpansion(cursor
).getName();
271 return CXChildVisit_Continue
;
273 FindFileMacroRefVisitData
*data
= (FindFileMacroRefVisitData
*)client_data
;
274 if (data
->Macro
!= Macro
)
275 return CXChildVisit_Continue
;
278 Loc
= cxloc::translateSourceLocation(clang_getCursorLocation(cursor
));
280 ASTContext
&Ctx
= data
->getASTContext();
281 SourceManager
&SM
= Ctx
.getSourceManager();
282 bool isInMacroDef
= false;
283 if (Loc
.isMacroID()) {
285 Loc
= getFileSpellingLoc(SM
, Loc
, isMacroArg
);
286 isInMacroDef
= !isMacroArg
;
289 // We are looking for identifiers in a specific file.
290 std::pair
<FileID
, unsigned> LocInfo
= SM
.getDecomposedLoc(Loc
);
291 if (SM
.getFileEntryForID(LocInfo
.first
) != data
->File
)
292 return CXChildVisit_Continue
;
295 // FIXME: For a macro definition make sure that all expansions
296 // of it expand to the same reference before allowing to point to it.
297 return CXChildVisit_Continue
;
300 if (data
->visitor
.visit(data
->visitor
.context
, cursor
,
301 cxloc::translateSourceRange(Ctx
, Loc
)) == CXVisit_Break
)
302 return CXChildVisit_Break
;
303 return CXChildVisit_Continue
;
306 static bool findMacroRefsInFile(CXTranslationUnit TU
, CXCursor Cursor
,
307 const FileEntry
*File
,
308 CXCursorAndRangeVisitor Visitor
) {
309 if (Cursor
.kind
!= CXCursor_MacroDefinition
&&
310 Cursor
.kind
!= CXCursor_MacroExpansion
)
313 ASTUnit
*Unit
= cxtu::getASTUnit(TU
);
314 SourceManager
&SM
= Unit
->getSourceManager();
316 FileID FID
= SM
.translateFile(File
);
317 const IdentifierInfo
*Macro
= nullptr;
318 if (Cursor
.kind
== CXCursor_MacroDefinition
)
319 Macro
= getCursorMacroDefinition(Cursor
)->getName();
321 Macro
= getCursorMacroExpansion(Cursor
).getName();
325 FindFileMacroRefVisitData
data(*Unit
, File
, Macro
, Visitor
);
327 SourceRange
Range(SM
.getLocForStartOfFile(FID
), SM
.getLocForEndOfFile(FID
));
328 CursorVisitor
FindMacroRefsVisitor(TU
,
329 findFileMacroRefVisit
, &data
,
330 /*VisitPreprocessorLast=*/false,
331 /*VisitIncludedEntities=*/false,
333 return FindMacroRefsVisitor
.visitPreprocessedEntitiesInRegion();
338 struct FindFileIncludesVisitor
{
340 const FileEntry
*File
;
341 CXCursorAndRangeVisitor visitor
;
343 FindFileIncludesVisitor(ASTUnit
&Unit
, const FileEntry
*File
,
344 CXCursorAndRangeVisitor visitor
)
345 : Unit(Unit
), File(File
), visitor(visitor
) { }
347 ASTContext
&getASTContext() const {
348 return Unit
.getASTContext();
351 enum CXChildVisitResult
visit(CXCursor cursor
, CXCursor parent
) {
352 if (cursor
.kind
!= CXCursor_InclusionDirective
)
353 return CXChildVisit_Continue
;
356 Loc
= cxloc::translateSourceLocation(clang_getCursorLocation(cursor
));
358 ASTContext
&Ctx
= getASTContext();
359 SourceManager
&SM
= Ctx
.getSourceManager();
361 // We are looking for includes in a specific file.
362 std::pair
<FileID
, unsigned> LocInfo
= SM
.getDecomposedLoc(Loc
);
363 if (SM
.getFileEntryForID(LocInfo
.first
) != File
)
364 return CXChildVisit_Continue
;
366 if (visitor
.visit(visitor
.context
, cursor
,
367 cxloc::translateSourceRange(Ctx
, Loc
)) == CXVisit_Break
)
368 return CXChildVisit_Break
;
369 return CXChildVisit_Continue
;
372 static enum CXChildVisitResult
visit(CXCursor cursor
, CXCursor parent
,
373 CXClientData client_data
) {
374 return static_cast<FindFileIncludesVisitor
*>(client_data
)->
375 visit(cursor
, parent
);
379 } // anonymous namespace
381 static bool findIncludesInFile(CXTranslationUnit TU
, const FileEntry
*File
,
382 CXCursorAndRangeVisitor Visitor
) {
383 assert(TU
&& File
&& Visitor
.visit
);
385 ASTUnit
*Unit
= cxtu::getASTUnit(TU
);
386 SourceManager
&SM
= Unit
->getSourceManager();
388 FileID FID
= SM
.translateFile(File
);
390 FindFileIncludesVisitor
IncludesVisitor(*Unit
, File
, Visitor
);
392 SourceRange
Range(SM
.getLocForStartOfFile(FID
), SM
.getLocForEndOfFile(FID
));
393 CursorVisitor
InclusionCursorsVisitor(TU
,
394 FindFileIncludesVisitor::visit
,
396 /*VisitPreprocessorLast=*/false,
397 /*VisitIncludedEntities=*/false,
399 return InclusionCursorsVisitor
.visitPreprocessedEntitiesInRegion();
403 //===----------------------------------------------------------------------===//
404 // libclang public APIs.
405 //===----------------------------------------------------------------------===//
409 CXResult
clang_findReferencesInFile(CXCursor cursor
, CXFile file
,
410 CXCursorAndRangeVisitor visitor
) {
411 LogRef Log
= Logger::make(__func__
);
413 if (clang_Cursor_isNull(cursor
)) {
415 *Log
<< "Null cursor";
416 return CXResult_Invalid
;
418 if (cursor
.kind
== CXCursor_NoDeclFound
) {
420 *Log
<< "Got CXCursor_NoDeclFound";
421 return CXResult_Invalid
;
426 return CXResult_Invalid
;
428 if (!visitor
.visit
) {
430 *Log
<< "Null visitor";
431 return CXResult_Invalid
;
435 *Log
<< cursor
<< " @" << static_cast<const FileEntry
*>(file
);
437 ASTUnit
*CXXUnit
= cxcursor::getCursorASTUnit(cursor
);
439 return CXResult_Invalid
;
441 ASTUnit::ConcurrencyCheck
Check(*CXXUnit
);
443 if (cursor
.kind
== CXCursor_MacroDefinition
||
444 cursor
.kind
== CXCursor_MacroExpansion
) {
445 if (findMacroRefsInFile(cxcursor::getCursorTU(cursor
),
447 static_cast<const FileEntry
*>(file
),
449 return CXResult_VisitBreak
;
450 return CXResult_Success
;
453 // We are interested in semantics of identifiers so for C++ constructor exprs
454 // prefer type references, e.g.:
456 // return MyStruct();
458 // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
459 // we are actually interested in the type declaration.
460 cursor
= cxcursor::getTypeRefCursor(cursor
);
462 CXCursor refCursor
= clang_getCursorReferenced(cursor
);
464 if (!clang_isDeclaration(refCursor
.kind
)) {
466 *Log
<< "cursor is not referencing a declaration";
467 return CXResult_Invalid
;
470 if (findIdRefsInFile(cxcursor::getCursorTU(cursor
),
472 static_cast<const FileEntry
*>(file
),
474 return CXResult_VisitBreak
;
475 return CXResult_Success
;
478 CXResult
clang_findIncludesInFile(CXTranslationUnit TU
, CXFile file
,
479 CXCursorAndRangeVisitor visitor
) {
480 if (cxtu::isNotUsableTU(TU
)) {
482 return CXResult_Invalid
;
485 LogRef Log
= Logger::make(__func__
);
489 return CXResult_Invalid
;
491 if (!visitor
.visit
) {
493 *Log
<< "Null visitor";
494 return CXResult_Invalid
;
498 *Log
<< TU
<< " @" << static_cast<const FileEntry
*>(file
);
500 ASTUnit
*CXXUnit
= cxtu::getASTUnit(TU
);
502 return CXResult_Invalid
;
504 ASTUnit::ConcurrencyCheck
Check(*CXXUnit
);
506 if (findIncludesInFile(TU
, static_cast<const FileEntry
*>(file
), visitor
))
507 return CXResult_VisitBreak
;
508 return CXResult_Success
;
511 static enum CXVisitorResult
_visitCursorAndRange(void *context
,
513 CXSourceRange range
) {
514 CXCursorAndRangeVisitorBlock block
= (CXCursorAndRangeVisitorBlock
)context
;
515 return INVOKE_BLOCK2(block
, cursor
, range
);
518 CXResult
clang_findReferencesInFileWithBlock(CXCursor cursor
,
520 CXCursorAndRangeVisitorBlock block
) {
521 CXCursorAndRangeVisitor visitor
= { block
,
522 block
? _visitCursorAndRange
: nullptr };
523 return clang_findReferencesInFile(cursor
, file
, visitor
);
526 CXResult
clang_findIncludesInFileWithBlock(CXTranslationUnit TU
,
528 CXCursorAndRangeVisitorBlock block
) {
529 CXCursorAndRangeVisitor visitor
= { block
,
530 block
? _visitCursorAndRange
: nullptr };
531 return clang_findIncludesInFile(TU
, file
, visitor
);