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