USRLocFinder.cpp revision 341825
1320535Sdim//===--- USRLocFinder.cpp - Clang refactoring library ---------------------===//
2320535Sdim//
3320535Sdim//                     The LLVM Compiler Infrastructure
4320535Sdim//
5320535Sdim// This file is distributed under the University of Illinois Open Source
6320535Sdim// License. See LICENSE.TXT for details.
7320535Sdim//
8320535Sdim//===----------------------------------------------------------------------===//
9320535Sdim///
10320535Sdim/// \file
11341825Sdim/// Methods for finding all instances of a USR. Our strategy is very
12320535Sdim/// simple; we just compare the USR at every relevant AST node with the one
13320535Sdim/// provided.
14320535Sdim///
15320535Sdim//===----------------------------------------------------------------------===//
16320535Sdim
17320535Sdim#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
18320535Sdim#include "clang/AST/ASTContext.h"
19320535Sdim#include "clang/AST/RecursiveASTVisitor.h"
20320535Sdim#include "clang/Basic/LLVM.h"
21320535Sdim#include "clang/Basic/SourceLocation.h"
22320535Sdim#include "clang/Basic/SourceManager.h"
23320535Sdim#include "clang/Lex/Lexer.h"
24320535Sdim#include "clang/Tooling/Core/Lookup.h"
25321238Sdim#include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
26327952Sdim#include "clang/Tooling/Refactoring/Rename/SymbolName.h"
27320535Sdim#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
28320535Sdim#include "llvm/ADT/StringRef.h"
29320535Sdim#include "llvm/Support/Casting.h"
30320535Sdim#include <cstddef>
31320535Sdim#include <set>
32320535Sdim#include <string>
33320535Sdim#include <vector>
34320535Sdim
35320535Sdimusing namespace llvm;
36320535Sdim
37320535Sdimnamespace clang {
38320535Sdimnamespace tooling {
39320535Sdim
40320535Sdimnamespace {
41320535Sdim
42327952Sdim// Returns true if the given Loc is valid for edit. We don't edit the
43327952Sdim// SourceLocations that are valid or in temporary buffer.
44327952Sdimbool IsValidEditLoc(const clang::SourceManager& SM, clang::SourceLocation Loc) {
45327952Sdim  if (Loc.isInvalid())
46327952Sdim    return false;
47327952Sdim  const clang::FullSourceLoc FullLoc(Loc, SM);
48327952Sdim  std::pair<clang::FileID, unsigned> FileIdAndOffset =
49327952Sdim      FullLoc.getSpellingLoc().getDecomposedLoc();
50327952Sdim  return SM.getFileEntryForID(FileIdAndOffset.first) != nullptr;
51327952Sdim}
52327952Sdim
53341825Sdim// This visitor recursively searches for all instances of a USR in a
54320535Sdim// translation unit and stores them for later usage.
55320535Sdimclass USRLocFindingASTVisitor
56321238Sdim    : public RecursiveSymbolVisitor<USRLocFindingASTVisitor> {
57320535Sdimpublic:
58320535Sdim  explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
59320535Sdim                                   StringRef PrevName,
60320535Sdim                                   const ASTContext &Context)
61321238Sdim      : RecursiveSymbolVisitor(Context.getSourceManager(),
62321238Sdim                               Context.getLangOpts()),
63321238Sdim        USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
64320535Sdim  }
65320535Sdim
66321238Sdim  bool visitSymbolOccurrence(const NamedDecl *ND,
67321238Sdim                             ArrayRef<SourceRange> NameRanges) {
68321238Sdim    if (USRSet.find(getUSRForDecl(ND)) != USRSet.end()) {
69321238Sdim      assert(NameRanges.size() == 1 &&
70321238Sdim             "Multiple name pieces are not supported yet!");
71321238Sdim      SourceLocation Loc = NameRanges[0].getBegin();
72321238Sdim      const SourceManager &SM = Context.getSourceManager();
73321238Sdim      // TODO: Deal with macro occurrences correctly.
74321238Sdim      if (Loc.isMacroID())
75321238Sdim        Loc = SM.getSpellingLoc(Loc);
76321238Sdim      checkAndAddLocation(Loc);
77320535Sdim    }
78320535Sdim    return true;
79320535Sdim  }
80320535Sdim
81320535Sdim  // Non-visitors:
82320535Sdim
83341825Sdim  /// Returns a set of unique symbol occurrences. Duplicate or
84327952Sdim  /// overlapping occurrences are erroneous and should be reported!
85327952Sdim  SymbolOccurrences takeOccurrences() { return std::move(Occurrences); }
86320535Sdim
87320535Sdimprivate:
88320535Sdim  void checkAndAddLocation(SourceLocation Loc) {
89320535Sdim    const SourceLocation BeginLoc = Loc;
90320535Sdim    const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
91320535Sdim        BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
92320535Sdim    StringRef TokenName =
93320535Sdim        Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
94320535Sdim                             Context.getSourceManager(), Context.getLangOpts());
95327952Sdim    size_t Offset = TokenName.find(PrevName.getNamePieces()[0]);
96320535Sdim
97320535Sdim    // The token of the source location we find actually has the old
98320535Sdim    // name.
99320535Sdim    if (Offset != StringRef::npos)
100327952Sdim      Occurrences.emplace_back(PrevName, SymbolOccurrence::MatchingSymbol,
101327952Sdim                               BeginLoc.getLocWithOffset(Offset));
102320535Sdim  }
103320535Sdim
104320535Sdim  const std::set<std::string> USRSet;
105327952Sdim  const SymbolName PrevName;
106327952Sdim  SymbolOccurrences Occurrences;
107320535Sdim  const ASTContext &Context;
108320535Sdim};
109320535Sdim
110320535SdimSourceLocation StartLocationForType(TypeLoc TL) {
111320535Sdim  // For elaborated types (e.g. `struct a::A`) we want the portion after the
112320535Sdim  // `struct` but including the namespace qualifier, `a::`.
113320535Sdim  if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) {
114320535Sdim    NestedNameSpecifierLoc NestedNameSpecifier =
115320535Sdim        ElaboratedTypeLoc.getQualifierLoc();
116320535Sdim    if (NestedNameSpecifier.getNestedNameSpecifier())
117320535Sdim      return NestedNameSpecifier.getBeginLoc();
118320535Sdim    TL = TL.getNextTypeLoc();
119320535Sdim  }
120320535Sdim  return TL.getLocStart();
121320535Sdim}
122320535Sdim
123320535SdimSourceLocation EndLocationForType(TypeLoc TL) {
124320535Sdim  // Dig past any namespace or keyword qualifications.
125320535Sdim  while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
126320535Sdim         TL.getTypeLocClass() == TypeLoc::Qualified)
127320535Sdim    TL = TL.getNextTypeLoc();
128320535Sdim
129320535Sdim  // The location for template specializations (e.g. Foo<int>) includes the
130320535Sdim  // templated types in its location range.  We want to restrict this to just
131320535Sdim  // before the `<` character.
132320535Sdim  if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
133320535Sdim    return TL.castAs<TemplateSpecializationTypeLoc>()
134320535Sdim        .getLAngleLoc()
135320535Sdim        .getLocWithOffset(-1);
136320535Sdim  }
137320535Sdim  return TL.getEndLoc();
138320535Sdim}
139320535Sdim
140320535SdimNestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
141320535Sdim  // Dig past any keyword qualifications.
142320535Sdim  while (TL.getTypeLocClass() == TypeLoc::Qualified)
143320535Sdim    TL = TL.getNextTypeLoc();
144320535Sdim
145320535Sdim  // For elaborated types (e.g. `struct a::A`) we want the portion after the
146320535Sdim  // `struct` but including the namespace qualifier, `a::`.
147320535Sdim  if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
148320535Sdim    return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
149320535Sdim  return nullptr;
150320535Sdim}
151320535Sdim
152320535Sdim// Find all locations identified by the given USRs for rename.
153320535Sdim//
154320535Sdim// This class will traverse the AST and find every AST node whose USR is in the
155320535Sdim// given USRs' set.
156320535Sdimclass RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
157320535Sdimpublic:
158320535Sdim  RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
159320535Sdim      : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
160320535Sdim
161320535Sdim  // A structure records all information of a symbol reference being renamed.
162320535Sdim  // We try to add as few prefix qualifiers as possible.
163320535Sdim  struct RenameInfo {
164320535Sdim    // The begin location of a symbol being renamed.
165320535Sdim    SourceLocation Begin;
166320535Sdim    // The end location of a symbol being renamed.
167320535Sdim    SourceLocation End;
168320535Sdim    // The declaration of a symbol being renamed (can be nullptr).
169320535Sdim    const NamedDecl *FromDecl;
170320535Sdim    // The declaration in which the nested name is contained (can be nullptr).
171320535Sdim    const Decl *Context;
172320535Sdim    // The nested name being replaced (can be nullptr).
173320535Sdim    const NestedNameSpecifier *Specifier;
174327952Sdim    // Determine whether the prefix qualifiers of the NewName should be ignored.
175327952Sdim    // Normally, we set it to true for the symbol declaration and definition to
176327952Sdim    // avoid adding prefix qualifiers.
177327952Sdim    // For example, if it is true and NewName is "a::b::foo", then the symbol
178327952Sdim    // occurrence which the RenameInfo points to will be renamed to "foo".
179327952Sdim    bool IgnorePrefixQualifers;
180320535Sdim  };
181320535Sdim
182320535Sdim  bool VisitNamedDecl(const NamedDecl *Decl) {
183320535Sdim    // UsingDecl has been handled in other place.
184320535Sdim    if (llvm::isa<UsingDecl>(Decl))
185320535Sdim      return true;
186320535Sdim
187320535Sdim    // DestructorDecl has been handled in Typeloc.
188320535Sdim    if (llvm::isa<CXXDestructorDecl>(Decl))
189320535Sdim      return true;
190320535Sdim
191320535Sdim    if (Decl->isImplicit())
192320535Sdim      return true;
193320535Sdim
194320535Sdim    if (isInUSRSet(Decl)) {
195327952Sdim      // For the case of renaming an alias template, we actually rename the
196327952Sdim      // underlying alias declaration of the template.
197327952Sdim      if (const auto* TAT = dyn_cast<TypeAliasTemplateDecl>(Decl))
198327952Sdim        Decl = TAT->getTemplatedDecl();
199327952Sdim
200327952Sdim      auto StartLoc = Decl->getLocation();
201327952Sdim      auto EndLoc = StartLoc;
202327952Sdim      if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
203327952Sdim        RenameInfo Info = {StartLoc,
204327952Sdim                           EndLoc,
205327952Sdim                           /*FromDecl=*/nullptr,
206327952Sdim                           /*Context=*/nullptr,
207327952Sdim                           /*Specifier=*/nullptr,
208327952Sdim                           /*IgnorePrefixQualifers=*/true};
209327952Sdim        RenameInfos.push_back(Info);
210327952Sdim      }
211320535Sdim    }
212320535Sdim    return true;
213320535Sdim  }
214320535Sdim
215327952Sdim  bool VisitMemberExpr(const MemberExpr *Expr) {
216327952Sdim    const NamedDecl *Decl = Expr->getFoundDecl();
217327952Sdim    auto StartLoc = Expr->getMemberLoc();
218327952Sdim    auto EndLoc = Expr->getMemberLoc();
219327952Sdim    if (isInUSRSet(Decl)) {
220327952Sdim      RenameInfos.push_back({StartLoc, EndLoc,
221327952Sdim                            /*FromDecl=*/nullptr,
222327952Sdim                            /*Context=*/nullptr,
223327952Sdim                            /*Specifier=*/nullptr,
224327952Sdim                            /*IgnorePrefixQualifiers=*/true});
225327952Sdim    }
226327952Sdim    return true;
227327952Sdim  }
228327952Sdim
229327952Sdim  bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) {
230327952Sdim    // Fix the constructor initializer when renaming class members.
231327952Sdim    for (const auto *Initializer : CD->inits()) {
232327952Sdim      // Ignore implicit initializers.
233327952Sdim      if (!Initializer->isWritten())
234327952Sdim        continue;
235327952Sdim
236327952Sdim      if (const FieldDecl *FD = Initializer->getMember()) {
237327952Sdim        if (isInUSRSet(FD)) {
238327952Sdim          auto Loc = Initializer->getSourceLocation();
239327952Sdim          RenameInfos.push_back({Loc, Loc,
240327952Sdim                                 /*FromDecl=*/nullptr,
241327952Sdim                                 /*Context=*/nullptr,
242327952Sdim                                 /*Specifier=*/nullptr,
243327952Sdim                                 /*IgnorePrefixQualifiers=*/true});
244327952Sdim        }
245327952Sdim      }
246327952Sdim    }
247327952Sdim    return true;
248327952Sdim  }
249327952Sdim
250320535Sdim  bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
251320535Sdim    const NamedDecl *Decl = Expr->getFoundDecl();
252327952Sdim    // Get the underlying declaration of the shadow declaration introduced by a
253327952Sdim    // using declaration.
254327952Sdim    if (auto *UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Decl)) {
255327952Sdim      Decl = UsingShadow->getTargetDecl();
256327952Sdim    }
257327952Sdim
258327952Sdim    auto StartLoc = Expr->getLocStart();
259327952Sdim    // For template function call expressions like `foo<int>()`, we want to
260327952Sdim    // restrict the end of location to just before the `<` character.
261327952Sdim    SourceLocation EndLoc = Expr->hasExplicitTemplateArgs()
262327952Sdim                                ? Expr->getLAngleLoc().getLocWithOffset(-1)
263327952Sdim                                : Expr->getLocEnd();
264327952Sdim
265327952Sdim    if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) {
266327952Sdim      if (isInUSRSet(MD)) {
267327952Sdim        // Handle renaming static template class methods, we only rename the
268327952Sdim        // name without prefix qualifiers and restrict the source range to the
269327952Sdim        // name.
270327952Sdim        RenameInfos.push_back({EndLoc, EndLoc,
271327952Sdim                               /*FromDecl=*/nullptr,
272327952Sdim                               /*Context=*/nullptr,
273327952Sdim                               /*Specifier=*/nullptr,
274327952Sdim                               /*IgnorePrefixQualifiers=*/true});
275327952Sdim        return true;
276327952Sdim      }
277327952Sdim    }
278327952Sdim
279327952Sdim    // In case of renaming an enum declaration, we have to explicitly handle
280327952Sdim    // unscoped enum constants referenced in expressions (e.g.
281327952Sdim    // "auto r = ns1::ns2::Green" where Green is an enum constant of an unscoped
282327952Sdim    // enum decl "ns1::ns2::Color") as these enum constants cannot be caught by
283327952Sdim    // TypeLoc.
284327952Sdim    if (const auto *T = llvm::dyn_cast<EnumConstantDecl>(Decl)) {
285327952Sdim      // FIXME: Handle the enum constant without prefix qualifiers (`a = Green`)
286327952Sdim      // when renaming an unscoped enum declaration with a new namespace.
287327952Sdim      if (!Expr->hasQualifier())
288327952Sdim        return true;
289327952Sdim
290327952Sdim      if (const auto *ED =
291327952Sdim              llvm::dyn_cast_or_null<EnumDecl>(getClosestAncestorDecl(*T))) {
292327952Sdim        if (ED->isScoped())
293327952Sdim          return true;
294327952Sdim        Decl = ED;
295327952Sdim      }
296327952Sdim      // The current fix would qualify "ns1::ns2::Green" as
297327952Sdim      // "ns1::ns2::Color::Green".
298327952Sdim      //
299327952Sdim      // Get the EndLoc of the replacement by moving 1 character backward (
300327952Sdim      // to exclude the last '::').
301327952Sdim      //
302327952Sdim      //    ns1::ns2::Green;
303327952Sdim      //    ^      ^^
304327952Sdim      // BeginLoc  |EndLoc of the qualifier
305327952Sdim      //           new EndLoc
306327952Sdim      EndLoc = Expr->getQualifierLoc().getEndLoc().getLocWithOffset(-1);
307327952Sdim      assert(EndLoc.isValid() &&
308327952Sdim             "The enum constant should have prefix qualifers.");
309327952Sdim    }
310327952Sdim    if (isInUSRSet(Decl) &&
311327952Sdim        IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
312327952Sdim      RenameInfo Info = {StartLoc,
313327952Sdim                         EndLoc,
314327952Sdim                         Decl,
315327952Sdim                         getClosestAncestorDecl(*Expr),
316327952Sdim                         Expr->getQualifier(),
317327952Sdim                         /*IgnorePrefixQualifers=*/false};
318320535Sdim      RenameInfos.push_back(Info);
319320535Sdim    }
320320535Sdim
321320535Sdim    return true;
322320535Sdim  }
323320535Sdim
324320535Sdim  bool VisitUsingDecl(const UsingDecl *Using) {
325320535Sdim    for (const auto *UsingShadow : Using->shadows()) {
326320535Sdim      if (isInUSRSet(UsingShadow->getTargetDecl())) {
327320535Sdim        UsingDecls.push_back(Using);
328320535Sdim        break;
329320535Sdim      }
330320535Sdim    }
331320535Sdim    return true;
332320535Sdim  }
333320535Sdim
334320535Sdim  bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
335320535Sdim    if (!NestedLoc.getNestedNameSpecifier()->getAsType())
336320535Sdim      return true;
337320535Sdim
338320535Sdim    if (const auto *TargetDecl =
339320535Sdim            getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
340320535Sdim      if (isInUSRSet(TargetDecl)) {
341320535Sdim        RenameInfo Info = {NestedLoc.getBeginLoc(),
342320535Sdim                           EndLocationForType(NestedLoc.getTypeLoc()),
343327952Sdim                           TargetDecl,
344327952Sdim                           getClosestAncestorDecl(NestedLoc),
345327952Sdim                           NestedLoc.getNestedNameSpecifier()->getPrefix(),
346327952Sdim                           /*IgnorePrefixQualifers=*/false};
347320535Sdim        RenameInfos.push_back(Info);
348320535Sdim      }
349320535Sdim    }
350320535Sdim    return true;
351320535Sdim  }
352320535Sdim
353320535Sdim  bool VisitTypeLoc(TypeLoc Loc) {
354320535Sdim    auto Parents = Context.getParents(Loc);
355320535Sdim    TypeLoc ParentTypeLoc;
356320535Sdim    if (!Parents.empty()) {
357320535Sdim      // Handle cases of nested name specificier locations.
358320535Sdim      //
359320535Sdim      // The VisitNestedNameSpecifierLoc interface is not impelmented in
360320535Sdim      // RecursiveASTVisitor, we have to handle it explicitly.
361320535Sdim      if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
362320535Sdim        VisitNestedNameSpecifierLocations(*NSL);
363320535Sdim        return true;
364320535Sdim      }
365320535Sdim
366320535Sdim      if (const auto *TL = Parents[0].get<TypeLoc>())
367320535Sdim        ParentTypeLoc = *TL;
368320535Sdim    }
369320535Sdim
370320535Sdim    // Handle the outermost TypeLoc which is directly linked to the interesting
371320535Sdim    // declaration and don't handle nested name specifier locations.
372320535Sdim    if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
373320535Sdim      if (isInUSRSet(TargetDecl)) {
374320535Sdim        // Only handle the outermost typeLoc.
375320535Sdim        //
376320535Sdim        // For a type like "a::Foo", there will be two typeLocs for it.
377320535Sdim        // One ElaboratedType, the other is RecordType:
378320535Sdim        //
379320535Sdim        //   ElaboratedType 0x33b9390 'a::Foo' sugar
380320535Sdim        //   `-RecordType 0x338fef0 'class a::Foo'
381320535Sdim        //     `-CXXRecord 0x338fe58 'Foo'
382320535Sdim        //
383320535Sdim        // Skip if this is an inner typeLoc.
384320535Sdim        if (!ParentTypeLoc.isNull() &&
385320535Sdim            isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
386320535Sdim          return true;
387327952Sdim
388327952Sdim        auto StartLoc = StartLocationForType(Loc);
389327952Sdim        auto EndLoc = EndLocationForType(Loc);
390327952Sdim        if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
391327952Sdim          RenameInfo Info = {StartLoc,
392327952Sdim                             EndLoc,
393327952Sdim                             TargetDecl,
394327952Sdim                             getClosestAncestorDecl(Loc),
395327952Sdim                             GetNestedNameForType(Loc),
396327952Sdim                             /*IgnorePrefixQualifers=*/false};
397327952Sdim          RenameInfos.push_back(Info);
398327952Sdim        }
399320535Sdim        return true;
400320535Sdim      }
401320535Sdim    }
402320535Sdim
403320535Sdim    // Handle specific template class specialiation cases.
404320535Sdim    if (const auto *TemplateSpecType =
405320535Sdim            dyn_cast<TemplateSpecializationType>(Loc.getType())) {
406320535Sdim      TypeLoc TargetLoc = Loc;
407320535Sdim      if (!ParentTypeLoc.isNull()) {
408320535Sdim        if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
409320535Sdim          TargetLoc = ParentTypeLoc;
410320535Sdim      }
411320535Sdim
412320535Sdim      if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
413320535Sdim        TypeLoc TargetLoc = Loc;
414320535Sdim        // FIXME: Find a better way to handle this case.
415320535Sdim        // For the qualified template class specification type like
416320535Sdim        // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
417320535Sdim        // (ElaboratedType) of the TemplateSpecializationType in order to
418320535Sdim        // catch the prefix qualifiers "ns::".
419320535Sdim        if (!ParentTypeLoc.isNull() &&
420320535Sdim            llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
421320535Sdim          TargetLoc = ParentTypeLoc;
422327952Sdim
423327952Sdim        auto StartLoc = StartLocationForType(TargetLoc);
424327952Sdim        auto EndLoc = EndLocationForType(TargetLoc);
425327952Sdim        if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
426327952Sdim          RenameInfo Info = {
427327952Sdim              StartLoc,
428327952Sdim              EndLoc,
429327952Sdim              TemplateSpecType->getTemplateName().getAsTemplateDecl(),
430327952Sdim              getClosestAncestorDecl(
431327952Sdim                  ast_type_traits::DynTypedNode::create(TargetLoc)),
432327952Sdim              GetNestedNameForType(TargetLoc),
433327952Sdim              /*IgnorePrefixQualifers=*/false};
434327952Sdim          RenameInfos.push_back(Info);
435327952Sdim        }
436320535Sdim      }
437320535Sdim    }
438320535Sdim    return true;
439320535Sdim  }
440320535Sdim
441320535Sdim  // Returns a list of RenameInfo.
442320535Sdim  const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
443320535Sdim
444320535Sdim  // Returns a list of using declarations which are needed to update.
445320535Sdim  const std::vector<const UsingDecl *> &getUsingDecls() const {
446320535Sdim    return UsingDecls;
447320535Sdim  }
448320535Sdim
449320535Sdimprivate:
450320535Sdim  // Get the supported declaration from a given typeLoc. If the declaration type
451320535Sdim  // is not supported, returns nullptr.
452320535Sdim  const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
453327952Sdim    if (const auto* TT = Loc.getType()->getAs<clang::TypedefType>())
454327952Sdim      return TT->getDecl();
455320535Sdim    if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
456320535Sdim      return RD;
457327952Sdim    if (const auto *ED =
458327952Sdim            llvm::dyn_cast_or_null<EnumDecl>(Loc.getType()->getAsTagDecl()))
459327952Sdim      return ED;
460320535Sdim    return nullptr;
461320535Sdim  }
462320535Sdim
463320535Sdim  // Get the closest ancester which is a declaration of a given AST node.
464320535Sdim  template <typename ASTNodeType>
465320535Sdim  const Decl *getClosestAncestorDecl(const ASTNodeType &Node) {
466320535Sdim    auto Parents = Context.getParents(Node);
467320535Sdim    // FIXME: figure out how to handle it when there are multiple parents.
468320535Sdim    if (Parents.size() != 1)
469320535Sdim      return nullptr;
470320535Sdim    if (ast_type_traits::ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(
471320535Sdim            Parents[0].getNodeKind()))
472320535Sdim      return Parents[0].template get<Decl>();
473320535Sdim    return getClosestAncestorDecl(Parents[0]);
474320535Sdim  }
475320535Sdim
476320535Sdim  // Get the parent typeLoc of a given typeLoc. If there is no such parent,
477320535Sdim  // return nullptr.
478320535Sdim  const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
479320535Sdim    auto Parents = Context.getParents(Loc);
480320535Sdim    // FIXME: figure out how to handle it when there are multiple parents.
481320535Sdim    if (Parents.size() != 1)
482320535Sdim      return nullptr;
483320535Sdim    return Parents[0].get<TypeLoc>();
484320535Sdim  }
485320535Sdim
486320535Sdim  // Check whether the USR of a given Decl is in the USRSet.
487320535Sdim  bool isInUSRSet(const Decl *Decl) const {
488320535Sdim    auto USR = getUSRForDecl(Decl);
489320535Sdim    if (USR.empty())
490320535Sdim      return false;
491320535Sdim    return llvm::is_contained(USRSet, USR);
492320535Sdim  }
493320535Sdim
494320535Sdim  const std::set<std::string> USRSet;
495320535Sdim  ASTContext &Context;
496320535Sdim  std::vector<RenameInfo> RenameInfos;
497320535Sdim  // Record all interested using declarations which contains the using-shadow
498320535Sdim  // declarations of the symbol declarations being renamed.
499320535Sdim  std::vector<const UsingDecl *> UsingDecls;
500320535Sdim};
501320535Sdim
502320535Sdim} // namespace
503320535Sdim
504327952SdimSymbolOccurrences getOccurrencesOfUSRs(ArrayRef<std::string> USRs,
505327952Sdim                                       StringRef PrevName, Decl *Decl) {
506320535Sdim  USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
507320535Sdim  Visitor.TraverseDecl(Decl);
508327952Sdim  return Visitor.takeOccurrences();
509320535Sdim}
510320535Sdim
511320535Sdimstd::vector<tooling::AtomicChange>
512320535SdimcreateRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
513320535Sdim                          llvm::StringRef NewName, Decl *TranslationUnitDecl) {
514320535Sdim  RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext());
515320535Sdim  Finder.TraverseDecl(TranslationUnitDecl);
516320535Sdim
517320535Sdim  const SourceManager &SM =
518320535Sdim      TranslationUnitDecl->getASTContext().getSourceManager();
519320535Sdim
520320535Sdim  std::vector<tooling::AtomicChange> AtomicChanges;
521320535Sdim  auto Replace = [&](SourceLocation Start, SourceLocation End,
522320535Sdim                     llvm::StringRef Text) {
523320535Sdim    tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start);
524320535Sdim    llvm::Error Err = ReplaceChange.replace(
525320535Sdim        SM, CharSourceRange::getTokenRange(Start, End), Text);
526320535Sdim    if (Err) {
527341825Sdim      llvm::errs() << "Failed to add replacement to AtomicChange: "
528320535Sdim                   << llvm::toString(std::move(Err)) << "\n";
529320535Sdim      return;
530320535Sdim    }
531320535Sdim    AtomicChanges.push_back(std::move(ReplaceChange));
532320535Sdim  };
533320535Sdim
534320535Sdim  for (const auto &RenameInfo : Finder.getRenameInfos()) {
535320535Sdim    std::string ReplacedName = NewName.str();
536327952Sdim    if (RenameInfo.IgnorePrefixQualifers) {
537327952Sdim      // Get the name without prefix qualifiers from NewName.
538327952Sdim      size_t LastColonPos = NewName.find_last_of(':');
539327952Sdim      if (LastColonPos != std::string::npos)
540327952Sdim        ReplacedName = NewName.substr(LastColonPos + 1);
541327952Sdim    } else {
542327952Sdim      if (RenameInfo.FromDecl && RenameInfo.Context) {
543327952Sdim        if (!llvm::isa<clang::TranslationUnitDecl>(
544327952Sdim                RenameInfo.Context->getDeclContext())) {
545327952Sdim          ReplacedName = tooling::replaceNestedName(
546327952Sdim              RenameInfo.Specifier, RenameInfo.Context->getDeclContext(),
547327952Sdim              RenameInfo.FromDecl,
548327952Sdim              NewName.startswith("::") ? NewName.str()
549327952Sdim                                       : ("::" + NewName).str());
550327952Sdim        } else {
551327952Sdim          // This fixes the case where type `T` is a parameter inside a function
552327952Sdim          // type (e.g. `std::function<void(T)>`) and the DeclContext of `T`
553327952Sdim          // becomes the translation unit. As a workaround, we simply use
554327952Sdim          // fully-qualified name here for all references whose `DeclContext` is
555327952Sdim          // the translation unit and ignore the possible existence of
556327952Sdim          // using-decls (in the global scope) that can shorten the replaced
557327952Sdim          // name.
558327952Sdim          llvm::StringRef ActualName = Lexer::getSourceText(
559327952Sdim              CharSourceRange::getTokenRange(
560327952Sdim                  SourceRange(RenameInfo.Begin, RenameInfo.End)),
561327952Sdim              SM, TranslationUnitDecl->getASTContext().getLangOpts());
562327952Sdim          // Add the leading "::" back if the name written in the code contains
563327952Sdim          // it.
564327952Sdim          if (ActualName.startswith("::") && !NewName.startswith("::")) {
565327952Sdim            ReplacedName = "::" + NewName.str();
566327952Sdim          }
567327952Sdim        }
568320535Sdim      }
569327952Sdim      // If the NewName contains leading "::", add it back.
570327952Sdim      if (NewName.startswith("::") && NewName.substr(2) == ReplacedName)
571327952Sdim        ReplacedName = NewName.str();
572320535Sdim    }
573320535Sdim    Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
574320535Sdim  }
575320535Sdim
576320535Sdim  // Hanlde using declarations explicitly as "using a::Foo" don't trigger
577320535Sdim  // typeLoc for "a::Foo".
578320535Sdim  for (const auto *Using : Finder.getUsingDecls())
579320535Sdim    Replace(Using->getLocStart(), Using->getLocEnd(), "using " + NewName.str());
580320535Sdim
581320535Sdim  return AtomicChanges;
582320535Sdim}
583320535Sdim
584320535Sdim} // end namespace tooling
585320535Sdim} // end namespace clang
586