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 15 #include "clang/Tooling/Refactoring/Rename/USRFindingAction.h" 16 #include "clang/AST/AST.h" 17 #include "clang/AST/ASTConsumer.h" 18 #include "clang/AST/ASTContext.h" 19 #include "clang/AST/Decl.h" 20 #include "clang/AST/RecursiveASTVisitor.h" 21 #include "clang/Basic/FileManager.h" 22 #include "clang/Frontend/CompilerInstance.h" 23 #include "clang/Frontend/FrontendAction.h" 24 #include "clang/Lex/Lexer.h" 25 #include "clang/Lex/Preprocessor.h" 26 #include "clang/Tooling/CommonOptionsParser.h" 27 #include "clang/Tooling/Refactoring.h" 28 #include "clang/Tooling/Refactoring/Rename/USRFinder.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 41 const NamedDecl *getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl) { 42 // If FoundDecl is a constructor or destructor, we want to instead take 43 // the Decl of the corresponding class. 44 if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl)) 45 FoundDecl = CtorDecl->getParent(); 46 else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl)) 47 FoundDecl = DtorDecl->getParent(); 48 // FIXME: (Alex L): Canonicalize implicit template instantions, just like 49 // the indexer does it. 50 51 // Note: please update the declaration's doc comment every time the 52 // canonicalization rules are changed. 53 return FoundDecl; 54 } 55 56 namespace { 57 // NamedDeclFindingConsumer should delegate finding USRs of given Decl to 58 // AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given 59 // Decl refers to class and adds USRs of all overridden methods if Decl refers 60 // to virtual method. 61 class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> { 62 public: 63 AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context) 64 : FoundDecl(FoundDecl), Context(Context) {} 65 66 std::vector<std::string> Find() { 67 // Fill OverriddenMethods and PartialSpecs storages. 68 TraverseDecl(Context.getTranslationUnitDecl()); 69 if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) { 70 addUSRsOfOverridenFunctions(MethodDecl); 71 for (const auto &OverriddenMethod : OverriddenMethods) { 72 if (checkIfOverriddenFunctionAscends(OverriddenMethod)) 73 USRSet.insert(getUSRForDecl(OverriddenMethod)); 74 } 75 addUSRsOfInstantiatedMethods(MethodDecl); 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 shouldVisitTemplateInstantiations() const { return true; } 88 89 bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) { 90 if (MethodDecl->isVirtual()) 91 OverriddenMethods.push_back(MethodDecl); 92 if (MethodDecl->getInstantiatedFromMemberFunction()) 93 InstantiatedMethods.push_back(MethodDecl); 94 return true; 95 } 96 97 bool VisitClassTemplatePartialSpecializationDecl( 98 const ClassTemplatePartialSpecializationDecl *PartialSpec) { 99 PartialSpecs.push_back(PartialSpec); 100 return true; 101 } 102 103 private: 104 void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) { 105 if (!RecordDecl->getDefinition()) { 106 USRSet.insert(getUSRForDecl(RecordDecl)); 107 return; 108 } 109 RecordDecl = RecordDecl->getDefinition(); 110 if (const auto *ClassTemplateSpecDecl = 111 dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl)) 112 handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate()); 113 addUSRsOfCtorDtors(RecordDecl); 114 } 115 116 void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) { 117 for (const auto *Specialization : TemplateDecl->specializations()) 118 addUSRsOfCtorDtors(Specialization); 119 120 for (const auto *PartialSpec : PartialSpecs) { 121 if (PartialSpec->getSpecializedTemplate() == TemplateDecl) 122 addUSRsOfCtorDtors(PartialSpec); 123 } 124 addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl()); 125 } 126 127 void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) { 128 RecordDecl = RecordDecl->getDefinition(); 129 130 // Skip if the CXXRecordDecl doesn't have definition. 131 if (!RecordDecl) 132 return; 133 134 for (const auto *CtorDecl : RecordDecl->ctors()) 135 USRSet.insert(getUSRForDecl(CtorDecl)); 136 137 USRSet.insert(getUSRForDecl(RecordDecl->getDestructor())); 138 USRSet.insert(getUSRForDecl(RecordDecl)); 139 } 140 141 void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) { 142 USRSet.insert(getUSRForDecl(MethodDecl)); 143 // Recursively visit each OverridenMethod. 144 for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) 145 addUSRsOfOverridenFunctions(OverriddenMethod); 146 } 147 148 void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) { 149 // For renaming a class template method, all references of the instantiated 150 // member methods should be renamed too, so add USRs of the instantiated 151 // methods to the USR set. 152 USRSet.insert(getUSRForDecl(MethodDecl)); 153 if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction()) 154 USRSet.insert(getUSRForDecl(FT)); 155 for (const auto *Method : InstantiatedMethods) { 156 if (USRSet.find(getUSRForDecl( 157 Method->getInstantiatedFromMemberFunction())) != USRSet.end()) 158 USRSet.insert(getUSRForDecl(Method)); 159 } 160 } 161 162 bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) { 163 for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) { 164 if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end()) 165 return true; 166 return checkIfOverriddenFunctionAscends(OverriddenMethod); 167 } 168 return false; 169 } 170 171 const Decl *FoundDecl; 172 ASTContext &Context; 173 std::set<std::string> USRSet; 174 std::vector<const CXXMethodDecl *> OverriddenMethods; 175 std::vector<const CXXMethodDecl *> InstantiatedMethods; 176 std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs; 177 }; 178 } // namespace 179 180 std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND, 181 ASTContext &Context) { 182 AdditionalUSRFinder Finder(ND, Context); 183 return Finder.Find(); 184 } 185 186 class NamedDeclFindingConsumer : public ASTConsumer { 187 public: 188 NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets, 189 ArrayRef<std::string> QualifiedNames, 190 std::vector<std::string> &SpellingNames, 191 std::vector<std::vector<std::string>> &USRList, 192 bool Force, bool &ErrorOccurred) 193 : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames), 194 SpellingNames(SpellingNames), USRList(USRList), Force(Force), 195 ErrorOccurred(ErrorOccurred) {} 196 197 private: 198 bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr, 199 unsigned SymbolOffset, const std::string &QualifiedName) { 200 DiagnosticsEngine &Engine = Context.getDiagnostics(); 201 const FileID MainFileID = SourceMgr.getMainFileID(); 202 203 if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) { 204 ErrorOccurred = true; 205 unsigned InvalidOffset = Engine.getCustomDiagID( 206 DiagnosticsEngine::Error, 207 "SourceLocation in file %0 at offset %1 is invalid"); 208 Engine.Report(SourceLocation(), InvalidOffset) 209 << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset; 210 return false; 211 } 212 213 const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID) 214 .getLocWithOffset(SymbolOffset); 215 const NamedDecl *FoundDecl = QualifiedName.empty() 216 ? getNamedDeclAt(Context, Point) 217 : getNamedDeclFor(Context, QualifiedName); 218 219 if (FoundDecl == nullptr) { 220 if (QualifiedName.empty()) { 221 FullSourceLoc FullLoc(Point, SourceMgr); 222 unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID( 223 DiagnosticsEngine::Error, 224 "clang-rename could not find symbol (offset %0)"); 225 Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset; 226 ErrorOccurred = true; 227 return false; 228 } 229 230 if (Force) { 231 SpellingNames.push_back(std::string()); 232 USRList.push_back(std::vector<std::string>()); 233 return true; 234 } 235 236 unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID( 237 DiagnosticsEngine::Error, "clang-rename could not find symbol %0"); 238 Engine.Report(CouldNotFindSymbolNamed) << QualifiedName; 239 ErrorOccurred = true; 240 return false; 241 } 242 243 FoundDecl = getCanonicalSymbolDeclaration(FoundDecl); 244 SpellingNames.push_back(FoundDecl->getNameAsString()); 245 AdditionalUSRFinder Finder(FoundDecl, Context); 246 USRList.push_back(Finder.Find()); 247 return true; 248 } 249 250 void HandleTranslationUnit(ASTContext &Context) override { 251 const SourceManager &SourceMgr = Context.getSourceManager(); 252 for (unsigned Offset : SymbolOffsets) { 253 if (!FindSymbol(Context, SourceMgr, Offset, "")) 254 return; 255 } 256 for (const std::string &QualifiedName : QualifiedNames) { 257 if (!FindSymbol(Context, SourceMgr, 0, QualifiedName)) 258 return; 259 } 260 } 261 262 ArrayRef<unsigned> SymbolOffsets; 263 ArrayRef<std::string> QualifiedNames; 264 std::vector<std::string> &SpellingNames; 265 std::vector<std::vector<std::string>> &USRList; 266 bool Force; 267 bool &ErrorOccurred; 268 }; 269 270 std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() { 271 return std::make_unique<NamedDeclFindingConsumer>( 272 SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force, 273 ErrorOccurred); 274 } 275 276 } // end namespace tooling 277 } // end namespace clang 278