clang  5.0.0
USRLocFinder.cpp
Go to the documentation of this file.
1 //===--- USRLocFinder.cpp - Clang refactoring library ---------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief Methods for finding all instances of a USR. Our strategy is very
12 /// simple; we just compare the USR at every relevant AST node with the one
13 /// provided.
14 ///
15 //===----------------------------------------------------------------------===//
16 
18 #include "clang/AST/ASTContext.h"
20 #include "clang/Basic/LLVM.h"
23 #include "clang/Lex/Lexer.h"
27 #include "llvm/ADT/StringRef.h"
28 #include "llvm/Support/Casting.h"
29 #include <cstddef>
30 #include <set>
31 #include <string>
32 #include <vector>
33 
34 using namespace llvm;
35 
36 namespace clang {
37 namespace tooling {
38 
39 namespace {
40 
41 // \brief This visitor recursively searches for all instances of a USR in a
42 // translation unit and stores them for later usage.
43 class USRLocFindingASTVisitor
44  : public RecursiveSymbolVisitor<USRLocFindingASTVisitor> {
45 public:
46  explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
47  StringRef PrevName,
48  const ASTContext &Context)
49  : RecursiveSymbolVisitor(Context.getSourceManager(),
50  Context.getLangOpts()),
51  USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
52  }
53 
54  bool visitSymbolOccurrence(const NamedDecl *ND,
55  ArrayRef<SourceRange> NameRanges) {
56  if (USRSet.find(getUSRForDecl(ND)) != USRSet.end()) {
57  assert(NameRanges.size() == 1 &&
58  "Multiple name pieces are not supported yet!");
59  SourceLocation Loc = NameRanges[0].getBegin();
60  const SourceManager &SM = Context.getSourceManager();
61  // TODO: Deal with macro occurrences correctly.
62  if (Loc.isMacroID())
63  Loc = SM.getSpellingLoc(Loc);
64  checkAndAddLocation(Loc);
65  }
66  return true;
67  }
68 
69  // Non-visitors:
70 
71  // \brief Returns a list of unique locations. Duplicate or overlapping
72  // locations are erroneous and should be reported!
73  const std::vector<clang::SourceLocation> &getLocationsFound() const {
74  return LocationsFound;
75  }
76 
77 private:
78  void checkAndAddLocation(SourceLocation Loc) {
79  const SourceLocation BeginLoc = Loc;
80  const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
81  BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
82  StringRef TokenName =
83  Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
84  Context.getSourceManager(), Context.getLangOpts());
85  size_t Offset = TokenName.find(PrevName);
86 
87  // The token of the source location we find actually has the old
88  // name.
89  if (Offset != StringRef::npos)
90  LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
91  }
92 
93  const std::set<std::string> USRSet;
94  const std::string PrevName;
95  std::vector<clang::SourceLocation> LocationsFound;
96  const ASTContext &Context;
97 };
98 
99 SourceLocation StartLocationForType(TypeLoc TL) {
100  // For elaborated types (e.g. `struct a::A`) we want the portion after the
101  // `struct` but including the namespace qualifier, `a::`.
102  if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) {
103  NestedNameSpecifierLoc NestedNameSpecifier =
104  ElaboratedTypeLoc.getQualifierLoc();
105  if (NestedNameSpecifier.getNestedNameSpecifier())
106  return NestedNameSpecifier.getBeginLoc();
107  TL = TL.getNextTypeLoc();
108  }
109  return TL.getLocStart();
110 }
111 
112 SourceLocation EndLocationForType(TypeLoc TL) {
113  // Dig past any namespace or keyword qualifications.
114  while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
115  TL.getTypeLocClass() == TypeLoc::Qualified)
116  TL = TL.getNextTypeLoc();
117 
118  // The location for template specializations (e.g. Foo<int>) includes the
119  // templated types in its location range. We want to restrict this to just
120  // before the `<` character.
121  if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
122  return TL.castAs<TemplateSpecializationTypeLoc>()
123  .getLAngleLoc()
124  .getLocWithOffset(-1);
125  }
126  return TL.getEndLoc();
127 }
128 
129 NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
130  // Dig past any keyword qualifications.
131  while (TL.getTypeLocClass() == TypeLoc::Qualified)
132  TL = TL.getNextTypeLoc();
133 
134  // For elaborated types (e.g. `struct a::A`) we want the portion after the
135  // `struct` but including the namespace qualifier, `a::`.
136  if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
137  return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
138  return nullptr;
139 }
140 
141 // Find all locations identified by the given USRs for rename.
142 //
143 // This class will traverse the AST and find every AST node whose USR is in the
144 // given USRs' set.
145 class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
146 public:
147  RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
148  : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
149 
150  // A structure records all information of a symbol reference being renamed.
151  // We try to add as few prefix qualifiers as possible.
152  struct RenameInfo {
153  // The begin location of a symbol being renamed.
154  SourceLocation Begin;
155  // The end location of a symbol being renamed.
156  SourceLocation End;
157  // The declaration of a symbol being renamed (can be nullptr).
158  const NamedDecl *FromDecl;
159  // The declaration in which the nested name is contained (can be nullptr).
160  const Decl *Context;
161  // The nested name being replaced (can be nullptr).
162  const NestedNameSpecifier *Specifier;
163  };
164 
165  // FIXME: Currently, prefix qualifiers will be added to the renamed symbol
166  // definition (e.g. "class Foo {};" => "class b::Bar {};" when renaming
167  // "a::Foo" to "b::Bar").
168  // For renaming declarations/definitions, prefix qualifiers should be filtered
169  // out.
170  bool VisitNamedDecl(const NamedDecl *Decl) {
171  // UsingDecl has been handled in other place.
172  if (llvm::isa<UsingDecl>(Decl))
173  return true;
174 
175  // DestructorDecl has been handled in Typeloc.
176  if (llvm::isa<CXXDestructorDecl>(Decl))
177  return true;
178 
179  if (Decl->isImplicit())
180  return true;
181 
182  if (isInUSRSet(Decl)) {
183  RenameInfo Info = {Decl->getLocation(), Decl->getLocation(), nullptr,
184  nullptr, nullptr};
185  RenameInfos.push_back(Info);
186  }
187  return true;
188  }
189 
190  bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
191  const NamedDecl *Decl = Expr->getFoundDecl();
192  if (isInUSRSet(Decl)) {
193  RenameInfo Info = {Expr->getSourceRange().getBegin(),
194  Expr->getSourceRange().getEnd(), Decl,
195  getClosestAncestorDecl(*Expr), Expr->getQualifier()};
196  RenameInfos.push_back(Info);
197  }
198 
199  return true;
200  }
201 
202  bool VisitUsingDecl(const UsingDecl *Using) {
203  for (const auto *UsingShadow : Using->shadows()) {
204  if (isInUSRSet(UsingShadow->getTargetDecl())) {
205  UsingDecls.push_back(Using);
206  break;
207  }
208  }
209  return true;
210  }
211 
212  bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
213  if (!NestedLoc.getNestedNameSpecifier()->getAsType())
214  return true;
215  if (IsTypeAliasWhichWillBeRenamedElsewhere(NestedLoc.getTypeLoc()))
216  return true;
217 
218  if (const auto *TargetDecl =
219  getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
220  if (isInUSRSet(TargetDecl)) {
221  RenameInfo Info = {NestedLoc.getBeginLoc(),
222  EndLocationForType(NestedLoc.getTypeLoc()),
223  TargetDecl, getClosestAncestorDecl(NestedLoc),
224  NestedLoc.getNestedNameSpecifier()->getPrefix()};
225  RenameInfos.push_back(Info);
226  }
227  }
228  return true;
229  }
230 
231  bool VisitTypeLoc(TypeLoc Loc) {
232  if (IsTypeAliasWhichWillBeRenamedElsewhere(Loc))
233  return true;
234 
235  auto Parents = Context.getParents(Loc);
236  TypeLoc ParentTypeLoc;
237  if (!Parents.empty()) {
238  // Handle cases of nested name specificier locations.
239  //
240  // The VisitNestedNameSpecifierLoc interface is not impelmented in
241  // RecursiveASTVisitor, we have to handle it explicitly.
242  if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
243  VisitNestedNameSpecifierLocations(*NSL);
244  return true;
245  }
246 
247  if (const auto *TL = Parents[0].get<TypeLoc>())
248  ParentTypeLoc = *TL;
249  }
250 
251  // Handle the outermost TypeLoc which is directly linked to the interesting
252  // declaration and don't handle nested name specifier locations.
253  if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
254  if (isInUSRSet(TargetDecl)) {
255  // Only handle the outermost typeLoc.
256  //
257  // For a type like "a::Foo", there will be two typeLocs for it.
258  // One ElaboratedType, the other is RecordType:
259  //
260  // ElaboratedType 0x33b9390 'a::Foo' sugar
261  // `-RecordType 0x338fef0 'class a::Foo'
262  // `-CXXRecord 0x338fe58 'Foo'
263  //
264  // Skip if this is an inner typeLoc.
265  if (!ParentTypeLoc.isNull() &&
266  isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
267  return true;
268  RenameInfo Info = {StartLocationForType(Loc), EndLocationForType(Loc),
269  TargetDecl, getClosestAncestorDecl(Loc),
270  GetNestedNameForType(Loc)};
271  RenameInfos.push_back(Info);
272  return true;
273  }
274  }
275 
276  // Handle specific template class specialiation cases.
277  if (const auto *TemplateSpecType =
278  dyn_cast<TemplateSpecializationType>(Loc.getType())) {
279  TypeLoc TargetLoc = Loc;
280  if (!ParentTypeLoc.isNull()) {
281  if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
282  TargetLoc = ParentTypeLoc;
283  }
284 
285  if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
286  TypeLoc TargetLoc = Loc;
287  // FIXME: Find a better way to handle this case.
288  // For the qualified template class specification type like
289  // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
290  // (ElaboratedType) of the TemplateSpecializationType in order to
291  // catch the prefix qualifiers "ns::".
292  if (!ParentTypeLoc.isNull() &&
293  llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
294  TargetLoc = ParentTypeLoc;
295  RenameInfo Info = {
296  StartLocationForType(TargetLoc), EndLocationForType(TargetLoc),
297  TemplateSpecType->getTemplateName().getAsTemplateDecl(),
298  getClosestAncestorDecl(
300  GetNestedNameForType(TargetLoc)};
301  RenameInfos.push_back(Info);
302  }
303  }
304  return true;
305  }
306 
307  // Returns a list of RenameInfo.
308  const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
309 
310  // Returns a list of using declarations which are needed to update.
311  const std::vector<const UsingDecl *> &getUsingDecls() const {
312  return UsingDecls;
313  }
314 
315 private:
316  // FIXME: This method may not be suitable for renaming other types like alias
317  // types. Need to figure out a way to handle it.
318  bool IsTypeAliasWhichWillBeRenamedElsewhere(TypeLoc TL) const {
319  while (!TL.isNull()) {
320  // SubstTemplateTypeParm is the TypeLocation class for a substituted type
321  // inside a template expansion so we ignore these. For example:
322  //
323  // template<typename T> struct S {
324  // T t; // <-- this T becomes a TypeLoc(int) with class
325  // // SubstTemplateTypeParm when S<int> is instantiated
326  // }
327  if (TL.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm)
328  return true;
329 
330  // Typedef is the TypeLocation class for a type which is a typedef to the
331  // type we want to replace. We ignore the use of the typedef as we will
332  // replace the definition of it. For example:
333  //
334  // typedef int T;
335  // T a; // <--- This T is a TypeLoc(int) with class Typedef.
336  if (TL.getTypeLocClass() == TypeLoc::Typedef)
337  return true;
338  TL = TL.getNextTypeLoc();
339  }
340  return false;
341  }
342 
343  // Get the supported declaration from a given typeLoc. If the declaration type
344  // is not supported, returns nullptr.
345  //
346  // FIXME: support more types, e.g. enum, type alias.
347  const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
348  if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
349  return RD;
350  return nullptr;
351  }
352 
353  // Get the closest ancester which is a declaration of a given AST node.
354  template <typename ASTNodeType>
355  const Decl *getClosestAncestorDecl(const ASTNodeType &Node) {
356  auto Parents = Context.getParents(Node);
357  // FIXME: figure out how to handle it when there are multiple parents.
358  if (Parents.size() != 1)
359  return nullptr;
360  if (ast_type_traits::ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(
361  Parents[0].getNodeKind()))
362  return Parents[0].template get<Decl>();
363  return getClosestAncestorDecl(Parents[0]);
364  }
365 
366  // Get the parent typeLoc of a given typeLoc. If there is no such parent,
367  // return nullptr.
368  const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
369  auto Parents = Context.getParents(Loc);
370  // FIXME: figure out how to handle it when there are multiple parents.
371  if (Parents.size() != 1)
372  return nullptr;
373  return Parents[0].get<TypeLoc>();
374  }
375 
376  // Check whether the USR of a given Decl is in the USRSet.
377  bool isInUSRSet(const Decl *Decl) const {
378  auto USR = getUSRForDecl(Decl);
379  if (USR.empty())
380  return false;
381  return llvm::is_contained(USRSet, USR);
382  }
383 
384  const std::set<std::string> USRSet;
385  ASTContext &Context;
386  std::vector<RenameInfo> RenameInfos;
387  // Record all interested using declarations which contains the using-shadow
388  // declarations of the symbol declarations being renamed.
389  std::vector<const UsingDecl *> UsingDecls;
390 };
391 
392 } // namespace
393 
394 std::vector<SourceLocation>
395 getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
396  Decl *Decl) {
397  USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
398  Visitor.TraverseDecl(Decl);
399  return Visitor.getLocationsFound();
400 }
401 
402 std::vector<tooling::AtomicChange>
404  llvm::StringRef NewName, Decl *TranslationUnitDecl) {
405  RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext());
406  Finder.TraverseDecl(TranslationUnitDecl);
407 
408  const SourceManager &SM =
409  TranslationUnitDecl->getASTContext().getSourceManager();
410 
411  std::vector<tooling::AtomicChange> AtomicChanges;
412  auto Replace = [&](SourceLocation Start, SourceLocation End,
413  llvm::StringRef Text) {
414  tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start);
415  llvm::Error Err = ReplaceChange.replace(
416  SM, CharSourceRange::getTokenRange(Start, End), Text);
417  if (Err) {
418  llvm::errs() << "Faile to add replacement to AtomicChange: "
419  << llvm::toString(std::move(Err)) << "\n";
420  return;
421  }
422  AtomicChanges.push_back(std::move(ReplaceChange));
423  };
424 
425  for (const auto &RenameInfo : Finder.getRenameInfos()) {
426  std::string ReplacedName = NewName.str();
427  if (RenameInfo.FromDecl && RenameInfo.Context) {
428  if (!llvm::isa<clang::TranslationUnitDecl>(
429  RenameInfo.Context->getDeclContext())) {
430  ReplacedName = tooling::replaceNestedName(
431  RenameInfo.Specifier, RenameInfo.Context->getDeclContext(),
432  RenameInfo.FromDecl,
433  NewName.startswith("::") ? NewName.str() : ("::" + NewName).str());
434  }
435  }
436  // If the NewName contains leading "::", add it back.
437  if (NewName.startswith("::") && NewName.substr(2) == ReplacedName)
438  ReplacedName = NewName.str();
439  Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
440  }
441 
442  // Hanlde using declarations explicitly as "using a::Foo" don't trigger
443  // typeLoc for "a::Foo".
444  for (const auto *Using : Finder.getUsingDecls())
445  Replace(Using->getLocStart(), Using->getLocEnd(), "using " + NewName.str());
446 
447  return AtomicChanges;
448 }
449 
450 } // end namespace tooling
451 } // end namespace clang
std::vector< tooling::AtomicChange > createRenameAtomicChanges(llvm::ArrayRef< std::string > USRs, llvm::StringRef NewName, Decl *TranslationUnitDecl)
Create atomic changes for renaming all symbol references which are identified by the USRs set to a gi...
Defines the clang::ASTContext interface.
llvm::Error replace(const SourceManager &SM, const CharSourceRange &Range, llvm::StringRef ReplacementText)
Adds a replacement that replaces the given Range with ReplacementText.
Defines the SourceManager interface.
const NestedNameSpecifier * Specifier
The l-value was an access to a declared entity or something equivalently strong, like the address of ...
A wrapper class around RecursiveASTVisitor that visits each occurrences of a named symbol...
const std::set< std::string > USRSet
uint32_t Offset
Definition: CacheTokens.cpp:43
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified...
std::vector< RenameInfo > RenameInfos
Methods for determining the USR of a symbol at a location in source code.
const std::string PrevName
std::string replaceNestedName(const NestedNameSpecifier *Use, const DeclContext *UseContext, const NamedDecl *FromDecl, StringRef ReplacementString)
Emulate a lookup to replace one nested name specifier with another using as few additional namespace ...
Definition: Lookup.cpp:117
Provides functionality for finding all instances of a USR in a given AST.
SourceLocation End
SourceLocation Begin
const SourceManager & SM
Definition: Format.cpp:1293
const NamedDecl * FromDecl
ASTMatchFinder *const Finder
Encodes a location in the source.
std::string getUSRForDecl(const Decl *Decl)
Definition: USRFinder.cpp:135
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:346
std::vector< const UsingDecl * > UsingDecls
std::vector< clang::SourceLocation > LocationsFound
ast_type_traits::DynTypedNode Node
std::unique_ptr< DiagnosticConsumer > create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords=false)
Returns a DiagnosticConsumer that serializes diagnostics to a bitcode file.
std::string toString(const til::SExpr *E)
SourceManager & getSourceManager()
Definition: ASTContext.h:616
Defines the clang::SourceLocation class and associated facilities.
TranslationUnitDecl - The top declaration context.
Definition: Decl.h:80
An atomic change is used to create and group a set of source edits, e.g.
Definition: AtomicChange.h:36
const ASTContext & Context
StringRef Text
Definition: Format.cpp:1302
std::vector< SourceLocation > getLocationsOfUSRs(const std::vector< std::string > &USRs, StringRef PrevName, Decl *Decl)
This class handles loading and caching of source files into memory.