1320535Sdim//===--- USRLocFinder.cpp - Clang refactoring library ---------------------===// 2320535Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6320535Sdim// 7320535Sdim//===----------------------------------------------------------------------===// 8320535Sdim/// 9320535Sdim/// \file 10341825Sdim/// Methods for finding all instances of a USR. Our strategy is very 11320535Sdim/// simple; we just compare the USR at every relevant AST node with the one 12320535Sdim/// provided. 13320535Sdim/// 14320535Sdim//===----------------------------------------------------------------------===// 15320535Sdim 16320535Sdim#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h" 17320535Sdim#include "clang/AST/ASTContext.h" 18320535Sdim#include "clang/AST/RecursiveASTVisitor.h" 19320535Sdim#include "clang/Basic/LLVM.h" 20320535Sdim#include "clang/Basic/SourceLocation.h" 21320535Sdim#include "clang/Basic/SourceManager.h" 22320535Sdim#include "clang/Lex/Lexer.h" 23320535Sdim#include "clang/Tooling/Core/Lookup.h" 24321238Sdim#include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h" 25327952Sdim#include "clang/Tooling/Refactoring/Rename/SymbolName.h" 26320535Sdim#include "clang/Tooling/Refactoring/Rename/USRFinder.h" 27320535Sdim#include "llvm/ADT/StringRef.h" 28320535Sdim#include "llvm/Support/Casting.h" 29320535Sdim#include <cstddef> 30320535Sdim#include <set> 31320535Sdim#include <string> 32320535Sdim#include <vector> 33320535Sdim 34320535Sdimusing namespace llvm; 35320535Sdim 36320535Sdimnamespace clang { 37320535Sdimnamespace tooling { 38320535Sdim 39320535Sdimnamespace { 40320535Sdim 41327952Sdim// Returns true if the given Loc is valid for edit. We don't edit the 42327952Sdim// SourceLocations that are valid or in temporary buffer. 43327952Sdimbool IsValidEditLoc(const clang::SourceManager& SM, clang::SourceLocation Loc) { 44327952Sdim if (Loc.isInvalid()) 45327952Sdim return false; 46327952Sdim const clang::FullSourceLoc FullLoc(Loc, SM); 47327952Sdim std::pair<clang::FileID, unsigned> FileIdAndOffset = 48327952Sdim FullLoc.getSpellingLoc().getDecomposedLoc(); 49327952Sdim return SM.getFileEntryForID(FileIdAndOffset.first) != nullptr; 50327952Sdim} 51327952Sdim 52341825Sdim// This visitor recursively searches for all instances of a USR in a 53320535Sdim// translation unit and stores them for later usage. 54320535Sdimclass USRLocFindingASTVisitor 55321238Sdim : public RecursiveSymbolVisitor<USRLocFindingASTVisitor> { 56320535Sdimpublic: 57320535Sdim explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs, 58320535Sdim StringRef PrevName, 59320535Sdim const ASTContext &Context) 60321238Sdim : RecursiveSymbolVisitor(Context.getSourceManager(), 61321238Sdim Context.getLangOpts()), 62321238Sdim USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) { 63320535Sdim } 64320535Sdim 65321238Sdim bool visitSymbolOccurrence(const NamedDecl *ND, 66321238Sdim ArrayRef<SourceRange> NameRanges) { 67321238Sdim if (USRSet.find(getUSRForDecl(ND)) != USRSet.end()) { 68321238Sdim assert(NameRanges.size() == 1 && 69321238Sdim "Multiple name pieces are not supported yet!"); 70321238Sdim SourceLocation Loc = NameRanges[0].getBegin(); 71321238Sdim const SourceManager &SM = Context.getSourceManager(); 72321238Sdim // TODO: Deal with macro occurrences correctly. 73321238Sdim if (Loc.isMacroID()) 74321238Sdim Loc = SM.getSpellingLoc(Loc); 75321238Sdim checkAndAddLocation(Loc); 76320535Sdim } 77320535Sdim return true; 78320535Sdim } 79320535Sdim 80320535Sdim // Non-visitors: 81320535Sdim 82341825Sdim /// Returns a set of unique symbol occurrences. Duplicate or 83327952Sdim /// overlapping occurrences are erroneous and should be reported! 84327952Sdim SymbolOccurrences takeOccurrences() { return std::move(Occurrences); } 85320535Sdim 86320535Sdimprivate: 87320535Sdim void checkAndAddLocation(SourceLocation Loc) { 88320535Sdim const SourceLocation BeginLoc = Loc; 89320535Sdim const SourceLocation EndLoc = Lexer::getLocForEndOfToken( 90320535Sdim BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); 91320535Sdim StringRef TokenName = 92320535Sdim Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc), 93320535Sdim Context.getSourceManager(), Context.getLangOpts()); 94327952Sdim size_t Offset = TokenName.find(PrevName.getNamePieces()[0]); 95320535Sdim 96320535Sdim // The token of the source location we find actually has the old 97320535Sdim // name. 98320535Sdim if (Offset != StringRef::npos) 99327952Sdim Occurrences.emplace_back(PrevName, SymbolOccurrence::MatchingSymbol, 100327952Sdim BeginLoc.getLocWithOffset(Offset)); 101320535Sdim } 102320535Sdim 103320535Sdim const std::set<std::string> USRSet; 104327952Sdim const SymbolName PrevName; 105327952Sdim SymbolOccurrences Occurrences; 106320535Sdim const ASTContext &Context; 107320535Sdim}; 108320535Sdim 109320535SdimSourceLocation StartLocationForType(TypeLoc TL) { 110320535Sdim // For elaborated types (e.g. `struct a::A`) we want the portion after the 111320535Sdim // `struct` but including the namespace qualifier, `a::`. 112320535Sdim if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) { 113320535Sdim NestedNameSpecifierLoc NestedNameSpecifier = 114320535Sdim ElaboratedTypeLoc.getQualifierLoc(); 115320535Sdim if (NestedNameSpecifier.getNestedNameSpecifier()) 116320535Sdim return NestedNameSpecifier.getBeginLoc(); 117320535Sdim TL = TL.getNextTypeLoc(); 118320535Sdim } 119344779Sdim return TL.getBeginLoc(); 120320535Sdim} 121320535Sdim 122320535SdimSourceLocation EndLocationForType(TypeLoc TL) { 123320535Sdim // Dig past any namespace or keyword qualifications. 124320535Sdim while (TL.getTypeLocClass() == TypeLoc::Elaborated || 125320535Sdim TL.getTypeLocClass() == TypeLoc::Qualified) 126320535Sdim TL = TL.getNextTypeLoc(); 127320535Sdim 128320535Sdim // The location for template specializations (e.g. Foo<int>) includes the 129320535Sdim // templated types in its location range. We want to restrict this to just 130320535Sdim // before the `<` character. 131320535Sdim if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) { 132320535Sdim return TL.castAs<TemplateSpecializationTypeLoc>() 133320535Sdim .getLAngleLoc() 134320535Sdim .getLocWithOffset(-1); 135320535Sdim } 136320535Sdim return TL.getEndLoc(); 137320535Sdim} 138320535Sdim 139320535SdimNestedNameSpecifier *GetNestedNameForType(TypeLoc TL) { 140320535Sdim // Dig past any keyword qualifications. 141320535Sdim while (TL.getTypeLocClass() == TypeLoc::Qualified) 142320535Sdim TL = TL.getNextTypeLoc(); 143320535Sdim 144320535Sdim // For elaborated types (e.g. `struct a::A`) we want the portion after the 145320535Sdim // `struct` but including the namespace qualifier, `a::`. 146320535Sdim if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) 147320535Sdim return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier(); 148320535Sdim return nullptr; 149320535Sdim} 150320535Sdim 151320535Sdim// Find all locations identified by the given USRs for rename. 152320535Sdim// 153320535Sdim// This class will traverse the AST and find every AST node whose USR is in the 154320535Sdim// given USRs' set. 155320535Sdimclass RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> { 156320535Sdimpublic: 157320535Sdim RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context) 158320535Sdim : USRSet(USRs.begin(), USRs.end()), Context(Context) {} 159320535Sdim 160320535Sdim // A structure records all information of a symbol reference being renamed. 161320535Sdim // We try to add as few prefix qualifiers as possible. 162320535Sdim struct RenameInfo { 163320535Sdim // The begin location of a symbol being renamed. 164320535Sdim SourceLocation Begin; 165320535Sdim // The end location of a symbol being renamed. 166320535Sdim SourceLocation End; 167320535Sdim // The declaration of a symbol being renamed (can be nullptr). 168320535Sdim const NamedDecl *FromDecl; 169320535Sdim // The declaration in which the nested name is contained (can be nullptr). 170320535Sdim const Decl *Context; 171320535Sdim // The nested name being replaced (can be nullptr). 172320535Sdim const NestedNameSpecifier *Specifier; 173327952Sdim // Determine whether the prefix qualifiers of the NewName should be ignored. 174327952Sdim // Normally, we set it to true for the symbol declaration and definition to 175327952Sdim // avoid adding prefix qualifiers. 176327952Sdim // For example, if it is true and NewName is "a::b::foo", then the symbol 177327952Sdim // occurrence which the RenameInfo points to will be renamed to "foo". 178327952Sdim bool IgnorePrefixQualifers; 179320535Sdim }; 180320535Sdim 181320535Sdim bool VisitNamedDecl(const NamedDecl *Decl) { 182320535Sdim // UsingDecl has been handled in other place. 183320535Sdim if (llvm::isa<UsingDecl>(Decl)) 184320535Sdim return true; 185320535Sdim 186320535Sdim // DestructorDecl has been handled in Typeloc. 187320535Sdim if (llvm::isa<CXXDestructorDecl>(Decl)) 188320535Sdim return true; 189320535Sdim 190320535Sdim if (Decl->isImplicit()) 191320535Sdim return true; 192320535Sdim 193320535Sdim if (isInUSRSet(Decl)) { 194327952Sdim // For the case of renaming an alias template, we actually rename the 195327952Sdim // underlying alias declaration of the template. 196327952Sdim if (const auto* TAT = dyn_cast<TypeAliasTemplateDecl>(Decl)) 197327952Sdim Decl = TAT->getTemplatedDecl(); 198327952Sdim 199327952Sdim auto StartLoc = Decl->getLocation(); 200327952Sdim auto EndLoc = StartLoc; 201327952Sdim if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { 202327952Sdim RenameInfo Info = {StartLoc, 203327952Sdim EndLoc, 204327952Sdim /*FromDecl=*/nullptr, 205327952Sdim /*Context=*/nullptr, 206327952Sdim /*Specifier=*/nullptr, 207327952Sdim /*IgnorePrefixQualifers=*/true}; 208327952Sdim RenameInfos.push_back(Info); 209327952Sdim } 210320535Sdim } 211320535Sdim return true; 212320535Sdim } 213320535Sdim 214327952Sdim bool VisitMemberExpr(const MemberExpr *Expr) { 215327952Sdim const NamedDecl *Decl = Expr->getFoundDecl(); 216327952Sdim auto StartLoc = Expr->getMemberLoc(); 217327952Sdim auto EndLoc = Expr->getMemberLoc(); 218327952Sdim if (isInUSRSet(Decl)) { 219327952Sdim RenameInfos.push_back({StartLoc, EndLoc, 220327952Sdim /*FromDecl=*/nullptr, 221327952Sdim /*Context=*/nullptr, 222327952Sdim /*Specifier=*/nullptr, 223327952Sdim /*IgnorePrefixQualifiers=*/true}); 224327952Sdim } 225327952Sdim return true; 226327952Sdim } 227327952Sdim 228327952Sdim bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) { 229327952Sdim // Fix the constructor initializer when renaming class members. 230327952Sdim for (const auto *Initializer : CD->inits()) { 231327952Sdim // Ignore implicit initializers. 232327952Sdim if (!Initializer->isWritten()) 233327952Sdim continue; 234327952Sdim 235327952Sdim if (const FieldDecl *FD = Initializer->getMember()) { 236327952Sdim if (isInUSRSet(FD)) { 237327952Sdim auto Loc = Initializer->getSourceLocation(); 238327952Sdim RenameInfos.push_back({Loc, Loc, 239327952Sdim /*FromDecl=*/nullptr, 240327952Sdim /*Context=*/nullptr, 241327952Sdim /*Specifier=*/nullptr, 242327952Sdim /*IgnorePrefixQualifiers=*/true}); 243327952Sdim } 244327952Sdim } 245327952Sdim } 246327952Sdim return true; 247327952Sdim } 248327952Sdim 249320535Sdim bool VisitDeclRefExpr(const DeclRefExpr *Expr) { 250320535Sdim const NamedDecl *Decl = Expr->getFoundDecl(); 251327952Sdim // Get the underlying declaration of the shadow declaration introduced by a 252327952Sdim // using declaration. 253327952Sdim if (auto *UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Decl)) { 254327952Sdim Decl = UsingShadow->getTargetDecl(); 255327952Sdim } 256327952Sdim 257344779Sdim auto StartLoc = Expr->getBeginLoc(); 258327952Sdim // For template function call expressions like `foo<int>()`, we want to 259327952Sdim // restrict the end of location to just before the `<` character. 260327952Sdim SourceLocation EndLoc = Expr->hasExplicitTemplateArgs() 261327952Sdim ? Expr->getLAngleLoc().getLocWithOffset(-1) 262344779Sdim : Expr->getEndLoc(); 263327952Sdim 264327952Sdim if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) { 265327952Sdim if (isInUSRSet(MD)) { 266327952Sdim // Handle renaming static template class methods, we only rename the 267327952Sdim // name without prefix qualifiers and restrict the source range to the 268327952Sdim // name. 269327952Sdim RenameInfos.push_back({EndLoc, EndLoc, 270327952Sdim /*FromDecl=*/nullptr, 271327952Sdim /*Context=*/nullptr, 272327952Sdim /*Specifier=*/nullptr, 273327952Sdim /*IgnorePrefixQualifiers=*/true}); 274327952Sdim return true; 275327952Sdim } 276327952Sdim } 277327952Sdim 278327952Sdim // In case of renaming an enum declaration, we have to explicitly handle 279327952Sdim // unscoped enum constants referenced in expressions (e.g. 280327952Sdim // "auto r = ns1::ns2::Green" where Green is an enum constant of an unscoped 281327952Sdim // enum decl "ns1::ns2::Color") as these enum constants cannot be caught by 282327952Sdim // TypeLoc. 283327952Sdim if (const auto *T = llvm::dyn_cast<EnumConstantDecl>(Decl)) { 284327952Sdim // FIXME: Handle the enum constant without prefix qualifiers (`a = Green`) 285327952Sdim // when renaming an unscoped enum declaration with a new namespace. 286327952Sdim if (!Expr->hasQualifier()) 287327952Sdim return true; 288327952Sdim 289327952Sdim if (const auto *ED = 290327952Sdim llvm::dyn_cast_or_null<EnumDecl>(getClosestAncestorDecl(*T))) { 291327952Sdim if (ED->isScoped()) 292327952Sdim return true; 293327952Sdim Decl = ED; 294327952Sdim } 295327952Sdim // The current fix would qualify "ns1::ns2::Green" as 296327952Sdim // "ns1::ns2::Color::Green". 297327952Sdim // 298327952Sdim // Get the EndLoc of the replacement by moving 1 character backward ( 299327952Sdim // to exclude the last '::'). 300327952Sdim // 301327952Sdim // ns1::ns2::Green; 302327952Sdim // ^ ^^ 303327952Sdim // BeginLoc |EndLoc of the qualifier 304327952Sdim // new EndLoc 305327952Sdim EndLoc = Expr->getQualifierLoc().getEndLoc().getLocWithOffset(-1); 306327952Sdim assert(EndLoc.isValid() && 307327952Sdim "The enum constant should have prefix qualifers."); 308327952Sdim } 309327952Sdim if (isInUSRSet(Decl) && 310327952Sdim IsValidEditLoc(Context.getSourceManager(), StartLoc)) { 311327952Sdim RenameInfo Info = {StartLoc, 312327952Sdim EndLoc, 313327952Sdim Decl, 314327952Sdim getClosestAncestorDecl(*Expr), 315327952Sdim Expr->getQualifier(), 316327952Sdim /*IgnorePrefixQualifers=*/false}; 317320535Sdim RenameInfos.push_back(Info); 318320535Sdim } 319320535Sdim 320320535Sdim return true; 321320535Sdim } 322320535Sdim 323320535Sdim bool VisitUsingDecl(const UsingDecl *Using) { 324320535Sdim for (const auto *UsingShadow : Using->shadows()) { 325320535Sdim if (isInUSRSet(UsingShadow->getTargetDecl())) { 326320535Sdim UsingDecls.push_back(Using); 327320535Sdim break; 328320535Sdim } 329320535Sdim } 330320535Sdim return true; 331320535Sdim } 332320535Sdim 333320535Sdim bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) { 334320535Sdim if (!NestedLoc.getNestedNameSpecifier()->getAsType()) 335320535Sdim return true; 336320535Sdim 337320535Sdim if (const auto *TargetDecl = 338320535Sdim getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) { 339320535Sdim if (isInUSRSet(TargetDecl)) { 340320535Sdim RenameInfo Info = {NestedLoc.getBeginLoc(), 341320535Sdim EndLocationForType(NestedLoc.getTypeLoc()), 342327952Sdim TargetDecl, 343327952Sdim getClosestAncestorDecl(NestedLoc), 344327952Sdim NestedLoc.getNestedNameSpecifier()->getPrefix(), 345327952Sdim /*IgnorePrefixQualifers=*/false}; 346320535Sdim RenameInfos.push_back(Info); 347320535Sdim } 348320535Sdim } 349320535Sdim return true; 350320535Sdim } 351320535Sdim 352320535Sdim bool VisitTypeLoc(TypeLoc Loc) { 353320535Sdim auto Parents = Context.getParents(Loc); 354320535Sdim TypeLoc ParentTypeLoc; 355320535Sdim if (!Parents.empty()) { 356320535Sdim // Handle cases of nested name specificier locations. 357320535Sdim // 358320535Sdim // The VisitNestedNameSpecifierLoc interface is not impelmented in 359320535Sdim // RecursiveASTVisitor, we have to handle it explicitly. 360320535Sdim if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) { 361320535Sdim VisitNestedNameSpecifierLocations(*NSL); 362320535Sdim return true; 363320535Sdim } 364320535Sdim 365320535Sdim if (const auto *TL = Parents[0].get<TypeLoc>()) 366320535Sdim ParentTypeLoc = *TL; 367320535Sdim } 368320535Sdim 369320535Sdim // Handle the outermost TypeLoc which is directly linked to the interesting 370320535Sdim // declaration and don't handle nested name specifier locations. 371320535Sdim if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) { 372320535Sdim if (isInUSRSet(TargetDecl)) { 373320535Sdim // Only handle the outermost typeLoc. 374320535Sdim // 375320535Sdim // For a type like "a::Foo", there will be two typeLocs for it. 376320535Sdim // One ElaboratedType, the other is RecordType: 377320535Sdim // 378320535Sdim // ElaboratedType 0x33b9390 'a::Foo' sugar 379320535Sdim // `-RecordType 0x338fef0 'class a::Foo' 380320535Sdim // `-CXXRecord 0x338fe58 'Foo' 381320535Sdim // 382320535Sdim // Skip if this is an inner typeLoc. 383320535Sdim if (!ParentTypeLoc.isNull() && 384320535Sdim isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc))) 385320535Sdim return true; 386327952Sdim 387327952Sdim auto StartLoc = StartLocationForType(Loc); 388327952Sdim auto EndLoc = EndLocationForType(Loc); 389327952Sdim if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { 390327952Sdim RenameInfo Info = {StartLoc, 391327952Sdim EndLoc, 392327952Sdim TargetDecl, 393327952Sdim getClosestAncestorDecl(Loc), 394327952Sdim GetNestedNameForType(Loc), 395327952Sdim /*IgnorePrefixQualifers=*/false}; 396327952Sdim RenameInfos.push_back(Info); 397327952Sdim } 398320535Sdim return true; 399320535Sdim } 400320535Sdim } 401320535Sdim 402320535Sdim // Handle specific template class specialiation cases. 403320535Sdim if (const auto *TemplateSpecType = 404320535Sdim dyn_cast<TemplateSpecializationType>(Loc.getType())) { 405320535Sdim TypeLoc TargetLoc = Loc; 406320535Sdim if (!ParentTypeLoc.isNull()) { 407320535Sdim if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType())) 408320535Sdim TargetLoc = ParentTypeLoc; 409320535Sdim } 410320535Sdim 411320535Sdim if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) { 412320535Sdim TypeLoc TargetLoc = Loc; 413320535Sdim // FIXME: Find a better way to handle this case. 414320535Sdim // For the qualified template class specification type like 415320535Sdim // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc 416320535Sdim // (ElaboratedType) of the TemplateSpecializationType in order to 417320535Sdim // catch the prefix qualifiers "ns::". 418320535Sdim if (!ParentTypeLoc.isNull() && 419320535Sdim llvm::isa<ElaboratedType>(ParentTypeLoc.getType())) 420320535Sdim TargetLoc = ParentTypeLoc; 421327952Sdim 422327952Sdim auto StartLoc = StartLocationForType(TargetLoc); 423327952Sdim auto EndLoc = EndLocationForType(TargetLoc); 424327952Sdim if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { 425327952Sdim RenameInfo Info = { 426327952Sdim StartLoc, 427327952Sdim EndLoc, 428327952Sdim TemplateSpecType->getTemplateName().getAsTemplateDecl(), 429327952Sdim getClosestAncestorDecl( 430327952Sdim ast_type_traits::DynTypedNode::create(TargetLoc)), 431327952Sdim GetNestedNameForType(TargetLoc), 432327952Sdim /*IgnorePrefixQualifers=*/false}; 433327952Sdim RenameInfos.push_back(Info); 434327952Sdim } 435320535Sdim } 436320535Sdim } 437320535Sdim return true; 438320535Sdim } 439320535Sdim 440320535Sdim // Returns a list of RenameInfo. 441320535Sdim const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; } 442320535Sdim 443320535Sdim // Returns a list of using declarations which are needed to update. 444320535Sdim const std::vector<const UsingDecl *> &getUsingDecls() const { 445320535Sdim return UsingDecls; 446320535Sdim } 447320535Sdim 448320535Sdimprivate: 449320535Sdim // Get the supported declaration from a given typeLoc. If the declaration type 450320535Sdim // is not supported, returns nullptr. 451320535Sdim const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) { 452327952Sdim if (const auto* TT = Loc.getType()->getAs<clang::TypedefType>()) 453327952Sdim return TT->getDecl(); 454320535Sdim if (const auto *RD = Loc.getType()->getAsCXXRecordDecl()) 455320535Sdim return RD; 456327952Sdim if (const auto *ED = 457327952Sdim llvm::dyn_cast_or_null<EnumDecl>(Loc.getType()->getAsTagDecl())) 458327952Sdim return ED; 459320535Sdim return nullptr; 460320535Sdim } 461320535Sdim 462320535Sdim // Get the closest ancester which is a declaration of a given AST node. 463320535Sdim template <typename ASTNodeType> 464320535Sdim const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { 465320535Sdim auto Parents = Context.getParents(Node); 466320535Sdim // FIXME: figure out how to handle it when there are multiple parents. 467320535Sdim if (Parents.size() != 1) 468320535Sdim return nullptr; 469320535Sdim if (ast_type_traits::ASTNodeKind::getFromNodeKind<Decl>().isBaseOf( 470320535Sdim Parents[0].getNodeKind())) 471320535Sdim return Parents[0].template get<Decl>(); 472320535Sdim return getClosestAncestorDecl(Parents[0]); 473320535Sdim } 474320535Sdim 475320535Sdim // Get the parent typeLoc of a given typeLoc. If there is no such parent, 476320535Sdim // return nullptr. 477320535Sdim const TypeLoc *getParentTypeLoc(TypeLoc Loc) const { 478320535Sdim auto Parents = Context.getParents(Loc); 479320535Sdim // FIXME: figure out how to handle it when there are multiple parents. 480320535Sdim if (Parents.size() != 1) 481320535Sdim return nullptr; 482320535Sdim return Parents[0].get<TypeLoc>(); 483320535Sdim } 484320535Sdim 485320535Sdim // Check whether the USR of a given Decl is in the USRSet. 486320535Sdim bool isInUSRSet(const Decl *Decl) const { 487320535Sdim auto USR = getUSRForDecl(Decl); 488320535Sdim if (USR.empty()) 489320535Sdim return false; 490320535Sdim return llvm::is_contained(USRSet, USR); 491320535Sdim } 492320535Sdim 493320535Sdim const std::set<std::string> USRSet; 494320535Sdim ASTContext &Context; 495320535Sdim std::vector<RenameInfo> RenameInfos; 496320535Sdim // Record all interested using declarations which contains the using-shadow 497320535Sdim // declarations of the symbol declarations being renamed. 498320535Sdim std::vector<const UsingDecl *> UsingDecls; 499320535Sdim}; 500320535Sdim 501320535Sdim} // namespace 502320535Sdim 503327952SdimSymbolOccurrences getOccurrencesOfUSRs(ArrayRef<std::string> USRs, 504327952Sdim StringRef PrevName, Decl *Decl) { 505320535Sdim USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext()); 506320535Sdim Visitor.TraverseDecl(Decl); 507327952Sdim return Visitor.takeOccurrences(); 508320535Sdim} 509320535Sdim 510320535Sdimstd::vector<tooling::AtomicChange> 511320535SdimcreateRenameAtomicChanges(llvm::ArrayRef<std::string> USRs, 512320535Sdim llvm::StringRef NewName, Decl *TranslationUnitDecl) { 513320535Sdim RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext()); 514320535Sdim Finder.TraverseDecl(TranslationUnitDecl); 515320535Sdim 516320535Sdim const SourceManager &SM = 517320535Sdim TranslationUnitDecl->getASTContext().getSourceManager(); 518320535Sdim 519320535Sdim std::vector<tooling::AtomicChange> AtomicChanges; 520320535Sdim auto Replace = [&](SourceLocation Start, SourceLocation End, 521320535Sdim llvm::StringRef Text) { 522320535Sdim tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start); 523320535Sdim llvm::Error Err = ReplaceChange.replace( 524320535Sdim SM, CharSourceRange::getTokenRange(Start, End), Text); 525320535Sdim if (Err) { 526341825Sdim llvm::errs() << "Failed to add replacement to AtomicChange: " 527320535Sdim << llvm::toString(std::move(Err)) << "\n"; 528320535Sdim return; 529320535Sdim } 530320535Sdim AtomicChanges.push_back(std::move(ReplaceChange)); 531320535Sdim }; 532320535Sdim 533320535Sdim for (const auto &RenameInfo : Finder.getRenameInfos()) { 534320535Sdim std::string ReplacedName = NewName.str(); 535327952Sdim if (RenameInfo.IgnorePrefixQualifers) { 536327952Sdim // Get the name without prefix qualifiers from NewName. 537327952Sdim size_t LastColonPos = NewName.find_last_of(':'); 538327952Sdim if (LastColonPos != std::string::npos) 539327952Sdim ReplacedName = NewName.substr(LastColonPos + 1); 540327952Sdim } else { 541327952Sdim if (RenameInfo.FromDecl && RenameInfo.Context) { 542327952Sdim if (!llvm::isa<clang::TranslationUnitDecl>( 543327952Sdim RenameInfo.Context->getDeclContext())) { 544327952Sdim ReplacedName = tooling::replaceNestedName( 545353358Sdim RenameInfo.Specifier, RenameInfo.Begin, 546353358Sdim RenameInfo.Context->getDeclContext(), RenameInfo.FromDecl, 547327952Sdim NewName.startswith("::") ? NewName.str() 548327952Sdim : ("::" + NewName).str()); 549327952Sdim } else { 550327952Sdim // This fixes the case where type `T` is a parameter inside a function 551327952Sdim // type (e.g. `std::function<void(T)>`) and the DeclContext of `T` 552327952Sdim // becomes the translation unit. As a workaround, we simply use 553327952Sdim // fully-qualified name here for all references whose `DeclContext` is 554327952Sdim // the translation unit and ignore the possible existence of 555327952Sdim // using-decls (in the global scope) that can shorten the replaced 556327952Sdim // name. 557327952Sdim llvm::StringRef ActualName = Lexer::getSourceText( 558327952Sdim CharSourceRange::getTokenRange( 559327952Sdim SourceRange(RenameInfo.Begin, RenameInfo.End)), 560327952Sdim SM, TranslationUnitDecl->getASTContext().getLangOpts()); 561327952Sdim // Add the leading "::" back if the name written in the code contains 562327952Sdim // it. 563327952Sdim if (ActualName.startswith("::") && !NewName.startswith("::")) { 564327952Sdim ReplacedName = "::" + NewName.str(); 565327952Sdim } 566327952Sdim } 567320535Sdim } 568327952Sdim // If the NewName contains leading "::", add it back. 569327952Sdim if (NewName.startswith("::") && NewName.substr(2) == ReplacedName) 570327952Sdim ReplacedName = NewName.str(); 571320535Sdim } 572320535Sdim Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName); 573320535Sdim } 574320535Sdim 575320535Sdim // Hanlde using declarations explicitly as "using a::Foo" don't trigger 576320535Sdim // typeLoc for "a::Foo". 577320535Sdim for (const auto *Using : Finder.getUsingDecls()) 578344779Sdim Replace(Using->getBeginLoc(), Using->getEndLoc(), "using " + NewName.str()); 579320535Sdim 580320535Sdim return AtomicChanges; 581320535Sdim} 582320535Sdim 583320535Sdim} // end namespace tooling 584320535Sdim} // end namespace clang 585