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