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 class NamedDeclFindingConsumer : public ASTConsumer {
158 public:
159   NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
160                            ArrayRef<std::string> QualifiedNames,
161                            std::vector<std::string> &SpellingNames,
162                            std::vector<std::vector<std::string>> &USRList,
163                            bool Force, bool &ErrorOccurred)
164       : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
165         SpellingNames(SpellingNames), USRList(USRList), Force(Force),
166         ErrorOccurred(ErrorOccurred) {}
167 
168 private:
169   bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
170                   unsigned SymbolOffset, const std::string &QualifiedName) {
171     DiagnosticsEngine &Engine = Context.getDiagnostics();
172     const FileID MainFileID = SourceMgr.getMainFileID();
173 
174     if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
175       ErrorOccurred = true;
176       unsigned InvalidOffset = Engine.getCustomDiagID(
177           DiagnosticsEngine::Error,
178           "SourceLocation in file %0 at offset %1 is invalid");
179       Engine.Report(SourceLocation(), InvalidOffset)
180           << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
181       return false;
182     }
183 
184     const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
185                                      .getLocWithOffset(SymbolOffset);
186     const NamedDecl *FoundDecl = QualifiedName.empty()
187                                      ? getNamedDeclAt(Context, Point)
188                                      : getNamedDeclFor(Context, QualifiedName);
189 
190     if (FoundDecl == nullptr) {
191       if (QualifiedName.empty()) {
192         FullSourceLoc FullLoc(Point, SourceMgr);
193         unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
194             DiagnosticsEngine::Error,
195             "clang-rename could not find symbol (offset %0)");
196         Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
197         ErrorOccurred = true;
198         return false;
199       }
200 
201       if (Force)
202         return true;
203 
204       unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
205           DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
206       Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
207       ErrorOccurred = true;
208       return false;
209     }
210 
211     FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
212     SpellingNames.push_back(FoundDecl->getNameAsString());
213     AdditionalUSRFinder Finder(FoundDecl, Context);
214     USRList.push_back(Finder.Find());
215     return true;
216   }
217 
218   void HandleTranslationUnit(ASTContext &Context) override {
219     const SourceManager &SourceMgr = Context.getSourceManager();
220     for (unsigned Offset : SymbolOffsets) {
221       if (!FindSymbol(Context, SourceMgr, Offset, ""))
222         return;
223     }
224     for (const std::string &QualifiedName : QualifiedNames) {
225       if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
226         return;
227     }
228   }
229 
230   ArrayRef<unsigned> SymbolOffsets;
231   ArrayRef<std::string> QualifiedNames;
232   std::vector<std::string> &SpellingNames;
233   std::vector<std::vector<std::string>> &USRList;
234   bool Force;
235   bool &ErrorOccurred;
236 };
237 
238 std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
239   return llvm::make_unique<NamedDeclFindingConsumer>(
240       SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
241       ErrorOccurred);
242 }
243 
244 } // end namespace tooling
245 } // end namespace clang
246