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 return JsonObj; 87 } 88 89 llvm::json::Object toJSON(llvm::StringMap<ClassData> const &Obj) { 90 using llvm::json::toJSON; 91 92 llvm::json::Object JsonObj; 93 for (const auto &Item : Obj) 94 JsonObj[Item.first()] = ::toJSON(Item.second); 95 return JsonObj; 96 } 97 98 void WriteJSON(StringRef JsonPath, llvm::json::Object &&ClassInheritance, 99 llvm::json::Object &&ClassesInClade, 100 llvm::json::Object &&ClassEntries) { 101 llvm::json::Object JsonObj; 102 103 using llvm::json::toJSON; 104 105 JsonObj["classInheritance"] = std::move(ClassInheritance); 106 JsonObj["classesInClade"] = std::move(ClassesInClade); 107 JsonObj["classEntries"] = std::move(ClassEntries); 108 109 llvm::json::Value JsonVal(std::move(JsonObj)); 110 111 bool WriteChange = false; 112 std::string OutString; 113 if (auto ExistingOrErr = MemoryBuffer::getFile(JsonPath, /*IsText=*/true)) { 114 raw_string_ostream Out(OutString); 115 Out << formatv("{0:2}", JsonVal); 116 if (ExistingOrErr.get()->getBuffer() == Out.str()) 117 return; 118 WriteChange = true; 119 } 120 121 std::error_code EC; 122 llvm::raw_fd_ostream JsonOut(JsonPath, EC, llvm::sys::fs::F_Text); 123 if (EC) 124 return; 125 126 if (WriteChange) 127 JsonOut << OutString; 128 else 129 JsonOut << formatv("{0:2}", JsonVal); 130 } 131 132 void ASTSrcLocProcessor::generate() { 133 WriteJSON(JsonPath, ::toJSON(ClassInheritance), ::toJSON(ClassesInClade), 134 ::toJSON(ClassEntries)); 135 } 136 137 void ASTSrcLocProcessor::generateEmpty() { WriteJSON(JsonPath, {}, {}, {}); } 138 139 std::vector<std::string> 140 CaptureMethods(std::string TypeString, const clang::CXXRecordDecl *ASTClass, 141 const MatchFinder::MatchResult &Result) { 142 143 auto publicAccessor = [](auto... InnerMatcher) { 144 return cxxMethodDecl(isPublic(), parameterCountIs(0), isConst(), 145 InnerMatcher...); 146 }; 147 148 auto BoundNodesVec = match( 149 findAll( 150 publicAccessor( 151 ofClass(cxxRecordDecl( 152 equalsNode(ASTClass), 153 optionally(isDerivedFrom( 154 cxxRecordDecl(hasAnyName("clang::Stmt", "clang::Decl")) 155 .bind("stmtOrDeclBase"))), 156 optionally(isDerivedFrom( 157 cxxRecordDecl(hasName("clang::Expr")).bind("exprBase"))), 158 optionally( 159 isDerivedFrom(cxxRecordDecl(hasName("clang::TypeLoc")) 160 .bind("typeLocBase"))))), 161 returns(asString(TypeString))) 162 .bind("classMethod")), 163 *ASTClass, *Result.Context); 164 165 std::vector<std::string> Methods; 166 for (const auto &BN : BoundNodesVec) { 167 if (const auto *Node = BN.getNodeAs<clang::NamedDecl>("classMethod")) { 168 const auto *StmtOrDeclBase = 169 BN.getNodeAs<clang::CXXRecordDecl>("stmtOrDeclBase"); 170 const auto *TypeLocBase = 171 BN.getNodeAs<clang::CXXRecordDecl>("typeLocBase"); 172 const auto *ExprBase = BN.getNodeAs<clang::CXXRecordDecl>("exprBase"); 173 // The clang AST has several methods on base classes which are overriden 174 // pseudo-virtually by derived classes. 175 // We record only the pseudo-virtual methods on the base classes to 176 // avoid duplication. 177 if (StmtOrDeclBase && 178 (Node->getName() == "getBeginLoc" || Node->getName() == "getEndLoc" || 179 Node->getName() == "getSourceRange")) 180 continue; 181 if (ExprBase && Node->getName() == "getExprLoc") 182 continue; 183 if (TypeLocBase && Node->getName() == "getLocalSourceRange") 184 continue; 185 if ((ASTClass->getName() == "PointerLikeTypeLoc" || 186 ASTClass->getName() == "TypeofLikeTypeLoc") && 187 Node->getName() == "getLocalSourceRange") 188 continue; 189 Methods.push_back(Node->getName().str()); 190 } 191 } 192 return Methods; 193 } 194 195 void ASTSrcLocProcessor::run(const MatchFinder::MatchResult &Result) { 196 197 const auto *ASTClass = 198 Result.Nodes.getNodeAs<clang::CXXRecordDecl>("className"); 199 200 StringRef CladeName; 201 if (ASTClass) { 202 if (const auto *NodeClade = 203 Result.Nodes.getNodeAs<clang::CXXRecordDecl>("nodeClade")) 204 CladeName = NodeClade->getName(); 205 } else { 206 ASTClass = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("templateName"); 207 CladeName = "TypeLoc"; 208 } 209 210 StringRef ClassName = ASTClass->getName(); 211 212 ClassData CD; 213 214 CD.ASTClassLocations = 215 CaptureMethods("class clang::SourceLocation", ASTClass, Result); 216 CD.ASTClassRanges = 217 CaptureMethods("class clang::SourceRange", ASTClass, Result); 218 CD.TypeSourceInfos = 219 CaptureMethods("class clang::TypeSourceInfo *", ASTClass, Result); 220 CD.TypeLocs = CaptureMethods("class clang::TypeLoc", ASTClass, Result); 221 222 if (const auto *DerivedFrom = 223 Result.Nodes.getNodeAs<clang::CXXRecordDecl>("derivedFrom")) { 224 225 if (const auto *Templ = 226 llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>( 227 DerivedFrom)) { 228 229 const auto &TArgs = Templ->getTemplateArgs(); 230 231 SmallString<256> TArgsString; 232 llvm::raw_svector_ostream OS(TArgsString); 233 OS << DerivedFrom->getName() << '<'; 234 235 clang::PrintingPolicy PPol(Result.Context->getLangOpts()); 236 PPol.TerseOutput = true; 237 238 for (unsigned I = 0; I < TArgs.size(); ++I) { 239 if (I > 0) 240 OS << ", "; 241 TArgs.get(I).getAsType().print(OS, PPol); 242 } 243 OS << '>'; 244 245 ClassInheritance[ClassName] = TArgsString.str().str(); 246 } else { 247 ClassInheritance[ClassName] = DerivedFrom->getName().str(); 248 } 249 } 250 251 if (const auto *Templ = ASTClass->getDescribedClassTemplate()) { 252 if (auto *TParams = Templ->getTemplateParameters()) { 253 for (const auto &TParam : *TParams) { 254 CD.TemplateParms.push_back(TParam->getName().str()); 255 } 256 } 257 } 258 259 ClassEntries[ClassName] = CD; 260 ClassesInClade[CladeName].push_back(ClassName); 261 } 262