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