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 
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 
37 using namespace llvm;
38 
39 namespace clang {
40 namespace tooling {
41 
42 const 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 
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     } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
77       handleCXXRecordDecl(RecordDecl);
78     } else if (const auto *TemplateDecl =
79                    dyn_cast<ClassTemplateDecl>(FoundDecl)) {
80       handleClassTemplateDecl(TemplateDecl);
81     } else {
82       USRSet.insert(getUSRForDecl(FoundDecl));
83     }
84     return std::vector<std::string>(USRSet.begin(), USRSet.end());
85   }
86 
87   bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
88     if (MethodDecl->isVirtual())
89       OverriddenMethods.push_back(MethodDecl);
90     return true;
91   }
92 
93   bool VisitClassTemplatePartialSpecializationDecl(
94       const ClassTemplatePartialSpecializationDecl *PartialSpec) {
95     PartialSpecs.push_back(PartialSpec);
96     return true;
97   }
98 
99 private:
100   void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
101     RecordDecl = RecordDecl->getDefinition();
102     if (const auto *ClassTemplateSpecDecl =
103             dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
104       handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
105     addUSRsOfCtorDtors(RecordDecl);
106   }
107 
108   void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
109     for (const auto *Specialization : TemplateDecl->specializations())
110       addUSRsOfCtorDtors(Specialization);
111 
112     for (const auto *PartialSpec : PartialSpecs) {
113       if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
114         addUSRsOfCtorDtors(PartialSpec);
115     }
116     addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
117   }
118 
119   void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
120     RecordDecl = RecordDecl->getDefinition();
121 
122     // Skip if the CXXRecordDecl doesn't have definition.
123     if (!RecordDecl)
124       return;
125 
126     for (const auto *CtorDecl : RecordDecl->ctors())
127       USRSet.insert(getUSRForDecl(CtorDecl));
128 
129     USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
130     USRSet.insert(getUSRForDecl(RecordDecl));
131   }
132 
133   void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
134     USRSet.insert(getUSRForDecl(MethodDecl));
135     // Recursively visit each OverridenMethod.
136     for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
137       addUSRsOfOverridenFunctions(OverriddenMethod);
138   }
139 
140   bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
141     for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
142       if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
143         return true;
144       return checkIfOverriddenFunctionAscends(OverriddenMethod);
145     }
146     return false;
147   }
148 
149   const Decl *FoundDecl;
150   ASTContext &Context;
151   std::set<std::string> USRSet;
152   std::vector<const CXXMethodDecl *> OverriddenMethods;
153   std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
154 };
155 } // namespace
156 
157 std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
158                                                ASTContext &Context) {
159   AdditionalUSRFinder Finder(ND, Context);
160   return Finder.Find();
161 }
162 
163 class NamedDeclFindingConsumer : public ASTConsumer {
164 public:
165   NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
166                            ArrayRef<std::string> QualifiedNames,
167                            std::vector<std::string> &SpellingNames,
168                            std::vector<std::vector<std::string>> &USRList,
169                            bool Force, bool &ErrorOccurred)
170       : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
171         SpellingNames(SpellingNames), USRList(USRList), Force(Force),
172         ErrorOccurred(ErrorOccurred) {}
173 
174 private:
175   bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
176                   unsigned SymbolOffset, const std::string &QualifiedName) {
177     DiagnosticsEngine &Engine = Context.getDiagnostics();
178     const FileID MainFileID = SourceMgr.getMainFileID();
179 
180     if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
181       ErrorOccurred = true;
182       unsigned InvalidOffset = Engine.getCustomDiagID(
183           DiagnosticsEngine::Error,
184           "SourceLocation in file %0 at offset %1 is invalid");
185       Engine.Report(SourceLocation(), InvalidOffset)
186           << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
187       return false;
188     }
189 
190     const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
191                                      .getLocWithOffset(SymbolOffset);
192     const NamedDecl *FoundDecl = QualifiedName.empty()
193                                      ? getNamedDeclAt(Context, Point)
194                                      : getNamedDeclFor(Context, QualifiedName);
195 
196     if (FoundDecl == nullptr) {
197       if (QualifiedName.empty()) {
198         FullSourceLoc FullLoc(Point, SourceMgr);
199         unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
200             DiagnosticsEngine::Error,
201             "clang-rename could not find symbol (offset %0)");
202         Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
203         ErrorOccurred = true;
204         return false;
205       }
206 
207       if (Force) {
208         SpellingNames.push_back(std::string());
209         USRList.push_back(std::vector<std::string>());
210         return true;
211       }
212 
213       unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
214           DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
215       Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
216       ErrorOccurred = true;
217       return false;
218     }
219 
220     FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
221     SpellingNames.push_back(FoundDecl->getNameAsString());
222     AdditionalUSRFinder Finder(FoundDecl, Context);
223     USRList.push_back(Finder.Find());
224     return true;
225   }
226 
227   void HandleTranslationUnit(ASTContext &Context) override {
228     const SourceManager &SourceMgr = Context.getSourceManager();
229     for (unsigned Offset : SymbolOffsets) {
230       if (!FindSymbol(Context, SourceMgr, Offset, ""))
231         return;
232     }
233     for (const std::string &QualifiedName : QualifiedNames) {
234       if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
235         return;
236     }
237   }
238 
239   ArrayRef<unsigned> SymbolOffsets;
240   ArrayRef<std::string> QualifiedNames;
241   std::vector<std::string> &SpellingNames;
242   std::vector<std::vector<std::string>> &USRList;
243   bool Force;
244   bool &ErrorOccurred;
245 };
246 
247 std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
248   return llvm::make_unique<NamedDeclFindingConsumer>(
249       SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
250       ErrorOccurred);
251 }
252 
253 } // end namespace tooling
254 } // end namespace clang
255