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