USRFindingAction.cpp revision 341825
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/// Provides an action to find USR for the symbol at <offset>, as well as
12/// all additional USRs.
13///
14//===----------------------------------------------------------------------===//
15
16#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
17#include "clang/AST/AST.h"
18#include "clang/AST/ASTConsumer.h"
19#include "clang/AST/ASTContext.h"
20#include "clang/AST/Decl.h"
21#include "clang/AST/RecursiveASTVisitor.h"
22#include "clang/Basic/FileManager.h"
23#include "clang/Frontend/CompilerInstance.h"
24#include "clang/Frontend/FrontendAction.h"
25#include "clang/Lex/Lexer.h"
26#include "clang/Lex/Preprocessor.h"
27#include "clang/Tooling/CommonOptionsParser.h"
28#include "clang/Tooling/Refactoring.h"
29#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
30#include "clang/Tooling/Tooling.h"
31
32#include <algorithm>
33#include <set>
34#include <string>
35#include <vector>
36
37using namespace llvm;
38
39namespace clang {
40namespace tooling {
41
42const NamedDecl *getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl) {
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
57namespace {
58// 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.
62class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
63public:
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
104private:
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
177std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
178                                               ASTContext &Context) {
179  AdditionalUSRFinder Finder(ND, Context);
180  return Finder.Find();
181}
182
183class NamedDeclFindingConsumer : public ASTConsumer {
184public:
185  NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
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
194private:
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(
203          DiagnosticsEngine::Error,
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(
220            DiagnosticsEngine::Error,
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
267std::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
275