clang  19.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 if (const auto *FD = dyn_cast<FunctionDecl>(FoundDecl)) {
84  USRSet.insert(getUSRForDecl(FD));
85  if (const auto *FTD = FD->getPrimaryTemplate())
86  handleFunctionTemplateDecl(FTD);
87  } else if (const auto *FD = dyn_cast<FunctionTemplateDecl>(FoundDecl)) {
88  handleFunctionTemplateDecl(FD);
89  } else if (const auto *VTD = dyn_cast<VarTemplateDecl>(FoundDecl)) {
90  handleVarTemplateDecl(VTD);
91  } else if (const auto *VD =
92  dyn_cast<VarTemplateSpecializationDecl>(FoundDecl)) {
93  // FIXME: figure out why FoundDecl can be a VarTemplateSpecializationDecl.
94  handleVarTemplateDecl(VD->getSpecializedTemplate());
95  } else if (const auto *VD = dyn_cast<VarDecl>(FoundDecl)) {
96  USRSet.insert(getUSRForDecl(VD));
97  if (const auto *VTD = VD->getDescribedVarTemplate())
98  handleVarTemplateDecl(VTD);
99  } else {
100  USRSet.insert(getUSRForDecl(FoundDecl));
101  }
102  return std::vector<std::string>(USRSet.begin(), USRSet.end());
103  }
104 
105  bool shouldVisitTemplateInstantiations() const { return true; }
106 
107  bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
108  if (MethodDecl->isVirtual())
109  OverriddenMethods.push_back(MethodDecl);
110  if (MethodDecl->getInstantiatedFromMemberFunction())
111  InstantiatedMethods.push_back(MethodDecl);
112  return true;
113  }
114 
115 private:
116  void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
117  if (!RecordDecl->getDefinition()) {
118  USRSet.insert(getUSRForDecl(RecordDecl));
119  return;
120  }
121  RecordDecl = RecordDecl->getDefinition();
122  if (const auto *ClassTemplateSpecDecl =
123  dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
124  handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
125  addUSRsOfCtorDtors(RecordDecl);
126  }
127 
128  void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
129  for (const auto *Specialization : TemplateDecl->specializations())
130  addUSRsOfCtorDtors(Specialization);
132  TemplateDecl->getPartialSpecializations(PartialSpecs);
133  for (const auto *Spec : PartialSpecs)
134  addUSRsOfCtorDtors(Spec);
135  addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
136  }
137 
138  void handleFunctionTemplateDecl(const FunctionTemplateDecl *FTD) {
139  USRSet.insert(getUSRForDecl(FTD));
140  USRSet.insert(getUSRForDecl(FTD->getTemplatedDecl()));
141  for (const auto *S : FTD->specializations())
142  USRSet.insert(getUSRForDecl(S));
143  }
144 
145  void handleVarTemplateDecl(const VarTemplateDecl *VTD) {
146  USRSet.insert(getUSRForDecl(VTD));
147  USRSet.insert(getUSRForDecl(VTD->getTemplatedDecl()));
148  for (const auto *Spec : VTD->specializations())
149  USRSet.insert(getUSRForDecl(Spec));
151  VTD->getPartialSpecializations(PartialSpecs);
152  for (const auto *Spec : PartialSpecs)
153  USRSet.insert(getUSRForDecl(Spec));
154  }
155 
156  void addUSRsOfCtorDtors(const CXXRecordDecl *RD) {
157  const auto* RecordDecl = RD->getDefinition();
158 
159  // Skip if the CXXRecordDecl doesn't have definition.
160  if (!RecordDecl) {
161  USRSet.insert(getUSRForDecl(RD));
162  return;
163  }
164 
165  for (const auto *CtorDecl : RecordDecl->ctors())
166  USRSet.insert(getUSRForDecl(CtorDecl));
167  // Add template constructor decls, they are not in ctors() unfortunately.
168  if (RecordDecl->hasUserDeclaredConstructor())
169  for (const auto *D : RecordDecl->decls())
170  if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
171  if (const auto *Ctor =
172  dyn_cast<CXXConstructorDecl>(FTD->getTemplatedDecl()))
173  USRSet.insert(getUSRForDecl(Ctor));
174 
175  USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
176  USRSet.insert(getUSRForDecl(RecordDecl));
177  }
178 
179  void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
180  USRSet.insert(getUSRForDecl(MethodDecl));
181  // Recursively visit each OverridenMethod.
182  for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
183  addUSRsOfOverridenFunctions(OverriddenMethod);
184  }
185 
186  void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) {
187  // For renaming a class template method, all references of the instantiated
188  // member methods should be renamed too, so add USRs of the instantiated
189  // methods to the USR set.
190  USRSet.insert(getUSRForDecl(MethodDecl));
191  if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction())
192  USRSet.insert(getUSRForDecl(FT));
193  for (const auto *Method : InstantiatedMethods) {
194  if (USRSet.find(getUSRForDecl(
195  Method->getInstantiatedFromMemberFunction())) != USRSet.end())
196  USRSet.insert(getUSRForDecl(Method));
197  }
198  }
199 
200  bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
201  for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
202  if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
203  return true;
204  return checkIfOverriddenFunctionAscends(OverriddenMethod);
205  }
206  return false;
207  }
208 
209  const Decl *FoundDecl;
210  ASTContext &Context;
211  std::set<std::string> USRSet;
212  std::vector<const CXXMethodDecl *> OverriddenMethods;
213  std::vector<const CXXMethodDecl *> InstantiatedMethods;
214 };
215 } // namespace
216 
217 std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
218  ASTContext &Context) {
219  AdditionalUSRFinder Finder(ND, Context);
220  return Finder.Find();
221 }
222 
224 public:
226  ArrayRef<std::string> QualifiedNames,
227  std::vector<std::string> &SpellingNames,
228  std::vector<std::vector<std::string>> &USRList,
229  bool Force, bool &ErrorOccurred)
230  : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
231  SpellingNames(SpellingNames), USRList(USRList), Force(Force),
232  ErrorOccurred(ErrorOccurred) {}
233 
234 private:
235  bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
236  unsigned SymbolOffset, const std::string &QualifiedName) {
237  DiagnosticsEngine &Engine = Context.getDiagnostics();
238  const FileID MainFileID = SourceMgr.getMainFileID();
239 
240  if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
241  ErrorOccurred = true;
242  unsigned InvalidOffset = Engine.getCustomDiagID(
244  "SourceLocation in file %0 at offset %1 is invalid");
245  Engine.Report(SourceLocation(), InvalidOffset)
246  << SourceMgr.getFileEntryRefForID(MainFileID)->getName()
247  << SymbolOffset;
248  return false;
249  }
250 
251  const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
252  .getLocWithOffset(SymbolOffset);
253  const NamedDecl *FoundDecl = QualifiedName.empty()
254  ? getNamedDeclAt(Context, Point)
255  : getNamedDeclFor(Context, QualifiedName);
256 
257  if (FoundDecl == nullptr) {
258  if (QualifiedName.empty()) {
259  FullSourceLoc FullLoc(Point, SourceMgr);
260  unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
262  "clang-rename could not find symbol (offset %0)");
263  Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
264  ErrorOccurred = true;
265  return false;
266  }
267 
268  if (Force) {
269  SpellingNames.push_back(std::string());
270  USRList.push_back(std::vector<std::string>());
271  return true;
272  }
273 
274  unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
275  DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
276  Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
277  ErrorOccurred = true;
278  return false;
279  }
280 
281  FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
282  SpellingNames.push_back(FoundDecl->getNameAsString());
283  AdditionalUSRFinder Finder(FoundDecl, Context);
284  USRList.push_back(Finder.Find());
285  return true;
286  }
287 
288  void HandleTranslationUnit(ASTContext &Context) override {
289  const SourceManager &SourceMgr = Context.getSourceManager();
290  for (unsigned Offset : SymbolOffsets) {
291  if (!FindSymbol(Context, SourceMgr, Offset, ""))
292  return;
293  }
294  for (const std::string &QualifiedName : QualifiedNames) {
295  if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
296  return;
297  }
298  }
299 
300  ArrayRef<unsigned> SymbolOffsets;
301  ArrayRef<std::string> QualifiedNames;
302  std::vector<std::string> &SpellingNames;
303  std::vector<std::vector<std::string>> &USRList;
304  bool Force;
305  bool &ErrorOccurred;
306 };
307 
308 std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
309  return std::make_unique<NamedDeclFindingConsumer>(
310  SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
311  ErrorOccurred);
312 }
313 
314 } // end namespace tooling
315 } // end namespace clang
Defines the clang::ASTContext interface.
Defines the clang::FileManager interface and associated types.
unsigned Offset
Definition: Format.cpp:2978
Defines the clang::FrontendAction interface and various convenience abstract classes (clang::ASTFront...
Defines the clang::Preprocessor interface.
Methods for determining the USR of a symbol at a location in source code.
Provides an action to find all relevant USRs at a point.
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs.
Definition: ASTConsumer.h:33
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:185
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:193
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1553
unsigned getCustomDiagID(Level L, const char(&FormatString)[N])
Return an ID for a diagnostic with the specified format string and level.
Definition: Diagnostic.h:879
StringRef getName() const
The name of this FileEntry.
Definition: FileEntry.h:61
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
A SourceLocation and its associated SourceManager.
This represents a decl that may have a name.
Definition: Decl.h:249
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Encodes a location in the source.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
OptionalFileEntryRef getFileEntryRefForID(FileID FID) const
Returns the FileEntryRef for the provided FileID.
FileID getMainFileID() const
Returns the FileID of the main source file.
unsigned getFileIDSize(FileID FID) const
The size of the SLocEntry that FID represents.
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file.
NamedDeclFindingConsumer(ArrayRef< unsigned > SymbolOffsets, ArrayRef< std::string > QualifiedNames, std::vector< std::string > &SpellingNames, std::vector< std::vector< std::string >> &USRList, bool Force, bool &ErrorOccurred)
@ Decl
The l-value was an access to a declared entity or something equivalently strong, like the address of ...
const NamedDecl * getNamedDeclFor(const ASTContext &Context, const std::string &Name)
Definition: USRFinder.cpp:128
const NamedDecl * getNamedDeclAt(const ASTContext &Context, const SourceLocation Point)
Definition: USRFinder.cpp:77
std::string getUSRForDecl(const Decl *Decl)
Definition: USRFinder.cpp:135
const NamedDecl * getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl)
Returns the canonical declaration that best represents a symbol that can be renamed.
std::vector< std::string > getUSRsForDeclaration(const NamedDecl *ND, ASTContext &Context)
Returns the set of USRs that correspond to the given declaration.
The JSON file list parser is used to communicate input to InstallAPI.
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30