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