clang 19.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.getFileEntryRefForID(MainFileID)->getName()
247 << SymbolOffset;
248 return false;
249 }
250
251 const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
252 .getLocWithOffset(SymbolOffset);
253 const NamedDecl *FoundDecl = QualifiedName.empty()
254 ? getNamedDeclAt(Context, Point)
255 : getNamedDeclFor(Context, QualifiedName);
256
257 if (FoundDecl == nullptr) {
258 if (QualifiedName.empty()) {
259 FullSourceLoc FullLoc(Point, SourceMgr);
260 unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
262 "clang-rename could not find symbol (offset %0)");
263 Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
264 ErrorOccurred = true;
265 return false;
266 }
267
268 if (Force) {
269 SpellingNames.push_back(std::string());
270 USRList.push_back(std::vector<std::string>());
271 return true;
272 }
273
274 unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
275 DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
276 Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
277 ErrorOccurred = true;
278 return false;
279 }
280
281 FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
282 SpellingNames.push_back(FoundDecl->getNameAsString());
283 AdditionalUSRFinder Finder(FoundDecl, Context);
284 USRList.push_back(Finder.Find());
285 return true;
286 }
287
288 void HandleTranslationUnit(ASTContext &Context) override {
289 const SourceManager &SourceMgr = Context.getSourceManager();
290 for (unsigned Offset : SymbolOffsets) {
291 if (!FindSymbol(Context, SourceMgr, Offset, ""))
292 return;
293 }
294 for (const std::string &QualifiedName : QualifiedNames) {
295 if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
296 return;
297 }
298 }
299
300 ArrayRef<unsigned> SymbolOffsets;
301 ArrayRef<std::string> QualifiedNames;
302 std::vector<std::string> &SpellingNames;
303 std::vector<std::vector<std::string>> &USRList;
304 bool Force;
305 bool &ErrorOccurred;
306};
307
308std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
309 return std::make_unique<NamedDeclFindingConsumer>(
310 SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
311 ErrorOccurred);
312}
313
314} // end namespace tooling
315} // end namespace clang
Defines the clang::ASTContext interface.
Defines the clang::FileManager interface and associated types.
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:86
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:1547
unsigned getCustomDiagID(Level L, const char(&FormatString)[N])
Return an ID for a diagnostic with the specified format string and level.
Definition: Diagnostic.h:873
StringRef getName() const
The name of this FileEntry.
Definition: FileEntry.h:61
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:249
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.
OptionalFileEntryRef getFileEntryRefForID(FileID FID) const
Returns the FileEntryRef for the provided FileID.
FileID getMainFileID() const
Returns the FileID of the main source file.
unsigned getFileIDSize(FileID FID) const
The size of the SLocEntry that FID represents.
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.
The JSON file list parser is used to communicate input to InstallAPI.
@ Specialization
We are substituting template parameters for template arguments in order to form a template specializa...
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30
std::unique_ptr< ASTConsumer > newASTConsumer()