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