#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 {
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;
}
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();
if (Loc.isMacroID())
Loc = SM.getSpellingLoc(Loc);
checkAndAddLocation(Loc);
}
return true;
}
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]);
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) {
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) {
while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
TL.getTypeLocClass() == TypeLoc::Qualified)
TL = TL.getNextTypeLoc();
if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
return TL.castAs<TemplateSpecializationTypeLoc>()
.getLAngleLoc()
.getLocWithOffset(-1);
}
return TL.getEndLoc();
}
NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
while (TL.getTypeLocClass() == TypeLoc::Qualified)
TL = TL.getNextTypeLoc();
if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
return nullptr;
}
class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
public:
RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
: USRSet(USRs.begin(), USRs.end()), Context(Context) {}
struct RenameInfo {
SourceLocation Begin;
SourceLocation End;
const NamedDecl *FromDecl;
const Decl *Context;
const NestedNameSpecifier *Specifier;
bool IgnorePrefixQualifers;
};
bool VisitNamedDecl(const NamedDecl *Decl) {
if (llvm::isa<UsingDecl>(Decl))
return true;
if (llvm::isa<CXXDestructorDecl>(Decl))
return true;
if (Decl->isImplicit())
return true;
if (isInUSRSet(Decl)) {
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,
nullptr,
nullptr,
nullptr,
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,
nullptr,
nullptr,
nullptr,
true});
}
return true;
}
bool VisitDesignatedInitExpr(const DesignatedInitExpr *E) {
for (const DesignatedInitExpr::Designator &D : E->designators()) {
if (D.isFieldDesignator() && D.getField()) {
const FieldDecl *Decl = D.getField();
if (isInUSRSet(Decl)) {
auto StartLoc = D.getFieldLoc();
auto EndLoc = D.getFieldLoc();
RenameInfos.push_back({StartLoc, EndLoc,
nullptr,
nullptr,
nullptr,
true});
}
}
}
return true;
}
bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) {
for (const auto *Initializer : CD->inits()) {
if (!Initializer->isWritten())
continue;
if (const FieldDecl *FD = Initializer->getMember()) {
if (isInUSRSet(FD)) {
auto Loc = Initializer->getSourceLocation();
RenameInfos.push_back({Loc, Loc,
nullptr,
nullptr,
nullptr,
true});
}
}
}
return true;
}
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
const NamedDecl *Decl = Expr->getFoundDecl();
if (auto *UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Decl)) {
Decl = UsingShadow->getTargetDecl();
}
auto StartLoc = Expr->getBeginLoc();
SourceLocation EndLoc = Expr->hasExplicitTemplateArgs()
? Expr->getLAngleLoc().getLocWithOffset(-1)
: Expr->getEndLoc();
if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) {
if (isInUSRSet(MD)) {
RenameInfos.push_back({EndLoc, EndLoc,
nullptr,
nullptr,
nullptr,
true});
return true;
}
}
if (const auto *T = llvm::dyn_cast<EnumConstantDecl>(Decl)) {
if (!Expr->hasQualifier())
return true;
if (const auto *ED =
llvm::dyn_cast_or_null<EnumDecl>(getClosestAncestorDecl(*T))) {
if (ED->isScoped())
return true;
Decl = ED;
}
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(),
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(),
false};
RenameInfos.push_back(Info);
}
}
return true;
}
bool VisitTypeLoc(TypeLoc Loc) {
auto Parents = Context.getParents(Loc);
TypeLoc ParentTypeLoc;
if (!Parents.empty()) {
if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
VisitNestedNameSpecifierLocations(*NSL);
return true;
}
if (const auto *TL = Parents[0].get<TypeLoc>())
ParentTypeLoc = *TL;
}
if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
if (isInUSRSet(TargetDecl)) {
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),
false};
RenameInfos.push_back(Info);
}
return true;
}
}
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;
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),
false};
RenameInfos.push_back(Info);
}
}
}
return true;
}
const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
const std::vector<const UsingDecl *> &getUsingDecls() const {
return UsingDecls;
}
private:
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;
}
template <typename ASTNodeType>
const Decl *getClosestAncestorDecl(const ASTNodeType &Node) {
auto Parents = Context.getParents(Node);
if (Parents.size() != 1)
return nullptr;
if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind()))
return Parents[0].template get<Decl>();
return getClosestAncestorDecl(Parents[0]);
}
const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
auto Parents = Context.getParents(Loc);
if (Parents.size() != 1)
return nullptr;
return Parents[0].get<TypeLoc>();
}
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;
std::vector<const UsingDecl *> UsingDecls;
};
}
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) {
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 {
llvm::StringRef ActualName = Lexer::getSourceText(
CharSourceRange::getTokenRange(
SourceRange(RenameInfo.Begin, RenameInfo.End)),
SM, TranslationUnitDecl->getASTContext().getLangOpts());
if (ActualName.startswith("::") && !NewName.startswith("::")) {
ReplacedName = "::" + NewName.str();
}
}
}
if (NewName.startswith("::") && NewName.substr(2) == ReplacedName)
ReplacedName = NewName.str();
}
Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
}
for (const auto *Using : Finder.getUsingDecls())
Replace(Using->getBeginLoc(), Using->getEndLoc(), "using " + NewName.str());
return AtomicChanges;
}
} }