584 lines
22 KiB
C++
584 lines
22 KiB
C++
//===--- USRLocFinder.cpp - Clang refactoring library ---------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// Methods for finding all instances of a USR. Our strategy is very
|
|
/// simple; we just compare the USR at every relevant AST node with the one
|
|
/// provided.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/ParentMapContext.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "clang/Basic/LLVM.h"
|
|
#include "clang/Basic/SourceLocation.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/Lex/Lexer.h"
|
|
#include "clang/Tooling/Refactoring/Lookup.h"
|
|
#include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
|
|
#include "clang/Tooling/Refactoring/Rename/SymbolName.h"
|
|
#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include <cstddef>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace clang {
|
|
namespace tooling {
|
|
|
|
namespace {
|
|
|
|
// Returns true if the given Loc is valid for edit. We don't edit the
|
|
// SourceLocations that are valid or in temporary buffer.
|
|
bool IsValidEditLoc(const clang::SourceManager& SM, clang::SourceLocation Loc) {
|
|
if (Loc.isInvalid())
|
|
return false;
|
|
const clang::FullSourceLoc FullLoc(Loc, SM);
|
|
std::pair<clang::FileID, unsigned> FileIdAndOffset =
|
|
FullLoc.getSpellingLoc().getDecomposedLoc();
|
|
return SM.getFileEntryForID(FileIdAndOffset.first) != nullptr;
|
|
}
|
|
|
|
// This visitor recursively searches for all instances of a USR in a
|
|
// translation unit and stores them for later usage.
|
|
class USRLocFindingASTVisitor
|
|
: public RecursiveSymbolVisitor<USRLocFindingASTVisitor> {
|
|
public:
|
|
explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
|
|
StringRef PrevName,
|
|
const ASTContext &Context)
|
|
: RecursiveSymbolVisitor(Context.getSourceManager(),
|
|
Context.getLangOpts()),
|
|
USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
|
|
}
|
|
|
|
bool visitSymbolOccurrence(const NamedDecl *ND,
|
|
ArrayRef<SourceRange> NameRanges) {
|
|
if (USRSet.find(getUSRForDecl(ND)) != USRSet.end()) {
|
|
assert(NameRanges.size() == 1 &&
|
|
"Multiple name pieces are not supported yet!");
|
|
SourceLocation Loc = NameRanges[0].getBegin();
|
|
const SourceManager &SM = Context.getSourceManager();
|
|
// TODO: Deal with macro occurrences correctly.
|
|
if (Loc.isMacroID())
|
|
Loc = SM.getSpellingLoc(Loc);
|
|
checkAndAddLocation(Loc);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Non-visitors:
|
|
|
|
/// Returns a set of unique symbol occurrences. Duplicate or
|
|
/// overlapping occurrences are erroneous and should be reported!
|
|
SymbolOccurrences takeOccurrences() { return std::move(Occurrences); }
|
|
|
|
private:
|
|
void checkAndAddLocation(SourceLocation Loc) {
|
|
const SourceLocation BeginLoc = Loc;
|
|
const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
|
|
BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
|
|
StringRef TokenName =
|
|
Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
|
|
Context.getSourceManager(), Context.getLangOpts());
|
|
size_t Offset = TokenName.find(PrevName.getNamePieces()[0]);
|
|
|
|
// The token of the source location we find actually has the old
|
|
// name.
|
|
if (Offset != StringRef::npos)
|
|
Occurrences.emplace_back(PrevName, SymbolOccurrence::MatchingSymbol,
|
|
BeginLoc.getLocWithOffset(Offset));
|
|
}
|
|
|
|
const std::set<std::string> USRSet;
|
|
const SymbolName PrevName;
|
|
SymbolOccurrences Occurrences;
|
|
const ASTContext &Context;
|
|
};
|
|
|
|
SourceLocation StartLocationForType(TypeLoc TL) {
|
|
// For elaborated types (e.g. `struct a::A`) we want the portion after the
|
|
// `struct` but including the namespace qualifier, `a::`.
|
|
if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) {
|
|
NestedNameSpecifierLoc NestedNameSpecifier =
|
|
ElaboratedTypeLoc.getQualifierLoc();
|
|
if (NestedNameSpecifier.getNestedNameSpecifier())
|
|
return NestedNameSpecifier.getBeginLoc();
|
|
TL = TL.getNextTypeLoc();
|
|
}
|
|
return TL.getBeginLoc();
|
|
}
|
|
|
|
SourceLocation EndLocationForType(TypeLoc TL) {
|
|
// Dig past any namespace or keyword qualifications.
|
|
while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
|
|
TL.getTypeLocClass() == TypeLoc::Qualified)
|
|
TL = TL.getNextTypeLoc();
|
|
|
|
// The location for template specializations (e.g. Foo<int>) includes the
|
|
// templated types in its location range. We want to restrict this to just
|
|
// before the `<` character.
|
|
if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
|
|
return TL.castAs<TemplateSpecializationTypeLoc>()
|
|
.getLAngleLoc()
|
|
.getLocWithOffset(-1);
|
|
}
|
|
return TL.getEndLoc();
|
|
}
|
|
|
|
NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
|
|
// Dig past any keyword qualifications.
|
|
while (TL.getTypeLocClass() == TypeLoc::Qualified)
|
|
TL = TL.getNextTypeLoc();
|
|
|
|
// For elaborated types (e.g. `struct a::A`) we want the portion after the
|
|
// `struct` but including the namespace qualifier, `a::`.
|
|
if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
|
|
return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
|
|
return nullptr;
|
|
}
|
|
|
|
// Find all locations identified by the given USRs for rename.
|
|
//
|
|
// This class will traverse the AST and find every AST node whose USR is in the
|
|
// given USRs' set.
|
|
class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
|
|
public:
|
|
RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
|
|
: USRSet(USRs.begin(), USRs.end()), Context(Context) {}
|
|
|
|
// A structure records all information of a symbol reference being renamed.
|
|
// We try to add as few prefix qualifiers as possible.
|
|
struct RenameInfo {
|
|
// The begin location of a symbol being renamed.
|
|
SourceLocation Begin;
|
|
// The end location of a symbol being renamed.
|
|
SourceLocation End;
|
|
// The declaration of a symbol being renamed (can be nullptr).
|
|
const NamedDecl *FromDecl;
|
|
// The declaration in which the nested name is contained (can be nullptr).
|
|
const Decl *Context;
|
|
// The nested name being replaced (can be nullptr).
|
|
const NestedNameSpecifier *Specifier;
|
|
// Determine whether the prefix qualifiers of the NewName should be ignored.
|
|
// Normally, we set it to true for the symbol declaration and definition to
|
|
// avoid adding prefix qualifiers.
|
|
// For example, if it is true and NewName is "a::b::foo", then the symbol
|
|
// occurrence which the RenameInfo points to will be renamed to "foo".
|
|
bool IgnorePrefixQualifers;
|
|
};
|
|
|
|
bool VisitNamedDecl(const NamedDecl *Decl) {
|
|
// UsingDecl has been handled in other place.
|
|
if (llvm::isa<UsingDecl>(Decl))
|
|
return true;
|
|
|
|
// DestructorDecl has been handled in Typeloc.
|
|
if (llvm::isa<CXXDestructorDecl>(Decl))
|
|
return true;
|
|
|
|
if (Decl->isImplicit())
|
|
return true;
|
|
|
|
if (isInUSRSet(Decl)) {
|
|
// For the case of renaming an alias template, we actually rename the
|
|
// underlying alias declaration of the template.
|
|
if (const auto* TAT = dyn_cast<TypeAliasTemplateDecl>(Decl))
|
|
Decl = TAT->getTemplatedDecl();
|
|
|
|
auto StartLoc = Decl->getLocation();
|
|
auto EndLoc = StartLoc;
|
|
if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
|
|
RenameInfo Info = {StartLoc,
|
|
EndLoc,
|
|
/*FromDecl=*/nullptr,
|
|
/*Context=*/nullptr,
|
|
/*Specifier=*/nullptr,
|
|
/*IgnorePrefixQualifers=*/true};
|
|
RenameInfos.push_back(Info);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool VisitMemberExpr(const MemberExpr *Expr) {
|
|
const NamedDecl *Decl = Expr->getFoundDecl();
|
|
auto StartLoc = Expr->getMemberLoc();
|
|
auto EndLoc = Expr->getMemberLoc();
|
|
if (isInUSRSet(Decl)) {
|
|
RenameInfos.push_back({StartLoc, EndLoc,
|
|
/*FromDecl=*/nullptr,
|
|
/*Context=*/nullptr,
|
|
/*Specifier=*/nullptr,
|
|
/*IgnorePrefixQualifiers=*/true});
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) {
|
|
// Fix the constructor initializer when renaming class members.
|
|
for (const auto *Initializer : CD->inits()) {
|
|
// Ignore implicit initializers.
|
|
if (!Initializer->isWritten())
|
|
continue;
|
|
|
|
if (const FieldDecl *FD = Initializer->getMember()) {
|
|
if (isInUSRSet(FD)) {
|
|
auto Loc = Initializer->getSourceLocation();
|
|
RenameInfos.push_back({Loc, Loc,
|
|
/*FromDecl=*/nullptr,
|
|
/*Context=*/nullptr,
|
|
/*Specifier=*/nullptr,
|
|
/*IgnorePrefixQualifiers=*/true});
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
|
|
const NamedDecl *Decl = Expr->getFoundDecl();
|
|
// Get the underlying declaration of the shadow declaration introduced by a
|
|
// using declaration.
|
|
if (auto *UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Decl)) {
|
|
Decl = UsingShadow->getTargetDecl();
|
|
}
|
|
|
|
auto StartLoc = Expr->getBeginLoc();
|
|
// For template function call expressions like `foo<int>()`, we want to
|
|
// restrict the end of location to just before the `<` character.
|
|
SourceLocation EndLoc = Expr->hasExplicitTemplateArgs()
|
|
? Expr->getLAngleLoc().getLocWithOffset(-1)
|
|
: Expr->getEndLoc();
|
|
|
|
if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) {
|
|
if (isInUSRSet(MD)) {
|
|
// Handle renaming static template class methods, we only rename the
|
|
// name without prefix qualifiers and restrict the source range to the
|
|
// name.
|
|
RenameInfos.push_back({EndLoc, EndLoc,
|
|
/*FromDecl=*/nullptr,
|
|
/*Context=*/nullptr,
|
|
/*Specifier=*/nullptr,
|
|
/*IgnorePrefixQualifiers=*/true});
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// In case of renaming an enum declaration, we have to explicitly handle
|
|
// unscoped enum constants referenced in expressions (e.g.
|
|
// "auto r = ns1::ns2::Green" where Green is an enum constant of an unscoped
|
|
// enum decl "ns1::ns2::Color") as these enum constants cannot be caught by
|
|
// TypeLoc.
|
|
if (const auto *T = llvm::dyn_cast<EnumConstantDecl>(Decl)) {
|
|
// FIXME: Handle the enum constant without prefix qualifiers (`a = Green`)
|
|
// when renaming an unscoped enum declaration with a new namespace.
|
|
if (!Expr->hasQualifier())
|
|
return true;
|
|
|
|
if (const auto *ED =
|
|
llvm::dyn_cast_or_null<EnumDecl>(getClosestAncestorDecl(*T))) {
|
|
if (ED->isScoped())
|
|
return true;
|
|
Decl = ED;
|
|
}
|
|
// The current fix would qualify "ns1::ns2::Green" as
|
|
// "ns1::ns2::Color::Green".
|
|
//
|
|
// Get the EndLoc of the replacement by moving 1 character backward (
|
|
// to exclude the last '::').
|
|
//
|
|
// ns1::ns2::Green;
|
|
// ^ ^^
|
|
// BeginLoc |EndLoc of the qualifier
|
|
// new EndLoc
|
|
EndLoc = Expr->getQualifierLoc().getEndLoc().getLocWithOffset(-1);
|
|
assert(EndLoc.isValid() &&
|
|
"The enum constant should have prefix qualifers.");
|
|
}
|
|
if (isInUSRSet(Decl) &&
|
|
IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
|
|
RenameInfo Info = {StartLoc,
|
|
EndLoc,
|
|
Decl,
|
|
getClosestAncestorDecl(*Expr),
|
|
Expr->getQualifier(),
|
|
/*IgnorePrefixQualifers=*/false};
|
|
RenameInfos.push_back(Info);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VisitUsingDecl(const UsingDecl *Using) {
|
|
for (const auto *UsingShadow : Using->shadows()) {
|
|
if (isInUSRSet(UsingShadow->getTargetDecl())) {
|
|
UsingDecls.push_back(Using);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
|
|
if (!NestedLoc.getNestedNameSpecifier()->getAsType())
|
|
return true;
|
|
|
|
if (const auto *TargetDecl =
|
|
getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
|
|
if (isInUSRSet(TargetDecl)) {
|
|
RenameInfo Info = {NestedLoc.getBeginLoc(),
|
|
EndLocationForType(NestedLoc.getTypeLoc()),
|
|
TargetDecl,
|
|
getClosestAncestorDecl(NestedLoc),
|
|
NestedLoc.getNestedNameSpecifier()->getPrefix(),
|
|
/*IgnorePrefixQualifers=*/false};
|
|
RenameInfos.push_back(Info);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool VisitTypeLoc(TypeLoc Loc) {
|
|
auto Parents = Context.getParents(Loc);
|
|
TypeLoc ParentTypeLoc;
|
|
if (!Parents.empty()) {
|
|
// Handle cases of nested name specificier locations.
|
|
//
|
|
// The VisitNestedNameSpecifierLoc interface is not impelmented in
|
|
// RecursiveASTVisitor, we have to handle it explicitly.
|
|
if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
|
|
VisitNestedNameSpecifierLocations(*NSL);
|
|
return true;
|
|
}
|
|
|
|
if (const auto *TL = Parents[0].get<TypeLoc>())
|
|
ParentTypeLoc = *TL;
|
|
}
|
|
|
|
// Handle the outermost TypeLoc which is directly linked to the interesting
|
|
// declaration and don't handle nested name specifier locations.
|
|
if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
|
|
if (isInUSRSet(TargetDecl)) {
|
|
// Only handle the outermost typeLoc.
|
|
//
|
|
// For a type like "a::Foo", there will be two typeLocs for it.
|
|
// One ElaboratedType, the other is RecordType:
|
|
//
|
|
// ElaboratedType 0x33b9390 'a::Foo' sugar
|
|
// `-RecordType 0x338fef0 'class a::Foo'
|
|
// `-CXXRecord 0x338fe58 'Foo'
|
|
//
|
|
// Skip if this is an inner typeLoc.
|
|
if (!ParentTypeLoc.isNull() &&
|
|
isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
|
|
return true;
|
|
|
|
auto StartLoc = StartLocationForType(Loc);
|
|
auto EndLoc = EndLocationForType(Loc);
|
|
if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
|
|
RenameInfo Info = {StartLoc,
|
|
EndLoc,
|
|
TargetDecl,
|
|
getClosestAncestorDecl(Loc),
|
|
GetNestedNameForType(Loc),
|
|
/*IgnorePrefixQualifers=*/false};
|
|
RenameInfos.push_back(Info);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Handle specific template class specialiation cases.
|
|
if (const auto *TemplateSpecType =
|
|
dyn_cast<TemplateSpecializationType>(Loc.getType())) {
|
|
TypeLoc TargetLoc = Loc;
|
|
if (!ParentTypeLoc.isNull()) {
|
|
if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
|
|
TargetLoc = ParentTypeLoc;
|
|
}
|
|
|
|
if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
|
|
TypeLoc TargetLoc = Loc;
|
|
// FIXME: Find a better way to handle this case.
|
|
// For the qualified template class specification type like
|
|
// "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
|
|
// (ElaboratedType) of the TemplateSpecializationType in order to
|
|
// catch the prefix qualifiers "ns::".
|
|
if (!ParentTypeLoc.isNull() &&
|
|
llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
|
|
TargetLoc = ParentTypeLoc;
|
|
|
|
auto StartLoc = StartLocationForType(TargetLoc);
|
|
auto EndLoc = EndLocationForType(TargetLoc);
|
|
if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
|
|
RenameInfo Info = {
|
|
StartLoc,
|
|
EndLoc,
|
|
TemplateSpecType->getTemplateName().getAsTemplateDecl(),
|
|
getClosestAncestorDecl(DynTypedNode::create(TargetLoc)),
|
|
GetNestedNameForType(TargetLoc),
|
|
/*IgnorePrefixQualifers=*/false};
|
|
RenameInfos.push_back(Info);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Returns a list of RenameInfo.
|
|
const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
|
|
|
|
// Returns a list of using declarations which are needed to update.
|
|
const std::vector<const UsingDecl *> &getUsingDecls() const {
|
|
return UsingDecls;
|
|
}
|
|
|
|
private:
|
|
// Get the supported declaration from a given typeLoc. If the declaration type
|
|
// is not supported, returns nullptr.
|
|
const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
|
|
if (const auto* TT = Loc.getType()->getAs<clang::TypedefType>())
|
|
return TT->getDecl();
|
|
if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
|
|
return RD;
|
|
if (const auto *ED =
|
|
llvm::dyn_cast_or_null<EnumDecl>(Loc.getType()->getAsTagDecl()))
|
|
return ED;
|
|
return nullptr;
|
|
}
|
|
|
|
// Get the closest ancester which is a declaration of a given AST node.
|
|
template <typename ASTNodeType>
|
|
const Decl *getClosestAncestorDecl(const ASTNodeType &Node) {
|
|
auto Parents = Context.getParents(Node);
|
|
// FIXME: figure out how to handle it when there are multiple parents.
|
|
if (Parents.size() != 1)
|
|
return nullptr;
|
|
if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind()))
|
|
return Parents[0].template get<Decl>();
|
|
return getClosestAncestorDecl(Parents[0]);
|
|
}
|
|
|
|
// Get the parent typeLoc of a given typeLoc. If there is no such parent,
|
|
// return nullptr.
|
|
const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
|
|
auto Parents = Context.getParents(Loc);
|
|
// FIXME: figure out how to handle it when there are multiple parents.
|
|
if (Parents.size() != 1)
|
|
return nullptr;
|
|
return Parents[0].get<TypeLoc>();
|
|
}
|
|
|
|
// Check whether the USR of a given Decl is in the USRSet.
|
|
bool isInUSRSet(const Decl *Decl) const {
|
|
auto USR = getUSRForDecl(Decl);
|
|
if (USR.empty())
|
|
return false;
|
|
return llvm::is_contained(USRSet, USR);
|
|
}
|
|
|
|
const std::set<std::string> USRSet;
|
|
ASTContext &Context;
|
|
std::vector<RenameInfo> RenameInfos;
|
|
// Record all interested using declarations which contains the using-shadow
|
|
// declarations of the symbol declarations being renamed.
|
|
std::vector<const UsingDecl *> UsingDecls;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
SymbolOccurrences getOccurrencesOfUSRs(ArrayRef<std::string> USRs,
|
|
StringRef PrevName, Decl *Decl) {
|
|
USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
|
|
Visitor.TraverseDecl(Decl);
|
|
return Visitor.takeOccurrences();
|
|
}
|
|
|
|
std::vector<tooling::AtomicChange>
|
|
createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
|
|
llvm::StringRef NewName, Decl *TranslationUnitDecl) {
|
|
RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext());
|
|
Finder.TraverseDecl(TranslationUnitDecl);
|
|
|
|
const SourceManager &SM =
|
|
TranslationUnitDecl->getASTContext().getSourceManager();
|
|
|
|
std::vector<tooling::AtomicChange> AtomicChanges;
|
|
auto Replace = [&](SourceLocation Start, SourceLocation End,
|
|
llvm::StringRef Text) {
|
|
tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start);
|
|
llvm::Error Err = ReplaceChange.replace(
|
|
SM, CharSourceRange::getTokenRange(Start, End), Text);
|
|
if (Err) {
|
|
llvm::errs() << "Failed to add replacement to AtomicChange: "
|
|
<< llvm::toString(std::move(Err)) << "\n";
|
|
return;
|
|
}
|
|
AtomicChanges.push_back(std::move(ReplaceChange));
|
|
};
|
|
|
|
for (const auto &RenameInfo : Finder.getRenameInfos()) {
|
|
std::string ReplacedName = NewName.str();
|
|
if (RenameInfo.IgnorePrefixQualifers) {
|
|
// Get the name without prefix qualifiers from NewName.
|
|
size_t LastColonPos = NewName.find_last_of(':');
|
|
if (LastColonPos != std::string::npos)
|
|
ReplacedName = std::string(NewName.substr(LastColonPos + 1));
|
|
} else {
|
|
if (RenameInfo.FromDecl && RenameInfo.Context) {
|
|
if (!llvm::isa<clang::TranslationUnitDecl>(
|
|
RenameInfo.Context->getDeclContext())) {
|
|
ReplacedName = tooling::replaceNestedName(
|
|
RenameInfo.Specifier, RenameInfo.Begin,
|
|
RenameInfo.Context->getDeclContext(), RenameInfo.FromDecl,
|
|
NewName.startswith("::") ? NewName.str()
|
|
: ("::" + NewName).str());
|
|
} else {
|
|
// This fixes the case where type `T` is a parameter inside a function
|
|
// type (e.g. `std::function<void(T)>`) and the DeclContext of `T`
|
|
// becomes the translation unit. As a workaround, we simply use
|
|
// fully-qualified name here for all references whose `DeclContext` is
|
|
// the translation unit and ignore the possible existence of
|
|
// using-decls (in the global scope) that can shorten the replaced
|
|
// name.
|
|
llvm::StringRef ActualName = Lexer::getSourceText(
|
|
CharSourceRange::getTokenRange(
|
|
SourceRange(RenameInfo.Begin, RenameInfo.End)),
|
|
SM, TranslationUnitDecl->getASTContext().getLangOpts());
|
|
// Add the leading "::" back if the name written in the code contains
|
|
// it.
|
|
if (ActualName.startswith("::") && !NewName.startswith("::")) {
|
|
ReplacedName = "::" + NewName.str();
|
|
}
|
|
}
|
|
}
|
|
// If the NewName contains leading "::", add it back.
|
|
if (NewName.startswith("::") && NewName.substr(2) == ReplacedName)
|
|
ReplacedName = NewName.str();
|
|
}
|
|
Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
|
|
}
|
|
|
|
// Hanlde using declarations explicitly as "using a::Foo" don't trigger
|
|
// typeLoc for "a::Foo".
|
|
for (const auto *Using : Finder.getUsingDecls())
|
|
Replace(Using->getBeginLoc(), Using->getEndLoc(), "using " + NewName.str());
|
|
|
|
return AtomicChanges;
|
|
}
|
|
|
|
} // end namespace tooling
|
|
} // end namespace clang
|