clang 17.0.0git
USRFindingAction.cpp
Go to the documentation of this file.
1//===--- USRFindingAction.cpp - Clang refactoring library -----------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// Provides an action to find USR for the symbol at <offset>, as well as
11/// all additional USRs.
12///
13//===----------------------------------------------------------------------===//
14
16#include "clang/AST/AST.h"
19#include "clang/AST/Decl.h"
24#include "clang/Lex/Lexer.h"
30
31#include <algorithm>
32#include <set>
33#include <string>
34#include <vector>
35
36using namespace llvm;
37
38namespace clang {
39namespace tooling {
40
42 if (!FoundDecl)
43 return nullptr;
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.
52
53 // Note: please update the declaration's doc comment every time the
54 // canonicalization rules are changed.
55 return FoundDecl;
56}
57
58namespace {
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
62// to virtual method.
63class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
64public:
65 AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
66 : FoundDecl(FoundDecl), Context(Context) {}
67
68 std::vector<std::string> Find() {
69 // Fill OverriddenMethods and PartialSpecs storages.
70 TraverseAST(Context);
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));
76 }
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);
99 } else {
100 USRSet.insert(getUSRForDecl(FoundDecl));
101 }
102 return std::vector<std::string>(USRSet.begin(), USRSet.end());
103 }
104
105 bool shouldVisitTemplateInstantiations() const { return true; }
106
107 bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
108 if (MethodDecl->isVirtual())
109 OverriddenMethods.push_back(MethodDecl);
110 if (MethodDecl->getInstantiatedFromMemberFunction())
111 InstantiatedMethods.push_back(MethodDecl);
112 return true;
113 }
114
115private:
116 void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
117 if (!RecordDecl->getDefinition()) {
118 USRSet.insert(getUSRForDecl(RecordDecl));
119 return;
120 }
121 RecordDecl = RecordDecl->getDefinition();
122 if (const auto *ClassTemplateSpecDecl =
123 dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
124 handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
125 addUSRsOfCtorDtors(RecordDecl);
126 }
127
128 void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
129 for (const auto *Specialization : TemplateDecl->specializations())
130 addUSRsOfCtorDtors(Specialization);
132 TemplateDecl->getPartialSpecializations(PartialSpecs);
133 for (const auto *Spec : PartialSpecs)
134 addUSRsOfCtorDtors(Spec);
135 addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
136 }
137
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));
143 }
144
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));
151 VTD->getPartialSpecializations(PartialSpecs);
152 for (const auto *Spec : PartialSpecs)
153 USRSet.insert(getUSRForDecl(Spec));
154 }
155
156 void addUSRsOfCtorDtors(const CXXRecordDecl *RD) {
157 const auto* RecordDecl = RD->getDefinition();
158
159 // Skip if the CXXRecordDecl doesn't have definition.
160 if (!RecordDecl) {
161 USRSet.insert(getUSRForDecl(RD));
162 return;
163 }
164
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));
174
175 USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
176 USRSet.insert(getUSRForDecl(RecordDecl));
177 }
178
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);
184 }
185
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));
197 }
198 }
199
200 bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
201 for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
202 if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
203 return true;
204 return checkIfOverriddenFunctionAscends(OverriddenMethod);
205 }
206 return false;
207 }
208
209 const Decl *FoundDecl;
210 ASTContext &Context;
211 std::set<std::string> USRSet;
212 std::vector<const CXXMethodDecl *> OverriddenMethods;
213 std::vector<const CXXMethodDecl *> InstantiatedMethods;
214};
215} // namespace
216
217std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
218 ASTContext &Context) {
219 AdditionalUSRFinder Finder(ND, Context);
220 return Finder.Find();
221}
222
224public:
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) {}
233
234private:
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();
239
240 if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
241 ErrorOccurred = true;
242 unsigned InvalidOffset = Engine.getCustomDiagID(
244 "SourceLocation in file %0 at offset %1 is invalid");
245 Engine.Report(SourceLocation(), InvalidOffset)
246 << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
247 return false;
248 }
249
250 const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
251 .getLocWithOffset(SymbolOffset);
252 const NamedDecl *FoundDecl = QualifiedName.empty()
253 ? getNamedDeclAt(Context, Point)
254 : getNamedDeclFor(Context, QualifiedName);
255
256 if (FoundDecl == nullptr) {
257 if (QualifiedName.empty()) {
258 FullSourceLoc FullLoc(Point, SourceMgr);
259 unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
261 "clang-rename could not find symbol (offset %0)");
262 Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
263 ErrorOccurred = true;
264 return false;
265 }
266
267 if (Force) {
268 SpellingNames.push_back(std::string());
269 USRList.push_back(std::vector<std::string>());
270 return true;
271 }
272
273 unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
274 DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
275 Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
276 ErrorOccurred = true;
277 return false;
278 }
279
280 FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
281 SpellingNames.push_back(FoundDecl->getNameAsString());
282 AdditionalUSRFinder Finder(FoundDecl, Context);
283 USRList.push_back(Finder.Find());
284 return true;
285 }
286
287 void HandleTranslationUnit(ASTContext &Context) override {
288 const SourceManager &SourceMgr = Context.getSourceManager();
289 for (unsigned Offset : SymbolOffsets) {
290 if (!FindSymbol(Context, SourceMgr, Offset, ""))
291 return;
292 }
293 for (const std::string &QualifiedName : QualifiedNames) {
294 if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
295 return;
296 }
297 }
298
299 ArrayRef<unsigned> SymbolOffsets;
300 ArrayRef<std::string> QualifiedNames;
301 std::vector<std::string> &SpellingNames;
302 std::vector<std::vector<std::string>> &USRList;
303 bool Force;
304 bool &ErrorOccurred;
305};
306
307std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
308 return std::make_unique<NamedDeclFindingConsumer>(
309 SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
310 ErrorOccurred);
311}
312
313} // end namespace tooling
314} // end namespace clang
Defines the clang::ASTContext interface.
Defines the clang::FileManager interface and associated types.
unsigned Offset
Definition: Format.cpp:2776
Defines the clang::FrontendAction interface and various convenience abstract classes (clang::ASTFront...
Defines the clang::Preprocessor interface.
Methods for determining the USR of a symbol at a location in source code.
Provides an action to find all relevant USRs at a point.
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs.
Definition: ASTConsumer.h:33
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:83
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:192
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1542
unsigned getCustomDiagID(Level L, const char(&FormatString)[N])
Return an ID for a diagnostic with the specified format string and level.
Definition: Diagnostic.h:868
StringRef getName() const
Definition: FileEntry.h:384
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
A SourceLocation and its associated SourceManager.
This represents a decl that may have a name.
Definition: Decl.h:247
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Encodes a location in the source.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
FileID getMainFileID() const
Returns the FileID of the main source file.
unsigned getFileIDSize(FileID FID) const
The size of the SLocEntry that FID represents.
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file.
NamedDeclFindingConsumer(ArrayRef< unsigned > SymbolOffsets, ArrayRef< std::string > QualifiedNames, std::vector< std::string > &SpellingNames, std::vector< std::vector< std::string > > &USRList, bool Force, bool &ErrorOccurred)
@ Decl
The l-value was an access to a declared entity or something equivalently strong, like the address of ...
const NamedDecl * getNamedDeclFor(const ASTContext &Context, const std::string &Name)
Definition: USRFinder.cpp:128
const NamedDecl * getNamedDeclAt(const ASTContext &Context, const SourceLocation Point)
Definition: USRFinder.cpp:77
std::string getUSRForDecl(const Decl *Decl)
Definition: USRFinder.cpp:135
const NamedDecl * getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl)
Returns the canonical declaration that best represents a symbol that can be renamed.
std::vector< std::string > getUSRsForDeclaration(const NamedDecl *ND, ASTContext &Context)
Returns the set of USRs that correspond to the given declaration.
@ Specialization
We are substituting template parameters for template arguments in order to form a template specializa...
YAML serialization mapping.
Definition: Dominators.h:30
std::unique_ptr< ASTConsumer > newASTConsumer()