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 14 using namespace clang::tooling; 15 using namespace llvm; 16 using namespace clang::ast_matchers; 17 18 ASTSrcLocProcessor::ASTSrcLocProcessor(StringRef JsonPath) 19 : JsonPath(JsonPath) { 20 21 MatchFinder::MatchFinderOptions FinderOptions; 22 23 Finder = std::make_unique<MatchFinder>(std::move(FinderOptions)); 24 Finder->addMatcher( 25 cxxRecordDecl( 26 isDefinition(), 27 isSameOrDerivedFrom( 28 // TODO: Extend this with other clades 29 namedDecl(hasAnyName("clang::Stmt", "clang::Decl", 30 "clang::CXXCtorInitializer", 31 "clang::NestedNameSpecifierLoc", 32 "clang::TemplateArgumentLoc", 33 "clang::CXXBaseSpecifier")) 34 .bind("nodeClade")), 35 optionally(isDerivedFrom(cxxRecordDecl().bind("derivedFrom")))) 36 .bind("className"), 37 this); 38 } 39 40 std::unique_ptr<clang::ASTConsumer> 41 ASTSrcLocProcessor::createASTConsumer(clang::CompilerInstance &Compiler, 42 StringRef File) { 43 return Finder->newASTConsumer(); 44 } 45 46 llvm::json::Object toJSON(llvm::StringMap<std::vector<StringRef>> const &Obj) { 47 using llvm::json::toJSON; 48 49 llvm::json::Object JsonObj; 50 for (const auto &Item : Obj) { 51 JsonObj[Item.first()] = Item.second; 52 } 53 return JsonObj; 54 } 55 56 llvm::json::Object toJSON(llvm::StringMap<StringRef> const &Obj) { 57 using llvm::json::toJSON; 58 59 llvm::json::Object JsonObj; 60 for (const auto &Item : Obj) { 61 JsonObj[Item.first()] = Item.second; 62 } 63 return JsonObj; 64 } 65 66 llvm::json::Object toJSON(ClassData const &Obj) { 67 llvm::json::Object JsonObj; 68 69 if (!Obj.ASTClassLocations.empty()) 70 JsonObj["sourceLocations"] = Obj.ASTClassLocations; 71 if (!Obj.ASTClassRanges.empty()) 72 JsonObj["sourceRanges"] = Obj.ASTClassRanges; 73 return JsonObj; 74 } 75 76 llvm::json::Object toJSON(llvm::StringMap<ClassData> const &Obj) { 77 using llvm::json::toJSON; 78 79 llvm::json::Object JsonObj; 80 for (const auto &Item : Obj) { 81 if (!Item.second.isEmpty()) 82 JsonObj[Item.first()] = ::toJSON(Item.second); 83 } 84 return JsonObj; 85 } 86 87 void WriteJSON(std::string JsonPath, llvm::json::Object &&ClassInheritance, 88 llvm::json::Object &&ClassesInClade, 89 llvm::json::Object &&ClassEntries) { 90 llvm::json::Object JsonObj; 91 92 using llvm::json::toJSON; 93 94 JsonObj["classInheritance"] = std::move(ClassInheritance); 95 JsonObj["classesInClade"] = std::move(ClassesInClade); 96 JsonObj["classEntries"] = std::move(ClassEntries); 97 98 std::error_code EC; 99 llvm::raw_fd_ostream JsonOut(JsonPath, EC, llvm::sys::fs::F_Text); 100 if (EC) 101 return; 102 103 llvm::json::Value JsonVal(std::move(JsonObj)); 104 JsonOut << formatv("{0:2}", JsonVal); 105 } 106 107 void ASTSrcLocProcessor::generate() { 108 WriteJSON(JsonPath, ::toJSON(ClassInheritance), ::toJSON(ClassesInClade), 109 ::toJSON(ClassEntries)); 110 } 111 112 void ASTSrcLocProcessor::generateEmpty() { WriteJSON(JsonPath, {}, {}, {}); } 113 114 std::vector<std::string> 115 CaptureMethods(std::string TypeString, const clang::CXXRecordDecl *ASTClass, 116 const MatchFinder::MatchResult &Result) { 117 118 auto publicAccessor = [](auto... InnerMatcher) { 119 return cxxMethodDecl(isPublic(), parameterCountIs(0), isConst(), 120 InnerMatcher...); 121 }; 122 123 auto BoundNodesVec = match( 124 findAll( 125 publicAccessor( 126 ofClass(cxxRecordDecl( 127 equalsNode(ASTClass), 128 optionally(isDerivedFrom( 129 cxxRecordDecl(hasAnyName("clang::Stmt", "clang::Decl")) 130 .bind("stmtOrDeclBase"))))), 131 returns(asString(TypeString))) 132 .bind("classMethod")), 133 *ASTClass, *Result.Context); 134 135 std::vector<std::string> Methods; 136 for (const auto &BN : BoundNodesVec) { 137 const auto *StmtOrDeclBase = 138 BN.getNodeAs<clang::CXXRecordDecl>("stmtOrDeclBase"); 139 if (const auto *Node = BN.getNodeAs<clang::NamedDecl>("classMethod")) { 140 // Only record the getBeginLoc etc on Stmt etc, because it will call 141 // more-derived implementations pseudo-virtually. 142 if (StmtOrDeclBase && 143 (Node->getName() == "getBeginLoc" || Node->getName() == "getEndLoc" || 144 Node->getName() == "getSourceRange")) 145 continue; 146 147 // Only record the getExprLoc on Expr, because it will call 148 // more-derived implementations pseudo-virtually. 149 if (ASTClass->getName() != "Expr" && Node->getName() == "getExprLoc") { 150 continue; 151 } 152 Methods.push_back(Node->getName().str()); 153 } 154 } 155 return Methods; 156 } 157 158 void ASTSrcLocProcessor::run(const MatchFinder::MatchResult &Result) { 159 160 const auto *ASTClass = 161 Result.Nodes.getNodeAs<clang::CXXRecordDecl>("className"); 162 163 StringRef ClassName = ASTClass->getName(); 164 165 ClassData CD; 166 167 const auto *NodeClade = 168 Result.Nodes.getNodeAs<clang::CXXRecordDecl>("nodeClade"); 169 StringRef CladeName = NodeClade->getName(); 170 171 if (const auto *DerivedFrom = 172 Result.Nodes.getNodeAs<clang::CXXRecordDecl>("derivedFrom")) 173 ClassInheritance[ClassName] = DerivedFrom->getName(); 174 175 CD.ASTClassLocations = 176 CaptureMethods("class clang::SourceLocation", ASTClass, Result); 177 CD.ASTClassRanges = 178 CaptureMethods("class clang::SourceRange", ASTClass, Result); 179 180 if (!CD.isEmpty()) { 181 ClassEntries[ClassName] = CD; 182 ClassesInClade[CladeName].push_back(ClassName); 183 } 184 } 185