clang  10.0.0svn
USRFindingAction.cpp
Go to the documentation of this file.
1 //===--- USRFindingAction.cpp - Clang refactoring library -----------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// Provides an action to find USR for the symbol at <offset>, as well as
11 /// all additional USRs.
12 ///
13 //===----------------------------------------------------------------------===//
14 
16 #include "clang/AST/AST.h"
17 #include "clang/AST/ASTConsumer.h"
18 #include "clang/AST/ASTContext.h"
19 #include "clang/AST/Decl.h"
24 #include "clang/Lex/Lexer.h"
25 #include "clang/Lex/Preprocessor.h"
29 #include "clang/Tooling/Tooling.h"
30 
31 #include <algorithm>
32 #include <set>
33 #include <string>
34 #include <vector>
35 
36 using namespace llvm;
37 
38 namespace clang {
39 namespace tooling {
40 
42  // If FoundDecl is a constructor or destructor, we want to instead take
43  // the Decl of the corresponding class.
44  if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
45  FoundDecl = CtorDecl->getParent();
46  else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
47  FoundDecl = DtorDecl->getParent();
48  // FIXME: (Alex L): Canonicalize implicit template instantions, just like
49  // the indexer does it.
50 
51  // Note: please update the declaration's doc comment every time the
52  // canonicalization rules are changed.
53  return FoundDecl;
54 }
55 
56 namespace {
57 // NamedDeclFindingConsumer should delegate finding USRs of given Decl to
58 // AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given
59 // Decl refers to class and adds USRs of all overridden methods if Decl refers
60 // to virtual method.
61 class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
62 public:
63  AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
64  : FoundDecl(FoundDecl), Context(Context) {}
65 
66  std::vector<std::string> Find() {
67  // Fill OverriddenMethods and PartialSpecs storages.
68  TraverseDecl(Context.getTranslationUnitDecl());
69  if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
70  addUSRsOfOverridenFunctions(MethodDecl);
71  for (const auto &OverriddenMethod : OverriddenMethods) {
72  if (checkIfOverriddenFunctionAscends(OverriddenMethod))
73  USRSet.insert(getUSRForDecl(OverriddenMethod));
74  }
75  addUSRsOfInstantiatedMethods(MethodDecl);
76  } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
77  handleCXXRecordDecl(RecordDecl);
78  } else if (const auto *TemplateDecl =
79  dyn_cast<ClassTemplateDecl>(FoundDecl)) {
80  handleClassTemplateDecl(TemplateDecl);
81  } else {
82  USRSet.insert(getUSRForDecl(FoundDecl));
83  }
84  return std::vector<std::string>(USRSet.begin(), USRSet.end());
85  }
86 
87  bool shouldVisitTemplateInstantiations() const { return true; }
88 
89  bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
90  if (MethodDecl->isVirtual())
91  OverriddenMethods.push_back(MethodDecl);
92  if (MethodDecl->getInstantiatedFromMemberFunction())
93  InstantiatedMethods.push_back(MethodDecl);
94  return true;
95  }
96 
97  bool VisitClassTemplatePartialSpecializationDecl(
98  const ClassTemplatePartialSpecializationDecl *PartialSpec) {
99  PartialSpecs.push_back(PartialSpec);
100  return true;
101  }
102 
103 private:
104  void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
105  if (!RecordDecl->getDefinition()) {
106  USRSet.insert(getUSRForDecl(RecordDecl));
107  return;
108  }
109  RecordDecl = RecordDecl->getDefinition();
110  if (const auto *ClassTemplateSpecDecl =
111  dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
112  handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
113  addUSRsOfCtorDtors(RecordDecl);
114  }
115 
116  void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
117  for (const auto *Specialization : TemplateDecl->specializations())
118  addUSRsOfCtorDtors(Specialization);
119 
120  for (const auto *PartialSpec : PartialSpecs) {
121  if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
122  addUSRsOfCtorDtors(PartialSpec);
123  }
124  addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
125  }
126 
127  void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
128  RecordDecl = RecordDecl->getDefinition();
129 
130  // Skip if the CXXRecordDecl doesn't have definition.
131  if (!RecordDecl)
132  return;
133 
134  for (const auto *CtorDecl : RecordDecl->ctors())
135  USRSet.insert(getUSRForDecl(CtorDecl));
136 
137  USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
138  USRSet.insert(getUSRForDecl(RecordDecl));
139  }
140 
141  void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
142  USRSet.insert(getUSRForDecl(MethodDecl));
143  // Recursively visit each OverridenMethod.
144  for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
145  addUSRsOfOverridenFunctions(OverriddenMethod);
146  }
147 
148  void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) {
149  // For renaming a class template method, all references of the instantiated
150  // member methods should be renamed too, so add USRs of the instantiated
151  // methods to the USR set.
152  USRSet.insert(getUSRForDecl(MethodDecl));
153  if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction())
154  USRSet.insert(getUSRForDecl(FT));
155  for (const auto *Method : InstantiatedMethods) {
156  if (USRSet.find(getUSRForDecl(
157  Method->getInstantiatedFromMemberFunction())) != USRSet.end())
158  USRSet.insert(getUSRForDecl(Method));
159  }
160  }
161 
162  bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
163  for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
164  if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
165  return true;
166  return checkIfOverriddenFunctionAscends(OverriddenMethod);
167  }
168  return false;
169  }
170 
171  const Decl *FoundDecl;
172  ASTContext &Context;
173  std::set<std::string> USRSet;
174  std::vector<const CXXMethodDecl *> OverriddenMethods;
175  std::vector<const CXXMethodDecl *> InstantiatedMethods;
176  std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
177 };
178 } // namespace
179 
180 std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
181  ASTContext &Context) {
182  AdditionalUSRFinder Finder(ND, Context);
183  return Finder.Find();
184 }
185 
187 public:
189  ArrayRef<std::string> QualifiedNames,
190  std::vector<std::string> &SpellingNames,
191  std::vector<std::vector<std::string>> &USRList,
192  bool Force, bool &ErrorOccurred)
193  : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
194  SpellingNames(SpellingNames), USRList(USRList), Force(Force),
195  ErrorOccurred(ErrorOccurred) {}
196 
197 private:
198  bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
199  unsigned SymbolOffset, const std::string &QualifiedName) {
200  DiagnosticsEngine &Engine = Context.getDiagnostics();
201  const FileID MainFileID = SourceMgr.getMainFileID();
202 
203  if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
204  ErrorOccurred = true;
205  unsigned InvalidOffset = Engine.getCustomDiagID(
207  "SourceLocation in file %0 at offset %1 is invalid");
208  Engine.Report(SourceLocation(), InvalidOffset)
209  << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
210  return false;
211  }
212 
213  const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
214  .getLocWithOffset(SymbolOffset);
215  const NamedDecl *FoundDecl = QualifiedName.empty()
216  ? getNamedDeclAt(Context, Point)
217  : getNamedDeclFor(Context, QualifiedName);
218 
219  if (FoundDecl == nullptr) {
220  if (QualifiedName.empty()) {
221  FullSourceLoc FullLoc(Point, SourceMgr);
222  unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
224  "clang-rename could not find symbol (offset %0)");
225  Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
226  ErrorOccurred = true;
227  return false;
228  }
229 
230  if (Force) {
231  SpellingNames.push_back(std::string());
232  USRList.push_back(std::vector<std::string>());
233  return true;
234  }
235 
236  unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
237  DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
238  Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
239  ErrorOccurred = true;
240  return false;
241  }
242 
243  FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
244  SpellingNames.push_back(FoundDecl->getNameAsString());
245  AdditionalUSRFinder Finder(FoundDecl, Context);
246  USRList.push_back(Finder.Find());
247  return true;
248  }
249 
250  void HandleTranslationUnit(ASTContext &Context) override {
251  const SourceManager &SourceMgr = Context.getSourceManager();
252  for (unsigned Offset : SymbolOffsets) {
253  if (!FindSymbol(Context, SourceMgr, Offset, ""))
254  return;
255  }
256  for (const std::string &QualifiedName : QualifiedNames) {
257  if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
258  return;
259  }
260  }
261 
262  ArrayRef<unsigned> SymbolOffsets;
263  ArrayRef<std::string> QualifiedNames;
264  std::vector<std::string> &SpellingNames;
265  std::vector<std::vector<std::string>> &USRList;
266  bool Force;
267  bool &ErrorOccurred;
268 };
269 
270 std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
271  return std::make_unique<NamedDeclFindingConsumer>(
272  SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
273  ErrorOccurred);
274 }
275 
276 } // end namespace tooling
277 } // end namespace clang
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file. ...
Defines the clang::ASTContext interface.
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs...
Definition: ASTConsumer.h:33
Defines the clang::FileManager interface and associated types.
Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be placed into a PointerUnion...
Definition: Dominators.h:30
std::vector< std::string > getUSRsForDeclaration(const NamedDecl *ND, ASTContext &Context)
Returns the set of USRs that correspond to the given declaration.
spec_range specializations() const
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:88
bool isVirtual() const
Definition: DeclCXX.h:1948
DiagnosticsEngine & getDiagnostics() const
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1290
Represents a struct/union/class.
Definition: Decl.h:3662
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:160
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:149
llvm::Error Error
Provides an action to find all relevant USRs at a point.
Methods for determining the USR of a symbol at a location in source code.
FunctionDecl * getInstantiatedFromMemberFunction() const
If this function is an instantiation of a member function of a class template specialization, retrieves the function from which it was instantiated.
Definition: Decl.cpp:3458
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
CXXDestructorDecl * getDestructor() const
Returns the destructor decl for this class.
Definition: DeclCXX.cpp:1765
CXXRecordDecl * getTemplatedDecl() const
Get the underlying class declarations of the template.
unsigned getFileIDSize(FileID FID) const
The size of the SLocEntry that FID represents.
const NamedDecl * getNamedDeclFor(const ASTContext &Context, const std::string &Name)
Definition: USRFinder.cpp:127
unsigned Offset
Definition: Format.cpp:1809
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
Defines the clang::Preprocessor interface.
ClassTemplateDecl * getSpecializedTemplate() const
Retrieve the template that this specialization specializes.
overridden_method_range overridden_methods() const
Definition: DeclCXX.cpp:2285
CXXRecordDecl * getDefinition() const
Definition: DeclCXX.h:535
const NamedDecl * getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl)
Returns the canonical declaration that best represents a symbol that can be renamed.
const NamedDecl * getNamedDeclAt(const ASTContext &Context, const SourceLocation Point)
Definition: USRFinder.cpp:76
Encodes a location in the source.
StringRef getName() const
Definition: FileManager.h:102
std::string getUSRForDecl(const Decl *Decl)
Definition: USRFinder.cpp:134
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
Definition: Decl.h:291
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:1905
unsigned getCustomDiagID(Level L, const char(&FormatString)[N])
Return an ID for a diagnostic with the specified format string and level.
Definition: Diagnostic.h:776
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Dataflow Directional Tag Classes.
The base class of all kinds of template declarations (e.g., class, function, etc.).
Definition: DeclTemplate.h:387
FileID getMainFileID() const
Returns the FileID of the main source file.
SourceManager & getSourceManager()
Definition: ASTContext.h:678
TranslationUnitDecl * getTranslationUnitDecl() const
Definition: ASTContext.h:1008
Represents a C++ struct/union/class.
Definition: DeclCXX.h:255
Declaration of a class template.
NamedDeclFindingConsumer(ArrayRef< unsigned > SymbolOffsets, ArrayRef< std::string > QualifiedNames, std::vector< std::string > &SpellingNames, std::vector< std::vector< std::string >> &USRList, bool Force, bool &ErrorOccurred)
A SourceLocation and its associated SourceManager.
Defines the clang::FrontendAction interface and various convenience abstract classes (clang::ASTFront...
This represents a decl that may have a name.
Definition: Decl.h:248
ctor_range ctors() const
Definition: DeclCXX.h:651
This class handles loading and caching of source files into memory.