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