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 auto FileIdAndOffset = FullLoc.getSpellingLoc().getDecomposedLoc(); 49 return SM.getFileEntryForID(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. 54 class USRLocFindingASTVisitor 55 : public RecursiveSymbolVisitor<USRLocFindingASTVisitor> { 56 public: 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(getUSRForDecl(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 86 private: 87 void checkAndAddLocation(SourceLocation Loc) { 88 const SourceLocation BeginLoc = Loc; 89 const SourceLocation EndLoc = Lexer::getLocForEndOfToken( 90 BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); 91 StringRef TokenName = 92 Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc), 93 Context.getSourceManager(), Context.getLangOpts()); 94 size_t Offset = TokenName.find(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(PrevName, SymbolOccurrence::MatchingSymbol, 100 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 109 SourceLocation 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 122 SourceLocation 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(-1); 135 } 136 return TL.getEndLoc(); 137 } 138 139 NestedNameSpecifier *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. 155 class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> { 156 public: 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>(Decl)) 184 return true; 185 186 // DestructorDecl has been handled in Typeloc. 187 if (llvm::isa<CXXDestructorDecl>(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>(Decl)) 197 Decl = TAT->getTemplatedDecl(); 198 199 auto StartLoc = Decl->getLocation(); 200 auto EndLoc = StartLoc; 201 if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { 202 RenameInfo Info = {StartLoc, 203 EndLoc, 204 /*FromDecl=*/nullptr, 205 /*Context=*/nullptr, 206 /*Specifier=*/nullptr, 207 /*IgnorePrefixQualifers=*/true}; 208 RenameInfos.push_back(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({StartLoc, EndLoc, 220 /*FromDecl=*/nullptr, 221 /*Context=*/nullptr, 222 /*Specifier=*/nullptr, 223 /*IgnorePrefixQualifiers=*/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({StartLoc, EndLoc, 236 /*FromDecl=*/nullptr, 237 /*Context=*/nullptr, 238 /*Specifier=*/nullptr, 239 /*IgnorePrefixQualifiers=*/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(FD)) { 256 auto Loc = Initializer->getSourceLocation(); 257 RenameInfos.push_back({Loc, Loc, 258 /*FromDecl=*/nullptr, 259 /*Context=*/nullptr, 260 /*Specifier=*/nullptr, 261 /*IgnorePrefixQualifiers=*/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>(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(-1) 281 : Expr->getEndLoc(); 282 283 if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) { 284 if (isInUSRSet(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({EndLoc, EndLoc, 289 /*FromDecl=*/nullptr, 290 /*Context=*/nullptr, 291 /*Specifier=*/nullptr, 292 /*IgnorePrefixQualifiers=*/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>(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>(getClosestAncestorDecl(*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(-1); 325 assert(EndLoc.isValid() && 326 "The enum constant should have prefix qualifers."); 327 } 328 if (isInUSRSet(Decl) && 329 IsValidEditLoc(Context.getSourceManager(), StartLoc)) { 330 RenameInfo Info = {StartLoc, 331 EndLoc, 332 Decl, 333 getClosestAncestorDecl(*Expr), 334 Expr->getQualifier(), 335 /*IgnorePrefixQualifers=*/false}; 336 RenameInfos.push_back(Info); 337 } 338 339 return true; 340 } 341 342 bool VisitUsingDecl(const UsingDecl *Using) { 343 for (const auto *UsingShadow : Using->shadows()) { 344 if (isInUSRSet(UsingShadow->getTargetDecl())) { 345 UsingDecls.push_back(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(NestedLoc.getTypeLoc())) { 358 if (isInUSRSet(TargetDecl)) { 359 RenameInfo Info = {NestedLoc.getBeginLoc(), 360 EndLocationForType(NestedLoc.getTypeLoc()), 361 TargetDecl, 362 getClosestAncestorDecl(NestedLoc), 363 NestedLoc.getNestedNameSpecifier()->getPrefix(), 364 /*IgnorePrefixQualifers=*/false}; 365 RenameInfos.push_back(Info); 366 } 367 } 368 return true; 369 } 370 371 bool VisitTypeLoc(TypeLoc Loc) { 372 auto Parents = Context.getParents(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(*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(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(getSupportedDeclFromTypeLoc(ParentTypeLoc))) 404 return true; 405 406 auto StartLoc = StartLocationForType(Loc); 407 auto EndLoc = EndLocationForType(Loc); 408 if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { 409 RenameInfo Info = {StartLoc, 410 EndLoc, 411 TargetDecl, 412 getClosestAncestorDecl(Loc), 413 GetNestedNameForType(Loc), 414 /*IgnorePrefixQualifers=*/false}; 415 RenameInfos.push_back(Info); 416 } 417 return true; 418 } 419 } 420 421 // Handle specific template class specialiation cases. 422 if (const auto *TemplateSpecType = 423 dyn_cast<TemplateSpecializationType>(Loc.getType())) { 424 TypeLoc TargetLoc = Loc; 425 if (!ParentTypeLoc.isNull()) { 426 if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType())) 427 TargetLoc = ParentTypeLoc; 428 } 429 430 if (isInUSRSet(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>(ParentTypeLoc.getType())) 439 TargetLoc = ParentTypeLoc; 440 441 auto StartLoc = StartLocationForType(TargetLoc); 442 auto EndLoc = EndLocationForType(TargetLoc); 443 if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { 444 RenameInfo Info = { 445 StartLoc, 446 EndLoc, 447 TemplateSpecType->getTemplateName().getAsTemplateDecl(), 448 getClosestAncestorDecl(DynTypedNode::create(TargetLoc)), 449 GetNestedNameForType(TargetLoc), 450 /*IgnorePrefixQualifers=*/false}; 451 RenameInfos.push_back(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 466 private: 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>(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(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(USRSet, 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 520 SymbolOccurrences getOccurrencesOfUSRs(ArrayRef<std::string> USRs, 521 StringRef PrevName, Decl *Decl) { 522 USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext()); 523 Visitor.TraverseDecl(Decl); 524 return Visitor.takeOccurrences(); 525 } 526 527 std::vector<tooling::AtomicChange> 528 createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs, 529 llvm::StringRef NewName, Decl *TranslationUnitDecl) { 530 RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext()); 531 Finder.TraverseDecl(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, CharSourceRange::getTokenRange(Start, End), Text); 542 if (Err) { 543 llvm::errs() << "Failed to add replacement to AtomicChange: " 544 << llvm::toString(std::move(Err)) << "\n"; 545 return; 546 } 547 AtomicChanges.push_back(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(':'); 555 if (LastColonPos != std::string::npos) 556 ReplacedName = std::string(NewName.substr(LastColonPos + 1)); 557 } else { 558 if (RenameInfo.FromDecl && RenameInfo.Context) { 559 if (!llvm::isa<clang::TranslationUnitDecl>( 560 RenameInfo.Context->getDeclContext())) { 561 ReplacedName = tooling::replaceNestedName( 562 RenameInfo.Specifier, RenameInfo.Begin, 563 RenameInfo.Context->getDeclContext(), RenameInfo.FromDecl, 564 NewName.starts_with("::") ? 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 CharSourceRange::getTokenRange( 576 SourceRange(RenameInfo.Begin, RenameInfo.End)), 577 SM, TranslationUnitDecl->getASTContext().getLangOpts()); 578 // Add the leading "::" back if the name written in the code contains 579 // it. 580 if (ActualName.starts_with("::") && !NewName.starts_with("::")) { 581 ReplacedName = "::" + NewName.str(); 582 } 583 } 584 } 585 // If the NewName contains leading "::", add it back. 586 if (NewName.starts_with("::") && NewName.substr(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