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