/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 344 | const NamedDecl *getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl) { |
42 | 344 | if (!FoundDecl) |
43 | 0 | return nullptr; |
44 | | // If FoundDecl is a constructor or destructor, we want to instead take |
45 | | // the Decl of the corresponding class. |
46 | 344 | if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl)) |
47 | 2 | FoundDecl = CtorDecl->getParent(); |
48 | 342 | else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl)) |
49 | 0 | 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 | 344 | return FoundDecl; |
56 | 344 | } |
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 | 345 | : FoundDecl(FoundDecl), Context(Context) {} |
67 | | |
68 | 345 | std::vector<std::string> Find() { |
69 | | // Fill OverriddenMethods and PartialSpecs storages. |
70 | 345 | TraverseAST(Context); |
71 | 345 | if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) { |
72 | 40 | addUSRsOfOverridenFunctions(MethodDecl); |
73 | 84 | for (const auto &OverriddenMethod : OverriddenMethods) { |
74 | 84 | if (checkIfOverriddenFunctionAscends(OverriddenMethod)) |
75 | 30 | USRSet.insert(getUSRForDecl(OverriddenMethod)); |
76 | 84 | } |
77 | 40 | addUSRsOfInstantiatedMethods(MethodDecl); |
78 | 305 | } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) { |
79 | 120 | handleCXXRecordDecl(RecordDecl); |
80 | 185 | } else if (const auto *TemplateDecl = |
81 | 185 | dyn_cast<ClassTemplateDecl>(FoundDecl)) { |
82 | 23 | handleClassTemplateDecl(TemplateDecl); |
83 | 162 | } else if (const auto *FD = dyn_cast<FunctionDecl>(FoundDecl)) { |
84 | 17 | USRSet.insert(getUSRForDecl(FD)); |
85 | 17 | if (const auto *FTD = FD->getPrimaryTemplate()) |
86 | 1 | handleFunctionTemplateDecl(FTD); |
87 | 145 | } else if (const auto *FD = dyn_cast<FunctionTemplateDecl>(FoundDecl)) { |
88 | 6 | handleFunctionTemplateDecl(FD); |
89 | 139 | } else if (const auto *VTD = dyn_cast<VarTemplateDecl>(FoundDecl)) { |
90 | 0 | handleVarTemplateDecl(VTD); |
91 | 139 | } else if (const auto *VD = |
92 | 139 | dyn_cast<VarTemplateSpecializationDecl>(FoundDecl)) { |
93 | | // FIXME: figure out why FoundDecl can be a VarTemplateSpecializationDecl. |
94 | 5 | handleVarTemplateDecl(VD->getSpecializedTemplate()); |
95 | 134 | } else if (const auto *VD = dyn_cast<VarDecl>(FoundDecl)) { |
96 | 13 | USRSet.insert(getUSRForDecl(VD)); |
97 | 13 | if (const auto *VTD = VD->getDescribedVarTemplate()) |
98 | 0 | handleVarTemplateDecl(VTD); |
99 | 121 | } else { |
100 | 121 | USRSet.insert(getUSRForDecl(FoundDecl)); |
101 | 121 | } |
102 | 345 | return std::vector<std::string>(USRSet.begin(), USRSet.end()); |
103 | 345 | } |
104 | | |
105 | 577 | bool shouldVisitTemplateInstantiations() const { return true; } |
106 | | |
107 | 1.35k | bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) { |
108 | 1.35k | if (MethodDecl->isVirtual()) |
109 | 114 | OverriddenMethods.push_back(MethodDecl); |
110 | 1.35k | if (MethodDecl->getInstantiatedFromMemberFunction()) |
111 | 101 | InstantiatedMethods.push_back(MethodDecl); |
112 | 1.35k | return true; |
113 | 1.35k | } |
114 | | |
115 | | private: |
116 | 120 | void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) { |
117 | 120 | if (!RecordDecl->getDefinition()) { |
118 | 1 | USRSet.insert(getUSRForDecl(RecordDecl)); |
119 | 1 | return; |
120 | 1 | } |
121 | 119 | RecordDecl = RecordDecl->getDefinition(); |
122 | 119 | if (const auto *ClassTemplateSpecDecl = |
123 | 119 | dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl)) |
124 | 0 | handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate()); |
125 | 119 | addUSRsOfCtorDtors(RecordDecl); |
126 | 119 | } |
127 | | |
128 | 23 | void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) { |
129 | 23 | for (const auto *Specialization : TemplateDecl->specializations()) |
130 | 31 | addUSRsOfCtorDtors(Specialization); |
131 | 23 | SmallVector<ClassTemplatePartialSpecializationDecl *, 4> PartialSpecs; |
132 | 23 | TemplateDecl->getPartialSpecializations(PartialSpecs); |
133 | 23 | for (const auto *Spec : PartialSpecs) |
134 | 0 | addUSRsOfCtorDtors(Spec); |
135 | 23 | addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl()); |
136 | 23 | } |
137 | | |
138 | 7 | void handleFunctionTemplateDecl(const FunctionTemplateDecl *FTD) { |
139 | 7 | USRSet.insert(getUSRForDecl(FTD)); |
140 | 7 | USRSet.insert(getUSRForDecl(FTD->getTemplatedDecl())); |
141 | 7 | for (const auto *S : FTD->specializations()) |
142 | 10 | USRSet.insert(getUSRForDecl(S)); |
143 | 7 | } |
144 | | |
145 | 5 | void handleVarTemplateDecl(const VarTemplateDecl *VTD) { |
146 | 5 | USRSet.insert(getUSRForDecl(VTD)); |
147 | 5 | USRSet.insert(getUSRForDecl(VTD->getTemplatedDecl())); |
148 | 5 | for (const auto *Spec : VTD->specializations()) |
149 | 10 | USRSet.insert(getUSRForDecl(Spec)); |
150 | 5 | SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs; |
151 | 5 | VTD->getPartialSpecializations(PartialSpecs); |
152 | 5 | for (const auto *Spec : PartialSpecs) |
153 | 5 | USRSet.insert(getUSRForDecl(Spec)); |
154 | 5 | } |
155 | | |
156 | 173 | void addUSRsOfCtorDtors(const CXXRecordDecl *RD) { |
157 | 173 | const auto* RecordDecl = RD->getDefinition(); |
158 | | |
159 | | // Skip if the CXXRecordDecl doesn't have definition. |
160 | 173 | if (!RecordDecl) { |
161 | 6 | USRSet.insert(getUSRForDecl(RD)); |
162 | 6 | return; |
163 | 6 | } |
164 | | |
165 | 167 | for (const auto *CtorDecl : RecordDecl->ctors()) |
166 | 210 | USRSet.insert(getUSRForDecl(CtorDecl)); |
167 | | // Add template constructor decls, they are not in ctors() unfortunately. |
168 | 167 | if (RecordDecl->hasUserDeclaredConstructor()) |
169 | 16 | for (const auto *D : RecordDecl->decls()) |
170 | 147 | if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) |
171 | 2 | if (const auto *Ctor = |
172 | 2 | dyn_cast<CXXConstructorDecl>(FTD->getTemplatedDecl())) |
173 | 2 | USRSet.insert(getUSRForDecl(Ctor)); |
174 | | |
175 | 167 | USRSet.insert(getUSRForDecl(RecordDecl->getDestructor())); |
176 | 167 | USRSet.insert(getUSRForDecl(RecordDecl)); |
177 | 167 | } |
178 | | |
179 | 53 | void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) { |
180 | 53 | USRSet.insert(getUSRForDecl(MethodDecl)); |
181 | | // Recursively visit each OverridenMethod. |
182 | 53 | for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) |
183 | 13 | addUSRsOfOverridenFunctions(OverriddenMethod); |
184 | 53 | } |
185 | | |
186 | 40 | 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 | 40 | USRSet.insert(getUSRForDecl(MethodDecl)); |
191 | 40 | if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction()) |
192 | 3 | USRSet.insert(getUSRForDecl(FT)); |
193 | 44 | for (const auto *Method : InstantiatedMethods) { |
194 | 44 | if (USRSet.find(getUSRForDecl( |
195 | 44 | Method->getInstantiatedFromMemberFunction())) != USRSet.end()) |
196 | 20 | USRSet.insert(getUSRForDecl(Method)); |
197 | 44 | } |
198 | 40 | } |
199 | | |
200 | 105 | bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) { |
201 | 105 | for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) { |
202 | 51 | if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end()) |
203 | 30 | return true; |
204 | 21 | return checkIfOverriddenFunctionAscends(OverriddenMethod); |
205 | 51 | } |
206 | 54 | return false; |
207 | 105 | } |
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 | | |
217 | | std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND, |
218 | 13 | ASTContext &Context) { |
219 | 13 | AdditionalUSRFinder Finder(ND, Context); |
220 | 13 | return Finder.Find(); |
221 | 13 | } |
222 | | |
223 | | class NamedDeclFindingConsumer : public ASTConsumer { |
224 | | public: |
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 | 330 | ErrorOccurred(ErrorOccurred) {} |
233 | | |
234 | | private: |
235 | | bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr, |
236 | 335 | unsigned SymbolOffset, const std::string &QualifiedName) { |
237 | 335 | DiagnosticsEngine &Engine = Context.getDiagnostics(); |
238 | 335 | const FileID MainFileID = SourceMgr.getMainFileID(); |
239 | | |
240 | 335 | if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) { |
241 | 1 | ErrorOccurred = true; |
242 | 1 | unsigned InvalidOffset = Engine.getCustomDiagID( |
243 | 1 | DiagnosticsEngine::Error, |
244 | 1 | "SourceLocation in file %0 at offset %1 is invalid"); |
245 | 1 | Engine.Report(SourceLocation(), InvalidOffset) |
246 | 1 | << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset; |
247 | 1 | return false; |
248 | 1 | } |
249 | | |
250 | 334 | const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID) |
251 | 334 | .getLocWithOffset(SymbolOffset); |
252 | 334 | const NamedDecl *FoundDecl = QualifiedName.empty() |
253 | 334 | ? getNamedDeclAt(Context, Point)64 |
254 | 334 | : getNamedDeclFor(Context, QualifiedName)270 ; |
255 | | |
256 | 334 | if (FoundDecl == nullptr) { |
257 | 2 | if (QualifiedName.empty()) { |
258 | 0 | FullSourceLoc FullLoc(Point, SourceMgr); |
259 | 0 | unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID( |
260 | 0 | DiagnosticsEngine::Error, |
261 | 0 | "clang-rename could not find symbol (offset %0)"); |
262 | 0 | Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset; |
263 | 0 | ErrorOccurred = true; |
264 | 0 | return false; |
265 | 0 | } |
266 | | |
267 | 2 | if (Force) { |
268 | 2 | SpellingNames.push_back(std::string()); |
269 | 2 | USRList.push_back(std::vector<std::string>()); |
270 | 2 | return true; |
271 | 2 | } |
272 | | |
273 | 0 | unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID( |
274 | 0 | DiagnosticsEngine::Error, "clang-rename could not find symbol %0"); |
275 | 0 | Engine.Report(CouldNotFindSymbolNamed) << QualifiedName; |
276 | 0 | ErrorOccurred = true; |
277 | 0 | return false; |
278 | 2 | } |
279 | | |
280 | 332 | FoundDecl = getCanonicalSymbolDeclaration(FoundDecl); |
281 | 332 | SpellingNames.push_back(FoundDecl->getNameAsString()); |
282 | 332 | AdditionalUSRFinder Finder(FoundDecl, Context); |
283 | 332 | USRList.push_back(Finder.Find()); |
284 | 332 | return true; |
285 | 334 | } |
286 | | |
287 | 330 | void HandleTranslationUnit(ASTContext &Context) override { |
288 | 330 | const SourceManager &SourceMgr = Context.getSourceManager(); |
289 | 330 | for (unsigned Offset : SymbolOffsets) { |
290 | 65 | if (!FindSymbol(Context, SourceMgr, Offset, "")) |
291 | 1 | return; |
292 | 65 | } |
293 | 329 | for (const std::string &QualifiedName : QualifiedNames) { |
294 | 270 | if (!FindSymbol(Context, SourceMgr, 0, QualifiedName)) |
295 | 0 | return; |
296 | 270 | } |
297 | 329 | } |
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 | | |
307 | 330 | std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() { |
308 | 330 | return std::make_unique<NamedDeclFindingConsumer>( |
309 | 330 | SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force, |
310 | 330 | ErrorOccurred); |
311 | 330 | } |
312 | | |
313 | | } // end namespace tooling |
314 | | } // end namespace clang |