clang  14.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  llvm::for_each(VTD->specializations(), [&](const auto *Spec) {
149  USRSet.insert(getUSRForDecl(Spec));
150  });
152  VTD->getPartialSpecializations(PartialSpecs);
153  llvm::for_each(PartialSpecs, [&](const auto *Spec) {
154  USRSet.insert(getUSRForDecl(Spec));
155  });
156  }
157 
158  void addUSRsOfCtorDtors(const CXXRecordDecl *RD) {
159  const auto* RecordDecl = RD->getDefinition();
160 
161  // Skip if the CXXRecordDecl doesn't have definition.
162  if (!RecordDecl) {
163  USRSet.insert(getUSRForDecl(RD));
164  return;
165  }
166 
167  for (const auto *CtorDecl : RecordDecl->ctors())
168  USRSet.insert(getUSRForDecl(CtorDecl));
169  // Add template constructor decls, they are not in ctors() unfortunately.
170  if (RecordDecl->hasUserDeclaredConstructor())
171  for (const auto *D : RecordDecl->decls())
172  if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
173  if (const auto *Ctor =
174  dyn_cast<CXXConstructorDecl>(FTD->getTemplatedDecl()))
175  USRSet.insert(getUSRForDecl(Ctor));
176 
177  USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
178  USRSet.insert(getUSRForDecl(RecordDecl));
179  }
180 
181  void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
182  USRSet.insert(getUSRForDecl(MethodDecl));
183  // Recursively visit each OverridenMethod.
184  for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
185  addUSRsOfOverridenFunctions(OverriddenMethod);
186  }
187 
188  void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) {
189  // For renaming a class template method, all references of the instantiated
190  // member methods should be renamed too, so add USRs of the instantiated
191  // methods to the USR set.
192  USRSet.insert(getUSRForDecl(MethodDecl));
193  if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction())
194  USRSet.insert(getUSRForDecl(FT));
195  for (const auto *Method : InstantiatedMethods) {
196  if (USRSet.find(getUSRForDecl(
197  Method->getInstantiatedFromMemberFunction())) != USRSet.end())
198  USRSet.insert(getUSRForDecl(Method));
199  }
200  }
201 
202  bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
203  for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
204  if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
205  return true;
206  return checkIfOverriddenFunctionAscends(OverriddenMethod);
207  }
208  return false;
209  }
210 
211  const Decl *FoundDecl;
212  ASTContext &Context;
213  std::set<std::string> USRSet;
214  std::vector<const CXXMethodDecl *> OverriddenMethods;
215  std::vector<const CXXMethodDecl *> InstantiatedMethods;
216 };
217 } // namespace
218 
219 std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
220  ASTContext &Context) {
221  AdditionalUSRFinder Finder(ND, Context);
222  return Finder.Find();
223 }
224 
226 public:
228  ArrayRef<std::string> QualifiedNames,
229  std::vector<std::string> &SpellingNames,
230  std::vector<std::vector<std::string>> &USRList,
231  bool Force, bool &ErrorOccurred)
232  : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
233  SpellingNames(SpellingNames), USRList(USRList), Force(Force),
234  ErrorOccurred(ErrorOccurred) {}
235 
236 private:
237  bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
238  unsigned SymbolOffset, const std::string &QualifiedName) {
239  DiagnosticsEngine &Engine = Context.getDiagnostics();
240  const FileID MainFileID = SourceMgr.getMainFileID();
241 
242  if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
243  ErrorOccurred = true;
244  unsigned InvalidOffset = Engine.getCustomDiagID(
246  "SourceLocation in file %0 at offset %1 is invalid");
247  Engine.Report(SourceLocation(), InvalidOffset)
248  << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
249  return false;
250  }
251 
252  const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
253  .getLocWithOffset(SymbolOffset);
254  const NamedDecl *FoundDecl = QualifiedName.empty()
255  ? getNamedDeclAt(Context, Point)
256  : getNamedDeclFor(Context, QualifiedName);
257 
258  if (FoundDecl == nullptr) {
259  if (QualifiedName.empty()) {
260  FullSourceLoc FullLoc(Point, SourceMgr);
261  unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
263  "clang-rename could not find symbol (offset %0)");
264  Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
265  ErrorOccurred = true;
266  return false;
267  }
268 
269  if (Force) {
270  SpellingNames.push_back(std::string());
271  USRList.push_back(std::vector<std::string>());
272  return true;
273  }
274 
275  unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
276  DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
277  Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
278  ErrorOccurred = true;
279  return false;
280  }
281 
282  FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
283  SpellingNames.push_back(FoundDecl->getNameAsString());
284  AdditionalUSRFinder Finder(FoundDecl, Context);
285  USRList.push_back(Finder.Find());
286  return true;
287  }
288 
289  void HandleTranslationUnit(ASTContext &Context) override {
290  const SourceManager &SourceMgr = Context.getSourceManager();
291  for (unsigned Offset : SymbolOffsets) {
292  if (!FindSymbol(Context, SourceMgr, Offset, ""))
293  return;
294  }
295  for (const std::string &QualifiedName : QualifiedNames) {
296  if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
297  return;
298  }
299  }
300 
301  ArrayRef<unsigned> SymbolOffsets;
302  ArrayRef<std::string> QualifiedNames;
303  std::vector<std::string> &SpellingNames;
304  std::vector<std::vector<std::string>> &USRList;
305  bool Force;
306  bool &ErrorOccurred;
307 };
308 
309 std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
310  return std::make_unique<NamedDeclFindingConsumer>(
311  SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
312  ErrorOccurred);
313 }
314 
315 } // end namespace tooling
316 } // end namespace clang
llvm
Definition: Dominators.h:30
clang::FullSourceLoc
A SourceLocation and its associated SourceManager.
Definition: SourceLocation.h:370
Error
llvm::Error Error
Definition: ByteCodeEmitter.cpp:20
string
string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} 0 ${PATH_LIB_START} PATH_HEAD) string(SUBSTRING $
Definition: CMakeLists.txt:22
llvm::SmallVector
Definition: LLVM.h:38
clang::SourceLocation
Encodes a location in the source.
Definition: SourceLocation.h:88
clang::SourceLocation::getLocWithOffset
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
Definition: SourceLocation.h:136
clang::NamedDecl
This represents a decl that may have a name.
Definition: Decl.h:249
clang::DiagnosticsEngine
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:191
clang::tooling::NamedDeclFindingConsumer::NamedDeclFindingConsumer
NamedDeclFindingConsumer(ArrayRef< unsigned > SymbolOffsets, ArrayRef< std::string > QualifiedNames, std::vector< std::string > &SpellingNames, std::vector< std::vector< std::string >> &USRList, bool Force, bool &ErrorOccurred)
Definition: USRFindingAction.cpp:227
FrontendAction.h
USRFinder.h
CommonOptionsParser.h
clang::SourceManager
This class handles loading and caching of source files into memory.
Definition: SourceManager.h:626
Preprocessor.h
Decl.h
Offset
unsigned Offset
Definition: Format.cpp:2335
clang::CodeGen::AlignmentSource::Decl
@ Decl
The l-value was an access to a declared entity or something equivalently strong, like the address of ...
clang::ASTContext
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:212
clang::SourceManager::getMainFileID
FileID getMainFileID() const
Returns the FileID of the main source file.
Definition: SourceManager.h:840
clang::RecursiveASTVisitor
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Definition: RecursiveASTVisitor.h:164
clang::ASTConsumer
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs.
Definition: ASTConsumer.h:33
ASTContext.h
clang::tooling::getCanonicalSymbolDeclaration
const NamedDecl * getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl)
Returns the canonical declaration that best represents a symbol that can be renamed.
Definition: USRFindingAction.cpp:41
Refactoring.h
clang::SourceManager::getLocForStartOfFile
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file.
Definition: SourceManager.h:1118
clang::tooling::NamedDeclFindingConsumer
Definition: USRFindingAction.cpp:225
Tooling.h
llvm::ArrayRef< unsigned >
Lexer.h
clang::Decl
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:89
ASTConsumer.h
clang::tooling::getNamedDeclFor
const NamedDecl * getNamedDeclFor(const ASTContext &Context, const std::string &Name)
Definition: USRFinder.cpp:128
clang::tooling::getUSRForDecl
std::string getUSRForDecl(const Decl *Decl)
Definition: USRFinder.cpp:135
clang::DiagnosticsEngine::getCustomDiagID
unsigned getCustomDiagID(Level L, const char(&FormatString)[N])
Return an ID for a diagnostic with the specified format string and level.
Definition: Diagnostic.h:866
clang::tooling::getUSRsForDeclaration
std::vector< std::string > getUSRsForDeclaration(const NamedDecl *ND, ASTContext &Context)
Returns the set of USRs that correspond to the given declaration.
Definition: USRFindingAction.cpp:219
clang
Definition: CalledOnceCheck.h:17
RecursiveASTVisitor.h
clang::tooling::getNamedDeclAt
const NamedDecl * getNamedDeclAt(const ASTContext &Context, const SourceLocation Point)
Definition: USRFinder.cpp:77
clang::FileEntry::getName
StringRef getName() const
Definition: FileEntry.h:364
clang::FileID
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Definition: SourceLocation.h:40
clang::SourceManager::getFileEntryForID
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
Definition: SourceManager.h:1030
CompilerInstance.h
FileManager.h
clang::SourceManager::getFileIDSize
unsigned getFileIDSize(FileID FID) const
The size of the SLocEntry that FID represents.
Definition: SourceManager.cpp:1617
AST.h
clang::DiagnosticsEngine::Report
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1523
USRFindingAction.h