28#include "llvm/ADT/StringRef.h"
29#include "llvm/Support/Casting.h"
44bool IsValidEditLoc(
const clang::SourceManager&
SM, clang::SourceLocation Loc) {
47 const clang::FullSourceLoc FullLoc(Loc,
SM);
48 auto FileIdAndOffset = FullLoc.getSpellingLoc().getDecomposedLoc();
49 return SM.getFileEntryForID(FileIdAndOffset.first) !=
nullptr;
54class USRLocFindingASTVisitor
57 explicit USRLocFindingASTVisitor(
const std::vector<std::string> &USRs,
59 const ASTContext &Context)
60 : RecursiveSymbolVisitor(Context.getSourceManager(),
61 Context.getLangOpts()),
62 USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
65 bool visitSymbolOccurrence(
const NamedDecl *ND,
66 ArrayRef<SourceRange> NameRanges) {
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();
74 Loc =
SM.getSpellingLoc(Loc);
75 checkAndAddLocation(Loc);
87 void checkAndAddLocation(SourceLocation Loc) {
88 const SourceLocation BeginLoc = Loc;
90 BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
93 Context.getSourceManager(), Context.getLangOpts());
94 size_t Offset = TokenName.find(PrevName.getNamePieces()[0]);
98 if (Offset != StringRef::npos)
100 BeginLoc.getLocWithOffset(Offset));
103 const std::set<std::string> USRSet;
104 const SymbolName PrevName;
106 const ASTContext &Context;
109SourceLocation StartLocationForType(TypeLoc TL) {
110 if (
auto QTL = TL.getAs<QualifiedTypeLoc>())
111 TL = QTL.getUnqualifiedLoc();
115 switch (TL.getTypeLocClass()) {
116 case TypeLoc::Record:
117 case TypeLoc::InjectedClassName:
118 case TypeLoc::Enum: {
119 auto TTL = TL.castAs<TagTypeLoc>();
120 if (NestedNameSpecifierLoc QualifierLoc = TTL.getQualifierLoc())
121 return QualifierLoc.getBeginLoc();
122 return TTL.getNameLoc();
124 case TypeLoc::Typedef: {
125 auto TTL = TL.castAs<TypedefTypeLoc>();
126 if (NestedNameSpecifierLoc QualifierLoc = TTL.getQualifierLoc())
127 return QualifierLoc.getBeginLoc();
128 return TTL.getNameLoc();
130 case TypeLoc::UnresolvedUsing: {
131 auto TTL = TL.castAs<UnresolvedUsingTypeLoc>();
132 if (NestedNameSpecifierLoc QualifierLoc = TTL.getQualifierLoc())
133 return QualifierLoc.getBeginLoc();
134 return TTL.getNameLoc();
136 case TypeLoc::Using: {
137 auto TTL = TL.castAs<UsingTypeLoc>();
138 if (NestedNameSpecifierLoc QualifierLoc = TTL.getQualifierLoc())
139 return QualifierLoc.getBeginLoc();
140 return TTL.getNameLoc();
142 case TypeLoc::TemplateSpecialization: {
143 auto TTL = TL.castAs<TemplateSpecializationTypeLoc>();
144 if (NestedNameSpecifierLoc QualifierLoc = TTL.getQualifierLoc())
145 return QualifierLoc.getBeginLoc();
146 return TTL.getTemplateNameLoc();
148 case TypeLoc::DeducedTemplateSpecialization: {
149 auto DTL = TL.castAs<clang::DeducedTemplateSpecializationTypeLoc>();
150 if (NestedNameSpecifierLoc QualifierLoc = DTL.getQualifierLoc())
151 return QualifierLoc.getBeginLoc();
152 return DTL.getTemplateNameLoc();
154 case TypeLoc::DependentName: {
155 auto TTL = TL.castAs<DependentNameTypeLoc>();
156 if (NestedNameSpecifierLoc QualifierLoc = TTL.getQualifierLoc())
157 return QualifierLoc.getBeginLoc();
158 return TTL.getNameLoc();
161 llvm_unreachable(
"unhandled TypeLoc class");
165SourceLocation EndLocationForType(TypeLoc TL) {
166 if (
auto QTL = TL.getAs<QualifiedTypeLoc>())
167 TL = QTL.getUnqualifiedLoc();
172 if (
auto TTL = TL.getAs<TemplateSpecializationTypeLoc>())
173 return TTL.getLAngleLoc().getLocWithOffset(-1);
174 return TL.getEndLoc();
177NestedNameSpecifier GetNestedNameForType(TypeLoc TL) {
178 if (
auto QTL = TL.getAs<QualifiedTypeLoc>())
179 TL = QTL.getUnqualifiedLoc();
180 return TL.getPrefix().getNestedNameSpecifier();
187class RenameLocFinder :
public RecursiveASTVisitor<RenameLocFinder> {
189 RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
190 : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
196 SourceLocation Begin;
200 const NamedDecl *FromDecl;
204 NestedNameSpecifier Specifier;
210 bool IgnorePrefixQualifiers;
213 bool VisitNamedDecl(
const NamedDecl *Decl) {
215 if (llvm::isa<UsingDecl>(Decl))
219 if (llvm::isa<CXXDestructorDecl>(Decl))
222 if (
Decl->isImplicit())
225 if (isInUSRSet(Decl)) {
228 if (
const auto* TAT = dyn_cast<TypeAliasTemplateDecl>(Decl))
229 Decl = TAT->getTemplatedDecl();
231 auto StartLoc =
Decl->getLocation();
232 auto EndLoc = StartLoc;
233 if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
234 RenameInfo Info = {StartLoc,
240 RenameInfos.push_back(Info);
246 bool VisitMemberExpr(
const MemberExpr *Expr) {
247 const NamedDecl *
Decl = Expr->getFoundDecl();
248 auto StartLoc = Expr->getMemberLoc();
249 auto EndLoc = Expr->getMemberLoc();
250 if (isInUSRSet(Decl)) {
251 RenameInfos.push_back({StartLoc, EndLoc,
260 bool VisitDesignatedInitExpr(
const DesignatedInitExpr *E) {
261 for (
const DesignatedInitExpr::Designator &D : E->designators()) {
262 if (D.isFieldDesignator()) {
263 if (
const FieldDecl *Decl = D.getFieldDecl()) {
264 if (isInUSRSet(Decl)) {
265 auto StartLoc = D.getFieldLoc();
266 auto EndLoc = D.getFieldLoc();
267 RenameInfos.push_back({StartLoc, EndLoc,
279 bool VisitCXXConstructorDecl(
const CXXConstructorDecl *CD) {
286 if (
const FieldDecl *FD =
Initializer->getMember()) {
287 if (isInUSRSet(FD)) {
289 RenameInfos.push_back({Loc, Loc,
300 bool VisitDeclRefExpr(
const DeclRefExpr *Expr) {
301 const NamedDecl *
Decl = Expr->getFoundDecl();
304 if (
auto *UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Decl)) {
305 Decl = UsingShadow->getTargetDecl();
308 auto StartLoc = Expr->getBeginLoc();
311 SourceLocation EndLoc = Expr->hasExplicitTemplateArgs()
312 ? Expr->getLAngleLoc().getLocWithOffset(-1)
315 if (
const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) {
316 if (isInUSRSet(MD)) {
320 RenameInfos.push_back({EndLoc, EndLoc,
334 if (
const auto *
T = llvm::dyn_cast<EnumConstantDecl>(Decl)) {
337 if (!Expr->hasQualifier())
341 llvm::dyn_cast_or_null<EnumDecl>(getClosestAncestorDecl(*
T))) {
356 EndLoc = Expr->getQualifierLoc().getEndLoc().getLocWithOffset(-1);
357 assert(EndLoc.isValid() &&
358 "The enum constant should have prefix qualifers.");
360 if (isInUSRSet(Decl) &&
361 IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
362 RenameInfo Info = {StartLoc,
365 getClosestAncestorDecl(*Expr),
366 Expr->getQualifier(),
368 RenameInfos.push_back(Info);
374 bool VisitUsingDecl(
const UsingDecl *Using) {
375 for (
const auto *UsingShadow :
Using->shadows()) {
376 if (isInUSRSet(UsingShadow->getTargetDecl())) {
377 UsingDecls.push_back(Using);
384 bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
385 TypeLoc TL = NestedLoc.getAsTypeLoc();
389 if (
const auto *TargetDecl = getSupportedDeclFromTypeLoc(TL)) {
390 if (isInUSRSet(TargetDecl)) {
391 RenameInfo Info = {NestedLoc.getBeginLoc(),
392 EndLocationForType(TL),
394 getClosestAncestorDecl(NestedLoc),
397 RenameInfos.push_back(Info);
403 bool VisitTypeLoc(TypeLoc Loc) {
404 auto Parents = Context.getParents(Loc);
405 TypeLoc ParentTypeLoc;
406 if (!Parents.empty()) {
411 if (
const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
412 VisitNestedNameSpecifierLocations(*NSL);
416 if (
const auto *TL = Parents[0].get<TypeLoc>())
422 if (
const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
423 if (isInUSRSet(TargetDecl)) {
434 if (!ParentTypeLoc.isNull() &&
435 isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
438 auto StartLoc = StartLocationForType(Loc);
439 auto EndLoc = EndLocationForType(Loc);
440 if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
441 RenameInfo Info = {StartLoc,
444 getClosestAncestorDecl(Loc),
445 GetNestedNameForType(Loc),
447 RenameInfos.push_back(Info);
454 if (
const auto *TemplateSpecType =
455 dyn_cast<TemplateSpecializationType>(Loc.getType())) {
456 if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
457 auto StartLoc = StartLocationForType(Loc);
458 auto EndLoc = EndLocationForType(Loc);
459 if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
463 TemplateSpecType->getTemplateName().getAsTemplateDecl(),
465 GetNestedNameForType(Loc),
467 RenameInfos.push_back(Info);
475 const std::vector<RenameInfo> &getRenameInfos()
const {
return RenameInfos; }
478 const std::vector<const UsingDecl *> &getUsingDecls()
const {
485 const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
486 if (
const auto* TT = Loc.getType()->getAs<clang::TypedefType>())
487 return TT->getDecl();
488 return Loc.getType()->getAsTagDecl();
492 template <
typename ASTNodeType>
493 const Decl *getClosestAncestorDecl(
const ASTNodeType &Node) {
494 auto Parents = Context.getParents(Node);
496 if (Parents.size() != 1)
499 return Parents[0].template get<Decl>();
500 return getClosestAncestorDecl(Parents[0]);
505 const TypeLoc *getParentTypeLoc(TypeLoc Loc)
const {
506 auto Parents = Context.getParents(Loc);
508 if (Parents.size() != 1)
510 return Parents[0].get<TypeLoc>();
514 bool isInUSRSet(
const Decl *Decl)
const {
518 return llvm::is_contained(USRSet, USR);
521 const std::set<std::string> USRSet;
523 std::vector<RenameInfo> RenameInfos;
526 std::vector<const UsingDecl *> UsingDecls;
534 Visitor.TraverseDecl(
Decl);
535 return Visitor.takeOccurrences();
538std::vector<tooling::AtomicChange>
549 llvm::StringRef
Text) {
551 llvm::Error Err = ReplaceChange.
replace(
554 llvm::errs() <<
"Failed to add replacement to AtomicChange: "
555 << llvm::toString(std::move(Err)) <<
"\n";
561 for (
const auto &RenameInfo : Finder.getRenameInfos()) {
562 std::string ReplacedName = NewName.str();
563 if (RenameInfo.IgnorePrefixQualifiers) {
565 size_t LastColonPos = NewName.find_last_of(
':');
566 if (LastColonPos != std::string::npos)
567 ReplacedName = std::string(NewName.substr(LastColonPos + 1));
569 if (RenameInfo.FromDecl && RenameInfo.Context) {
570 if (!llvm::isa<clang::TranslationUnitDecl>(
571 RenameInfo.Context->getDeclContext())) {
573 RenameInfo.Specifier, RenameInfo.Begin,
574 RenameInfo.Context->getDeclContext(), RenameInfo.FromDecl,
575 NewName.starts_with(
"::") ? NewName.str()
576 : (
"::" + NewName).str());
591 if (ActualName.starts_with(
"::") && !NewName.starts_with(
"::")) {
592 ReplacedName =
"::" + NewName.str();
597 if (NewName.starts_with(
"::") && NewName.substr(2) == ReplacedName)
598 ReplacedName = NewName.str();
600 Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
605 for (
const auto *Using : Finder.getUsingDecls())
606 Replace(Using->getBeginLoc(), Using->getEndLoc(),
"using " + NewName.str());
Defines the clang::ASTContext interface.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
A wrapper class around RecursiveASTVisitor that visits each occurrences of a named symbol.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Methods for determining the USR of a symbol at a location in source code.
Provides functionality for finding all instances of a USR in a given AST.
SourceManager & getSourceManager()
const LangOptions & getLangOpts() const
static constexpr ASTNodeKind getFromNodeKind()
Construct an identifier for T.
static CharSourceRange getTokenRange(SourceRange R)
Decl - This represents one declaration (or definition), e.g.
ASTContext & getASTContext() const LLVM_READONLY
static DynTypedNode create(const T &Node)
Creates a DynTypedNode from Node.
static StringRef getSourceText(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, bool *Invalid=nullptr)
Returns a string for the source that the range encompasses.
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
Encodes a location in the source.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
The top declaration context.
ASTContext & getASTContext() const
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
Diagnostic wrappers for TextAPI types for error reporting.