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