1 //===- ASTSrcLocProcessor.cpp --------------------------------*- C++ -*----===// 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 #include "ASTSrcLocProcessor.h" 10 11 #include "clang/Frontend/CompilerInstance.h" 12 #include "llvm/Support/JSON.h" 13 #include "llvm/Support/MemoryBuffer.h" 14 15 using namespace clang::tooling; 16 using namespace llvm; 17 using namespace clang::ast_matchers; 18 19 ASTSrcLocProcessor::ASTSrcLocProcessor(StringRef JsonPath) 20 : JsonPath(JsonPath) { 21 22 MatchFinder::MatchFinderOptions FinderOptions; 23 24 Finder = std::make_unique<MatchFinder>(std::move(FinderOptions)); 25 Finder->addMatcher( 26 cxxRecordDecl( 27 isDefinition(), 28 isSameOrDerivedFrom( 29 // TODO: Extend this with other clades 30 namedDecl(hasAnyName("clang::Stmt", "clang::Decl", 31 "clang::CXXCtorInitializer", 32 "clang::NestedNameSpecifierLoc", 33 "clang::TemplateArgumentLoc", 34 "clang::CXXBaseSpecifier", 35 "clang::TypeLoc")) 36 .bind("nodeClade")), 37 optionally(isDerivedFrom(cxxRecordDecl().bind("derivedFrom")))) 38 .bind("className"), 39 this); 40 Finder->addMatcher( 41 cxxRecordDecl(isDefinition(), hasAnyName("clang::PointerLikeTypeLoc", 42 "clang::TypeofLikeTypeLoc")) 43 .bind("templateName"), 44 this); 45 } 46 47 std::unique_ptr<clang::ASTConsumer> 48 ASTSrcLocProcessor::createASTConsumer(clang::CompilerInstance &Compiler, 49 StringRef File) { 50 return Finder->newASTConsumer(); 51 } 52 53 llvm::json::Object toJSON(llvm::StringMap<std::vector<StringRef>> const &Obj) { 54 using llvm::json::toJSON; 55 56 llvm::json::Object JsonObj; 57 for (const auto &Item : Obj) { 58 JsonObj[Item.first()] = Item.second; 59 } 60 return JsonObj; 61 } 62 63 llvm::json::Object toJSON(llvm::StringMap<std::string> const &Obj) { 64 using llvm::json::toJSON; 65 66 llvm::json::Object JsonObj; 67 for (const auto &Item : Obj) { 68 JsonObj[Item.first()] = Item.second; 69 } 70 return JsonObj; 71 } 72 73 llvm::json::Object toJSON(ClassData const &Obj) { 74 llvm::json::Object JsonObj; 75 76 if (!Obj.ASTClassLocations.empty()) 77 JsonObj["sourceLocations"] = Obj.ASTClassLocations; 78 if (!Obj.ASTClassRanges.empty()) 79 JsonObj["sourceRanges"] = Obj.ASTClassRanges; 80 if (!Obj.TemplateParms.empty()) 81 JsonObj["templateParms"] = Obj.TemplateParms; 82 if (!Obj.TypeSourceInfos.empty()) 83 JsonObj["typeSourceInfos"] = Obj.TypeSourceInfos; 84 if (!Obj.TypeLocs.empty()) 85 JsonObj["typeLocs"] = Obj.TypeLocs; 86 if (!Obj.NestedNameLocs.empty()) 87 JsonObj["nestedNameLocs"] = Obj.NestedNameLocs; 88 return JsonObj; 89 } 90 91 llvm::json::Object toJSON(llvm::StringMap<ClassData> const &Obj) { 92 using llvm::json::toJSON; 93 94 llvm::json::Object JsonObj; 95 for (const auto &Item : Obj) 96 JsonObj[Item.first()] = ::toJSON(Item.second); 97 return JsonObj; 98 } 99 100 void WriteJSON(StringRef JsonPath, llvm::json::Object &&ClassInheritance, 101 llvm::json::Object &&ClassesInClade, 102 llvm::json::Object &&ClassEntries) { 103 llvm::json::Object JsonObj; 104 105 using llvm::json::toJSON; 106 107 JsonObj["classInheritance"] = std::move(ClassInheritance); 108 JsonObj["classesInClade"] = std::move(ClassesInClade); 109 JsonObj["classEntries"] = std::move(ClassEntries); 110 111 llvm::json::Value JsonVal(std::move(JsonObj)); 112 113 bool WriteChange = false; 114 std::string OutString; 115 if (auto ExistingOrErr = MemoryBuffer::getFile(JsonPath, /*IsText=*/true)) { 116 raw_string_ostream Out(OutString); 117 Out << formatv("{0:2}", JsonVal); 118 if (ExistingOrErr.get()->getBuffer() == Out.str()) 119 return; 120 WriteChange = true; 121 } 122 123 std::error_code EC; 124 llvm::raw_fd_ostream JsonOut(JsonPath, EC, llvm::sys::fs::F_Text); 125 if (EC) 126 return; 127 128 if (WriteChange) 129 JsonOut << OutString; 130 else 131 JsonOut << formatv("{0:2}", JsonVal); 132 } 133 134 void ASTSrcLocProcessor::generate() { 135 WriteJSON(JsonPath, ::toJSON(ClassInheritance), ::toJSON(ClassesInClade), 136 ::toJSON(ClassEntries)); 137 } 138 139 void ASTSrcLocProcessor::generateEmpty() { WriteJSON(JsonPath, {}, {}, {}); } 140 141 std::vector<std::string> 142 CaptureMethods(std::string TypeString, const clang::CXXRecordDecl *ASTClass, 143 const MatchFinder::MatchResult &Result) { 144 145 auto publicAccessor = [](auto... InnerMatcher) { 146 return cxxMethodDecl(isPublic(), parameterCountIs(0), isConst(), 147 InnerMatcher...); 148 }; 149 150 auto BoundNodesVec = match( 151 findAll( 152 publicAccessor( 153 ofClass(cxxRecordDecl( 154 equalsNode(ASTClass), 155 optionally(isDerivedFrom( 156 cxxRecordDecl(hasAnyName("clang::Stmt", "clang::Decl")) 157 .bind("stmtOrDeclBase"))), 158 optionally(isDerivedFrom( 159 cxxRecordDecl(hasName("clang::Expr")).bind("exprBase"))), 160 optionally( 161 isDerivedFrom(cxxRecordDecl(hasName("clang::TypeLoc")) 162 .bind("typeLocBase"))))), 163 returns(asString(TypeString))) 164 .bind("classMethod")), 165 *ASTClass, *Result.Context); 166 167 std::vector<std::string> Methods; 168 for (const auto &BN : BoundNodesVec) { 169 if (const auto *Node = BN.getNodeAs<clang::NamedDecl>("classMethod")) { 170 const auto *StmtOrDeclBase = 171 BN.getNodeAs<clang::CXXRecordDecl>("stmtOrDeclBase"); 172 const auto *TypeLocBase = 173 BN.getNodeAs<clang::CXXRecordDecl>("typeLocBase"); 174 const auto *ExprBase = BN.getNodeAs<clang::CXXRecordDecl>("exprBase"); 175 // The clang AST has several methods on base classes which are overriden 176 // pseudo-virtually by derived classes. 177 // We record only the pseudo-virtual methods on the base classes to 178 // avoid duplication. 179 if (StmtOrDeclBase && 180 (Node->getName() == "getBeginLoc" || Node->getName() == "getEndLoc" || 181 Node->getName() == "getSourceRange")) 182 continue; 183 if (ExprBase && Node->getName() == "getExprLoc") 184 continue; 185 if (TypeLocBase && Node->getName() == "getLocalSourceRange") 186 continue; 187 if ((ASTClass->getName() == "PointerLikeTypeLoc" || 188 ASTClass->getName() == "TypeofLikeTypeLoc") && 189 Node->getName() == "getLocalSourceRange") 190 continue; 191 Methods.push_back(Node->getName().str()); 192 } 193 } 194 return Methods; 195 } 196 197 void ASTSrcLocProcessor::run(const MatchFinder::MatchResult &Result) { 198 199 const auto *ASTClass = 200 Result.Nodes.getNodeAs<clang::CXXRecordDecl>("className"); 201 202 StringRef CladeName; 203 if (ASTClass) { 204 if (const auto *NodeClade = 205 Result.Nodes.getNodeAs<clang::CXXRecordDecl>("nodeClade")) 206 CladeName = NodeClade->getName(); 207 } else { 208 ASTClass = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("templateName"); 209 CladeName = "TypeLoc"; 210 } 211 212 StringRef ClassName = ASTClass->getName(); 213 214 ClassData CD; 215 216 CD.ASTClassLocations = 217 CaptureMethods("class clang::SourceLocation", ASTClass, Result); 218 CD.ASTClassRanges = 219 CaptureMethods("class clang::SourceRange", ASTClass, Result); 220 CD.TypeSourceInfos = 221 CaptureMethods("class clang::TypeSourceInfo *", ASTClass, Result); 222 CD.TypeLocs = CaptureMethods("class clang::TypeLoc", ASTClass, Result); 223 CD.NestedNameLocs = 224 CaptureMethods("class clang::NestedNameSpecifierLoc", ASTClass, Result); 225 226 if (const auto *DerivedFrom = 227 Result.Nodes.getNodeAs<clang::CXXRecordDecl>("derivedFrom")) { 228 229 if (const auto *Templ = 230 llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>( 231 DerivedFrom)) { 232 233 const auto &TArgs = Templ->getTemplateArgs(); 234 235 SmallString<256> TArgsString; 236 llvm::raw_svector_ostream OS(TArgsString); 237 OS << DerivedFrom->getName() << '<'; 238 239 clang::PrintingPolicy PPol(Result.Context->getLangOpts()); 240 PPol.TerseOutput = true; 241 242 for (unsigned I = 0; I < TArgs.size(); ++I) { 243 if (I > 0) 244 OS << ", "; 245 TArgs.get(I).getAsType().print(OS, PPol); 246 } 247 OS << '>'; 248 249 ClassInheritance[ClassName] = TArgsString.str().str(); 250 } else { 251 ClassInheritance[ClassName] = DerivedFrom->getName().str(); 252 } 253 } 254 255 if (const auto *Templ = ASTClass->getDescribedClassTemplate()) { 256 if (auto *TParams = Templ->getTemplateParameters()) { 257 for (const auto &TParam : *TParams) { 258 CD.TemplateParms.push_back(TParam->getName().str()); 259 } 260 } 261 } 262 263 ClassEntries[ClassName] = CD; 264 ClassesInClade[CladeName].push_back(ClassName); 265 } 266