/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- USRLocFinder.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 | | /// Methods for finding all instances of a USR. Our strategy is very |
11 | | /// simple; we just compare the USR at every relevant AST node with the one |
12 | | /// provided. |
13 | | /// |
14 | | //===----------------------------------------------------------------------===// |
15 | | |
16 | | #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h" |
17 | | #include "clang/AST/ASTContext.h" |
18 | | #include "clang/AST/ParentMapContext.h" |
19 | | #include "clang/AST/RecursiveASTVisitor.h" |
20 | | #include "clang/Basic/LLVM.h" |
21 | | #include "clang/Basic/SourceLocation.h" |
22 | | #include "clang/Basic/SourceManager.h" |
23 | | #include "clang/Lex/Lexer.h" |
24 | | #include "clang/Tooling/Refactoring/Lookup.h" |
25 | | #include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h" |
26 | | #include "clang/Tooling/Refactoring/Rename/SymbolName.h" |
27 | | #include "clang/Tooling/Refactoring/Rename/USRFinder.h" |
28 | | #include "llvm/ADT/StringRef.h" |
29 | | #include "llvm/Support/Casting.h" |
30 | | #include <cstddef> |
31 | | #include <set> |
32 | | #include <string> |
33 | | #include <vector> |
34 | | |
35 | | using namespace llvm; |
36 | | |
37 | | namespace clang { |
38 | | namespace tooling { |
39 | | |
40 | | namespace { |
41 | | |
42 | | // Returns true if the given Loc is valid for edit. We don't edit the |
43 | | // SourceLocations that are valid or in temporary buffer. |
44 | 551 | bool IsValidEditLoc(const clang::SourceManager& SM, clang::SourceLocation Loc) { |
45 | 551 | if (Loc.isInvalid()) |
46 | 0 | return false; |
47 | 551 | const clang::FullSourceLoc FullLoc(Loc, SM); |
48 | 551 | std::pair<clang::FileID, unsigned> FileIdAndOffset = |
49 | 551 | FullLoc.getSpellingLoc().getDecomposedLoc(); |
50 | 551 | return SM.getFileEntryForID(FileIdAndOffset.first) != nullptr; |
51 | 551 | } |
52 | | |
53 | | // This visitor recursively searches for all instances of a USR in a |
54 | | // translation unit and stores them for later usage. |
55 | | class USRLocFindingASTVisitor |
56 | | : public RecursiveSymbolVisitor<USRLocFindingASTVisitor> { |
57 | | public: |
58 | | explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs, |
59 | | StringRef PrevName, |
60 | | const ASTContext &Context) |
61 | 84 | : RecursiveSymbolVisitor(Context.getSourceManager(), |
62 | 84 | Context.getLangOpts()), |
63 | 84 | USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) { |
64 | 84 | } |
65 | | |
66 | | bool visitSymbolOccurrence(const NamedDecl *ND, |
67 | 1.52k | ArrayRef<SourceRange> NameRanges) { |
68 | 1.52k | if (USRSet.find(getUSRForDecl(ND)) != USRSet.end()) { |
69 | 460 | assert(NameRanges.size() == 1 && |
70 | 460 | "Multiple name pieces are not supported yet!"); |
71 | 460 | SourceLocation Loc = NameRanges[0].getBegin(); |
72 | 460 | const SourceManager &SM = Context.getSourceManager(); |
73 | | // TODO: Deal with macro occurrences correctly. |
74 | 460 | if (Loc.isMacroID()) |
75 | 16 | Loc = SM.getSpellingLoc(Loc); |
76 | 460 | checkAndAddLocation(Loc); |
77 | 460 | } |
78 | 1.52k | return true; |
79 | 1.52k | } |
80 | | |
81 | | // Non-visitors: |
82 | | |
83 | | /// Returns a set of unique symbol occurrences. Duplicate or |
84 | | /// overlapping occurrences are erroneous and should be reported! |
85 | 84 | SymbolOccurrences takeOccurrences() { return std::move(Occurrences); } |
86 | | |
87 | | private: |
88 | 460 | void checkAndAddLocation(SourceLocation Loc) { |
89 | 460 | const SourceLocation BeginLoc = Loc; |
90 | 460 | const SourceLocation EndLoc = Lexer::getLocForEndOfToken( |
91 | 460 | BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); |
92 | 460 | StringRef TokenName = |
93 | 460 | Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc), |
94 | 460 | Context.getSourceManager(), Context.getLangOpts()); |
95 | 460 | size_t Offset = TokenName.find(PrevName.getNamePieces()[0]); |
96 | | |
97 | | // The token of the source location we find actually has the old |
98 | | // name. |
99 | 460 | if (Offset != StringRef::npos) |
100 | 459 | Occurrences.emplace_back(PrevName, SymbolOccurrence::MatchingSymbol, |
101 | 459 | BeginLoc.getLocWithOffset(Offset)); |
102 | 460 | } |
103 | | |
104 | | const std::set<std::string> USRSet; |
105 | | const SymbolName PrevName; |
106 | | SymbolOccurrences Occurrences; |
107 | | const ASTContext &Context; |
108 | | }; |
109 | | |
110 | 215 | SourceLocation StartLocationForType(TypeLoc TL) { |
111 | | // For elaborated types (e.g. `struct a::A`) we want the portion after the |
112 | | // `struct` but including the namespace qualifier, `a::`. |
113 | 215 | if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) { |
114 | 209 | NestedNameSpecifierLoc NestedNameSpecifier = |
115 | 209 | ElaboratedTypeLoc.getQualifierLoc(); |
116 | 209 | if (NestedNameSpecifier.getNestedNameSpecifier()) |
117 | 149 | return NestedNameSpecifier.getBeginLoc(); |
118 | 60 | TL = TL.getNextTypeLoc(); |
119 | 60 | } |
120 | 66 | return TL.getBeginLoc(); |
121 | 215 | } |
122 | | |
123 | 302 | SourceLocation EndLocationForType(TypeLoc TL) { |
124 | | // Dig past any namespace or keyword qualifications. |
125 | 511 | while (TL.getTypeLocClass() == TypeLoc::Elaborated || |
126 | 511 | TL.getTypeLocClass() == TypeLoc::Qualified302 ) |
127 | 209 | TL = TL.getNextTypeLoc(); |
128 | | |
129 | | // The location for template specializations (e.g. Foo<int>) includes the |
130 | | // templated types in its location range. We want to restrict this to just |
131 | | // before the `<` character. |
132 | 302 | if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) { |
133 | 31 | return TL.castAs<TemplateSpecializationTypeLoc>() |
134 | 31 | .getLAngleLoc() |
135 | 31 | .getLocWithOffset(-1); |
136 | 31 | } |
137 | 271 | return TL.getEndLoc(); |
138 | 302 | } |
139 | | |
140 | 214 | NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) { |
141 | | // Dig past any keyword qualifications. |
142 | 214 | while (TL.getTypeLocClass() == TypeLoc::Qualified) |
143 | 0 | TL = TL.getNextTypeLoc(); |
144 | | |
145 | | // For elaborated types (e.g. `struct a::A`) we want the portion after the |
146 | | // `struct` but including the namespace qualifier, `a::`. |
147 | 214 | if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) |
148 | 208 | return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier(); |
149 | 6 | return nullptr; |
150 | 214 | } |
151 | | |
152 | | // Find all locations identified by the given USRs for rename. |
153 | | // |
154 | | // This class will traverse the AST and find every AST node whose USR is in the |
155 | | // given USRs' set. |
156 | | class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> { |
157 | | public: |
158 | | RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context) |
159 | 261 | : USRSet(USRs.begin(), USRs.end()), Context(Context) {} |
160 | | |
161 | | // A structure records all information of a symbol reference being renamed. |
162 | | // We try to add as few prefix qualifiers as possible. |
163 | | struct RenameInfo { |
164 | | // The begin location of a symbol being renamed. |
165 | | SourceLocation Begin; |
166 | | // The end location of a symbol being renamed. |
167 | | SourceLocation End; |
168 | | // The declaration of a symbol being renamed (can be nullptr). |
169 | | const NamedDecl *FromDecl; |
170 | | // The declaration in which the nested name is contained (can be nullptr). |
171 | | const Decl *Context; |
172 | | // The nested name being replaced (can be nullptr). |
173 | | const NestedNameSpecifier *Specifier; |
174 | | // Determine whether the prefix qualifiers of the NewName should be ignored. |
175 | | // Normally, we set it to true for the symbol declaration and definition to |
176 | | // avoid adding prefix qualifiers. |
177 | | // For example, if it is true and NewName is "a::b::foo", then the symbol |
178 | | // occurrence which the RenameInfo points to will be renamed to "foo". |
179 | | bool IgnorePrefixQualifers; |
180 | | }; |
181 | | |
182 | 6.28k | bool VisitNamedDecl(const NamedDecl *Decl) { |
183 | | // UsingDecl has been handled in other place. |
184 | 6.28k | if (llvm::isa<UsingDecl>(Decl)) |
185 | 19 | return true; |
186 | | |
187 | | // DestructorDecl has been handled in Typeloc. |
188 | 6.26k | if (llvm::isa<CXXDestructorDecl>(Decl)) |
189 | 6 | return true; |
190 | | |
191 | 6.25k | if (Decl->isImplicit()) |
192 | 0 | return true; |
193 | | |
194 | 6.25k | if (isInUSRSet(Decl)) { |
195 | | // For the case of renaming an alias template, we actually rename the |
196 | | // underlying alias declaration of the template. |
197 | 305 | if (const auto* TAT = dyn_cast<TypeAliasTemplateDecl>(Decl)) |
198 | 3 | Decl = TAT->getTemplatedDecl(); |
199 | | |
200 | 305 | auto StartLoc = Decl->getLocation(); |
201 | 305 | auto EndLoc = StartLoc; |
202 | 305 | if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { |
203 | 304 | RenameInfo Info = {StartLoc, |
204 | 304 | EndLoc, |
205 | 304 | /*FromDecl=*/nullptr, |
206 | 304 | /*Context=*/nullptr, |
207 | 304 | /*Specifier=*/nullptr, |
208 | 304 | /*IgnorePrefixQualifers=*/true}; |
209 | 304 | RenameInfos.push_back(Info); |
210 | 304 | } |
211 | 305 | } |
212 | 6.25k | return true; |
213 | 6.25k | } |
214 | | |
215 | 25 | bool VisitMemberExpr(const MemberExpr *Expr) { |
216 | 25 | const NamedDecl *Decl = Expr->getFoundDecl(); |
217 | 25 | auto StartLoc = Expr->getMemberLoc(); |
218 | 25 | auto EndLoc = Expr->getMemberLoc(); |
219 | 25 | if (isInUSRSet(Decl)) { |
220 | 15 | RenameInfos.push_back({StartLoc, EndLoc, |
221 | 15 | /*FromDecl=*/nullptr, |
222 | 15 | /*Context=*/nullptr, |
223 | 15 | /*Specifier=*/nullptr, |
224 | 15 | /*IgnorePrefixQualifiers=*/true}); |
225 | 15 | } |
226 | 25 | return true; |
227 | 25 | } |
228 | | |
229 | 1 | bool VisitDesignatedInitExpr(const DesignatedInitExpr *E) { |
230 | 1 | for (const DesignatedInitExpr::Designator &D : E->designators()) { |
231 | 1 | if (D.isFieldDesignator()) { |
232 | 1 | if (const FieldDecl *Decl = D.getFieldDecl()) { |
233 | 1 | if (isInUSRSet(Decl)) { |
234 | 1 | auto StartLoc = D.getFieldLoc(); |
235 | 1 | auto EndLoc = D.getFieldLoc(); |
236 | 1 | RenameInfos.push_back({StartLoc, EndLoc, |
237 | 1 | /*FromDecl=*/nullptr, |
238 | 1 | /*Context=*/nullptr, |
239 | 1 | /*Specifier=*/nullptr, |
240 | 1 | /*IgnorePrefixQualifiers=*/true}); |
241 | 1 | } |
242 | 1 | } |
243 | 1 | } |
244 | 1 | } |
245 | 1 | return true; |
246 | 1 | } |
247 | | |
248 | 11 | bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) { |
249 | | // Fix the constructor initializer when renaming class members. |
250 | 11 | for (const auto *Initializer : CD->inits()) { |
251 | | // Ignore implicit initializers. |
252 | 4 | if (!Initializer->isWritten()) |
253 | 1 | continue; |
254 | | |
255 | 3 | if (const FieldDecl *FD = Initializer->getMember()) { |
256 | 3 | if (isInUSRSet(FD)) { |
257 | 1 | auto Loc = Initializer->getSourceLocation(); |
258 | 1 | RenameInfos.push_back({Loc, Loc, |
259 | 1 | /*FromDecl=*/nullptr, |
260 | 1 | /*Context=*/nullptr, |
261 | 1 | /*Specifier=*/nullptr, |
262 | 1 | /*IgnorePrefixQualifiers=*/true}); |
263 | 1 | } |
264 | 3 | } |
265 | 3 | } |
266 | 11 | return true; |
267 | 11 | } |
268 | | |
269 | 193 | bool VisitDeclRefExpr(const DeclRefExpr *Expr) { |
270 | 193 | const NamedDecl *Decl = Expr->getFoundDecl(); |
271 | | // Get the underlying declaration of the shadow declaration introduced by a |
272 | | // using declaration. |
273 | 193 | if (auto *UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Decl)) { |
274 | 1 | Decl = UsingShadow->getTargetDecl(); |
275 | 1 | } |
276 | | |
277 | 193 | auto StartLoc = Expr->getBeginLoc(); |
278 | | // For template function call expressions like `foo<int>()`, we want to |
279 | | // restrict the end of location to just before the `<` character. |
280 | 193 | SourceLocation EndLoc = Expr->hasExplicitTemplateArgs() |
281 | 193 | ? Expr->getLAngleLoc().getLocWithOffset(-1)10 |
282 | 193 | : Expr->getEndLoc()183 ; |
283 | | |
284 | 193 | if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) { |
285 | 44 | if (isInUSRSet(MD)) { |
286 | | // Handle renaming static template class methods, we only rename the |
287 | | // name without prefix qualifiers and restrict the source range to the |
288 | | // name. |
289 | 21 | RenameInfos.push_back({EndLoc, EndLoc, |
290 | 21 | /*FromDecl=*/nullptr, |
291 | 21 | /*Context=*/nullptr, |
292 | 21 | /*Specifier=*/nullptr, |
293 | 21 | /*IgnorePrefixQualifiers=*/true}); |
294 | 21 | return true; |
295 | 21 | } |
296 | 44 | } |
297 | | |
298 | | // In case of renaming an enum declaration, we have to explicitly handle |
299 | | // unscoped enum constants referenced in expressions (e.g. |
300 | | // "auto r = ns1::ns2::Green" where Green is an enum constant of an unscoped |
301 | | // enum decl "ns1::ns2::Color") as these enum constants cannot be caught by |
302 | | // TypeLoc. |
303 | 172 | if (const auto *T = llvm::dyn_cast<EnumConstantDecl>(Decl)) { |
304 | | // FIXME: Handle the enum constant without prefix qualifiers (`a = Green`) |
305 | | // when renaming an unscoped enum declaration with a new namespace. |
306 | 17 | if (!Expr->hasQualifier()) |
307 | 2 | return true; |
308 | | |
309 | 15 | if (const auto *ED = |
310 | 15 | llvm::dyn_cast_or_null<EnumDecl>(getClosestAncestorDecl(*T))) { |
311 | 15 | if (ED->isScoped()) |
312 | 4 | return true; |
313 | 11 | Decl = ED; |
314 | 11 | } |
315 | | // The current fix would qualify "ns1::ns2::Green" as |
316 | | // "ns1::ns2::Color::Green". |
317 | | // |
318 | | // Get the EndLoc of the replacement by moving 1 character backward ( |
319 | | // to exclude the last '::'). |
320 | | // |
321 | | // ns1::ns2::Green; |
322 | | // ^ ^^ |
323 | | // BeginLoc |EndLoc of the qualifier |
324 | | // new EndLoc |
325 | 11 | EndLoc = Expr->getQualifierLoc().getEndLoc().getLocWithOffset(-1); |
326 | 11 | assert(EndLoc.isValid() && |
327 | 11 | "The enum constant should have prefix qualifers."); |
328 | 11 | } |
329 | 166 | if (isInUSRSet(Decl) && |
330 | 166 | IsValidEditLoc(Context.getSourceManager(), StartLoc)31 ) { |
331 | 31 | RenameInfo Info = {StartLoc, |
332 | 31 | EndLoc, |
333 | 31 | Decl, |
334 | 31 | getClosestAncestorDecl(*Expr), |
335 | 31 | Expr->getQualifier(), |
336 | 31 | /*IgnorePrefixQualifers=*/false}; |
337 | 31 | RenameInfos.push_back(Info); |
338 | 31 | } |
339 | | |
340 | 166 | return true; |
341 | 172 | } |
342 | | |
343 | 19 | bool VisitUsingDecl(const UsingDecl *Using) { |
344 | 21 | for (const auto *UsingShadow : Using->shadows()) { |
345 | 21 | if (isInUSRSet(UsingShadow->getTargetDecl())) { |
346 | 18 | UsingDecls.push_back(Using); |
347 | 18 | break; |
348 | 18 | } |
349 | 21 | } |
350 | 19 | return true; |
351 | 19 | } |
352 | | |
353 | 134 | bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) { |
354 | 134 | if (!NestedLoc.getNestedNameSpecifier()->getAsType()) |
355 | 0 | return true; |
356 | | |
357 | 134 | if (const auto *TargetDecl = |
358 | 134 | getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) { |
359 | 132 | if (isInUSRSet(TargetDecl)) { |
360 | 87 | RenameInfo Info = {NestedLoc.getBeginLoc(), |
361 | 87 | EndLocationForType(NestedLoc.getTypeLoc()), |
362 | 87 | TargetDecl, |
363 | 87 | getClosestAncestorDecl(NestedLoc), |
364 | 87 | NestedLoc.getNestedNameSpecifier()->getPrefix(), |
365 | 87 | /*IgnorePrefixQualifers=*/false}; |
366 | 87 | RenameInfos.push_back(Info); |
367 | 87 | } |
368 | 132 | } |
369 | 134 | return true; |
370 | 134 | } |
371 | | |
372 | 5.52k | bool VisitTypeLoc(TypeLoc Loc) { |
373 | 5.52k | auto Parents = Context.getParents(Loc); |
374 | 5.52k | TypeLoc ParentTypeLoc; |
375 | 5.52k | if (!Parents.empty()) { |
376 | | // Handle cases of nested name specificier locations. |
377 | | // |
378 | | // The VisitNestedNameSpecifierLoc interface is not impelmented in |
379 | | // RecursiveASTVisitor, we have to handle it explicitly. |
380 | 5.48k | if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) { |
381 | 134 | VisitNestedNameSpecifierLocations(*NSL); |
382 | 134 | return true; |
383 | 134 | } |
384 | | |
385 | 5.35k | if (const auto *TL = Parents[0].get<TypeLoc>()) |
386 | 2.62k | ParentTypeLoc = *TL; |
387 | 5.35k | } |
388 | | |
389 | | // Handle the outermost TypeLoc which is directly linked to the interesting |
390 | | // declaration and don't handle nested name specifier locations. |
391 | 5.39k | if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) { |
392 | 1.28k | if (isInUSRSet(TargetDecl)) { |
393 | | // Only handle the outermost typeLoc. |
394 | | // |
395 | | // For a type like "a::Foo", there will be two typeLocs for it. |
396 | | // One ElaboratedType, the other is RecordType: |
397 | | // |
398 | | // ElaboratedType 0x33b9390 'a::Foo' sugar |
399 | | // `-RecordType 0x338fef0 'class a::Foo' |
400 | | // `-CXXRecord 0x338fe58 'Foo' |
401 | | // |
402 | | // Skip if this is an inner typeLoc. |
403 | 416 | if (!ParentTypeLoc.isNull() && |
404 | 416 | isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc))249 ) |
405 | 205 | return true; |
406 | | |
407 | 211 | auto StartLoc = StartLocationForType(Loc); |
408 | 211 | auto EndLoc = EndLocationForType(Loc); |
409 | 211 | if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { |
410 | 210 | RenameInfo Info = {StartLoc, |
411 | 210 | EndLoc, |
412 | 210 | TargetDecl, |
413 | 210 | getClosestAncestorDecl(Loc), |
414 | 210 | GetNestedNameForType(Loc), |
415 | 210 | /*IgnorePrefixQualifers=*/false}; |
416 | 210 | RenameInfos.push_back(Info); |
417 | 210 | } |
418 | 211 | return true; |
419 | 416 | } |
420 | 1.28k | } |
421 | | |
422 | | // Handle specific template class specialiation cases. |
423 | 4.97k | if (const auto *TemplateSpecType = |
424 | 4.97k | dyn_cast<TemplateSpecializationType>(Loc.getType())) { |
425 | 152 | TypeLoc TargetLoc = Loc; |
426 | 152 | if (!ParentTypeLoc.isNull()) { |
427 | 152 | if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType())) |
428 | 152 | TargetLoc = ParentTypeLoc; |
429 | 152 | } |
430 | | |
431 | 152 | if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) { |
432 | 4 | TypeLoc TargetLoc = Loc; |
433 | | // FIXME: Find a better way to handle this case. |
434 | | // For the qualified template class specification type like |
435 | | // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc |
436 | | // (ElaboratedType) of the TemplateSpecializationType in order to |
437 | | // catch the prefix qualifiers "ns::". |
438 | 4 | if (!ParentTypeLoc.isNull() && |
439 | 4 | llvm::isa<ElaboratedType>(ParentTypeLoc.getType())) |
440 | 4 | TargetLoc = ParentTypeLoc; |
441 | | |
442 | 4 | auto StartLoc = StartLocationForType(TargetLoc); |
443 | 4 | auto EndLoc = EndLocationForType(TargetLoc); |
444 | 4 | if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { |
445 | 4 | RenameInfo Info = { |
446 | 4 | StartLoc, |
447 | 4 | EndLoc, |
448 | 4 | TemplateSpecType->getTemplateName().getAsTemplateDecl(), |
449 | 4 | getClosestAncestorDecl(DynTypedNode::create(TargetLoc)), |
450 | 4 | GetNestedNameForType(TargetLoc), |
451 | 4 | /*IgnorePrefixQualifers=*/false}; |
452 | 4 | RenameInfos.push_back(Info); |
453 | 4 | } |
454 | 4 | } |
455 | 152 | } |
456 | 4.97k | return true; |
457 | 5.39k | } |
458 | | |
459 | | // Returns a list of RenameInfo. |
460 | 261 | const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; } |
461 | | |
462 | | // Returns a list of using declarations which are needed to update. |
463 | 261 | const std::vector<const UsingDecl *> &getUsingDecls() const { |
464 | 261 | return UsingDecls; |
465 | 261 | } |
466 | | |
467 | | private: |
468 | | // Get the supported declaration from a given typeLoc. If the declaration type |
469 | | // is not supported, returns nullptr. |
470 | 5.77k | const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) { |
471 | 5.77k | if (const auto* TT = Loc.getType()->getAs<clang::TypedefType>()) |
472 | 208 | return TT->getDecl(); |
473 | 5.56k | if (const auto *RD = Loc.getType()->getAsCXXRecordDecl()) |
474 | 1.28k | return RD; |
475 | 4.27k | if (const auto *ED = |
476 | 4.27k | llvm::dyn_cast_or_null<EnumDecl>(Loc.getType()->getAsTagDecl())) |
477 | 137 | return ED; |
478 | 4.14k | return nullptr; |
479 | 4.27k | } |
480 | | |
481 | | // Get the closest ancester which is a declaration of a given AST node. |
482 | | template <typename ASTNodeType> |
483 | 659 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { |
484 | 659 | auto Parents = Context.getParents(Node); |
485 | | // FIXME: figure out how to handle it when there are multiple parents. |
486 | 659 | if (Parents.size() != 1) |
487 | 9 | return nullptr; |
488 | 650 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) |
489 | 338 | return Parents[0].template get<Decl>(); |
490 | 312 | return getClosestAncestorDecl(Parents[0]); |
491 | 650 | } USRLocFinder.cpp:clang::Decl const* clang::tooling::(anonymous namespace)::RenameLocFinder::getClosestAncestorDecl<clang::NestedNameSpecifierLoc>(clang::NestedNameSpecifierLoc const&) Line | Count | Source | 483 | 87 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { | 484 | 87 | auto Parents = Context.getParents(Node); | 485 | | // FIXME: figure out how to handle it when there are multiple parents. | 486 | 87 | if (Parents.size() != 1) | 487 | 0 | return nullptr; | 488 | 87 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) | 489 | 54 | return Parents[0].template get<Decl>(); | 490 | 33 | return getClosestAncestorDecl(Parents[0]); | 491 | 87 | } |
USRLocFinder.cpp:clang::Decl const* clang::tooling::(anonymous namespace)::RenameLocFinder::getClosestAncestorDecl<clang::TypeLoc>(clang::TypeLoc const&) Line | Count | Source | 483 | 210 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { | 484 | 210 | auto Parents = Context.getParents(Node); | 485 | | // FIXME: figure out how to handle it when there are multiple parents. | 486 | 210 | if (Parents.size() != 1) | 487 | 9 | return nullptr; | 488 | 201 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) | 489 | 134 | return Parents[0].template get<Decl>(); | 490 | 67 | return getClosestAncestorDecl(Parents[0]); | 491 | 201 | } |
USRLocFinder.cpp:clang::Decl const* clang::tooling::(anonymous namespace)::RenameLocFinder::getClosestAncestorDecl<clang::DynTypedNode>(clang::DynTypedNode const&) Line | Count | Source | 483 | 316 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { | 484 | 316 | auto Parents = Context.getParents(Node); | 485 | | // FIXME: figure out how to handle it when there are multiple parents. | 486 | 316 | if (Parents.size() != 1) | 487 | 0 | return nullptr; | 488 | 316 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) | 489 | 128 | return Parents[0].template get<Decl>(); | 490 | 188 | return getClosestAncestorDecl(Parents[0]); | 491 | 316 | } |
USRLocFinder.cpp:clang::Decl const* clang::tooling::(anonymous namespace)::RenameLocFinder::getClosestAncestorDecl<clang::EnumConstantDecl>(clang::EnumConstantDecl const&) Line | Count | Source | 483 | 15 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { | 484 | 15 | auto Parents = Context.getParents(Node); | 485 | | // FIXME: figure out how to handle it when there are multiple parents. | 486 | 15 | if (Parents.size() != 1) | 487 | 0 | return nullptr; | 488 | 15 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) | 489 | 15 | return Parents[0].template get<Decl>(); | 490 | 0 | return getClosestAncestorDecl(Parents[0]); | 491 | 15 | } |
USRLocFinder.cpp:clang::Decl const* clang::tooling::(anonymous namespace)::RenameLocFinder::getClosestAncestorDecl<clang::DeclRefExpr>(clang::DeclRefExpr const&) Line | Count | Source | 483 | 31 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { | 484 | 31 | auto Parents = Context.getParents(Node); | 485 | | // FIXME: figure out how to handle it when there are multiple parents. | 486 | 31 | if (Parents.size() != 1) | 487 | 0 | return nullptr; | 488 | 31 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) | 489 | 7 | return Parents[0].template get<Decl>(); | 490 | 24 | return getClosestAncestorDecl(Parents[0]); | 491 | 31 | } |
|
492 | | |
493 | | // Get the parent typeLoc of a given typeLoc. If there is no such parent, |
494 | | // return nullptr. |
495 | 0 | const TypeLoc *getParentTypeLoc(TypeLoc Loc) const { |
496 | 0 | auto Parents = Context.getParents(Loc); |
497 | 0 | // FIXME: figure out how to handle it when there are multiple parents. |
498 | 0 | if (Parents.size() != 1) |
499 | 0 | return nullptr; |
500 | 0 | return Parents[0].get<TypeLoc>(); |
501 | 0 | } |
502 | | |
503 | | // Check whether the USR of a given Decl is in the USRSet. |
504 | 8.33k | bool isInUSRSet(const Decl *Decl) const { |
505 | 8.33k | auto USR = getUSRForDecl(Decl); |
506 | 8.33k | if (USR.empty()) |
507 | 36 | return false; |
508 | 8.29k | return llvm::is_contained(USRSet, USR); |
509 | 8.33k | } |
510 | | |
511 | | const std::set<std::string> USRSet; |
512 | | ASTContext &Context; |
513 | | std::vector<RenameInfo> RenameInfos; |
514 | | // Record all interested using declarations which contains the using-shadow |
515 | | // declarations of the symbol declarations being renamed. |
516 | | std::vector<const UsingDecl *> UsingDecls; |
517 | | }; |
518 | | |
519 | | } // namespace |
520 | | |
521 | | SymbolOccurrences getOccurrencesOfUSRs(ArrayRef<std::string> USRs, |
522 | 84 | StringRef PrevName, Decl *Decl) { |
523 | 84 | USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext()); |
524 | 84 | Visitor.TraverseDecl(Decl); |
525 | 84 | return Visitor.takeOccurrences(); |
526 | 84 | } |
527 | | |
528 | | std::vector<tooling::AtomicChange> |
529 | | createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs, |
530 | 261 | llvm::StringRef NewName, Decl *TranslationUnitDecl) { |
531 | 261 | RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext()); |
532 | 261 | Finder.TraverseDecl(TranslationUnitDecl); |
533 | | |
534 | 261 | const SourceManager &SM = |
535 | 261 | TranslationUnitDecl->getASTContext().getSourceManager(); |
536 | | |
537 | 261 | std::vector<tooling::AtomicChange> AtomicChanges; |
538 | 261 | auto Replace = [&](SourceLocation Start, SourceLocation End, |
539 | 692 | llvm::StringRef Text) { |
540 | 692 | tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start); |
541 | 692 | llvm::Error Err = ReplaceChange.replace( |
542 | 692 | SM, CharSourceRange::getTokenRange(Start, End), Text); |
543 | 692 | if (Err) { |
544 | 0 | llvm::errs() << "Failed to add replacement to AtomicChange: " |
545 | 0 | << llvm::toString(std::move(Err)) << "\n"; |
546 | 0 | return; |
547 | 0 | } |
548 | 692 | AtomicChanges.push_back(std::move(ReplaceChange)); |
549 | 692 | }; |
550 | | |
551 | 674 | for (const auto &RenameInfo : Finder.getRenameInfos()) { |
552 | 674 | std::string ReplacedName = NewName.str(); |
553 | 674 | if (RenameInfo.IgnorePrefixQualifers) { |
554 | | // Get the name without prefix qualifiers from NewName. |
555 | 342 | size_t LastColonPos = NewName.find_last_of(':'); |
556 | 342 | if (LastColonPos != std::string::npos) |
557 | 304 | ReplacedName = std::string(NewName.substr(LastColonPos + 1)); |
558 | 342 | } else { |
559 | 332 | if (RenameInfo.FromDecl && RenameInfo.Context) { |
560 | 323 | if (!llvm::isa<clang::TranslationUnitDecl>( |
561 | 323 | RenameInfo.Context->getDeclContext())) { |
562 | 226 | ReplacedName = tooling::replaceNestedName( |
563 | 226 | RenameInfo.Specifier, RenameInfo.Begin, |
564 | 226 | RenameInfo.Context->getDeclContext(), RenameInfo.FromDecl, |
565 | 226 | NewName.startswith("::") ? NewName.str()18 |
566 | 226 | : ("::" + NewName).str()208 ); |
567 | 226 | } else { |
568 | | // This fixes the case where type `T` is a parameter inside a function |
569 | | // type (e.g. `std::function<void(T)>`) and the DeclContext of `T` |
570 | | // becomes the translation unit. As a workaround, we simply use |
571 | | // fully-qualified name here for all references whose `DeclContext` is |
572 | | // the translation unit and ignore the possible existence of |
573 | | // using-decls (in the global scope) that can shorten the replaced |
574 | | // name. |
575 | 97 | llvm::StringRef ActualName = Lexer::getSourceText( |
576 | 97 | CharSourceRange::getTokenRange( |
577 | 97 | SourceRange(RenameInfo.Begin, RenameInfo.End)), |
578 | 97 | SM, TranslationUnitDecl->getASTContext().getLangOpts()); |
579 | | // Add the leading "::" back if the name written in the code contains |
580 | | // it. |
581 | 97 | if (ActualName.startswith("::") && !NewName.startswith("::")4 ) { |
582 | 4 | ReplacedName = "::" + NewName.str(); |
583 | 4 | } |
584 | 97 | } |
585 | 323 | } |
586 | | // If the NewName contains leading "::", add it back. |
587 | 332 | if (NewName.startswith("::") && NewName.substr(2) == ReplacedName22 ) |
588 | 9 | ReplacedName = NewName.str(); |
589 | 332 | } |
590 | 674 | Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName); |
591 | 674 | } |
592 | | |
593 | | // Hanlde using declarations explicitly as "using a::Foo" don't trigger |
594 | | // typeLoc for "a::Foo". |
595 | 261 | for (const auto *Using : Finder.getUsingDecls()) |
596 | 18 | Replace(Using->getBeginLoc(), Using->getEndLoc(), "using " + NewName.str()); |
597 | | |
598 | 261 | return AtomicChanges; |
599 | 261 | } |
600 | | |
601 | | } // end namespace tooling |
602 | | } // end namespace clang |