clang 8.0.0

USRFindingAction.cpp
Go to the documentation of this file.
1 //===--- USRFindingAction.cpp - Clang refactoring library -----------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// Provides an action to find USR for the symbol at <offset>, as well as
12 /// all additional USRs.
13 ///
14 //===----------------------------------------------------------------------===//
15
16 #include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
17 #include "clang/AST/AST.h"
18 #include "clang/AST/ASTConsumer.h"
19 #include "clang/AST/ASTContext.h"
20 #include "clang/AST/Decl.h"
21 #include "clang/AST/RecursiveASTVisitor.h"
22 #include "clang/Basic/FileManager.h"
23 #include "clang/Frontend/CompilerInstance.h"
24 #include "clang/Frontend/FrontendAction.h"
25 #include "clang/Lex/Lexer.h"
26 #include "clang/Lex/Preprocessor.h"
27 #include "clang/Tooling/CommonOptionsParser.h"
28 #include "clang/Tooling/Refactoring.h"
29 #include "clang/Tooling/Refactoring/Rename/USRFinder.h"
30 #include "clang/Tooling/Tooling.h"
31
32 #include <algorithm>
33 #include <set>
34 #include <string>
35 #include <vector>
36
37 using namespace llvm;
38
39 namespace clang {
40 namespace tooling {
41
42 const NamedDecl *getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl) {
43 // If FoundDecl is a constructor or destructor, we want to instead take
44 // the Decl of the corresponding class.
45 if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
46 FoundDecl = CtorDecl->getParent();
47 else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
48 FoundDecl = DtorDecl->getParent();
49 // FIXME: (Alex L): Canonicalize implicit template instantions, just like
50 // the indexer does it.
51
52 // Note: please update the declaration's doc comment every time the
53 // canonicalization rules are changed.
54 return FoundDecl;
55 }
56
57 namespace {
58 // NamedDeclFindingConsumer should delegate finding USRs of given Decl to
59 // AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given
60 // Decl refers to class and adds USRs of all overridden methods if Decl refers
61 // to virtual method.
62 class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
63 public:
64 AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
65 : FoundDecl(FoundDecl), Context(Context) {}
66
67 std::vector<std::string> Find() {
68 // Fill OverriddenMethods and PartialSpecs storages.
69 TraverseDecl(Context.getTranslationUnitDecl());
70 if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
71 addUSRsOfOverridenFunctions(MethodDecl);
72 for (const auto &OverriddenMethod : OverriddenMethods) {
73 if (checkIfOverriddenFunctionAscends(OverriddenMethod))
74 USRSet.insert(getUSRForDecl(OverriddenMethod));
75 }
76 addUSRsOfInstantiatedMethods(MethodDecl);
77 } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
78 handleCXXRecordDecl(RecordDecl);
79 } else if (const auto *TemplateDecl =
80 dyn_cast<ClassTemplateDecl>(FoundDecl)) {
81 handleClassTemplateDecl(TemplateDecl);
82 } else {
83 USRSet.insert(getUSRForDecl(FoundDecl));
84 }
85 return std::vector<std::string>(USRSet.begin(), USRSet.end());
86 }
87
88 bool shouldVisitTemplateInstantiations() const { return true; }
89
90 bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
91 if (MethodDecl->isVirtual())
92 OverriddenMethods.push_back(MethodDecl);
93 if (MethodDecl->getInstantiatedFromMemberFunction())
94 InstantiatedMethods.push_back(MethodDecl);
95 return true;
96 }
97
98 bool VisitClassTemplatePartialSpecializationDecl(
99 const ClassTemplatePartialSpecializationDecl *PartialSpec) {
100 PartialSpecs.push_back(PartialSpec);
101 return true;
102 }
103
104 private:
105 void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
106 RecordDecl = RecordDecl->getDefinition();
107 if (const auto *ClassTemplateSpecDecl =
108 dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
109 handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
110 addUSRsOfCtorDtors(RecordDecl);
111 }
112
113 void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
114 for (const auto *Specialization : TemplateDecl->specializations())
115 addUSRsOfCtorDtors(Specialization);
116
117 for (const auto *PartialSpec : PartialSpecs) {
118 if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
119 addUSRsOfCtorDtors(PartialSpec);
120 }
121 addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
122 }
123
124 void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
125 RecordDecl = RecordDecl->getDefinition();
126
127 // Skip if the CXXRecordDecl doesn't have definition.
128 if (!RecordDecl)
129 return;
130
131 for (const auto *CtorDecl : RecordDecl->ctors())
132 USRSet.insert(getUSRForDecl(CtorDecl));
133
134 USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
135 USRSet.insert(getUSRForDecl(RecordDecl));
136 }
137
138 void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
139 USRSet.insert(getUSRForDecl(MethodDecl));
140 // Recursively visit each OverridenMethod.
141 for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
142 addUSRsOfOverridenFunctions(OverriddenMethod);
143 }
144
145 void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) {
146 // For renaming a class template method, all references of the instantiated
147 // member methods should be renamed too, so add USRs of the instantiated
148 // methods to the USR set.
149 USRSet.insert(getUSRForDecl(MethodDecl));
150 if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction())
151 USRSet.insert(getUSRForDecl(FT));
152 for (const auto *Method : InstantiatedMethods) {
153 if (USRSet.find(getUSRForDecl(
154 Method->getInstantiatedFromMemberFunction())) != USRSet.end())
155 USRSet.insert(getUSRForDecl(Method));
156 }
157 }
158
159 bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
160 for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
161 if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
162 return true;
163 return checkIfOverriddenFunctionAscends(OverriddenMethod);
164 }
165 return false;
166 }
167
168 const Decl *FoundDecl;
169 ASTContext &Context;
170 std::set<std::string> USRSet;
171 std::vector<const CXXMethodDecl *> OverriddenMethods;
172 std::vector<const CXXMethodDecl *> InstantiatedMethods;
173 std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
174 };
175 } // namespace
176
177 std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
178 ASTContext &Context) {
179 AdditionalUSRFinder Finder(ND, Context);
180 return Finder.Find();
181 }
182
183 class NamedDeclFindingConsumer : public ASTConsumer {
184 public:
185 NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
186 ArrayRef<std::string> QualifiedNames,
187 std::vector<std::string> &SpellingNames,
188 std::vector<std::vector<std::string>> &USRList,
189 bool Force, bool &ErrorOccurred)
190 : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
191 SpellingNames(SpellingNames), USRList(USRList), Force(Force),
192 ErrorOccurred(ErrorOccurred) {}
193
194 private:
195 bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
196 unsigned SymbolOffset, const std::string &QualifiedName) {
197 DiagnosticsEngine &Engine = Context.getDiagnostics();
198 const FileID MainFileID = SourceMgr.getMainFileID();
199
200 if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
201 ErrorOccurred = true;
202 unsigned InvalidOffset = Engine.getCustomDiagID(
203 DiagnosticsEngine::Error,
204 "SourceLocation in file %0 at offset %1 is invalid");
205 Engine.Report(SourceLocation(), InvalidOffset)
206 << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
207 return false;
208 }
209
210 const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
211 .getLocWithOffset(SymbolOffset);
212 const NamedDecl *FoundDecl = QualifiedName.empty()
213 ? getNamedDeclAt(Context, Point)
214 : getNamedDeclFor(Context, QualifiedName);
215
216 if (FoundDecl == nullptr) {
217 if (QualifiedName.empty()) {
218 FullSourceLoc FullLoc(Point, SourceMgr);
219 unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
220 DiagnosticsEngine::Error,
221 "clang-rename could not find symbol (offset %0)");
222 Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
223 ErrorOccurred = true;
224 return false;
225 }
226
227 if (Force) {
228 SpellingNames.push_back(std::string());
229 USRList.push_back(std::vector<std::string>());
230 return true;
231 }
232
233 unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
234 DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
235 Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
236 ErrorOccurred = true;
237 return false;
238 }
239
240 FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
241 SpellingNames.push_back(FoundDecl->getNameAsString());
242 AdditionalUSRFinder Finder(FoundDecl, Context);
243 USRList.push_back(Finder.Find());
244 return true;
245 }
246
247 void HandleTranslationUnit(ASTContext &Context) override {
248 const SourceManager &SourceMgr = Context.getSourceManager();
249 for (unsigned Offset : SymbolOffsets) {
250 if (!FindSymbol(Context, SourceMgr, Offset, ""))
251 return;
252 }
253 for (const std::string &QualifiedName : QualifiedNames) {
254 if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
255 return;
256 }
257 }
258
259 ArrayRef<unsigned> SymbolOffsets;
260 ArrayRef<std::string> QualifiedNames;
261 std::vector<std::string> &SpellingNames;
262 std::vector<std::vector<std::string>> &USRList;
263 bool Force;
264 bool &ErrorOccurred;
265 };
266
267 std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
268 return llvm::make_unique<NamedDeclFindingConsumer>(
269 SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
270 ErrorOccurred);
271 }
272
273 } // end namespace tooling
274 } // 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:1067
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:134
clang::ASTConsumer
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs...
Definition: ASTConsumer.h:34
FileManager.h
Defines the clang::FileManager interface and associated types.
AST.h
llvm
DominatorTree GraphTraits specialization so the DominatorTree can be iterable by generic graph iterat...
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:177
clang::tooling::NamedDeclFindingConsumer
Definition: USRFindingAction.cpp:183
clang::ClassTemplateDecl::specializations
spec_range specializations() const
Definition: DeclTemplate.h:2223
clang::Decl
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:87
clang::CXXMethodDecl::isVirtual
bool isVirtual() const
Definition: DeclCXX.h:2086
clang::ASTContext::getDiagnostics
DiagnosticsEngine & getDiagnostics() const
Definition: ASTContext.cpp:1335
clang::DiagnosticsEngine::Report
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1295
clang::RecordDecl
Represents a struct/union/class.
Definition: Decl.h:3593
clang::ASTContext
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:155
clang::serialized_diags::Error
Definition: SerializedDiagnostics.h:48
Refactoring.h
clang::DiagnosticsEngine
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:149
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:3332
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:1697
llvm::ArrayRef< unsigned >
clang::ClassTemplateDecl::getTemplatedDecl
CXXRecordDecl * getTemplatedDecl() const
Get the underlying class declarations of the template.
Definition: DeclTemplate.h:2103
clang::SourceManager::getFileIDSize
unsigned getFileIDSize(FileID FID) const
The size of the SLocEntry that FID represents.
Definition: SourceManager.cpp:1509
CompilerInstance.h
clang::tooling::getNamedDeclFor
const NamedDecl * getNamedDeclFor(const ASTContext &Context, const std::string &Name)
Definition: USRFinder.cpp:128
Offset
unsigned Offset
Definition: Format.cpp:1631
clang::SourceManager::getFileEntryForID
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
Definition: SourceManager.h:986
Preprocessor.h
Defines the clang::Preprocessor interface.
clang::ClassTemplateSpecializationDecl::getSpecializedTemplate
ClassTemplateDecl * getSpecializedTemplate() const
Retrieve the template that this specialization specializes.
Definition: DeclTemplate.cpp:781
clang::CXXMethodDecl::overridden_methods
overridden_method_range overridden_methods() const
Definition: DeclCXX.cpp:2170
clang::CXXRecordDecl::getDefinition
CXXRecordDecl * getDefinition() const
Definition: DeclCXX.h:769
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:42
ASTConsumer.h
RecursiveASTVisitor.h
clang::tooling::getNamedDeclAt
const NamedDecl * getNamedDeclAt(const ASTContext &Context, const SourceLocation Point)
Definition: USRFinder.cpp:77
clang::SourceLocation
Encodes a location in the source.
Definition: SourceLocation.h:88
clang::FileEntry::getName
StringRef getName() const
Definition: FileManager.h:85
clang::tooling::getUSRForDecl
std::string getUSRForDecl(const Decl *Decl)
Definition: USRFinder.cpp:135
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:292
clang::CXXMethodDecl
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:2041
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:776
clang::FileID
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Definition: SourceLocation.h:40
Tooling.h
Decl.h
clang
Dataflow Directional Tag Classes.
Definition: CFGReachabilityAnalysis.h:22
clang::TemplateDecl
The base class of all kinds of template declarations (e.g., class, function, etc.).
Definition: DeclTemplate.h:399
clang::ClassTemplatePartialSpecializationDecl
Definition: DeclTemplate.h:1898
clang::SourceManager::getMainFileID
FileID getMainFileID() const
Returns the FileID of the main source file.
Definition: SourceManager.h:793
clang::ASTContext::getSourceManager
SourceManager & getSourceManager()
Definition: ASTContext.h:662
clang::ASTContext::getTranslationUnitDecl
TranslationUnitDecl * getTranslationUnitDecl() const
Definition: ASTContext.h:1009
clang::CXXRecordDecl
Represents a C++ struct/union/class.
Definition: DeclCXX.h:300
clang::ClassTemplateDecl
Declaration of a class template.
Definition: DeclTemplate.h:2049
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:185
clang::FullSourceLoc
A SourceLocation and its associated SourceManager.
Definition: SourceLocation.h:340
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:249
clang::CXXRecordDecl::ctors
ctor_range ctors() const
Definition: DeclCXX.h:885
clang::SourceManager
This class handles loading and caching of source files into memory.
Definition: SourceManager.h:582