clang  10.0.0git
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)
43  return nullptr;
44  // If FoundDecl is a constructor or destructor, we want to instead take
45  // the Decl of the corresponding class.
46  if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
47  FoundDecl = CtorDecl->getParent();
48  else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
49  FoundDecl = DtorDecl->getParent();
50  // FIXME: (Alex L): Canonicalize implicit template instantions, just like
51  // the indexer does it.
52 
53  // Note: please update the declaration's doc comment every time the
54  // canonicalization rules are changed.
55  return FoundDecl;
56 }
57 
58 namespace {
59 // NamedDeclFindingConsumer should delegate finding USRs of given Decl to
60 // AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given
61 // Decl refers to class and adds USRs of all overridden methods if Decl refers
62 // to virtual method.
63 class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
64 public:
65  AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
66  : FoundDecl(FoundDecl), Context(Context) {}
67 
68  std::vector<std::string> Find() {
69  // Fill OverriddenMethods and PartialSpecs storages.
70  TraverseAST(Context);
71  if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
72  addUSRsOfOverridenFunctions(MethodDecl);
73  for (const auto &OverriddenMethod : OverriddenMethods) {
74  if (checkIfOverriddenFunctionAscends(OverriddenMethod))
75  USRSet.insert(getUSRForDecl(OverriddenMethod));
76  }
77  addUSRsOfInstantiatedMethods(MethodDecl);
78  } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
79  handleCXXRecordDecl(RecordDecl);
80  } else if (const auto *TemplateDecl =
81  dyn_cast<ClassTemplateDecl>(FoundDecl)) {
82  handleClassTemplateDecl(TemplateDecl);
83  } else {
84  USRSet.insert(getUSRForDecl(FoundDecl));
85  }
86  return std::vector<std::string>(USRSet.begin(), USRSet.end());
87  }
88 
89  bool shouldVisitTemplateInstantiations() const { return true; }
90 
91  bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
92  if (MethodDecl->isVirtual())
93  OverriddenMethods.push_back(MethodDecl);
94  if (MethodDecl->getInstantiatedFromMemberFunction())
95  InstantiatedMethods.push_back(MethodDecl);
96  return true;
97  }
98 
99  bool VisitClassTemplatePartialSpecializationDecl(
100  const ClassTemplatePartialSpecializationDecl *PartialSpec) {
101  PartialSpecs.push_back(PartialSpec);
102  return true;
103  }
104 
105 private:
106  void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
107  if (!RecordDecl->getDefinition()) {
108  USRSet.insert(getUSRForDecl(RecordDecl));
109  return;
110  }
111  RecordDecl = RecordDecl->getDefinition();
112  if (const auto *ClassTemplateSpecDecl =
113  dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
114  handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
115  addUSRsOfCtorDtors(RecordDecl);
116  }
117 
118  void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
119  for (const auto *Specialization : TemplateDecl->specializations())
120  addUSRsOfCtorDtors(Specialization);
121 
122  for (const auto *PartialSpec : PartialSpecs) {
123  if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
124  addUSRsOfCtorDtors(PartialSpec);
125  }
126  addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
127  }
128 
129  void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
130  RecordDecl = RecordDecl->getDefinition();
131 
132  // Skip if the CXXRecordDecl doesn't have definition.
133  if (!RecordDecl)
134  return;
135 
136  for (const auto *CtorDecl : RecordDecl->ctors())
137  USRSet.insert(getUSRForDecl(CtorDecl));
138 
139  USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
140  USRSet.insert(getUSRForDecl(RecordDecl));
141  }
142 
143  void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
144  USRSet.insert(getUSRForDecl(MethodDecl));
145  // Recursively visit each OverridenMethod.
146  for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
147  addUSRsOfOverridenFunctions(OverriddenMethod);
148  }
149 
150  void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) {
151  // For renaming a class template method, all references of the instantiated
152  // member methods should be renamed too, so add USRs of the instantiated
153  // methods to the USR set.
154  USRSet.insert(getUSRForDecl(MethodDecl));
155  if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction())
156  USRSet.insert(getUSRForDecl(FT));
157  for (const auto *Method : InstantiatedMethods) {
158  if (USRSet.find(getUSRForDecl(
159  Method->getInstantiatedFromMemberFunction())) != USRSet.end())
160  USRSet.insert(getUSRForDecl(Method));
161  }
162  }
163 
164  bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
165  for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
166  if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
167  return true;
168  return checkIfOverriddenFunctionAscends(OverriddenMethod);
169  }
170  return false;
171  }
172 
173  const Decl *FoundDecl;
174  ASTContext &Context;
175  std::set<std::string> USRSet;
176  std::vector<const CXXMethodDecl *> OverriddenMethods;
177  std::vector<const CXXMethodDecl *> InstantiatedMethods;
178  std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
179 };
180 } // namespace
181 
182 std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
183  ASTContext &Context) {
184  AdditionalUSRFinder Finder(ND, Context);
185  return Finder.Find();
186 }
187 
189 public:
191  ArrayRef<std::string> QualifiedNames,
192  std::vector<std::string> &SpellingNames,
193  std::vector<std::vector<std::string>> &USRList,
194  bool Force, bool &ErrorOccurred)
195  : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
196  SpellingNames(SpellingNames), USRList(USRList), Force(Force),
197  ErrorOccurred(ErrorOccurred) {}
198 
199 private:
200  bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
201  unsigned SymbolOffset, const std::string &QualifiedName) {
202  DiagnosticsEngine &Engine = Context.getDiagnostics();
203  const FileID MainFileID = SourceMgr.getMainFileID();
204 
205  if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
206  ErrorOccurred = true;
207  unsigned InvalidOffset = Engine.getCustomDiagID(
209  "SourceLocation in file %0 at offset %1 is invalid");
210  Engine.Report(SourceLocation(), InvalidOffset)
211  << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
212  return false;
213  }
214 
215  const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
216  .getLocWithOffset(SymbolOffset);
217  const NamedDecl *FoundDecl = QualifiedName.empty()
218  ? getNamedDeclAt(Context, Point)
219  : getNamedDeclFor(Context, QualifiedName);
220 
221  if (FoundDecl == nullptr) {
222  if (QualifiedName.empty()) {
223  FullSourceLoc FullLoc(Point, SourceMgr);
224  unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
226  "clang-rename could not find symbol (offset %0)");
227  Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
228  ErrorOccurred = true;
229  return false;
230  }
231 
232  if (Force) {
233  SpellingNames.push_back(std::string());
234  USRList.push_back(std::vector<std::string>());
235  return true;
236  }
237 
238  unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
239  DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
240  Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
241  ErrorOccurred = true;
242  return false;
243  }
244 
245  FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
246  SpellingNames.push_back(FoundDecl->getNameAsString());
247  AdditionalUSRFinder Finder(FoundDecl, Context);
248  USRList.push_back(Finder.Find());
249  return true;
250  }
251 
252  void HandleTranslationUnit(ASTContext &Context) override {
253  const SourceManager &SourceMgr = Context.getSourceManager();
254  for (unsigned Offset : SymbolOffsets) {
255  if (!FindSymbol(Context, SourceMgr, Offset, ""))
256  return;
257  }
258  for (const std::string &QualifiedName : QualifiedNames) {
259  if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
260  return;
261  }
262  }
263 
264  ArrayRef<unsigned> SymbolOffsets;
265  ArrayRef<std::string> QualifiedNames;
266  std::vector<std::string> &SpellingNames;
267  std::vector<std::vector<std::string>> &USRList;
268  bool Force;
269  bool &ErrorOccurred;
270 };
271 
272 std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
273  return std::make_unique<NamedDeclFindingConsumer>(
274  SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
275  ErrorOccurred);
276 }
277 
278 } // end namespace tooling
279 } // 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:1976
DiagnosticsEngine & getDiagnostics() const
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1300
Represents a struct/union/class.
Definition: Decl.h:3748
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:168
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:3533
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:1770
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:1827
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:2327
CXXRecordDecl * getDefinition() const
Definition: DeclCXX.h:533
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:266
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:1931
unsigned getCustomDiagID(Level L, const char(&FormatString)[N])
Return an ID for a diagnostic with the specified format string and level.
Definition: Diagnostic.h:782
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:402
FileID getMainFileID() const
Returns the FileID of the main source file.
SourceManager & getSourceManager()
Definition: ASTContext.h:679
Represents a C++ struct/union/class.
Definition: DeclCXX.h:253
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:223
ctor_range ctors() const
Definition: DeclCXX.h:649
This class handles loading and caching of source files into memory.