Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
35295 views
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(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(getUSRForDecl(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
BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
92
StringRef TokenName =
93
Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
94
Context.getSourceManager(), Context.getLangOpts());
95
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
if (Offset != StringRef::npos)
100
Occurrences.emplace_back(PrevName, SymbolOccurrence::MatchingSymbol,
101
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(-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>(Decl))
185
return true;
186
187
// DestructorDecl has been handled in Typeloc.
188
if (llvm::isa<CXXDestructorDecl>(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>(Decl))
198
Decl = TAT->getTemplatedDecl();
199
200
auto StartLoc = Decl->getLocation();
201
auto EndLoc = StartLoc;
202
if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
203
RenameInfo Info = {StartLoc,
204
EndLoc,
205
/*FromDecl=*/nullptr,
206
/*Context=*/nullptr,
207
/*Specifier=*/nullptr,
208
/*IgnorePrefixQualifers=*/true};
209
RenameInfos.push_back(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({StartLoc, EndLoc,
221
/*FromDecl=*/nullptr,
222
/*Context=*/nullptr,
223
/*Specifier=*/nullptr,
224
/*IgnorePrefixQualifiers=*/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({StartLoc, EndLoc,
237
/*FromDecl=*/nullptr,
238
/*Context=*/nullptr,
239
/*Specifier=*/nullptr,
240
/*IgnorePrefixQualifiers=*/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(FD)) {
257
auto Loc = Initializer->getSourceLocation();
258
RenameInfos.push_back({Loc, Loc,
259
/*FromDecl=*/nullptr,
260
/*Context=*/nullptr,
261
/*Specifier=*/nullptr,
262
/*IgnorePrefixQualifiers=*/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>(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(-1)
282
: Expr->getEndLoc();
283
284
if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) {
285
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
RenameInfos.push_back({EndLoc, EndLoc,
290
/*FromDecl=*/nullptr,
291
/*Context=*/nullptr,
292
/*Specifier=*/nullptr,
293
/*IgnorePrefixQualifiers=*/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>(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>(getClosestAncestorDecl(*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(-1);
326
assert(EndLoc.isValid() &&
327
"The enum constant should have prefix qualifers.");
328
}
329
if (isInUSRSet(Decl) &&
330
IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
331
RenameInfo Info = {StartLoc,
332
EndLoc,
333
Decl,
334
getClosestAncestorDecl(*Expr),
335
Expr->getQualifier(),
336
/*IgnorePrefixQualifers=*/false};
337
RenameInfos.push_back(Info);
338
}
339
340
return true;
341
}
342
343
bool VisitUsingDecl(const UsingDecl *Using) {
344
for (const auto *UsingShadow : Using->shadows()) {
345
if (isInUSRSet(UsingShadow->getTargetDecl())) {
346
UsingDecls.push_back(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(NestedLoc.getTypeLoc())) {
359
if (isInUSRSet(TargetDecl)) {
360
RenameInfo Info = {NestedLoc.getBeginLoc(),
361
EndLocationForType(NestedLoc.getTypeLoc()),
362
TargetDecl,
363
getClosestAncestorDecl(NestedLoc),
364
NestedLoc.getNestedNameSpecifier()->getPrefix(),
365
/*IgnorePrefixQualifers=*/false};
366
RenameInfos.push_back(Info);
367
}
368
}
369
return true;
370
}
371
372
bool VisitTypeLoc(TypeLoc Loc) {
373
auto Parents = Context.getParents(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(*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(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(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
405
return true;
406
407
auto StartLoc = StartLocationForType(Loc);
408
auto EndLoc = EndLocationForType(Loc);
409
if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
410
RenameInfo Info = {StartLoc,
411
EndLoc,
412
TargetDecl,
413
getClosestAncestorDecl(Loc),
414
GetNestedNameForType(Loc),
415
/*IgnorePrefixQualifers=*/false};
416
RenameInfos.push_back(Info);
417
}
418
return true;
419
}
420
}
421
422
// Handle specific template class specialiation cases.
423
if (const auto *TemplateSpecType =
424
dyn_cast<TemplateSpecializationType>(Loc.getType())) {
425
TypeLoc TargetLoc = Loc;
426
if (!ParentTypeLoc.isNull()) {
427
if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
428
TargetLoc = ParentTypeLoc;
429
}
430
431
if (isInUSRSet(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>(ParentTypeLoc.getType()))
440
TargetLoc = ParentTypeLoc;
441
442
auto StartLoc = StartLocationForType(TargetLoc);
443
auto EndLoc = EndLocationForType(TargetLoc);
444
if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
445
RenameInfo Info = {
446
StartLoc,
447
EndLoc,
448
TemplateSpecType->getTemplateName().getAsTemplateDecl(),
449
getClosestAncestorDecl(DynTypedNode::create(TargetLoc)),
450
GetNestedNameForType(TargetLoc),
451
/*IgnorePrefixQualifers=*/false};
452
RenameInfos.push_back(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>(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(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(USRSet, 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(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(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, CharSourceRange::getTokenRange(Start, End), Text);
543
if (Err) {
544
llvm::errs() << "Failed to add replacement to AtomicChange: "
545
<< llvm::toString(std::move(Err)) << "\n";
546
return;
547
}
548
AtomicChanges.push_back(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(':');
556
if (LastColonPos != std::string::npos)
557
ReplacedName = std::string(NewName.substr(LastColonPos + 1));
558
} else {
559
if (RenameInfo.FromDecl && RenameInfo.Context) {
560
if (!llvm::isa<clang::TranslationUnitDecl>(
561
RenameInfo.Context->getDeclContext())) {
562
ReplacedName = tooling::replaceNestedName(
563
RenameInfo.Specifier, RenameInfo.Begin,
564
RenameInfo.Context->getDeclContext(), RenameInfo.FromDecl,
565
NewName.starts_with("::") ? 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
CharSourceRange::getTokenRange(
577
SourceRange(RenameInfo.Begin, RenameInfo.End)),
578
SM, TranslationUnitDecl->getASTContext().getLangOpts());
579
// Add the leading "::" back if the name written in the code contains
580
// it.
581
if (ActualName.starts_with("::") && !NewName.starts_with("::")) {
582
ReplacedName = "::" + NewName.str();
583
}
584
}
585
}
586
// If the NewName contains leading "::", add it back.
587
if (NewName.starts_with("::") && NewName.substr(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
604