1 //===--- USRFinder.cpp - Clang refactoring library ------------------------===//
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 /// \file Implements a recursive AST visitor that finds the USR of a symbol at a
12 //===----------------------------------------------------------------------===//
14 #include "clang/Tooling/Refactoring/Rename/USRFinder.h"
15 #include "clang/AST/AST.h"
16 #include "clang/AST/ASTContext.h"
17 #include "clang/AST/RecursiveASTVisitor.h"
18 #include "clang/Basic/SourceManager.h"
19 #include "clang/Index/USRGeneration.h"
20 #include "clang/Lex/Lexer.h"
21 #include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
22 #include "llvm/ADT/SmallVector.h"
31 /// Recursively visits each AST node to find the symbol underneath the cursor.
32 class NamedDeclOccurrenceFindingVisitor
33 : public RecursiveSymbolVisitor
<NamedDeclOccurrenceFindingVisitor
> {
35 // Finds the NamedDecl at a point in the source.
36 // \param Point the location in the source to search for the NamedDecl.
37 explicit NamedDeclOccurrenceFindingVisitor(const SourceLocation Point
,
38 const ASTContext
&Context
)
39 : RecursiveSymbolVisitor(Context
.getSourceManager(),
40 Context
.getLangOpts()),
41 Point(Point
), Context(Context
) {}
43 bool visitSymbolOccurrence(const NamedDecl
*ND
,
44 ArrayRef
<SourceRange
> NameRanges
) {
47 for (const auto &Range
: NameRanges
) {
48 SourceLocation Start
= Range
.getBegin();
49 SourceLocation End
= Range
.getEnd();
50 if (!Start
.isValid() || !Start
.isFileID() || !End
.isValid() ||
51 !End
.isFileID() || !isPointWithin(Start
, End
))
58 const NamedDecl
*getNamedDecl() const { return Result
; }
61 // Determines if the Point is within Start and End.
62 bool isPointWithin(const SourceLocation Start
, const SourceLocation End
) {
63 // FIXME: Add tests for Point == End.
64 return Point
== Start
|| Point
== End
||
65 (Context
.getSourceManager().isBeforeInTranslationUnit(Start
,
67 Context
.getSourceManager().isBeforeInTranslationUnit(Point
, End
));
70 const NamedDecl
*Result
= nullptr;
71 const SourceLocation Point
; // The location to find the NamedDecl.
72 const ASTContext
&Context
;
75 } // end anonymous namespace
77 const NamedDecl
*getNamedDeclAt(const ASTContext
&Context
,
78 const SourceLocation Point
) {
79 const SourceManager
&SM
= Context
.getSourceManager();
80 NamedDeclOccurrenceFindingVisitor
Visitor(Point
, Context
);
82 // Try to be clever about pruning down the number of top-level declarations we
83 // see. If both start and end is either before or after the point we're
84 // looking for the point cannot be inside of this decl. Don't even look at it.
85 for (auto *CurrDecl
: Context
.getTranslationUnitDecl()->decls()) {
86 SourceLocation StartLoc
= CurrDecl
->getBeginLoc();
87 SourceLocation EndLoc
= CurrDecl
->getEndLoc();
88 if (StartLoc
.isValid() && EndLoc
.isValid() &&
89 SM
.isBeforeInTranslationUnit(StartLoc
, Point
) !=
90 SM
.isBeforeInTranslationUnit(EndLoc
, Point
))
91 Visitor
.TraverseDecl(CurrDecl
);
94 return Visitor
.getNamedDecl();
99 /// Recursively visits each NamedDecl node to find the declaration with a
101 class NamedDeclFindingVisitor
102 : public RecursiveASTVisitor
<NamedDeclFindingVisitor
> {
104 explicit NamedDeclFindingVisitor(StringRef Name
) : Name(Name
) {}
106 // We don't have to traverse the uses to find some declaration with a
107 // specific name, so just visit the named declarations.
108 bool VisitNamedDecl(const NamedDecl
*ND
) {
111 // Fully qualified name is used to find the declaration.
112 if (Name
!= ND
->getQualifiedNameAsString() &&
113 Name
!= "::" + ND
->getQualifiedNameAsString())
119 const NamedDecl
*getNamedDecl() const { return Result
; }
122 const NamedDecl
*Result
= nullptr;
126 } // end anonymous namespace
128 const NamedDecl
*getNamedDeclFor(const ASTContext
&Context
,
129 const std::string
&Name
) {
130 NamedDeclFindingVisitor
Visitor(Name
);
131 Visitor
.TraverseDecl(Context
.getTranslationUnitDecl());
132 return Visitor
.getNamedDecl();
135 std::string
getUSRForDecl(const Decl
*Decl
) {
136 llvm::SmallString
<128> Buff
;
138 // FIXME: Add test for the nullptr case.
139 if (Decl
== nullptr || index::generateUSRForDecl(Decl
, Buff
))
142 return std::string(Buff
);
145 } // end namespace tooling
146 } // end namespace clang