10b57cec5SDimitry Andric //===--- USRLocFinder.cpp - Clang refactoring library ---------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric ///
90b57cec5SDimitry Andric /// \file
100b57cec5SDimitry Andric /// Methods for finding all instances of a USR. Our strategy is very
110b57cec5SDimitry Andric /// simple; we just compare the USR at every relevant AST node with the one
120b57cec5SDimitry Andric /// provided.
130b57cec5SDimitry Andric ///
140b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
150b57cec5SDimitry Andric
160b57cec5SDimitry Andric #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
170b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
185ffd83dbSDimitry Andric #include "clang/AST/ParentMapContext.h"
190b57cec5SDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
200b57cec5SDimitry Andric #include "clang/Basic/LLVM.h"
210b57cec5SDimitry Andric #include "clang/Basic/SourceLocation.h"
220b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h"
230b57cec5SDimitry Andric #include "clang/Lex/Lexer.h"
24e8d8bef9SDimitry Andric #include "clang/Tooling/Refactoring/Lookup.h"
250b57cec5SDimitry Andric #include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
260b57cec5SDimitry Andric #include "clang/Tooling/Refactoring/Rename/SymbolName.h"
270b57cec5SDimitry Andric #include "clang/Tooling/Refactoring/Rename/USRFinder.h"
280b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
290b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
300b57cec5SDimitry Andric #include <cstddef>
310b57cec5SDimitry Andric #include <set>
320b57cec5SDimitry Andric #include <string>
330b57cec5SDimitry Andric #include <vector>
340b57cec5SDimitry Andric
350b57cec5SDimitry Andric using namespace llvm;
360b57cec5SDimitry Andric
370b57cec5SDimitry Andric namespace clang {
380b57cec5SDimitry Andric namespace tooling {
390b57cec5SDimitry Andric
400b57cec5SDimitry Andric namespace {
410b57cec5SDimitry Andric
420b57cec5SDimitry Andric // Returns true if the given Loc is valid for edit. We don't edit the
430b57cec5SDimitry Andric // SourceLocations that are valid or in temporary buffer.
IsValidEditLoc(const clang::SourceManager & SM,clang::SourceLocation Loc)440b57cec5SDimitry Andric bool IsValidEditLoc(const clang::SourceManager& SM, clang::SourceLocation Loc) {
450b57cec5SDimitry Andric if (Loc.isInvalid())
460b57cec5SDimitry Andric return false;
470b57cec5SDimitry Andric const clang::FullSourceLoc FullLoc(Loc, SM);
480b57cec5SDimitry Andric std::pair<clang::FileID, unsigned> FileIdAndOffset =
490b57cec5SDimitry Andric FullLoc.getSpellingLoc().getDecomposedLoc();
500b57cec5SDimitry Andric return SM.getFileEntryForID(FileIdAndOffset.first) != nullptr;
510b57cec5SDimitry Andric }
520b57cec5SDimitry Andric
530b57cec5SDimitry Andric // This visitor recursively searches for all instances of a USR in a
540b57cec5SDimitry Andric // translation unit and stores them for later usage.
550b57cec5SDimitry Andric class USRLocFindingASTVisitor
560b57cec5SDimitry Andric : public RecursiveSymbolVisitor<USRLocFindingASTVisitor> {
570b57cec5SDimitry Andric public:
USRLocFindingASTVisitor(const std::vector<std::string> & USRs,StringRef PrevName,const ASTContext & Context)580b57cec5SDimitry Andric explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
590b57cec5SDimitry Andric StringRef PrevName,
600b57cec5SDimitry Andric const ASTContext &Context)
610b57cec5SDimitry Andric : RecursiveSymbolVisitor(Context.getSourceManager(),
620b57cec5SDimitry Andric Context.getLangOpts()),
630b57cec5SDimitry Andric USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
640b57cec5SDimitry Andric }
650b57cec5SDimitry Andric
visitSymbolOccurrence(const NamedDecl * ND,ArrayRef<SourceRange> NameRanges)660b57cec5SDimitry Andric bool visitSymbolOccurrence(const NamedDecl *ND,
670b57cec5SDimitry Andric ArrayRef<SourceRange> NameRanges) {
680b57cec5SDimitry Andric if (USRSet.find(getUSRForDecl(ND)) != USRSet.end()) {
690b57cec5SDimitry Andric assert(NameRanges.size() == 1 &&
700b57cec5SDimitry Andric "Multiple name pieces are not supported yet!");
710b57cec5SDimitry Andric SourceLocation Loc = NameRanges[0].getBegin();
720b57cec5SDimitry Andric const SourceManager &SM = Context.getSourceManager();
730b57cec5SDimitry Andric // TODO: Deal with macro occurrences correctly.
740b57cec5SDimitry Andric if (Loc.isMacroID())
750b57cec5SDimitry Andric Loc = SM.getSpellingLoc(Loc);
760b57cec5SDimitry Andric checkAndAddLocation(Loc);
770b57cec5SDimitry Andric }
780b57cec5SDimitry Andric return true;
790b57cec5SDimitry Andric }
800b57cec5SDimitry Andric
810b57cec5SDimitry Andric // Non-visitors:
820b57cec5SDimitry Andric
830b57cec5SDimitry Andric /// Returns a set of unique symbol occurrences. Duplicate or
840b57cec5SDimitry Andric /// overlapping occurrences are erroneous and should be reported!
takeOccurrences()850b57cec5SDimitry Andric SymbolOccurrences takeOccurrences() { return std::move(Occurrences); }
860b57cec5SDimitry Andric
870b57cec5SDimitry Andric private:
checkAndAddLocation(SourceLocation Loc)880b57cec5SDimitry Andric void checkAndAddLocation(SourceLocation Loc) {
890b57cec5SDimitry Andric const SourceLocation BeginLoc = Loc;
900b57cec5SDimitry Andric const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
910b57cec5SDimitry Andric BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
920b57cec5SDimitry Andric StringRef TokenName =
930b57cec5SDimitry Andric Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
940b57cec5SDimitry Andric Context.getSourceManager(), Context.getLangOpts());
950b57cec5SDimitry Andric size_t Offset = TokenName.find(PrevName.getNamePieces()[0]);
960b57cec5SDimitry Andric
970b57cec5SDimitry Andric // The token of the source location we find actually has the old
980b57cec5SDimitry Andric // name.
990b57cec5SDimitry Andric if (Offset != StringRef::npos)
1000b57cec5SDimitry Andric Occurrences.emplace_back(PrevName, SymbolOccurrence::MatchingSymbol,
1010b57cec5SDimitry Andric BeginLoc.getLocWithOffset(Offset));
1020b57cec5SDimitry Andric }
1030b57cec5SDimitry Andric
1040b57cec5SDimitry Andric const std::set<std::string> USRSet;
1050b57cec5SDimitry Andric const SymbolName PrevName;
1060b57cec5SDimitry Andric SymbolOccurrences Occurrences;
1070b57cec5SDimitry Andric const ASTContext &Context;
1080b57cec5SDimitry Andric };
1090b57cec5SDimitry Andric
StartLocationForType(TypeLoc TL)1100b57cec5SDimitry Andric SourceLocation StartLocationForType(TypeLoc TL) {
1110b57cec5SDimitry Andric // For elaborated types (e.g. `struct a::A`) we want the portion after the
1120b57cec5SDimitry Andric // `struct` but including the namespace qualifier, `a::`.
1130b57cec5SDimitry Andric if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) {
1140b57cec5SDimitry Andric NestedNameSpecifierLoc NestedNameSpecifier =
1150b57cec5SDimitry Andric ElaboratedTypeLoc.getQualifierLoc();
1160b57cec5SDimitry Andric if (NestedNameSpecifier.getNestedNameSpecifier())
1170b57cec5SDimitry Andric return NestedNameSpecifier.getBeginLoc();
1180b57cec5SDimitry Andric TL = TL.getNextTypeLoc();
1190b57cec5SDimitry Andric }
1200b57cec5SDimitry Andric return TL.getBeginLoc();
1210b57cec5SDimitry Andric }
1220b57cec5SDimitry Andric
EndLocationForType(TypeLoc TL)1230b57cec5SDimitry Andric SourceLocation EndLocationForType(TypeLoc TL) {
1240b57cec5SDimitry Andric // Dig past any namespace or keyword qualifications.
1250b57cec5SDimitry Andric while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
1260b57cec5SDimitry Andric TL.getTypeLocClass() == TypeLoc::Qualified)
1270b57cec5SDimitry Andric TL = TL.getNextTypeLoc();
1280b57cec5SDimitry Andric
1290b57cec5SDimitry Andric // The location for template specializations (e.g. Foo<int>) includes the
1300b57cec5SDimitry Andric // templated types in its location range. We want to restrict this to just
1310b57cec5SDimitry Andric // before the `<` character.
1320b57cec5SDimitry Andric if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
1330b57cec5SDimitry Andric return TL.castAs<TemplateSpecializationTypeLoc>()
1340b57cec5SDimitry Andric .getLAngleLoc()
1350b57cec5SDimitry Andric .getLocWithOffset(-1);
1360b57cec5SDimitry Andric }
1370b57cec5SDimitry Andric return TL.getEndLoc();
1380b57cec5SDimitry Andric }
1390b57cec5SDimitry Andric
GetNestedNameForType(TypeLoc TL)1400b57cec5SDimitry Andric NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
1410b57cec5SDimitry Andric // Dig past any keyword qualifications.
1420b57cec5SDimitry Andric while (TL.getTypeLocClass() == TypeLoc::Qualified)
1430b57cec5SDimitry Andric TL = TL.getNextTypeLoc();
1440b57cec5SDimitry Andric
1450b57cec5SDimitry Andric // For elaborated types (e.g. `struct a::A`) we want the portion after the
1460b57cec5SDimitry Andric // `struct` but including the namespace qualifier, `a::`.
1470b57cec5SDimitry Andric if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
1480b57cec5SDimitry Andric return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
1490b57cec5SDimitry Andric return nullptr;
1500b57cec5SDimitry Andric }
1510b57cec5SDimitry Andric
1520b57cec5SDimitry Andric // Find all locations identified by the given USRs for rename.
1530b57cec5SDimitry Andric //
1540b57cec5SDimitry Andric // This class will traverse the AST and find every AST node whose USR is in the
1550b57cec5SDimitry Andric // given USRs' set.
1560b57cec5SDimitry Andric class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
1570b57cec5SDimitry Andric public:
RenameLocFinder(llvm::ArrayRef<std::string> USRs,ASTContext & Context)1580b57cec5SDimitry Andric RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
1590b57cec5SDimitry Andric : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
1600b57cec5SDimitry Andric
1610b57cec5SDimitry Andric // A structure records all information of a symbol reference being renamed.
1620b57cec5SDimitry Andric // We try to add as few prefix qualifiers as possible.
1630b57cec5SDimitry Andric struct RenameInfo {
1640b57cec5SDimitry Andric // The begin location of a symbol being renamed.
1650b57cec5SDimitry Andric SourceLocation Begin;
1660b57cec5SDimitry Andric // The end location of a symbol being renamed.
1670b57cec5SDimitry Andric SourceLocation End;
1680b57cec5SDimitry Andric // The declaration of a symbol being renamed (can be nullptr).
1690b57cec5SDimitry Andric const NamedDecl *FromDecl;
1700b57cec5SDimitry Andric // The declaration in which the nested name is contained (can be nullptr).
1710b57cec5SDimitry Andric const Decl *Context;
1720b57cec5SDimitry Andric // The nested name being replaced (can be nullptr).
1730b57cec5SDimitry Andric const NestedNameSpecifier *Specifier;
1740b57cec5SDimitry Andric // Determine whether the prefix qualifiers of the NewName should be ignored.
1750b57cec5SDimitry Andric // Normally, we set it to true for the symbol declaration and definition to
1760b57cec5SDimitry Andric // avoid adding prefix qualifiers.
1770b57cec5SDimitry Andric // For example, if it is true and NewName is "a::b::foo", then the symbol
1780b57cec5SDimitry Andric // occurrence which the RenameInfo points to will be renamed to "foo".
1790b57cec5SDimitry Andric bool IgnorePrefixQualifers;
1800b57cec5SDimitry Andric };
1810b57cec5SDimitry Andric
VisitNamedDecl(const NamedDecl * Decl)1820b57cec5SDimitry Andric bool VisitNamedDecl(const NamedDecl *Decl) {
1830b57cec5SDimitry Andric // UsingDecl has been handled in other place.
1840b57cec5SDimitry Andric if (llvm::isa<UsingDecl>(Decl))
1850b57cec5SDimitry Andric return true;
1860b57cec5SDimitry Andric
1870b57cec5SDimitry Andric // DestructorDecl has been handled in Typeloc.
1880b57cec5SDimitry Andric if (llvm::isa<CXXDestructorDecl>(Decl))
1890b57cec5SDimitry Andric return true;
1900b57cec5SDimitry Andric
1910b57cec5SDimitry Andric if (Decl->isImplicit())
1920b57cec5SDimitry Andric return true;
1930b57cec5SDimitry Andric
1940b57cec5SDimitry Andric if (isInUSRSet(Decl)) {
1950b57cec5SDimitry Andric // For the case of renaming an alias template, we actually rename the
1960b57cec5SDimitry Andric // underlying alias declaration of the template.
1970b57cec5SDimitry Andric if (const auto* TAT = dyn_cast<TypeAliasTemplateDecl>(Decl))
1980b57cec5SDimitry Andric Decl = TAT->getTemplatedDecl();
1990b57cec5SDimitry Andric
2000b57cec5SDimitry Andric auto StartLoc = Decl->getLocation();
2010b57cec5SDimitry Andric auto EndLoc = StartLoc;
2020b57cec5SDimitry Andric if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
2030b57cec5SDimitry Andric RenameInfo Info = {StartLoc,
2040b57cec5SDimitry Andric EndLoc,
2050b57cec5SDimitry Andric /*FromDecl=*/nullptr,
2060b57cec5SDimitry Andric /*Context=*/nullptr,
2070b57cec5SDimitry Andric /*Specifier=*/nullptr,
2080b57cec5SDimitry Andric /*IgnorePrefixQualifers=*/true};
2090b57cec5SDimitry Andric RenameInfos.push_back(Info);
2100b57cec5SDimitry Andric }
2110b57cec5SDimitry Andric }
2120b57cec5SDimitry Andric return true;
2130b57cec5SDimitry Andric }
2140b57cec5SDimitry Andric
VisitMemberExpr(const MemberExpr * Expr)2150b57cec5SDimitry Andric bool VisitMemberExpr(const MemberExpr *Expr) {
2160b57cec5SDimitry Andric const NamedDecl *Decl = Expr->getFoundDecl();
2170b57cec5SDimitry Andric auto StartLoc = Expr->getMemberLoc();
2180b57cec5SDimitry Andric auto EndLoc = Expr->getMemberLoc();
2190b57cec5SDimitry Andric if (isInUSRSet(Decl)) {
2200b57cec5SDimitry Andric RenameInfos.push_back({StartLoc, EndLoc,
2210b57cec5SDimitry Andric /*FromDecl=*/nullptr,
2220b57cec5SDimitry Andric /*Context=*/nullptr,
2230b57cec5SDimitry Andric /*Specifier=*/nullptr,
2240b57cec5SDimitry Andric /*IgnorePrefixQualifiers=*/true});
2250b57cec5SDimitry Andric }
2260b57cec5SDimitry Andric return true;
2270b57cec5SDimitry Andric }
2280b57cec5SDimitry Andric
VisitDesignatedInitExpr(const DesignatedInitExpr * E)229fe6060f1SDimitry Andric bool VisitDesignatedInitExpr(const DesignatedInitExpr *E) {
230fe6060f1SDimitry Andric for (const DesignatedInitExpr::Designator &D : E->designators()) {
23106c3fb27SDimitry Andric if (D.isFieldDesignator()) {
23206c3fb27SDimitry Andric if (const FieldDecl *Decl = D.getFieldDecl()) {
233fe6060f1SDimitry Andric if (isInUSRSet(Decl)) {
234fe6060f1SDimitry Andric auto StartLoc = D.getFieldLoc();
235fe6060f1SDimitry Andric auto EndLoc = D.getFieldLoc();
236fe6060f1SDimitry Andric RenameInfos.push_back({StartLoc, EndLoc,
237fe6060f1SDimitry Andric /*FromDecl=*/nullptr,
238fe6060f1SDimitry Andric /*Context=*/nullptr,
239fe6060f1SDimitry Andric /*Specifier=*/nullptr,
240fe6060f1SDimitry Andric /*IgnorePrefixQualifiers=*/true});
241fe6060f1SDimitry Andric }
242fe6060f1SDimitry Andric }
243fe6060f1SDimitry Andric }
24406c3fb27SDimitry Andric }
245fe6060f1SDimitry Andric return true;
246fe6060f1SDimitry Andric }
247fe6060f1SDimitry Andric
VisitCXXConstructorDecl(const CXXConstructorDecl * CD)2480b57cec5SDimitry Andric bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) {
2490b57cec5SDimitry Andric // Fix the constructor initializer when renaming class members.
2500b57cec5SDimitry Andric for (const auto *Initializer : CD->inits()) {
2510b57cec5SDimitry Andric // Ignore implicit initializers.
2520b57cec5SDimitry Andric if (!Initializer->isWritten())
2530b57cec5SDimitry Andric continue;
2540b57cec5SDimitry Andric
2550b57cec5SDimitry Andric if (const FieldDecl *FD = Initializer->getMember()) {
2560b57cec5SDimitry Andric if (isInUSRSet(FD)) {
2570b57cec5SDimitry Andric auto Loc = Initializer->getSourceLocation();
2580b57cec5SDimitry Andric RenameInfos.push_back({Loc, Loc,
2590b57cec5SDimitry Andric /*FromDecl=*/nullptr,
2600b57cec5SDimitry Andric /*Context=*/nullptr,
2610b57cec5SDimitry Andric /*Specifier=*/nullptr,
2620b57cec5SDimitry Andric /*IgnorePrefixQualifiers=*/true});
2630b57cec5SDimitry Andric }
2640b57cec5SDimitry Andric }
2650b57cec5SDimitry Andric }
2660b57cec5SDimitry Andric return true;
2670b57cec5SDimitry Andric }
2680b57cec5SDimitry Andric
VisitDeclRefExpr(const DeclRefExpr * Expr)2690b57cec5SDimitry Andric bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
2700b57cec5SDimitry Andric const NamedDecl *Decl = Expr->getFoundDecl();
2710b57cec5SDimitry Andric // Get the underlying declaration of the shadow declaration introduced by a
2720b57cec5SDimitry Andric // using declaration.
2730b57cec5SDimitry Andric if (auto *UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Decl)) {
2740b57cec5SDimitry Andric Decl = UsingShadow->getTargetDecl();
2750b57cec5SDimitry Andric }
2760b57cec5SDimitry Andric
2770b57cec5SDimitry Andric auto StartLoc = Expr->getBeginLoc();
2780b57cec5SDimitry Andric // For template function call expressions like `foo<int>()`, we want to
2790b57cec5SDimitry Andric // restrict the end of location to just before the `<` character.
2800b57cec5SDimitry Andric SourceLocation EndLoc = Expr->hasExplicitTemplateArgs()
2810b57cec5SDimitry Andric ? Expr->getLAngleLoc().getLocWithOffset(-1)
2820b57cec5SDimitry Andric : Expr->getEndLoc();
2830b57cec5SDimitry Andric
2840b57cec5SDimitry Andric if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) {
2850b57cec5SDimitry Andric if (isInUSRSet(MD)) {
2860b57cec5SDimitry Andric // Handle renaming static template class methods, we only rename the
2870b57cec5SDimitry Andric // name without prefix qualifiers and restrict the source range to the
2880b57cec5SDimitry Andric // name.
2890b57cec5SDimitry Andric RenameInfos.push_back({EndLoc, EndLoc,
2900b57cec5SDimitry Andric /*FromDecl=*/nullptr,
2910b57cec5SDimitry Andric /*Context=*/nullptr,
2920b57cec5SDimitry Andric /*Specifier=*/nullptr,
2930b57cec5SDimitry Andric /*IgnorePrefixQualifiers=*/true});
2940b57cec5SDimitry Andric return true;
2950b57cec5SDimitry Andric }
2960b57cec5SDimitry Andric }
2970b57cec5SDimitry Andric
2980b57cec5SDimitry Andric // In case of renaming an enum declaration, we have to explicitly handle
2990b57cec5SDimitry Andric // unscoped enum constants referenced in expressions (e.g.
3000b57cec5SDimitry Andric // "auto r = ns1::ns2::Green" where Green is an enum constant of an unscoped
3010b57cec5SDimitry Andric // enum decl "ns1::ns2::Color") as these enum constants cannot be caught by
3020b57cec5SDimitry Andric // TypeLoc.
3030b57cec5SDimitry Andric if (const auto *T = llvm::dyn_cast<EnumConstantDecl>(Decl)) {
3040b57cec5SDimitry Andric // FIXME: Handle the enum constant without prefix qualifiers (`a = Green`)
3050b57cec5SDimitry Andric // when renaming an unscoped enum declaration with a new namespace.
3060b57cec5SDimitry Andric if (!Expr->hasQualifier())
3070b57cec5SDimitry Andric return true;
3080b57cec5SDimitry Andric
3090b57cec5SDimitry Andric if (const auto *ED =
3100b57cec5SDimitry Andric llvm::dyn_cast_or_null<EnumDecl>(getClosestAncestorDecl(*T))) {
3110b57cec5SDimitry Andric if (ED->isScoped())
3120b57cec5SDimitry Andric return true;
3130b57cec5SDimitry Andric Decl = ED;
3140b57cec5SDimitry Andric }
3150b57cec5SDimitry Andric // The current fix would qualify "ns1::ns2::Green" as
3160b57cec5SDimitry Andric // "ns1::ns2::Color::Green".
3170b57cec5SDimitry Andric //
3180b57cec5SDimitry Andric // Get the EndLoc of the replacement by moving 1 character backward (
3190b57cec5SDimitry Andric // to exclude the last '::').
3200b57cec5SDimitry Andric //
3210b57cec5SDimitry Andric // ns1::ns2::Green;
3220b57cec5SDimitry Andric // ^ ^^
3230b57cec5SDimitry Andric // BeginLoc |EndLoc of the qualifier
3240b57cec5SDimitry Andric // new EndLoc
3250b57cec5SDimitry Andric EndLoc = Expr->getQualifierLoc().getEndLoc().getLocWithOffset(-1);
3260b57cec5SDimitry Andric assert(EndLoc.isValid() &&
3270b57cec5SDimitry Andric "The enum constant should have prefix qualifers.");
3280b57cec5SDimitry Andric }
3290b57cec5SDimitry Andric if (isInUSRSet(Decl) &&
3300b57cec5SDimitry Andric IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
3310b57cec5SDimitry Andric RenameInfo Info = {StartLoc,
3320b57cec5SDimitry Andric EndLoc,
3330b57cec5SDimitry Andric Decl,
3340b57cec5SDimitry Andric getClosestAncestorDecl(*Expr),
3350b57cec5SDimitry Andric Expr->getQualifier(),
3360b57cec5SDimitry Andric /*IgnorePrefixQualifers=*/false};
3370b57cec5SDimitry Andric RenameInfos.push_back(Info);
3380b57cec5SDimitry Andric }
3390b57cec5SDimitry Andric
3400b57cec5SDimitry Andric return true;
3410b57cec5SDimitry Andric }
3420b57cec5SDimitry Andric
VisitUsingDecl(const UsingDecl * Using)3430b57cec5SDimitry Andric bool VisitUsingDecl(const UsingDecl *Using) {
3440b57cec5SDimitry Andric for (const auto *UsingShadow : Using->shadows()) {
3450b57cec5SDimitry Andric if (isInUSRSet(UsingShadow->getTargetDecl())) {
3460b57cec5SDimitry Andric UsingDecls.push_back(Using);
3470b57cec5SDimitry Andric break;
3480b57cec5SDimitry Andric }
3490b57cec5SDimitry Andric }
3500b57cec5SDimitry Andric return true;
3510b57cec5SDimitry Andric }
3520b57cec5SDimitry Andric
VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc)3530b57cec5SDimitry Andric bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
3540b57cec5SDimitry Andric if (!NestedLoc.getNestedNameSpecifier()->getAsType())
3550b57cec5SDimitry Andric return true;
3560b57cec5SDimitry Andric
3570b57cec5SDimitry Andric if (const auto *TargetDecl =
3580b57cec5SDimitry Andric getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
3590b57cec5SDimitry Andric if (isInUSRSet(TargetDecl)) {
3600b57cec5SDimitry Andric RenameInfo Info = {NestedLoc.getBeginLoc(),
3610b57cec5SDimitry Andric EndLocationForType(NestedLoc.getTypeLoc()),
3620b57cec5SDimitry Andric TargetDecl,
3630b57cec5SDimitry Andric getClosestAncestorDecl(NestedLoc),
3640b57cec5SDimitry Andric NestedLoc.getNestedNameSpecifier()->getPrefix(),
3650b57cec5SDimitry Andric /*IgnorePrefixQualifers=*/false};
3660b57cec5SDimitry Andric RenameInfos.push_back(Info);
3670b57cec5SDimitry Andric }
3680b57cec5SDimitry Andric }
3690b57cec5SDimitry Andric return true;
3700b57cec5SDimitry Andric }
3710b57cec5SDimitry Andric
VisitTypeLoc(TypeLoc Loc)3720b57cec5SDimitry Andric bool VisitTypeLoc(TypeLoc Loc) {
3730b57cec5SDimitry Andric auto Parents = Context.getParents(Loc);
3740b57cec5SDimitry Andric TypeLoc ParentTypeLoc;
3750b57cec5SDimitry Andric if (!Parents.empty()) {
3760b57cec5SDimitry Andric // Handle cases of nested name specificier locations.
3770b57cec5SDimitry Andric //
3780b57cec5SDimitry Andric // The VisitNestedNameSpecifierLoc interface is not impelmented in
3790b57cec5SDimitry Andric // RecursiveASTVisitor, we have to handle it explicitly.
3800b57cec5SDimitry Andric if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
3810b57cec5SDimitry Andric VisitNestedNameSpecifierLocations(*NSL);
3820b57cec5SDimitry Andric return true;
3830b57cec5SDimitry Andric }
3840b57cec5SDimitry Andric
3850b57cec5SDimitry Andric if (const auto *TL = Parents[0].get<TypeLoc>())
3860b57cec5SDimitry Andric ParentTypeLoc = *TL;
3870b57cec5SDimitry Andric }
3880b57cec5SDimitry Andric
3890b57cec5SDimitry Andric // Handle the outermost TypeLoc which is directly linked to the interesting
3900b57cec5SDimitry Andric // declaration and don't handle nested name specifier locations.
3910b57cec5SDimitry Andric if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
3920b57cec5SDimitry Andric if (isInUSRSet(TargetDecl)) {
3930b57cec5SDimitry Andric // Only handle the outermost typeLoc.
3940b57cec5SDimitry Andric //
3950b57cec5SDimitry Andric // For a type like "a::Foo", there will be two typeLocs for it.
3960b57cec5SDimitry Andric // One ElaboratedType, the other is RecordType:
3970b57cec5SDimitry Andric //
3980b57cec5SDimitry Andric // ElaboratedType 0x33b9390 'a::Foo' sugar
3990b57cec5SDimitry Andric // `-RecordType 0x338fef0 'class a::Foo'
4000b57cec5SDimitry Andric // `-CXXRecord 0x338fe58 'Foo'
4010b57cec5SDimitry Andric //
4020b57cec5SDimitry Andric // Skip if this is an inner typeLoc.
4030b57cec5SDimitry Andric if (!ParentTypeLoc.isNull() &&
4040b57cec5SDimitry Andric isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
4050b57cec5SDimitry Andric return true;
4060b57cec5SDimitry Andric
4070b57cec5SDimitry Andric auto StartLoc = StartLocationForType(Loc);
4080b57cec5SDimitry Andric auto EndLoc = EndLocationForType(Loc);
4090b57cec5SDimitry Andric if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
4100b57cec5SDimitry Andric RenameInfo Info = {StartLoc,
4110b57cec5SDimitry Andric EndLoc,
4120b57cec5SDimitry Andric TargetDecl,
4130b57cec5SDimitry Andric getClosestAncestorDecl(Loc),
4140b57cec5SDimitry Andric GetNestedNameForType(Loc),
4150b57cec5SDimitry Andric /*IgnorePrefixQualifers=*/false};
4160b57cec5SDimitry Andric RenameInfos.push_back(Info);
4170b57cec5SDimitry Andric }
4180b57cec5SDimitry Andric return true;
4190b57cec5SDimitry Andric }
4200b57cec5SDimitry Andric }
4210b57cec5SDimitry Andric
4220b57cec5SDimitry Andric // Handle specific template class specialiation cases.
4230b57cec5SDimitry Andric if (const auto *TemplateSpecType =
4240b57cec5SDimitry Andric dyn_cast<TemplateSpecializationType>(Loc.getType())) {
4250b57cec5SDimitry Andric TypeLoc TargetLoc = Loc;
4260b57cec5SDimitry Andric if (!ParentTypeLoc.isNull()) {
4270b57cec5SDimitry Andric if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
4280b57cec5SDimitry Andric TargetLoc = ParentTypeLoc;
4290b57cec5SDimitry Andric }
4300b57cec5SDimitry Andric
4310b57cec5SDimitry Andric if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
4320b57cec5SDimitry Andric TypeLoc TargetLoc = Loc;
4330b57cec5SDimitry Andric // FIXME: Find a better way to handle this case.
4340b57cec5SDimitry Andric // For the qualified template class specification type like
4350b57cec5SDimitry Andric // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
4360b57cec5SDimitry Andric // (ElaboratedType) of the TemplateSpecializationType in order to
4370b57cec5SDimitry Andric // catch the prefix qualifiers "ns::".
4380b57cec5SDimitry Andric if (!ParentTypeLoc.isNull() &&
4390b57cec5SDimitry Andric llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
4400b57cec5SDimitry Andric TargetLoc = ParentTypeLoc;
4410b57cec5SDimitry Andric
4420b57cec5SDimitry Andric auto StartLoc = StartLocationForType(TargetLoc);
4430b57cec5SDimitry Andric auto EndLoc = EndLocationForType(TargetLoc);
4440b57cec5SDimitry Andric if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
4450b57cec5SDimitry Andric RenameInfo Info = {
4460b57cec5SDimitry Andric StartLoc,
4470b57cec5SDimitry Andric EndLoc,
4480b57cec5SDimitry Andric TemplateSpecType->getTemplateName().getAsTemplateDecl(),
4495ffd83dbSDimitry Andric getClosestAncestorDecl(DynTypedNode::create(TargetLoc)),
4500b57cec5SDimitry Andric GetNestedNameForType(TargetLoc),
4510b57cec5SDimitry Andric /*IgnorePrefixQualifers=*/false};
4520b57cec5SDimitry Andric RenameInfos.push_back(Info);
4530b57cec5SDimitry Andric }
4540b57cec5SDimitry Andric }
4550b57cec5SDimitry Andric }
4560b57cec5SDimitry Andric return true;
4570b57cec5SDimitry Andric }
4580b57cec5SDimitry Andric
4590b57cec5SDimitry Andric // Returns a list of RenameInfo.
getRenameInfos() const4600b57cec5SDimitry Andric const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
4610b57cec5SDimitry Andric
4620b57cec5SDimitry Andric // Returns a list of using declarations which are needed to update.
getUsingDecls() const4630b57cec5SDimitry Andric const std::vector<const UsingDecl *> &getUsingDecls() const {
4640b57cec5SDimitry Andric return UsingDecls;
4650b57cec5SDimitry Andric }
4660b57cec5SDimitry Andric
4670b57cec5SDimitry Andric private:
4680b57cec5SDimitry Andric // Get the supported declaration from a given typeLoc. If the declaration type
4690b57cec5SDimitry Andric // is not supported, returns nullptr.
getSupportedDeclFromTypeLoc(TypeLoc Loc)4700b57cec5SDimitry Andric const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
4710b57cec5SDimitry Andric if (const auto* TT = Loc.getType()->getAs<clang::TypedefType>())
4720b57cec5SDimitry Andric return TT->getDecl();
4730b57cec5SDimitry Andric if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
4740b57cec5SDimitry Andric return RD;
4750b57cec5SDimitry Andric if (const auto *ED =
4760b57cec5SDimitry Andric llvm::dyn_cast_or_null<EnumDecl>(Loc.getType()->getAsTagDecl()))
4770b57cec5SDimitry Andric return ED;
4780b57cec5SDimitry Andric return nullptr;
4790b57cec5SDimitry Andric }
4800b57cec5SDimitry Andric
4810b57cec5SDimitry Andric // Get the closest ancester which is a declaration of a given AST node.
4820b57cec5SDimitry Andric template <typename ASTNodeType>
getClosestAncestorDecl(const ASTNodeType & Node)4830b57cec5SDimitry Andric const Decl *getClosestAncestorDecl(const ASTNodeType &Node) {
4840b57cec5SDimitry Andric auto Parents = Context.getParents(Node);
4850b57cec5SDimitry Andric // FIXME: figure out how to handle it when there are multiple parents.
4860b57cec5SDimitry Andric if (Parents.size() != 1)
4870b57cec5SDimitry Andric return nullptr;
4885ffd83dbSDimitry Andric if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind()))
4890b57cec5SDimitry Andric return Parents[0].template get<Decl>();
4900b57cec5SDimitry Andric return getClosestAncestorDecl(Parents[0]);
4910b57cec5SDimitry Andric }
4920b57cec5SDimitry Andric
4930b57cec5SDimitry Andric // Get the parent typeLoc of a given typeLoc. If there is no such parent,
4940b57cec5SDimitry Andric // return nullptr.
getParentTypeLoc(TypeLoc Loc) const4950b57cec5SDimitry Andric const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
4960b57cec5SDimitry Andric auto Parents = Context.getParents(Loc);
4970b57cec5SDimitry Andric // FIXME: figure out how to handle it when there are multiple parents.
4980b57cec5SDimitry Andric if (Parents.size() != 1)
4990b57cec5SDimitry Andric return nullptr;
5000b57cec5SDimitry Andric return Parents[0].get<TypeLoc>();
5010b57cec5SDimitry Andric }
5020b57cec5SDimitry Andric
5030b57cec5SDimitry Andric // Check whether the USR of a given Decl is in the USRSet.
isInUSRSet(const Decl * Decl) const5040b57cec5SDimitry Andric bool isInUSRSet(const Decl *Decl) const {
5050b57cec5SDimitry Andric auto USR = getUSRForDecl(Decl);
5060b57cec5SDimitry Andric if (USR.empty())
5070b57cec5SDimitry Andric return false;
5080b57cec5SDimitry Andric return llvm::is_contained(USRSet, USR);
5090b57cec5SDimitry Andric }
5100b57cec5SDimitry Andric
5110b57cec5SDimitry Andric const std::set<std::string> USRSet;
5120b57cec5SDimitry Andric ASTContext &Context;
5130b57cec5SDimitry Andric std::vector<RenameInfo> RenameInfos;
5140b57cec5SDimitry Andric // Record all interested using declarations which contains the using-shadow
5150b57cec5SDimitry Andric // declarations of the symbol declarations being renamed.
5160b57cec5SDimitry Andric std::vector<const UsingDecl *> UsingDecls;
5170b57cec5SDimitry Andric };
5180b57cec5SDimitry Andric
5190b57cec5SDimitry Andric } // namespace
5200b57cec5SDimitry Andric
getOccurrencesOfUSRs(ArrayRef<std::string> USRs,StringRef PrevName,Decl * Decl)5210b57cec5SDimitry Andric SymbolOccurrences getOccurrencesOfUSRs(ArrayRef<std::string> USRs,
5220b57cec5SDimitry Andric StringRef PrevName, Decl *Decl) {
5230b57cec5SDimitry Andric USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
5240b57cec5SDimitry Andric Visitor.TraverseDecl(Decl);
5250b57cec5SDimitry Andric return Visitor.takeOccurrences();
5260b57cec5SDimitry Andric }
5270b57cec5SDimitry Andric
5280b57cec5SDimitry Andric std::vector<tooling::AtomicChange>
createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,llvm::StringRef NewName,Decl * TranslationUnitDecl)5290b57cec5SDimitry Andric createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
5300b57cec5SDimitry Andric llvm::StringRef NewName, Decl *TranslationUnitDecl) {
5310b57cec5SDimitry Andric RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext());
5320b57cec5SDimitry Andric Finder.TraverseDecl(TranslationUnitDecl);
5330b57cec5SDimitry Andric
5340b57cec5SDimitry Andric const SourceManager &SM =
5350b57cec5SDimitry Andric TranslationUnitDecl->getASTContext().getSourceManager();
5360b57cec5SDimitry Andric
5370b57cec5SDimitry Andric std::vector<tooling::AtomicChange> AtomicChanges;
5380b57cec5SDimitry Andric auto Replace = [&](SourceLocation Start, SourceLocation End,
5390b57cec5SDimitry Andric llvm::StringRef Text) {
5400b57cec5SDimitry Andric tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start);
5410b57cec5SDimitry Andric llvm::Error Err = ReplaceChange.replace(
5420b57cec5SDimitry Andric SM, CharSourceRange::getTokenRange(Start, End), Text);
5430b57cec5SDimitry Andric if (Err) {
5440b57cec5SDimitry Andric llvm::errs() << "Failed to add replacement to AtomicChange: "
5450b57cec5SDimitry Andric << llvm::toString(std::move(Err)) << "\n";
5460b57cec5SDimitry Andric return;
5470b57cec5SDimitry Andric }
5480b57cec5SDimitry Andric AtomicChanges.push_back(std::move(ReplaceChange));
5490b57cec5SDimitry Andric };
5500b57cec5SDimitry Andric
5510b57cec5SDimitry Andric for (const auto &RenameInfo : Finder.getRenameInfos()) {
5520b57cec5SDimitry Andric std::string ReplacedName = NewName.str();
5530b57cec5SDimitry Andric if (RenameInfo.IgnorePrefixQualifers) {
5540b57cec5SDimitry Andric // Get the name without prefix qualifiers from NewName.
5550b57cec5SDimitry Andric size_t LastColonPos = NewName.find_last_of(':');
5560b57cec5SDimitry Andric if (LastColonPos != std::string::npos)
5575ffd83dbSDimitry Andric ReplacedName = std::string(NewName.substr(LastColonPos + 1));
5580b57cec5SDimitry Andric } else {
5590b57cec5SDimitry Andric if (RenameInfo.FromDecl && RenameInfo.Context) {
5600b57cec5SDimitry Andric if (!llvm::isa<clang::TranslationUnitDecl>(
5610b57cec5SDimitry Andric RenameInfo.Context->getDeclContext())) {
5620b57cec5SDimitry Andric ReplacedName = tooling::replaceNestedName(
5630b57cec5SDimitry Andric RenameInfo.Specifier, RenameInfo.Begin,
5640b57cec5SDimitry Andric RenameInfo.Context->getDeclContext(), RenameInfo.FromDecl,
565*5f757f3fSDimitry Andric NewName.starts_with("::") ? NewName.str()
5660b57cec5SDimitry Andric : ("::" + NewName).str());
5670b57cec5SDimitry Andric } else {
5680b57cec5SDimitry Andric // This fixes the case where type `T` is a parameter inside a function
5690b57cec5SDimitry Andric // type (e.g. `std::function<void(T)>`) and the DeclContext of `T`
5700b57cec5SDimitry Andric // becomes the translation unit. As a workaround, we simply use
5710b57cec5SDimitry Andric // fully-qualified name here for all references whose `DeclContext` is
5720b57cec5SDimitry Andric // the translation unit and ignore the possible existence of
5730b57cec5SDimitry Andric // using-decls (in the global scope) that can shorten the replaced
5740b57cec5SDimitry Andric // name.
5750b57cec5SDimitry Andric llvm::StringRef ActualName = Lexer::getSourceText(
5760b57cec5SDimitry Andric CharSourceRange::getTokenRange(
5770b57cec5SDimitry Andric SourceRange(RenameInfo.Begin, RenameInfo.End)),
5780b57cec5SDimitry Andric SM, TranslationUnitDecl->getASTContext().getLangOpts());
5790b57cec5SDimitry Andric // Add the leading "::" back if the name written in the code contains
5800b57cec5SDimitry Andric // it.
581*5f757f3fSDimitry Andric if (ActualName.starts_with("::") && !NewName.starts_with("::")) {
5820b57cec5SDimitry Andric ReplacedName = "::" + NewName.str();
5830b57cec5SDimitry Andric }
5840b57cec5SDimitry Andric }
5850b57cec5SDimitry Andric }
5860b57cec5SDimitry Andric // If the NewName contains leading "::", add it back.
587*5f757f3fSDimitry Andric if (NewName.starts_with("::") && NewName.substr(2) == ReplacedName)
5880b57cec5SDimitry Andric ReplacedName = NewName.str();
5890b57cec5SDimitry Andric }
5900b57cec5SDimitry Andric Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
5910b57cec5SDimitry Andric }
5920b57cec5SDimitry Andric
5930b57cec5SDimitry Andric // Hanlde using declarations explicitly as "using a::Foo" don't trigger
5940b57cec5SDimitry Andric // typeLoc for "a::Foo".
5950b57cec5SDimitry Andric for (const auto *Using : Finder.getUsingDecls())
5960b57cec5SDimitry Andric Replace(Using->getBeginLoc(), Using->getEndLoc(), "using " + NewName.str());
5970b57cec5SDimitry Andric
5980b57cec5SDimitry Andric return AtomicChanges;
5990b57cec5SDimitry Andric }
6000b57cec5SDimitry Andric
6010b57cec5SDimitry Andric } // end namespace tooling
6020b57cec5SDimitry Andric } // end namespace clang
603