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