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 | bool IsValidEditLoc(const clang::SourceManager& SM, clang::SourceLocation Loc) { |
45 | if (Loc.isInvalid()) |
46 | return false; |
47 | const clang::FullSourceLoc FullLoc(Loc, SM); |
48 | std::pair<clang::FileID, unsigned> FileIdAndOffset = |
49 | FullLoc.getSpellingLoc().getDecomposedLoc(); |
50 | return SM.getFileEntryForID(FID: FileIdAndOffset.first) != nullptr; |
51 | } |
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 | : RecursiveSymbolVisitor(Context.getSourceManager(), |
62 | Context.getLangOpts()), |
63 | USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) { |
64 | } |
65 | |
66 | bool visitSymbolOccurrence(const NamedDecl *ND, |
67 | ArrayRef<SourceRange> NameRanges) { |
68 | if (USRSet.find(x: getUSRForDecl(Decl: ND)) != USRSet.end()) { |
69 | assert(NameRanges.size() == 1 && |
70 | "Multiple name pieces are not supported yet!" ); |
71 | SourceLocation Loc = NameRanges[0].getBegin(); |
72 | const SourceManager &SM = Context.getSourceManager(); |
73 | // TODO: Deal with macro occurrences correctly. |
74 | if (Loc.isMacroID()) |
75 | Loc = SM.getSpellingLoc(Loc); |
76 | checkAndAddLocation(Loc); |
77 | } |
78 | return true; |
79 | } |
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 | SymbolOccurrences takeOccurrences() { return std::move(Occurrences); } |
86 | |
87 | private: |
88 | void checkAndAddLocation(SourceLocation Loc) { |
89 | const SourceLocation BeginLoc = Loc; |
90 | const SourceLocation EndLoc = Lexer::getLocForEndOfToken( |
91 | Loc: BeginLoc, Offset: 0, SM: Context.getSourceManager(), LangOpts: Context.getLangOpts()); |
92 | StringRef TokenName = |
93 | Lexer::getSourceText(Range: CharSourceRange::getTokenRange(B: BeginLoc, E: EndLoc), |
94 | SM: Context.getSourceManager(), LangOpts: Context.getLangOpts()); |
95 | size_t Offset = TokenName.find(Str: PrevName.getNamePieces()[0]); |
96 | |
97 | // The token of the source location we find actually has the old |
98 | // name. |
99 | if (Offset != StringRef::npos) |
100 | Occurrences.emplace_back(args: PrevName, args: SymbolOccurrence::MatchingSymbol, |
101 | args: BeginLoc.getLocWithOffset(Offset)); |
102 | } |
103 | |
104 | const std::set<std::string> USRSet; |
105 | const SymbolName PrevName; |
106 | SymbolOccurrences Occurrences; |
107 | const ASTContext &Context; |
108 | }; |
109 | |
110 | 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 | if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) { |
114 | NestedNameSpecifierLoc NestedNameSpecifier = |
115 | ElaboratedTypeLoc.getQualifierLoc(); |
116 | if (NestedNameSpecifier.getNestedNameSpecifier()) |
117 | return NestedNameSpecifier.getBeginLoc(); |
118 | TL = TL.getNextTypeLoc(); |
119 | } |
120 | return TL.getBeginLoc(); |
121 | } |
122 | |
123 | SourceLocation EndLocationForType(TypeLoc TL) { |
124 | // Dig past any namespace or keyword qualifications. |
125 | while (TL.getTypeLocClass() == TypeLoc::Elaborated || |
126 | TL.getTypeLocClass() == TypeLoc::Qualified) |
127 | 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 | if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) { |
133 | return TL.castAs<TemplateSpecializationTypeLoc>() |
134 | .getLAngleLoc() |
135 | .getLocWithOffset(Offset: -1); |
136 | } |
137 | return TL.getEndLoc(); |
138 | } |
139 | |
140 | NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) { |
141 | // Dig past any keyword qualifications. |
142 | while (TL.getTypeLocClass() == TypeLoc::Qualified) |
143 | 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 | if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) |
148 | return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier(); |
149 | return nullptr; |
150 | } |
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 | : 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 | bool VisitNamedDecl(const NamedDecl *Decl) { |
183 | // UsingDecl has been handled in other place. |
184 | if (llvm::isa<UsingDecl>(Val: Decl)) |
185 | return true; |
186 | |
187 | // DestructorDecl has been handled in Typeloc. |
188 | if (llvm::isa<CXXDestructorDecl>(Val: Decl)) |
189 | return true; |
190 | |
191 | if (Decl->isImplicit()) |
192 | return true; |
193 | |
194 | if (isInUSRSet(Decl)) { |
195 | // For the case of renaming an alias template, we actually rename the |
196 | // underlying alias declaration of the template. |
197 | if (const auto* TAT = dyn_cast<TypeAliasTemplateDecl>(Val: Decl)) |
198 | Decl = TAT->getTemplatedDecl(); |
199 | |
200 | auto StartLoc = Decl->getLocation(); |
201 | auto EndLoc = StartLoc; |
202 | if (IsValidEditLoc(SM: Context.getSourceManager(), Loc: StartLoc)) { |
203 | RenameInfo Info = {.Begin: StartLoc, |
204 | .End: EndLoc, |
205 | /*FromDecl=*/nullptr, |
206 | /*Context=*/nullptr, |
207 | /*Specifier=*/nullptr, |
208 | /*IgnorePrefixQualifers=*/true}; |
209 | RenameInfos.push_back(x: Info); |
210 | } |
211 | } |
212 | return true; |
213 | } |
214 | |
215 | bool VisitMemberExpr(const MemberExpr *Expr) { |
216 | const NamedDecl *Decl = Expr->getFoundDecl(); |
217 | auto StartLoc = Expr->getMemberLoc(); |
218 | auto EndLoc = Expr->getMemberLoc(); |
219 | if (isInUSRSet(Decl)) { |
220 | RenameInfos.push_back(x: {.Begin: StartLoc, .End: EndLoc, |
221 | /*FromDecl=*/nullptr, |
222 | /*Context=*/nullptr, |
223 | /*Specifier=*/nullptr, |
224 | /*IgnorePrefixQualifiers=*/.IgnorePrefixQualifers: true}); |
225 | } |
226 | return true; |
227 | } |
228 | |
229 | bool VisitDesignatedInitExpr(const DesignatedInitExpr *E) { |
230 | for (const DesignatedInitExpr::Designator &D : E->designators()) { |
231 | if (D.isFieldDesignator()) { |
232 | if (const FieldDecl *Decl = D.getFieldDecl()) { |
233 | if (isInUSRSet(Decl)) { |
234 | auto StartLoc = D.getFieldLoc(); |
235 | auto EndLoc = D.getFieldLoc(); |
236 | RenameInfos.push_back(x: {.Begin: StartLoc, .End: EndLoc, |
237 | /*FromDecl=*/nullptr, |
238 | /*Context=*/nullptr, |
239 | /*Specifier=*/nullptr, |
240 | /*IgnorePrefixQualifiers=*/.IgnorePrefixQualifers: true}); |
241 | } |
242 | } |
243 | } |
244 | } |
245 | return true; |
246 | } |
247 | |
248 | bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) { |
249 | // Fix the constructor initializer when renaming class members. |
250 | for (const auto *Initializer : CD->inits()) { |
251 | // Ignore implicit initializers. |
252 | if (!Initializer->isWritten()) |
253 | continue; |
254 | |
255 | if (const FieldDecl *FD = Initializer->getMember()) { |
256 | if (isInUSRSet(Decl: FD)) { |
257 | auto Loc = Initializer->getSourceLocation(); |
258 | RenameInfos.push_back(x: {.Begin: Loc, .End: Loc, |
259 | /*FromDecl=*/nullptr, |
260 | /*Context=*/nullptr, |
261 | /*Specifier=*/nullptr, |
262 | /*IgnorePrefixQualifiers=*/.IgnorePrefixQualifers: true}); |
263 | } |
264 | } |
265 | } |
266 | return true; |
267 | } |
268 | |
269 | bool VisitDeclRefExpr(const DeclRefExpr *Expr) { |
270 | const NamedDecl *Decl = Expr->getFoundDecl(); |
271 | // Get the underlying declaration of the shadow declaration introduced by a |
272 | // using declaration. |
273 | if (auto *UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Val: Decl)) { |
274 | Decl = UsingShadow->getTargetDecl(); |
275 | } |
276 | |
277 | 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 | SourceLocation EndLoc = Expr->hasExplicitTemplateArgs() |
281 | ? Expr->getLAngleLoc().getLocWithOffset(Offset: -1) |
282 | : Expr->getEndLoc(); |
283 | |
284 | if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Val: Decl)) { |
285 | if (isInUSRSet(Decl: 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 | RenameInfos.push_back(x: {.Begin: EndLoc, .End: EndLoc, |
290 | /*FromDecl=*/nullptr, |
291 | /*Context=*/nullptr, |
292 | /*Specifier=*/nullptr, |
293 | /*IgnorePrefixQualifiers=*/.IgnorePrefixQualifers: true}); |
294 | return true; |
295 | } |
296 | } |
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 | if (const auto *T = llvm::dyn_cast<EnumConstantDecl>(Val: Decl)) { |
304 | // FIXME: Handle the enum constant without prefix qualifiers (`a = Green`) |
305 | // when renaming an unscoped enum declaration with a new namespace. |
306 | if (!Expr->hasQualifier()) |
307 | return true; |
308 | |
309 | if (const auto *ED = |
310 | llvm::dyn_cast_or_null<EnumDecl>(Val: getClosestAncestorDecl(Node: *T))) { |
311 | if (ED->isScoped()) |
312 | return true; |
313 | Decl = ED; |
314 | } |
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 | EndLoc = Expr->getQualifierLoc().getEndLoc().getLocWithOffset(Offset: -1); |
326 | assert(EndLoc.isValid() && |
327 | "The enum constant should have prefix qualifers." ); |
328 | } |
329 | if (isInUSRSet(Decl) && |
330 | IsValidEditLoc(SM: Context.getSourceManager(), Loc: StartLoc)) { |
331 | RenameInfo Info = {.Begin: StartLoc, |
332 | .End: EndLoc, |
333 | .FromDecl: Decl, |
334 | .Context: getClosestAncestorDecl(Node: *Expr), |
335 | .Specifier: Expr->getQualifier(), |
336 | /*IgnorePrefixQualifers=*/false}; |
337 | RenameInfos.push_back(x: Info); |
338 | } |
339 | |
340 | return true; |
341 | } |
342 | |
343 | bool VisitUsingDecl(const UsingDecl *Using) { |
344 | for (const auto *UsingShadow : Using->shadows()) { |
345 | if (isInUSRSet(Decl: UsingShadow->getTargetDecl())) { |
346 | UsingDecls.push_back(x: Using); |
347 | break; |
348 | } |
349 | } |
350 | return true; |
351 | } |
352 | |
353 | bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) { |
354 | if (!NestedLoc.getNestedNameSpecifier()->getAsType()) |
355 | return true; |
356 | |
357 | if (const auto *TargetDecl = |
358 | getSupportedDeclFromTypeLoc(Loc: NestedLoc.getTypeLoc())) { |
359 | if (isInUSRSet(Decl: TargetDecl)) { |
360 | RenameInfo Info = {.Begin: NestedLoc.getBeginLoc(), |
361 | .End: EndLocationForType(TL: NestedLoc.getTypeLoc()), |
362 | .FromDecl: TargetDecl, |
363 | .Context: getClosestAncestorDecl(Node: NestedLoc), |
364 | .Specifier: NestedLoc.getNestedNameSpecifier()->getPrefix(), |
365 | /*IgnorePrefixQualifers=*/false}; |
366 | RenameInfos.push_back(x: Info); |
367 | } |
368 | } |
369 | return true; |
370 | } |
371 | |
372 | bool VisitTypeLoc(TypeLoc Loc) { |
373 | auto Parents = Context.getParents(Node: Loc); |
374 | TypeLoc ParentTypeLoc; |
375 | 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 | if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) { |
381 | VisitNestedNameSpecifierLocations(NestedLoc: *NSL); |
382 | return true; |
383 | } |
384 | |
385 | if (const auto *TL = Parents[0].get<TypeLoc>()) |
386 | ParentTypeLoc = *TL; |
387 | } |
388 | |
389 | // Handle the outermost TypeLoc which is directly linked to the interesting |
390 | // declaration and don't handle nested name specifier locations. |
391 | if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) { |
392 | if (isInUSRSet(Decl: 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 | if (!ParentTypeLoc.isNull() && |
404 | isInUSRSet(Decl: getSupportedDeclFromTypeLoc(Loc: ParentTypeLoc))) |
405 | return true; |
406 | |
407 | auto StartLoc = StartLocationForType(TL: Loc); |
408 | auto EndLoc = EndLocationForType(TL: Loc); |
409 | if (IsValidEditLoc(SM: Context.getSourceManager(), Loc: StartLoc)) { |
410 | RenameInfo Info = {.Begin: StartLoc, |
411 | .End: EndLoc, |
412 | .FromDecl: TargetDecl, |
413 | .Context: getClosestAncestorDecl(Node: Loc), |
414 | .Specifier: GetNestedNameForType(TL: Loc), |
415 | /*IgnorePrefixQualifers=*/false}; |
416 | RenameInfos.push_back(x: Info); |
417 | } |
418 | return true; |
419 | } |
420 | } |
421 | |
422 | // Handle specific template class specialiation cases. |
423 | if (const auto *TemplateSpecType = |
424 | dyn_cast<TemplateSpecializationType>(Val: Loc.getType())) { |
425 | TypeLoc TargetLoc = Loc; |
426 | if (!ParentTypeLoc.isNull()) { |
427 | if (llvm::isa<ElaboratedType>(Val: ParentTypeLoc.getType())) |
428 | TargetLoc = ParentTypeLoc; |
429 | } |
430 | |
431 | if (isInUSRSet(Decl: TemplateSpecType->getTemplateName().getAsTemplateDecl())) { |
432 | 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 | if (!ParentTypeLoc.isNull() && |
439 | llvm::isa<ElaboratedType>(Val: ParentTypeLoc.getType())) |
440 | TargetLoc = ParentTypeLoc; |
441 | |
442 | auto StartLoc = StartLocationForType(TL: TargetLoc); |
443 | auto EndLoc = EndLocationForType(TL: TargetLoc); |
444 | if (IsValidEditLoc(SM: Context.getSourceManager(), Loc: StartLoc)) { |
445 | RenameInfo Info = { |
446 | .Begin: StartLoc, |
447 | .End: EndLoc, |
448 | .FromDecl: TemplateSpecType->getTemplateName().getAsTemplateDecl(), |
449 | .Context: getClosestAncestorDecl(Node: DynTypedNode::create(Node: TargetLoc)), |
450 | .Specifier: GetNestedNameForType(TL: TargetLoc), |
451 | /*IgnorePrefixQualifers=*/false}; |
452 | RenameInfos.push_back(x: Info); |
453 | } |
454 | } |
455 | } |
456 | return true; |
457 | } |
458 | |
459 | // Returns a list of RenameInfo. |
460 | const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; } |
461 | |
462 | // Returns a list of using declarations which are needed to update. |
463 | const std::vector<const UsingDecl *> &getUsingDecls() const { |
464 | return UsingDecls; |
465 | } |
466 | |
467 | private: |
468 | // Get the supported declaration from a given typeLoc. If the declaration type |
469 | // is not supported, returns nullptr. |
470 | const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) { |
471 | if (const auto* TT = Loc.getType()->getAs<clang::TypedefType>()) |
472 | return TT->getDecl(); |
473 | if (const auto *RD = Loc.getType()->getAsCXXRecordDecl()) |
474 | return RD; |
475 | if (const auto *ED = |
476 | llvm::dyn_cast_or_null<EnumDecl>(Val: Loc.getType()->getAsTagDecl())) |
477 | return ED; |
478 | return nullptr; |
479 | } |
480 | |
481 | // Get the closest ancester which is a declaration of a given AST node. |
482 | template <typename ASTNodeType> |
483 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { |
484 | auto Parents = Context.getParents(Node); |
485 | // FIXME: figure out how to handle it when there are multiple parents. |
486 | if (Parents.size() != 1) |
487 | return nullptr; |
488 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) |
489 | return Parents[0].template get<Decl>(); |
490 | return getClosestAncestorDecl(Parents[0]); |
491 | } |
492 | |
493 | // Get the parent typeLoc of a given typeLoc. If there is no such parent, |
494 | // return nullptr. |
495 | const TypeLoc *getParentTypeLoc(TypeLoc Loc) const { |
496 | auto Parents = Context.getParents(Node: Loc); |
497 | // FIXME: figure out how to handle it when there are multiple parents. |
498 | if (Parents.size() != 1) |
499 | return nullptr; |
500 | return Parents[0].get<TypeLoc>(); |
501 | } |
502 | |
503 | // Check whether the USR of a given Decl is in the USRSet. |
504 | bool isInUSRSet(const Decl *Decl) const { |
505 | auto USR = getUSRForDecl(Decl); |
506 | if (USR.empty()) |
507 | return false; |
508 | return llvm::is_contained(Range: USRSet, Element: USR); |
509 | } |
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 | StringRef PrevName, Decl *Decl) { |
523 | USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext()); |
524 | Visitor.TraverseDecl(D: Decl); |
525 | return Visitor.takeOccurrences(); |
526 | } |
527 | |
528 | std::vector<tooling::AtomicChange> |
529 | createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs, |
530 | llvm::StringRef NewName, Decl *TranslationUnitDecl) { |
531 | RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext()); |
532 | Finder.TraverseDecl(D: TranslationUnitDecl); |
533 | |
534 | const SourceManager &SM = |
535 | TranslationUnitDecl->getASTContext().getSourceManager(); |
536 | |
537 | std::vector<tooling::AtomicChange> AtomicChanges; |
538 | auto Replace = [&](SourceLocation Start, SourceLocation End, |
539 | llvm::StringRef Text) { |
540 | tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start); |
541 | llvm::Error Err = ReplaceChange.replace( |
542 | SM, Range: CharSourceRange::getTokenRange(B: Start, E: End), ReplacementText: Text); |
543 | if (Err) { |
544 | llvm::errs() << "Failed to add replacement to AtomicChange: " |
545 | << llvm::toString(E: std::move(Err)) << "\n" ; |
546 | return; |
547 | } |
548 | AtomicChanges.push_back(x: std::move(ReplaceChange)); |
549 | }; |
550 | |
551 | for (const auto &RenameInfo : Finder.getRenameInfos()) { |
552 | std::string ReplacedName = NewName.str(); |
553 | if (RenameInfo.IgnorePrefixQualifers) { |
554 | // Get the name without prefix qualifiers from NewName. |
555 | size_t LastColonPos = NewName.find_last_of(C: ':'); |
556 | if (LastColonPos != std::string::npos) |
557 | ReplacedName = std::string(NewName.substr(Start: LastColonPos + 1)); |
558 | } else { |
559 | if (RenameInfo.FromDecl && RenameInfo.Context) { |
560 | if (!llvm::isa<clang::TranslationUnitDecl>( |
561 | Val: RenameInfo.Context->getDeclContext())) { |
562 | ReplacedName = tooling::replaceNestedName( |
563 | Use: RenameInfo.Specifier, UseLoc: RenameInfo.Begin, |
564 | UseContext: RenameInfo.Context->getDeclContext(), FromDecl: RenameInfo.FromDecl, |
565 | ReplacementString: NewName.starts_with(Prefix: "::" ) ? NewName.str() |
566 | : ("::" + NewName).str()); |
567 | } 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 | llvm::StringRef ActualName = Lexer::getSourceText( |
576 | Range: CharSourceRange::getTokenRange( |
577 | R: SourceRange(RenameInfo.Begin, RenameInfo.End)), |
578 | SM, LangOpts: TranslationUnitDecl->getASTContext().getLangOpts()); |
579 | // Add the leading "::" back if the name written in the code contains |
580 | // it. |
581 | if (ActualName.starts_with(Prefix: "::" ) && !NewName.starts_with(Prefix: "::" )) { |
582 | ReplacedName = "::" + NewName.str(); |
583 | } |
584 | } |
585 | } |
586 | // If the NewName contains leading "::", add it back. |
587 | if (NewName.starts_with(Prefix: "::" ) && NewName.substr(Start: 2) == ReplacedName) |
588 | ReplacedName = NewName.str(); |
589 | } |
590 | Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName); |
591 | } |
592 | |
593 | // Hanlde using declarations explicitly as "using a::Foo" don't trigger |
594 | // typeLoc for "a::Foo". |
595 | for (const auto *Using : Finder.getUsingDecls()) |
596 | Replace(Using->getBeginLoc(), Using->getEndLoc(), "using " + NewName.str()); |
597 | |
598 | return AtomicChanges; |
599 | } |
600 | |
601 | } // end namespace tooling |
602 | } // end namespace clang |
603 | |