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