1 //===--- USRLocFinder.cpp - Clang refactoring library ---------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 /// Methods for finding all instances of a USR. Our strategy is very
11 /// simple; we just compare the USR at every relevant AST node with the one
14 //===----------------------------------------------------------------------===//
16 #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
17 #include "clang/AST/ASTContext.h"
18 #include "clang/AST/ParentMapContext.h"
19 #include "clang/AST/RecursiveASTVisitor.h"
20 #include "clang/Basic/LLVM.h"
21 #include "clang/Basic/SourceLocation.h"
22 #include "clang/Basic/SourceManager.h"
23 #include "clang/Lex/Lexer.h"
24 #include "clang/Tooling/Refactoring/Lookup.h"
25 #include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
26 #include "clang/Tooling/Refactoring/Rename/SymbolName.h"
27 #include "clang/Tooling/Refactoring/Rename/USRFinder.h"
28 #include "llvm/ADT/StringRef.h"
29 #include "llvm/Support/Casting.h"
42 // Returns true if the given Loc is valid for edit. We don't edit the
43 // SourceLocations that are valid or in temporary buffer.
44 bool IsValidEditLoc(const clang::SourceManager
& SM
, clang::SourceLocation Loc
) {
47 const clang::FullSourceLoc
FullLoc(Loc
, SM
);
48 std::pair
<clang::FileID
, unsigned> FileIdAndOffset
=
49 FullLoc
.getSpellingLoc().getDecomposedLoc();
50 return SM
.getFileEntryForID(FileIdAndOffset
.first
) != nullptr;
53 // This visitor recursively searches for all instances of a USR in a
54 // translation unit and stores them for later usage.
55 class USRLocFindingASTVisitor
56 : public RecursiveSymbolVisitor
<USRLocFindingASTVisitor
> {
58 explicit USRLocFindingASTVisitor(const std::vector
<std::string
> &USRs
,
60 const ASTContext
&Context
)
61 : RecursiveSymbolVisitor(Context
.getSourceManager(),
62 Context
.getLangOpts()),
63 USRSet(USRs
.begin(), USRs
.end()), PrevName(PrevName
), Context(Context
) {
66 bool visitSymbolOccurrence(const NamedDecl
*ND
,
67 ArrayRef
<SourceRange
> NameRanges
) {
68 if (USRSet
.find(getUSRForDecl(ND
)) != USRSet
.end()) {
69 assert(NameRanges
.size() == 1 &&
70 "Multiple name pieces are not supported yet!");
71 SourceLocation Loc
= NameRanges
[0].getBegin();
72 const SourceManager
&SM
= Context
.getSourceManager();
73 // TODO: Deal with macro occurrences correctly.
75 Loc
= SM
.getSpellingLoc(Loc
);
76 checkAndAddLocation(Loc
);
83 /// Returns a set of unique symbol occurrences. Duplicate or
84 /// overlapping occurrences are erroneous and should be reported!
85 SymbolOccurrences
takeOccurrences() { return std::move(Occurrences
); }
88 void checkAndAddLocation(SourceLocation Loc
) {
89 const SourceLocation BeginLoc
= Loc
;
90 const SourceLocation EndLoc
= Lexer::getLocForEndOfToken(
91 BeginLoc
, 0, Context
.getSourceManager(), Context
.getLangOpts());
93 Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc
, EndLoc
),
94 Context
.getSourceManager(), Context
.getLangOpts());
95 size_t Offset
= TokenName
.find(PrevName
.getNamePieces()[0]);
97 // The token of the source location we find actually has the old
99 if (Offset
!= StringRef::npos
)
100 Occurrences
.emplace_back(PrevName
, SymbolOccurrence::MatchingSymbol
,
101 BeginLoc
.getLocWithOffset(Offset
));
104 const std::set
<std::string
> USRSet
;
105 const SymbolName PrevName
;
106 SymbolOccurrences Occurrences
;
107 const ASTContext
&Context
;
110 SourceLocation
StartLocationForType(TypeLoc TL
) {
111 // For elaborated types (e.g. `struct a::A`) we want the portion after the
112 // `struct` but including the namespace qualifier, `a::`.
113 if (auto ElaboratedTypeLoc
= TL
.getAs
<clang::ElaboratedTypeLoc
>()) {
114 NestedNameSpecifierLoc NestedNameSpecifier
=
115 ElaboratedTypeLoc
.getQualifierLoc();
116 if (NestedNameSpecifier
.getNestedNameSpecifier())
117 return NestedNameSpecifier
.getBeginLoc();
118 TL
= TL
.getNextTypeLoc();
120 return TL
.getBeginLoc();
123 SourceLocation
EndLocationForType(TypeLoc TL
) {
124 // Dig past any namespace or keyword qualifications.
125 while (TL
.getTypeLocClass() == TypeLoc::Elaborated
||
126 TL
.getTypeLocClass() == TypeLoc::Qualified
)
127 TL
= TL
.getNextTypeLoc();
129 // The location for template specializations (e.g. Foo<int>) includes the
130 // templated types in its location range. We want to restrict this to just
131 // before the `<` character.
132 if (TL
.getTypeLocClass() == TypeLoc::TemplateSpecialization
) {
133 return TL
.castAs
<TemplateSpecializationTypeLoc
>()
135 .getLocWithOffset(-1);
137 return TL
.getEndLoc();
140 NestedNameSpecifier
*GetNestedNameForType(TypeLoc TL
) {
141 // Dig past any keyword qualifications.
142 while (TL
.getTypeLocClass() == TypeLoc::Qualified
)
143 TL
= TL
.getNextTypeLoc();
145 // For elaborated types (e.g. `struct a::A`) we want the portion after the
146 // `struct` but including the namespace qualifier, `a::`.
147 if (auto ElaboratedTypeLoc
= TL
.getAs
<clang::ElaboratedTypeLoc
>())
148 return ElaboratedTypeLoc
.getQualifierLoc().getNestedNameSpecifier();
152 // Find all locations identified by the given USRs for rename.
154 // This class will traverse the AST and find every AST node whose USR is in the
156 class RenameLocFinder
: public RecursiveASTVisitor
<RenameLocFinder
> {
158 RenameLocFinder(llvm::ArrayRef
<std::string
> USRs
, ASTContext
&Context
)
159 : USRSet(USRs
.begin(), USRs
.end()), Context(Context
) {}
161 // A structure records all information of a symbol reference being renamed.
162 // We try to add as few prefix qualifiers as possible.
164 // The begin location of a symbol being renamed.
165 SourceLocation Begin
;
166 // The end location of a symbol being renamed.
168 // The declaration of a symbol being renamed (can be nullptr).
169 const NamedDecl
*FromDecl
;
170 // The declaration in which the nested name is contained (can be nullptr).
172 // The nested name being replaced (can be nullptr).
173 const NestedNameSpecifier
*Specifier
;
174 // Determine whether the prefix qualifiers of the NewName should be ignored.
175 // Normally, we set it to true for the symbol declaration and definition to
176 // avoid adding prefix qualifiers.
177 // For example, if it is true and NewName is "a::b::foo", then the symbol
178 // occurrence which the RenameInfo points to will be renamed to "foo".
179 bool IgnorePrefixQualifers
;
182 bool VisitNamedDecl(const NamedDecl
*Decl
) {
183 // UsingDecl has been handled in other place.
184 if (llvm::isa
<UsingDecl
>(Decl
))
187 // DestructorDecl has been handled in Typeloc.
188 if (llvm::isa
<CXXDestructorDecl
>(Decl
))
191 if (Decl
->isImplicit())
194 if (isInUSRSet(Decl
)) {
195 // For the case of renaming an alias template, we actually rename the
196 // underlying alias declaration of the template.
197 if (const auto* TAT
= dyn_cast
<TypeAliasTemplateDecl
>(Decl
))
198 Decl
= TAT
->getTemplatedDecl();
200 auto StartLoc
= Decl
->getLocation();
201 auto EndLoc
= StartLoc
;
202 if (IsValidEditLoc(Context
.getSourceManager(), StartLoc
)) {
203 RenameInfo Info
= {StartLoc
,
205 /*FromDecl=*/nullptr,
207 /*Specifier=*/nullptr,
208 /*IgnorePrefixQualifers=*/true};
209 RenameInfos
.push_back(Info
);
215 bool VisitMemberExpr(const MemberExpr
*Expr
) {
216 const NamedDecl
*Decl
= Expr
->getFoundDecl();
217 auto StartLoc
= Expr
->getMemberLoc();
218 auto EndLoc
= Expr
->getMemberLoc();
219 if (isInUSRSet(Decl
)) {
220 RenameInfos
.push_back({StartLoc
, EndLoc
,
221 /*FromDecl=*/nullptr,
223 /*Specifier=*/nullptr,
224 /*IgnorePrefixQualifiers=*/true});
229 bool VisitDesignatedInitExpr(const DesignatedInitExpr
*E
) {
230 for (const DesignatedInitExpr::Designator
&D
: E
->designators()) {
231 if (D
.isFieldDesignator()) {
232 if (const FieldDecl
*Decl
= D
.getFieldDecl()) {
233 if (isInUSRSet(Decl
)) {
234 auto StartLoc
= D
.getFieldLoc();
235 auto EndLoc
= D
.getFieldLoc();
236 RenameInfos
.push_back({StartLoc
, EndLoc
,
237 /*FromDecl=*/nullptr,
239 /*Specifier=*/nullptr,
240 /*IgnorePrefixQualifiers=*/true});
248 bool VisitCXXConstructorDecl(const CXXConstructorDecl
*CD
) {
249 // Fix the constructor initializer when renaming class members.
250 for (const auto *Initializer
: CD
->inits()) {
251 // Ignore implicit initializers.
252 if (!Initializer
->isWritten())
255 if (const FieldDecl
*FD
= Initializer
->getMember()) {
256 if (isInUSRSet(FD
)) {
257 auto Loc
= Initializer
->getSourceLocation();
258 RenameInfos
.push_back({Loc
, Loc
,
259 /*FromDecl=*/nullptr,
261 /*Specifier=*/nullptr,
262 /*IgnorePrefixQualifiers=*/true});
269 bool VisitDeclRefExpr(const DeclRefExpr
*Expr
) {
270 const NamedDecl
*Decl
= Expr
->getFoundDecl();
271 // Get the underlying declaration of the shadow declaration introduced by a
272 // using declaration.
273 if (auto *UsingShadow
= llvm::dyn_cast
<UsingShadowDecl
>(Decl
)) {
274 Decl
= UsingShadow
->getTargetDecl();
277 auto StartLoc
= Expr
->getBeginLoc();
278 // For template function call expressions like `foo<int>()`, we want to
279 // restrict the end of location to just before the `<` character.
280 SourceLocation EndLoc
= Expr
->hasExplicitTemplateArgs()
281 ? Expr
->getLAngleLoc().getLocWithOffset(-1)
284 if (const auto *MD
= llvm::dyn_cast
<CXXMethodDecl
>(Decl
)) {
285 if (isInUSRSet(MD
)) {
286 // Handle renaming static template class methods, we only rename the
287 // name without prefix qualifiers and restrict the source range to the
289 RenameInfos
.push_back({EndLoc
, EndLoc
,
290 /*FromDecl=*/nullptr,
292 /*Specifier=*/nullptr,
293 /*IgnorePrefixQualifiers=*/true});
298 // In case of renaming an enum declaration, we have to explicitly handle
299 // unscoped enum constants referenced in expressions (e.g.
300 // "auto r = ns1::ns2::Green" where Green is an enum constant of an unscoped
301 // enum decl "ns1::ns2::Color") as these enum constants cannot be caught by
303 if (const auto *T
= llvm::dyn_cast
<EnumConstantDecl
>(Decl
)) {
304 // FIXME: Handle the enum constant without prefix qualifiers (`a = Green`)
305 // when renaming an unscoped enum declaration with a new namespace.
306 if (!Expr
->hasQualifier())
310 llvm::dyn_cast_or_null
<EnumDecl
>(getClosestAncestorDecl(*T
))) {
315 // The current fix would qualify "ns1::ns2::Green" as
316 // "ns1::ns2::Color::Green".
318 // Get the EndLoc of the replacement by moving 1 character backward (
319 // to exclude the last '::').
323 // BeginLoc |EndLoc of the qualifier
325 EndLoc
= Expr
->getQualifierLoc().getEndLoc().getLocWithOffset(-1);
326 assert(EndLoc
.isValid() &&
327 "The enum constant should have prefix qualifers.");
329 if (isInUSRSet(Decl
) &&
330 IsValidEditLoc(Context
.getSourceManager(), StartLoc
)) {
331 RenameInfo Info
= {StartLoc
,
334 getClosestAncestorDecl(*Expr
),
335 Expr
->getQualifier(),
336 /*IgnorePrefixQualifers=*/false};
337 RenameInfos
.push_back(Info
);
343 bool VisitUsingDecl(const UsingDecl
*Using
) {
344 for (const auto *UsingShadow
: Using
->shadows()) {
345 if (isInUSRSet(UsingShadow
->getTargetDecl())) {
346 UsingDecls
.push_back(Using
);
353 bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc
) {
354 if (!NestedLoc
.getNestedNameSpecifier()->getAsType())
357 if (const auto *TargetDecl
=
358 getSupportedDeclFromTypeLoc(NestedLoc
.getTypeLoc())) {
359 if (isInUSRSet(TargetDecl
)) {
360 RenameInfo Info
= {NestedLoc
.getBeginLoc(),
361 EndLocationForType(NestedLoc
.getTypeLoc()),
363 getClosestAncestorDecl(NestedLoc
),
364 NestedLoc
.getNestedNameSpecifier()->getPrefix(),
365 /*IgnorePrefixQualifers=*/false};
366 RenameInfos
.push_back(Info
);
372 bool VisitTypeLoc(TypeLoc Loc
) {
373 auto Parents
= Context
.getParents(Loc
);
374 TypeLoc ParentTypeLoc
;
375 if (!Parents
.empty()) {
376 // Handle cases of nested name specificier locations.
378 // The VisitNestedNameSpecifierLoc interface is not impelmented in
379 // RecursiveASTVisitor, we have to handle it explicitly.
380 if (const auto *NSL
= Parents
[0].get
<NestedNameSpecifierLoc
>()) {
381 VisitNestedNameSpecifierLocations(*NSL
);
385 if (const auto *TL
= Parents
[0].get
<TypeLoc
>())
389 // Handle the outermost TypeLoc which is directly linked to the interesting
390 // declaration and don't handle nested name specifier locations.
391 if (const auto *TargetDecl
= getSupportedDeclFromTypeLoc(Loc
)) {
392 if (isInUSRSet(TargetDecl
)) {
393 // Only handle the outermost typeLoc.
395 // For a type like "a::Foo", there will be two typeLocs for it.
396 // One ElaboratedType, the other is RecordType:
398 // ElaboratedType 0x33b9390 'a::Foo' sugar
399 // `-RecordType 0x338fef0 'class a::Foo'
400 // `-CXXRecord 0x338fe58 'Foo'
402 // Skip if this is an inner typeLoc.
403 if (!ParentTypeLoc
.isNull() &&
404 isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc
)))
407 auto StartLoc
= StartLocationForType(Loc
);
408 auto EndLoc
= EndLocationForType(Loc
);
409 if (IsValidEditLoc(Context
.getSourceManager(), StartLoc
)) {
410 RenameInfo Info
= {StartLoc
,
413 getClosestAncestorDecl(Loc
),
414 GetNestedNameForType(Loc
),
415 /*IgnorePrefixQualifers=*/false};
416 RenameInfos
.push_back(Info
);
422 // Handle specific template class specialiation cases.
423 if (const auto *TemplateSpecType
=
424 dyn_cast
<TemplateSpecializationType
>(Loc
.getType())) {
425 TypeLoc TargetLoc
= Loc
;
426 if (!ParentTypeLoc
.isNull()) {
427 if (llvm::isa
<ElaboratedType
>(ParentTypeLoc
.getType()))
428 TargetLoc
= ParentTypeLoc
;
431 if (isInUSRSet(TemplateSpecType
->getTemplateName().getAsTemplateDecl())) {
432 TypeLoc TargetLoc
= Loc
;
433 // FIXME: Find a better way to handle this case.
434 // For the qualified template class specification type like
435 // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
436 // (ElaboratedType) of the TemplateSpecializationType in order to
437 // catch the prefix qualifiers "ns::".
438 if (!ParentTypeLoc
.isNull() &&
439 llvm::isa
<ElaboratedType
>(ParentTypeLoc
.getType()))
440 TargetLoc
= ParentTypeLoc
;
442 auto StartLoc
= StartLocationForType(TargetLoc
);
443 auto EndLoc
= EndLocationForType(TargetLoc
);
444 if (IsValidEditLoc(Context
.getSourceManager(), StartLoc
)) {
448 TemplateSpecType
->getTemplateName().getAsTemplateDecl(),
449 getClosestAncestorDecl(DynTypedNode::create(TargetLoc
)),
450 GetNestedNameForType(TargetLoc
),
451 /*IgnorePrefixQualifers=*/false};
452 RenameInfos
.push_back(Info
);
459 // Returns a list of RenameInfo.
460 const std::vector
<RenameInfo
> &getRenameInfos() const { return RenameInfos
; }
462 // Returns a list of using declarations which are needed to update.
463 const std::vector
<const UsingDecl
*> &getUsingDecls() const {
468 // Get the supported declaration from a given typeLoc. If the declaration type
469 // is not supported, returns nullptr.
470 const NamedDecl
*getSupportedDeclFromTypeLoc(TypeLoc Loc
) {
471 if (const auto* TT
= Loc
.getType()->getAs
<clang::TypedefType
>())
472 return TT
->getDecl();
473 if (const auto *RD
= Loc
.getType()->getAsCXXRecordDecl())
476 llvm::dyn_cast_or_null
<EnumDecl
>(Loc
.getType()->getAsTagDecl()))
481 // Get the closest ancester which is a declaration of a given AST node.
482 template <typename ASTNodeType
>
483 const Decl
*getClosestAncestorDecl(const ASTNodeType
&Node
) {
484 auto Parents
= Context
.getParents(Node
);
485 // FIXME: figure out how to handle it when there are multiple parents.
486 if (Parents
.size() != 1)
488 if (ASTNodeKind::getFromNodeKind
<Decl
>().isBaseOf(Parents
[0].getNodeKind()))
489 return Parents
[0].template get
<Decl
>();
490 return getClosestAncestorDecl(Parents
[0]);
493 // Get the parent typeLoc of a given typeLoc. If there is no such parent,
495 const TypeLoc
*getParentTypeLoc(TypeLoc Loc
) const {
496 auto Parents
= Context
.getParents(Loc
);
497 // FIXME: figure out how to handle it when there are multiple parents.
498 if (Parents
.size() != 1)
500 return Parents
[0].get
<TypeLoc
>();
503 // Check whether the USR of a given Decl is in the USRSet.
504 bool isInUSRSet(const Decl
*Decl
) const {
505 auto USR
= getUSRForDecl(Decl
);
508 return llvm::is_contained(USRSet
, USR
);
511 const std::set
<std::string
> USRSet
;
513 std::vector
<RenameInfo
> RenameInfos
;
514 // Record all interested using declarations which contains the using-shadow
515 // declarations of the symbol declarations being renamed.
516 std::vector
<const UsingDecl
*> UsingDecls
;
521 SymbolOccurrences
getOccurrencesOfUSRs(ArrayRef
<std::string
> USRs
,
522 StringRef PrevName
, Decl
*Decl
) {
523 USRLocFindingASTVisitor
Visitor(USRs
, PrevName
, Decl
->getASTContext());
524 Visitor
.TraverseDecl(Decl
);
525 return Visitor
.takeOccurrences();
528 std::vector
<tooling::AtomicChange
>
529 createRenameAtomicChanges(llvm::ArrayRef
<std::string
> USRs
,
530 llvm::StringRef NewName
, Decl
*TranslationUnitDecl
) {
531 RenameLocFinder
Finder(USRs
, TranslationUnitDecl
->getASTContext());
532 Finder
.TraverseDecl(TranslationUnitDecl
);
534 const SourceManager
&SM
=
535 TranslationUnitDecl
->getASTContext().getSourceManager();
537 std::vector
<tooling::AtomicChange
> AtomicChanges
;
538 auto Replace
= [&](SourceLocation Start
, SourceLocation End
,
539 llvm::StringRef Text
) {
540 tooling::AtomicChange ReplaceChange
= tooling::AtomicChange(SM
, Start
);
541 llvm::Error Err
= ReplaceChange
.replace(
542 SM
, CharSourceRange::getTokenRange(Start
, End
), Text
);
544 llvm::errs() << "Failed to add replacement to AtomicChange: "
545 << llvm::toString(std::move(Err
)) << "\n";
548 AtomicChanges
.push_back(std::move(ReplaceChange
));
551 for (const auto &RenameInfo
: Finder
.getRenameInfos()) {
552 std::string ReplacedName
= NewName
.str();
553 if (RenameInfo
.IgnorePrefixQualifers
) {
554 // Get the name without prefix qualifiers from NewName.
555 size_t LastColonPos
= NewName
.find_last_of(':');
556 if (LastColonPos
!= std::string::npos
)
557 ReplacedName
= std::string(NewName
.substr(LastColonPos
+ 1));
559 if (RenameInfo
.FromDecl
&& RenameInfo
.Context
) {
560 if (!llvm::isa
<clang::TranslationUnitDecl
>(
561 RenameInfo
.Context
->getDeclContext())) {
562 ReplacedName
= tooling::replaceNestedName(
563 RenameInfo
.Specifier
, RenameInfo
.Begin
,
564 RenameInfo
.Context
->getDeclContext(), RenameInfo
.FromDecl
,
565 NewName
.starts_with("::") ? NewName
.str()
566 : ("::" + NewName
).str());
568 // This fixes the case where type `T` is a parameter inside a function
569 // type (e.g. `std::function<void(T)>`) and the DeclContext of `T`
570 // becomes the translation unit. As a workaround, we simply use
571 // fully-qualified name here for all references whose `DeclContext` is
572 // the translation unit and ignore the possible existence of
573 // using-decls (in the global scope) that can shorten the replaced
575 llvm::StringRef ActualName
= Lexer::getSourceText(
576 CharSourceRange::getTokenRange(
577 SourceRange(RenameInfo
.Begin
, RenameInfo
.End
)),
578 SM
, TranslationUnitDecl
->getASTContext().getLangOpts());
579 // Add the leading "::" back if the name written in the code contains
581 if (ActualName
.starts_with("::") && !NewName
.starts_with("::")) {
582 ReplacedName
= "::" + NewName
.str();
586 // If the NewName contains leading "::", add it back.
587 if (NewName
.starts_with("::") && NewName
.substr(2) == ReplacedName
)
588 ReplacedName
= NewName
.str();
590 Replace(RenameInfo
.Begin
, RenameInfo
.End
, ReplacedName
);
593 // Hanlde using declarations explicitly as "using a::Foo" don't trigger
594 // typeLoc for "a::Foo".
595 for (const auto *Using
: Finder
.getUsingDecls())
596 Replace(Using
->getBeginLoc(), Using
->getEndLoc(), "using " + NewName
.str());
598 return AtomicChanges
;
601 } // end namespace tooling
602 } // end namespace clang