/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 | 10 | llvm::for_each(VTD->specializations(), [&](const auto *Spec) { |
149 | 10 | USRSet.insert(getUSRForDecl(Spec)); |
150 | 10 | }); |
151 | 5 | SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs; |
152 | 5 | VTD->getPartialSpecializations(PartialSpecs); |
153 | 5 | llvm::for_each(PartialSpecs, [&](const auto *Spec) { |
154 | 5 | USRSet.insert(getUSRForDecl(Spec)); |
155 | 5 | }); |
156 | 5 | } |
157 | | |
158 | 173 | void addUSRsOfCtorDtors(const CXXRecordDecl *RD) { |
159 | 173 | const auto* RecordDecl = RD->getDefinition(); |
160 | | |
161 | | // Skip if the CXXRecordDecl doesn't have definition. |
162 | 173 | if (!RecordDecl) { |
163 | 6 | USRSet.insert(getUSRForDecl(RD)); |
164 | 6 | return; |
165 | 6 | } |
166 | | |
167 | 167 | for (const auto *CtorDecl : RecordDecl->ctors()) |
168 | 210 | USRSet.insert(getUSRForDecl(CtorDecl)); |
169 | | // Add template constructor decls, they are not in ctors() unfortunately. |
170 | 167 | if (RecordDecl->hasUserDeclaredConstructor()) |
171 | 16 | for (const auto *D : RecordDecl->decls()) |
172 | 147 | if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) |
173 | 2 | if (const auto *Ctor = |
174 | 2 | dyn_cast<CXXConstructorDecl>(FTD->getTemplatedDecl())) |
175 | 2 | USRSet.insert(getUSRForDecl(Ctor)); |
176 | | |
177 | 167 | USRSet.insert(getUSRForDecl(RecordDecl->getDestructor())); |
178 | 167 | USRSet.insert(getUSRForDecl(RecordDecl)); |
179 | 167 | } |
180 | | |
181 | 53 | void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) { |
182 | 53 | USRSet.insert(getUSRForDecl(MethodDecl)); |
183 | | // Recursively visit each OverridenMethod. |
184 | 53 | for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) |
185 | 13 | addUSRsOfOverridenFunctions(OverriddenMethod); |
186 | 53 | } |
187 | | |
188 | 40 | void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) { |
189 | | // For renaming a class template method, all references of the instantiated |
190 | | // member methods should be renamed too, so add USRs of the instantiated |
191 | | // methods to the USR set. |
192 | 40 | USRSet.insert(getUSRForDecl(MethodDecl)); |
193 | 40 | if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction()) |
194 | 3 | USRSet.insert(getUSRForDecl(FT)); |
195 | 44 | for (const auto *Method : InstantiatedMethods) { |
196 | 44 | if (USRSet.find(getUSRForDecl( |
197 | 44 | Method->getInstantiatedFromMemberFunction())) != USRSet.end()) |
198 | 20 | USRSet.insert(getUSRForDecl(Method)); |
199 | 44 | } |
200 | 40 | } |
201 | | |
202 | 105 | bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) { |
203 | 105 | for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) { |
204 | 51 | if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end()) |
205 | 30 | return true; |
206 | 21 | return checkIfOverriddenFunctionAscends(OverriddenMethod); |
207 | 51 | } |
208 | 54 | return false; |
209 | 105 | } |
210 | | |
211 | | const Decl *FoundDecl; |
212 | | ASTContext &Context; |
213 | | std::set<std::string> USRSet; |
214 | | std::vector<const CXXMethodDecl *> OverriddenMethods; |
215 | | std::vector<const CXXMethodDecl *> InstantiatedMethods; |
216 | | }; |
217 | | } // namespace |
218 | | |
219 | | std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND, |
220 | 13 | ASTContext &Context) { |
221 | 13 | AdditionalUSRFinder Finder(ND, Context); |
222 | 13 | return Finder.Find(); |
223 | 13 | } |
224 | | |
225 | | class NamedDeclFindingConsumer : public ASTConsumer { |
226 | | public: |
227 | | NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets, |
228 | | ArrayRef<std::string> QualifiedNames, |
229 | | std::vector<std::string> &SpellingNames, |
230 | | std::vector<std::vector<std::string>> &USRList, |
231 | | bool Force, bool &ErrorOccurred) |
232 | | : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames), |
233 | | SpellingNames(SpellingNames), USRList(USRList), Force(Force), |
234 | 330 | ErrorOccurred(ErrorOccurred) {} |
235 | | |
236 | | private: |
237 | | bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr, |
238 | 335 | unsigned SymbolOffset, const std::string &QualifiedName) { |
239 | 335 | DiagnosticsEngine &Engine = Context.getDiagnostics(); |
240 | 335 | const FileID MainFileID = SourceMgr.getMainFileID(); |
241 | | |
242 | 335 | if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) { |
243 | 1 | ErrorOccurred = true; |
244 | 1 | unsigned InvalidOffset = Engine.getCustomDiagID( |
245 | 1 | DiagnosticsEngine::Error, |
246 | 1 | "SourceLocation in file %0 at offset %1 is invalid"); |
247 | 1 | Engine.Report(SourceLocation(), InvalidOffset) |
248 | 1 | << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset; |
249 | 1 | return false; |
250 | 1 | } |
251 | | |
252 | 334 | const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID) |
253 | 334 | .getLocWithOffset(SymbolOffset); |
254 | 334 | const NamedDecl *FoundDecl = QualifiedName.empty() |
255 | 334 | ? getNamedDeclAt(Context, Point)64 |
256 | 334 | : getNamedDeclFor(Context, QualifiedName)270 ; |
257 | | |
258 | 334 | if (FoundDecl == nullptr) { |
259 | 2 | if (QualifiedName.empty()) { |
260 | 0 | FullSourceLoc FullLoc(Point, SourceMgr); |
261 | 0 | unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID( |
262 | 0 | DiagnosticsEngine::Error, |
263 | 0 | "clang-rename could not find symbol (offset %0)"); |
264 | 0 | Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset; |
265 | 0 | ErrorOccurred = true; |
266 | 0 | return false; |
267 | 0 | } |
268 | | |
269 | 2 | if (Force) { |
270 | 2 | SpellingNames.push_back(std::string()); |
271 | 2 | USRList.push_back(std::vector<std::string>()); |
272 | 2 | return true; |
273 | 2 | } |
274 | | |
275 | 0 | unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID( |
276 | 0 | DiagnosticsEngine::Error, "clang-rename could not find symbol %0"); |
277 | 0 | Engine.Report(CouldNotFindSymbolNamed) << QualifiedName; |
278 | 0 | ErrorOccurred = true; |
279 | 0 | return false; |
280 | 2 | } |
281 | | |
282 | 332 | FoundDecl = getCanonicalSymbolDeclaration(FoundDecl); |
283 | 332 | SpellingNames.push_back(FoundDecl->getNameAsString()); |
284 | 332 | AdditionalUSRFinder Finder(FoundDecl, Context); |
285 | 332 | USRList.push_back(Finder.Find()); |
286 | 332 | return true; |
287 | 334 | } |
288 | | |
289 | 330 | void HandleTranslationUnit(ASTContext &Context) override { |
290 | 330 | const SourceManager &SourceMgr = Context.getSourceManager(); |
291 | 330 | for (unsigned Offset : SymbolOffsets) { |
292 | 65 | if (!FindSymbol(Context, SourceMgr, Offset, "")) |
293 | 1 | return; |
294 | 65 | } |
295 | 329 | for (const std::string &QualifiedName : QualifiedNames) { |
296 | 270 | if (!FindSymbol(Context, SourceMgr, 0, QualifiedName)) |
297 | 0 | return; |
298 | 270 | } |
299 | 329 | } |
300 | | |
301 | | ArrayRef<unsigned> SymbolOffsets; |
302 | | ArrayRef<std::string> QualifiedNames; |
303 | | std::vector<std::string> &SpellingNames; |
304 | | std::vector<std::vector<std::string>> &USRList; |
305 | | bool Force; |
306 | | bool &ErrorOccurred; |
307 | | }; |
308 | | |
309 | 330 | std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() { |
310 | 330 | return std::make_unique<NamedDeclFindingConsumer>( |
311 | 330 | SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force, |
312 | 330 | ErrorOccurred); |
313 | 330 | } |
314 | | |
315 | | } // end namespace tooling |
316 | | } // end namespace clang |