clang 10.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
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"
30
31 #include <algorithm>
32 #include <set>
33 #include <string>
34 #include <vector>
35
36 using namespace llvm;
37
38 namespace clang {
39 namespace tooling {
40
41 const NamedDecl *getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl) {
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
58 namespace {
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.
63 class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
64 public:
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 {
84 USRSet.insert(getUSRForDecl(FoundDecl));
85 }
86 return std::vector<std::string>(USRSet.begin(), USRSet.end());
87 }
88
89 bool shouldVisitTemplateInstantiations() const { return true; }
90
91 bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
92 if (MethodDecl->isVirtual())
93 OverriddenMethods.push_back(MethodDecl);
94 if (MethodDecl->getInstantiatedFromMemberFunction())
95 InstantiatedMethods.push_back(MethodDecl);
96 return true;
97 }
98
99 bool VisitClassTemplatePartialSpecializationDecl(
100 const ClassTemplatePartialSpecializationDecl *PartialSpec) {
101 PartialSpecs.push_back(PartialSpec);
102 return true;
103 }
104
105 private:
106 void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
107 if (!RecordDecl->getDefinition()) {
108 USRSet.insert(getUSRForDecl(RecordDecl));
109 return;
110 }
111 RecordDecl = RecordDecl->getDefinition();
112 if (const auto *ClassTemplateSpecDecl =
113 dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
114 handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
115 addUSRsOfCtorDtors(RecordDecl);
116 }
117
118 void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
119 for (const auto *Specialization : TemplateDecl->specializations())
120 addUSRsOfCtorDtors(Specialization);
121
122 for (const auto *PartialSpec : PartialSpecs) {
123 if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
124 addUSRsOfCtorDtors(PartialSpec);
125 }
126 addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
127 }
128
129 void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
130 RecordDecl = RecordDecl->getDefinition();
131
132 // Skip if the CXXRecordDecl doesn't have definition.
133 if (!RecordDecl)
134 return;
135
136 for (const auto *CtorDecl : RecordDecl->ctors())
137 USRSet.insert(getUSRForDecl(CtorDecl));
138
139 USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
140 USRSet.insert(getUSRForDecl(RecordDecl));
141 }
142
143 void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
144 USRSet.insert(getUSRForDecl(MethodDecl));
145 // Recursively visit each OverridenMethod.
146 for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
147 addUSRsOfOverridenFunctions(OverriddenMethod);
148 }
149
150 void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) {
151 // For renaming a class template method, all references of the instantiated
152 // member methods should be renamed too, so add USRs of the instantiated
153 // methods to the USR set.
154 USRSet.insert(getUSRForDecl(MethodDecl));
155 if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction())
156 USRSet.insert(getUSRForDecl(FT));
157 for (const auto *Method : InstantiatedMethods) {
158 if (USRSet.find(getUSRForDecl(
159 Method->getInstantiatedFromMemberFunction())) != USRSet.end())
160 USRSet.insert(getUSRForDecl(Method));
161 }
162 }
163
164 bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
165 for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
166 if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
167 return true;
168 return checkIfOverriddenFunctionAscends(OverriddenMethod);
169 }
170 return false;
171 }
172
173 const Decl *FoundDecl;
174 ASTContext &Context;
175 std::set<std::string> USRSet;
176 std::vector<const CXXMethodDecl *> OverriddenMethods;
177 std::vector<const CXXMethodDecl *> InstantiatedMethods;
178 std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
179 };
180 } // namespace
181
182 std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
183 ASTContext &Context) {
184 AdditionalUSRFinder Finder(ND, Context);
185 return Finder.Find();
186 }
187
188 class NamedDeclFindingConsumer : public ASTConsumer {
189 public:
190 NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
191 ArrayRef<std::string> QualifiedNames,
192 std::vector<std::string> &SpellingNames,
193 std::vector<std::vector<std::string>> &USRList,
194 bool Force, bool &ErrorOccurred)
195 : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
196 SpellingNames(SpellingNames), USRList(USRList), Force(Force),
197 ErrorOccurred(ErrorOccurred) {}
198
199 private:
200 bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
201 unsigned SymbolOffset, const std::string &QualifiedName) {
202 DiagnosticsEngine &Engine = Context.getDiagnostics();
203 const FileID MainFileID = SourceMgr.getMainFileID();
204
205 if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
206 ErrorOccurred = true;
207 unsigned InvalidOffset = Engine.getCustomDiagID(
208 DiagnosticsEngine::Error,
209 "SourceLocation in file %0 at offset %1 is invalid");
210 Engine.Report(SourceLocation(), InvalidOffset)
211 << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
212 return false;
213 }
214
215 const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
216 .getLocWithOffset(SymbolOffset);
217 const NamedDecl *FoundDecl = QualifiedName.empty()
218 ? getNamedDeclAt(Context, Point)
219 : getNamedDeclFor(Context, QualifiedName);
220
221 if (FoundDecl == nullptr) {
222 if (QualifiedName.empty()) {
223 FullSourceLoc FullLoc(Point, SourceMgr);
224 unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
225 DiagnosticsEngine::Error,
226 "clang-rename could not find symbol (offset %0)");
227 Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
228 ErrorOccurred = true;
229 return false;
230 }
231
232 if (Force) {
233 SpellingNames.push_back(std::string());
234 USRList.push_back(std::vector<std::string>());
235 return true;
236 }
237
238 unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
239 DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
240 Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
241 ErrorOccurred = true;
242 return false;
243 }
244
245 FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
246 SpellingNames.push_back(FoundDecl->getNameAsString());
247 AdditionalUSRFinder Finder(FoundDecl, Context);
248 USRList.push_back(Finder.Find());
249 return true;
250 }
251
252 void HandleTranslationUnit(ASTContext &Context) override {
253 const SourceManager &SourceMgr = Context.getSourceManager();
254 for (unsigned Offset : SymbolOffsets) {
255 if (!FindSymbol(Context, SourceMgr, Offset, ""))
256 return;
257 }
258 for (const std::string &QualifiedName : QualifiedNames) {
259 if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
260 return;
261 }
262 }
263
264 ArrayRef<unsigned> SymbolOffsets;
265 ArrayRef<std::string> QualifiedNames;
266 std::vector<std::string> &SpellingNames;
267 std::vector<std::vector<std::string>> &USRList;
268 bool Force;
269 bool &ErrorOccurred;
270 };
271
272 std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
273 return std::make_unique<NamedDeclFindingConsumer>(
274 SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
275 ErrorOccurred);
276 }
277
278 } // end namespace tooling
279 } // end namespace clang
clang::SourceManager::getLocForStartOfFile
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file. ...
Definition: SourceManager.h:1109
ASTContext.h
Defines the clang::ASTContext interface.
clang::SourceLocation::getLocWithOffset
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
Definition: SourceLocation.h:132
clang::ASTConsumer
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs...
Definition: ASTConsumer.h:33
FileManager.h
Defines the clang::FileManager interface and associated types.
AST.h
llvm
Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be placed into a PointerUnion...
Definition: Dominators.h:30
clang::tooling::getUSRsForDeclaration
std::vector< std::string > getUSRsForDeclaration(const NamedDecl *ND, ASTContext &Context)
Returns the set of USRs that correspond to the given declaration.
Definition: USRFindingAction.cpp:182
clang::tooling::NamedDeclFindingConsumer
Definition: USRFindingAction.cpp:188
clang::ClassTemplateDecl::specializations
spec_range specializations() const
Definition: DeclTemplate.h:2405
clang::Decl
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:88
clang::CXXMethodDecl::isVirtual
bool isVirtual() const
Definition: DeclCXX.h:1976
clang::ASTContext::getDiagnostics
DiagnosticsEngine & getDiagnostics() const
Definition: ASTContext.cpp:1516
clang::DiagnosticsEngine::Report
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1300
clang::RecordDecl
Represents a struct/union/class.
Definition: Decl.h:3748
clang::ASTContext
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:168
Refactoring.h
clang::DiagnosticsEngine
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:149
Error
llvm::Error Error
Definition: ByteCodeEmitter.cpp:19
USRFindingAction.h
Provides an action to find all relevant USRs at a point.
USRFinder.h
Methods for determining the USR of a symbol at a location in source code.
clang::FunctionDecl::getInstantiatedFromMemberFunction
FunctionDecl * getInstantiatedFromMemberFunction() const
If this function is an instantiation of a member function of a class template specialization, retrieves the function from which it was instantiated.
Definition: Decl.cpp:3533
clang::RecursiveASTVisitor
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Definition: RecursiveASTVisitor.h:151
clang::CXXRecordDecl::getDestructor
CXXDestructorDecl * getDestructor() const
Returns the destructor decl for this class.
Definition: DeclCXX.cpp:1770
llvm::ArrayRef< unsigned >
clang::ClassTemplateDecl::getTemplatedDecl
CXXRecordDecl * getTemplatedDecl() const
Get the underlying class declarations of the template.
Definition: DeclTemplate.h:2286
clang::SourceManager::getFileIDSize
unsigned getFileIDSize(FileID FID) const
The size of the SLocEntry that FID represents.
Definition: SourceManager.cpp:1543
CompilerInstance.h
clang::tooling::getNamedDeclFor
const NamedDecl * getNamedDeclFor(const ASTContext &Context, const std::string &Name)
Definition: USRFinder.cpp:127
Offset
unsigned Offset
Definition: Format.cpp:1827
clang::SourceManager::getFileEntryForID
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
Definition: SourceManager.h:1015
Preprocessor.h
Defines the clang::Preprocessor interface.
clang::ClassTemplateSpecializationDecl::getSpecializedTemplate
ClassTemplateDecl * getSpecializedTemplate() const
Retrieve the template that this specialization specializes.
Definition: DeclTemplate.cpp:925
clang::CXXMethodDecl::overridden_methods
overridden_method_range overridden_methods() const
Definition: DeclCXX.cpp:2303
clang::CXXRecordDecl::getDefinition
CXXRecordDecl * getDefinition() const
Definition: DeclCXX.h:533
Lexer.h
clang::tooling::getCanonicalSymbolDeclaration
const NamedDecl * getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl)
Returns the canonical declaration that best represents a symbol that can be renamed.
Definition: USRFindingAction.cpp:41
ASTConsumer.h
RecursiveASTVisitor.h
clang::tooling::getNamedDeclAt
const NamedDecl * getNamedDeclAt(const ASTContext &Context, const SourceLocation Point)
Definition: USRFinder.cpp:76
clang::SourceLocation
Encodes a location in the source.
Definition: SourceLocation.h:86
clang::FileEntry::getName
StringRef getName() const
Definition: FileManager.h:102
clang::tooling::getUSRForDecl
std::string getUSRForDecl(const Decl *Decl)
Definition: USRFinder.cpp:134
clang::NamedDecl::getNameAsString
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
Definition: Decl.h:266
clang::CXXMethodDecl
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:1931
CommonOptionsParser.h
clang::DiagnosticsEngine::getCustomDiagID
unsigned getCustomDiagID(Level L, const char(&FormatString)[N])
Return an ID for a diagnostic with the specified format string and level.
Definition: Diagnostic.h:782
clang::FileID
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Definition: SourceLocation.h:38
Tooling.h
Decl.h
clang
Dataflow Directional Tag Classes.
Definition: CFGReachabilityAnalysis.h:21
clang::TemplateDecl
The base class of all kinds of template declarations (e.g., class, function, etc.).
Definition: DeclTemplate.h:402
clang::ClassTemplatePartialSpecializationDecl
Definition: DeclTemplate.h:2066
clang::SourceManager::getMainFileID
FileID getMainFileID() const
Returns the FileID of the main source file.
Definition: SourceManager.h:807
clang::ASTContext::getSourceManager
SourceManager & getSourceManager()
Definition: ASTContext.h:679
clang::CXXRecordDecl
Represents a C++ struct/union/class.
Definition: DeclCXX.h:253
clang::ClassTemplateDecl
Declaration of a class template.
Definition: DeclTemplate.h:2238
clang::tooling::NamedDeclFindingConsumer::NamedDeclFindingConsumer
NamedDeclFindingConsumer(ArrayRef< unsigned > SymbolOffsets, ArrayRef< std::string > QualifiedNames, std::vector< std::string > &SpellingNames, std::vector< std::vector< std::string >> &USRList, bool Force, bool &ErrorOccurred)
Definition: USRFindingAction.cpp:190
clang::FullSourceLoc
A SourceLocation and its associated SourceManager.
Definition: SourceLocation.h:361
FrontendAction.h
Defines the clang::FrontendAction interface and various convenience abstract classes (clang::ASTFront...
clang::NamedDecl
This represents a decl that may have a name.
Definition: Decl.h:223
clang::CXXRecordDecl::ctors
ctor_range ctors() const
Definition: DeclCXX.h:649
clang::SourceManager
This class handles loading and caching of source files into memory.
Definition: SourceManager.h:596