1 //===--- USRFindingAction.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 //===----------------------------------------------------------------------===//
10 /// Provides an action to find USR for the symbol at <offset>, as well as
11 /// all additional USRs.
13 //===----------------------------------------------------------------------===//
15 #include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
16 #include "clang/AST/AST.h"
17 #include "clang/AST/ASTConsumer.h"
18 #include "clang/AST/ASTContext.h"
19 #include "clang/AST/Decl.h"
20 #include "clang/AST/RecursiveASTVisitor.h"
21 #include "clang/Basic/FileManager.h"
22 #include "clang/Frontend/CompilerInstance.h"
23 #include "clang/Frontend/FrontendAction.h"
24 #include "clang/Lex/Lexer.h"
25 #include "clang/Lex/Preprocessor.h"
26 #include "clang/Tooling/CommonOptionsParser.h"
27 #include "clang/Tooling/Refactoring.h"
28 #include "clang/Tooling/Refactoring/Rename/USRFinder.h"
29 #include "clang/Tooling/Tooling.h"
41 const NamedDecl
*getCanonicalSymbolDeclaration(const NamedDecl
*FoundDecl
) {
44 // If FoundDecl is a constructor or destructor, we want to instead take
45 // the Decl of the corresponding class.
46 if (const auto *CtorDecl
= dyn_cast
<CXXConstructorDecl
>(FoundDecl
))
47 FoundDecl
= CtorDecl
->getParent();
48 else if (const auto *DtorDecl
= dyn_cast
<CXXDestructorDecl
>(FoundDecl
))
49 FoundDecl
= DtorDecl
->getParent();
50 // FIXME: (Alex L): Canonicalize implicit template instantions, just like
51 // the indexer does it.
53 // Note: please update the declaration's doc comment every time the
54 // canonicalization rules are changed.
59 // NamedDeclFindingConsumer should delegate finding USRs of given Decl to
60 // AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given
61 // Decl refers to class and adds USRs of all overridden methods if Decl refers
63 class AdditionalUSRFinder
: public RecursiveASTVisitor
<AdditionalUSRFinder
> {
65 AdditionalUSRFinder(const Decl
*FoundDecl
, ASTContext
&Context
)
66 : FoundDecl(FoundDecl
), Context(Context
) {}
68 std::vector
<std::string
> Find() {
69 // Fill OverriddenMethods and PartialSpecs storages.
71 if (const auto *MethodDecl
= dyn_cast
<CXXMethodDecl
>(FoundDecl
)) {
72 addUSRsOfOverridenFunctions(MethodDecl
);
73 for (const auto &OverriddenMethod
: OverriddenMethods
) {
74 if (checkIfOverriddenFunctionAscends(OverriddenMethod
))
75 USRSet
.insert(getUSRForDecl(OverriddenMethod
));
77 addUSRsOfInstantiatedMethods(MethodDecl
);
78 } else if (const auto *RecordDecl
= dyn_cast
<CXXRecordDecl
>(FoundDecl
)) {
79 handleCXXRecordDecl(RecordDecl
);
80 } else if (const auto *TemplateDecl
=
81 dyn_cast
<ClassTemplateDecl
>(FoundDecl
)) {
82 handleClassTemplateDecl(TemplateDecl
);
83 } else if (const auto *FD
= dyn_cast
<FunctionDecl
>(FoundDecl
)) {
84 USRSet
.insert(getUSRForDecl(FD
));
85 if (const auto *FTD
= FD
->getPrimaryTemplate())
86 handleFunctionTemplateDecl(FTD
);
87 } else if (const auto *FD
= dyn_cast
<FunctionTemplateDecl
>(FoundDecl
)) {
88 handleFunctionTemplateDecl(FD
);
89 } else if (const auto *VTD
= dyn_cast
<VarTemplateDecl
>(FoundDecl
)) {
90 handleVarTemplateDecl(VTD
);
91 } else if (const auto *VD
=
92 dyn_cast
<VarTemplateSpecializationDecl
>(FoundDecl
)) {
93 // FIXME: figure out why FoundDecl can be a VarTemplateSpecializationDecl.
94 handleVarTemplateDecl(VD
->getSpecializedTemplate());
95 } else if (const auto *VD
= dyn_cast
<VarDecl
>(FoundDecl
)) {
96 USRSet
.insert(getUSRForDecl(VD
));
97 if (const auto *VTD
= VD
->getDescribedVarTemplate())
98 handleVarTemplateDecl(VTD
);
100 USRSet
.insert(getUSRForDecl(FoundDecl
));
102 return std::vector
<std::string
>(USRSet
.begin(), USRSet
.end());
105 bool shouldVisitTemplateInstantiations() const { return true; }
107 bool VisitCXXMethodDecl(const CXXMethodDecl
*MethodDecl
) {
108 if (MethodDecl
->isVirtual())
109 OverriddenMethods
.push_back(MethodDecl
);
110 if (MethodDecl
->getInstantiatedFromMemberFunction())
111 InstantiatedMethods
.push_back(MethodDecl
);
116 void handleCXXRecordDecl(const CXXRecordDecl
*RecordDecl
) {
117 if (!RecordDecl
->getDefinition()) {
118 USRSet
.insert(getUSRForDecl(RecordDecl
));
121 RecordDecl
= RecordDecl
->getDefinition();
122 if (const auto *ClassTemplateSpecDecl
=
123 dyn_cast
<ClassTemplateSpecializationDecl
>(RecordDecl
))
124 handleClassTemplateDecl(ClassTemplateSpecDecl
->getSpecializedTemplate());
125 addUSRsOfCtorDtors(RecordDecl
);
128 void handleClassTemplateDecl(const ClassTemplateDecl
*TemplateDecl
) {
129 for (const auto *Specialization
: TemplateDecl
->specializations())
130 addUSRsOfCtorDtors(Specialization
);
131 SmallVector
<ClassTemplatePartialSpecializationDecl
*, 4> PartialSpecs
;
132 TemplateDecl
->getPartialSpecializations(PartialSpecs
);
133 for (const auto *Spec
: PartialSpecs
)
134 addUSRsOfCtorDtors(Spec
);
135 addUSRsOfCtorDtors(TemplateDecl
->getTemplatedDecl());
138 void handleFunctionTemplateDecl(const FunctionTemplateDecl
*FTD
) {
139 USRSet
.insert(getUSRForDecl(FTD
));
140 USRSet
.insert(getUSRForDecl(FTD
->getTemplatedDecl()));
141 for (const auto *S
: FTD
->specializations())
142 USRSet
.insert(getUSRForDecl(S
));
145 void handleVarTemplateDecl(const VarTemplateDecl
*VTD
) {
146 USRSet
.insert(getUSRForDecl(VTD
));
147 USRSet
.insert(getUSRForDecl(VTD
->getTemplatedDecl()));
148 for (const auto *Spec
: VTD
->specializations())
149 USRSet
.insert(getUSRForDecl(Spec
));
150 SmallVector
<VarTemplatePartialSpecializationDecl
*, 4> PartialSpecs
;
151 VTD
->getPartialSpecializations(PartialSpecs
);
152 for (const auto *Spec
: PartialSpecs
)
153 USRSet
.insert(getUSRForDecl(Spec
));
156 void addUSRsOfCtorDtors(const CXXRecordDecl
*RD
) {
157 const auto* RecordDecl
= RD
->getDefinition();
159 // Skip if the CXXRecordDecl doesn't have definition.
161 USRSet
.insert(getUSRForDecl(RD
));
165 for (const auto *CtorDecl
: RecordDecl
->ctors())
166 USRSet
.insert(getUSRForDecl(CtorDecl
));
167 // Add template constructor decls, they are not in ctors() unfortunately.
168 if (RecordDecl
->hasUserDeclaredConstructor())
169 for (const auto *D
: RecordDecl
->decls())
170 if (const auto *FTD
= dyn_cast
<FunctionTemplateDecl
>(D
))
171 if (const auto *Ctor
=
172 dyn_cast
<CXXConstructorDecl
>(FTD
->getTemplatedDecl()))
173 USRSet
.insert(getUSRForDecl(Ctor
));
175 USRSet
.insert(getUSRForDecl(RecordDecl
->getDestructor()));
176 USRSet
.insert(getUSRForDecl(RecordDecl
));
179 void addUSRsOfOverridenFunctions(const CXXMethodDecl
*MethodDecl
) {
180 USRSet
.insert(getUSRForDecl(MethodDecl
));
181 // Recursively visit each OverridenMethod.
182 for (const auto &OverriddenMethod
: MethodDecl
->overridden_methods())
183 addUSRsOfOverridenFunctions(OverriddenMethod
);
186 void addUSRsOfInstantiatedMethods(const CXXMethodDecl
*MethodDecl
) {
187 // For renaming a class template method, all references of the instantiated
188 // member methods should be renamed too, so add USRs of the instantiated
189 // methods to the USR set.
190 USRSet
.insert(getUSRForDecl(MethodDecl
));
191 if (const auto *FT
= MethodDecl
->getInstantiatedFromMemberFunction())
192 USRSet
.insert(getUSRForDecl(FT
));
193 for (const auto *Method
: InstantiatedMethods
) {
194 if (USRSet
.find(getUSRForDecl(
195 Method
->getInstantiatedFromMemberFunction())) != USRSet
.end())
196 USRSet
.insert(getUSRForDecl(Method
));
200 bool checkIfOverriddenFunctionAscends(const CXXMethodDecl
*MethodDecl
) {
201 for (const auto &OverriddenMethod
: MethodDecl
->overridden_methods()) {
202 if (USRSet
.find(getUSRForDecl(OverriddenMethod
)) != USRSet
.end())
204 return checkIfOverriddenFunctionAscends(OverriddenMethod
);
209 const Decl
*FoundDecl
;
211 std::set
<std::string
> USRSet
;
212 std::vector
<const CXXMethodDecl
*> OverriddenMethods
;
213 std::vector
<const CXXMethodDecl
*> InstantiatedMethods
;
217 std::vector
<std::string
> getUSRsForDeclaration(const NamedDecl
*ND
,
218 ASTContext
&Context
) {
219 AdditionalUSRFinder
Finder(ND
, Context
);
220 return Finder
.Find();
223 class NamedDeclFindingConsumer
: public ASTConsumer
{
225 NamedDeclFindingConsumer(ArrayRef
<unsigned> SymbolOffsets
,
226 ArrayRef
<std::string
> QualifiedNames
,
227 std::vector
<std::string
> &SpellingNames
,
228 std::vector
<std::vector
<std::string
>> &USRList
,
229 bool Force
, bool &ErrorOccurred
)
230 : SymbolOffsets(SymbolOffsets
), QualifiedNames(QualifiedNames
),
231 SpellingNames(SpellingNames
), USRList(USRList
), Force(Force
),
232 ErrorOccurred(ErrorOccurred
) {}
235 bool FindSymbol(ASTContext
&Context
, const SourceManager
&SourceMgr
,
236 unsigned SymbolOffset
, const std::string
&QualifiedName
) {
237 DiagnosticsEngine
&Engine
= Context
.getDiagnostics();
238 const FileID MainFileID
= SourceMgr
.getMainFileID();
240 if (SymbolOffset
>= SourceMgr
.getFileIDSize(MainFileID
)) {
241 ErrorOccurred
= true;
242 unsigned InvalidOffset
= Engine
.getCustomDiagID(
243 DiagnosticsEngine::Error
,
244 "SourceLocation in file %0 at offset %1 is invalid");
245 Engine
.Report(SourceLocation(), InvalidOffset
)
246 << SourceMgr
.getFileEntryRefForID(MainFileID
)->getName()
251 const SourceLocation Point
= SourceMgr
.getLocForStartOfFile(MainFileID
)
252 .getLocWithOffset(SymbolOffset
);
253 const NamedDecl
*FoundDecl
= QualifiedName
.empty()
254 ? getNamedDeclAt(Context
, Point
)
255 : getNamedDeclFor(Context
, QualifiedName
);
257 if (FoundDecl
== nullptr) {
258 if (QualifiedName
.empty()) {
259 FullSourceLoc
FullLoc(Point
, SourceMgr
);
260 unsigned CouldNotFindSymbolAt
= Engine
.getCustomDiagID(
261 DiagnosticsEngine::Error
,
262 "clang-rename could not find symbol (offset %0)");
263 Engine
.Report(Point
, CouldNotFindSymbolAt
) << SymbolOffset
;
264 ErrorOccurred
= true;
269 SpellingNames
.push_back(std::string());
270 USRList
.push_back(std::vector
<std::string
>());
274 unsigned CouldNotFindSymbolNamed
= Engine
.getCustomDiagID(
275 DiagnosticsEngine::Error
, "clang-rename could not find symbol %0");
276 Engine
.Report(CouldNotFindSymbolNamed
) << QualifiedName
;
277 ErrorOccurred
= true;
281 FoundDecl
= getCanonicalSymbolDeclaration(FoundDecl
);
282 SpellingNames
.push_back(FoundDecl
->getNameAsString());
283 AdditionalUSRFinder
Finder(FoundDecl
, Context
);
284 USRList
.push_back(Finder
.Find());
288 void HandleTranslationUnit(ASTContext
&Context
) override
{
289 const SourceManager
&SourceMgr
= Context
.getSourceManager();
290 for (unsigned Offset
: SymbolOffsets
) {
291 if (!FindSymbol(Context
, SourceMgr
, Offset
, ""))
294 for (const std::string
&QualifiedName
: QualifiedNames
) {
295 if (!FindSymbol(Context
, SourceMgr
, 0, QualifiedName
))
300 ArrayRef
<unsigned> SymbolOffsets
;
301 ArrayRef
<std::string
> QualifiedNames
;
302 std::vector
<std::string
> &SpellingNames
;
303 std::vector
<std::vector
<std::string
>> &USRList
;
308 std::unique_ptr
<ASTConsumer
> USRFindingAction::newASTConsumer() {
309 return std::make_unique
<NamedDeclFindingConsumer
>(
310 SymbolOffsets
, QualifiedNames
, SpellingNames
, USRList
, Force
,
314 } // end namespace tooling
315 } // end namespace clang