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 #include "llvm/Support/MemoryBuffer.h"
14 
15 using namespace clang::tooling;
16 using namespace llvm;
17 using namespace clang::ast_matchers;
18 
19 ASTSrcLocProcessor::ASTSrcLocProcessor(StringRef JsonPath)
20     : JsonPath(JsonPath) {
21 
22   MatchFinder::MatchFinderOptions FinderOptions;
23 
24   Finder = std::make_unique<MatchFinder>(std::move(FinderOptions));
25   Finder->addMatcher(
26           cxxRecordDecl(
27               isDefinition(),
28               isSameOrDerivedFrom(
29                   // TODO: Extend this with other clades
30                   namedDecl(hasAnyName("clang::Stmt", "clang::Decl",
31                                        "clang::CXXCtorInitializer",
32                                        "clang::NestedNameSpecifierLoc",
33                                        "clang::TemplateArgumentLoc",
34                                        "clang::CXXBaseSpecifier",
35                                        "clang::TypeLoc"))
36                       .bind("nodeClade")),
37               optionally(isDerivedFrom(cxxRecordDecl().bind("derivedFrom"))))
38               .bind("className"),
39       this);
40   Finder->addMatcher(
41           cxxRecordDecl(isDefinition(), hasAnyName("clang::PointerLikeTypeLoc",
42                                                    "clang::TypeofLikeTypeLoc"))
43               .bind("templateName"),
44       this);
45 }
46 
47 std::unique_ptr<clang::ASTConsumer>
48 ASTSrcLocProcessor::createASTConsumer(clang::CompilerInstance &Compiler,
49                                       StringRef File) {
50   return Finder->newASTConsumer();
51 }
52 
53 llvm::json::Object toJSON(llvm::StringMap<std::vector<StringRef>> const &Obj) {
54   using llvm::json::toJSON;
55 
56   llvm::json::Object JsonObj;
57   for (const auto &Item : Obj) {
58     JsonObj[Item.first()] = Item.second;
59   }
60   return JsonObj;
61 }
62 
63 llvm::json::Object toJSON(llvm::StringMap<std::string> const &Obj) {
64   using llvm::json::toJSON;
65 
66   llvm::json::Object JsonObj;
67   for (const auto &Item : Obj) {
68     JsonObj[Item.first()] = Item.second;
69   }
70   return JsonObj;
71 }
72 
73 llvm::json::Object toJSON(ClassData const &Obj) {
74   llvm::json::Object JsonObj;
75 
76   if (!Obj.ASTClassLocations.empty())
77     JsonObj["sourceLocations"] = Obj.ASTClassLocations;
78   if (!Obj.ASTClassRanges.empty())
79     JsonObj["sourceRanges"] = Obj.ASTClassRanges;
80   if (!Obj.TemplateParms.empty())
81     JsonObj["templateParms"] = Obj.TemplateParms;
82   if (!Obj.TypeSourceInfos.empty())
83     JsonObj["typeSourceInfos"] = Obj.TypeSourceInfos;
84   if (!Obj.TypeLocs.empty())
85     JsonObj["typeLocs"] = Obj.TypeLocs;
86   if (!Obj.NestedNameLocs.empty())
87     JsonObj["nestedNameLocs"] = Obj.NestedNameLocs;
88   return JsonObj;
89 }
90 
91 llvm::json::Object toJSON(llvm::StringMap<ClassData> const &Obj) {
92   using llvm::json::toJSON;
93 
94   llvm::json::Object JsonObj;
95   for (const auto &Item : Obj)
96     JsonObj[Item.first()] = ::toJSON(Item.second);
97   return JsonObj;
98 }
99 
100 void WriteJSON(StringRef JsonPath, llvm::json::Object &&ClassInheritance,
101                llvm::json::Object &&ClassesInClade,
102                llvm::json::Object &&ClassEntries) {
103   llvm::json::Object JsonObj;
104 
105   using llvm::json::toJSON;
106 
107   JsonObj["classInheritance"] = std::move(ClassInheritance);
108   JsonObj["classesInClade"] = std::move(ClassesInClade);
109   JsonObj["classEntries"] = std::move(ClassEntries);
110 
111   llvm::json::Value JsonVal(std::move(JsonObj));
112 
113   bool WriteChange = false;
114   std::string OutString;
115   if (auto ExistingOrErr = MemoryBuffer::getFile(JsonPath, /*IsText=*/true)) {
116     raw_string_ostream Out(OutString);
117     Out << formatv("{0:2}", JsonVal);
118     if (ExistingOrErr.get()->getBuffer() == Out.str())
119       return;
120     WriteChange = true;
121   }
122 
123   std::error_code EC;
124   llvm::raw_fd_ostream JsonOut(JsonPath, EC, llvm::sys::fs::F_Text);
125   if (EC)
126     return;
127 
128   if (WriteChange)
129     JsonOut << OutString;
130   else
131     JsonOut << formatv("{0:2}", JsonVal);
132 }
133 
134 void ASTSrcLocProcessor::generate() {
135   WriteJSON(JsonPath, ::toJSON(ClassInheritance), ::toJSON(ClassesInClade),
136             ::toJSON(ClassEntries));
137 }
138 
139 void ASTSrcLocProcessor::generateEmpty() { WriteJSON(JsonPath, {}, {}, {}); }
140 
141 std::vector<std::string>
142 CaptureMethods(std::string TypeString, const clang::CXXRecordDecl *ASTClass,
143                const MatchFinder::MatchResult &Result) {
144 
145   auto publicAccessor = [](auto... InnerMatcher) {
146     return cxxMethodDecl(isPublic(), parameterCountIs(0), isConst(),
147                          InnerMatcher...);
148   };
149 
150   auto BoundNodesVec = match(
151       findAll(
152           publicAccessor(
153               ofClass(cxxRecordDecl(
154                   equalsNode(ASTClass),
155                   optionally(isDerivedFrom(
156                       cxxRecordDecl(hasAnyName("clang::Stmt", "clang::Decl"))
157                           .bind("stmtOrDeclBase"))),
158                   optionally(isDerivedFrom(
159                       cxxRecordDecl(hasName("clang::Expr")).bind("exprBase"))),
160                   optionally(
161                       isDerivedFrom(cxxRecordDecl(hasName("clang::TypeLoc"))
162                                         .bind("typeLocBase"))))),
163               returns(asString(TypeString)))
164               .bind("classMethod")),
165       *ASTClass, *Result.Context);
166 
167   std::vector<std::string> Methods;
168   for (const auto &BN : BoundNodesVec) {
169     if (const auto *Node = BN.getNodeAs<clang::NamedDecl>("classMethod")) {
170       const auto *StmtOrDeclBase =
171           BN.getNodeAs<clang::CXXRecordDecl>("stmtOrDeclBase");
172       const auto *TypeLocBase =
173           BN.getNodeAs<clang::CXXRecordDecl>("typeLocBase");
174       const auto *ExprBase = BN.getNodeAs<clang::CXXRecordDecl>("exprBase");
175       // The clang AST has several methods on base classes which are overriden
176       // pseudo-virtually by derived classes.
177       // We record only the pseudo-virtual methods on the base classes to
178       // avoid duplication.
179       if (StmtOrDeclBase &&
180           (Node->getName() == "getBeginLoc" || Node->getName() == "getEndLoc" ||
181            Node->getName() == "getSourceRange"))
182         continue;
183       if (ExprBase && Node->getName() == "getExprLoc")
184         continue;
185       if (TypeLocBase && Node->getName() == "getLocalSourceRange")
186         continue;
187       if ((ASTClass->getName() == "PointerLikeTypeLoc" ||
188            ASTClass->getName() == "TypeofLikeTypeLoc") &&
189           Node->getName() == "getLocalSourceRange")
190         continue;
191       Methods.push_back(Node->getName().str());
192     }
193   }
194   return Methods;
195 }
196 
197 void ASTSrcLocProcessor::run(const MatchFinder::MatchResult &Result) {
198 
199   const auto *ASTClass =
200       Result.Nodes.getNodeAs<clang::CXXRecordDecl>("className");
201 
202   StringRef CladeName;
203   if (ASTClass) {
204     if (const auto *NodeClade =
205             Result.Nodes.getNodeAs<clang::CXXRecordDecl>("nodeClade"))
206       CladeName = NodeClade->getName();
207   } else {
208     ASTClass = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("templateName");
209     CladeName = "TypeLoc";
210   }
211 
212   StringRef ClassName = ASTClass->getName();
213 
214   ClassData CD;
215 
216   CD.ASTClassLocations =
217       CaptureMethods("class clang::SourceLocation", ASTClass, Result);
218   CD.ASTClassRanges =
219       CaptureMethods("class clang::SourceRange", ASTClass, Result);
220   CD.TypeSourceInfos =
221       CaptureMethods("class clang::TypeSourceInfo *", ASTClass, Result);
222   CD.TypeLocs = CaptureMethods("class clang::TypeLoc", ASTClass, Result);
223   CD.NestedNameLocs =
224       CaptureMethods("class clang::NestedNameSpecifierLoc", ASTClass, Result);
225 
226   if (const auto *DerivedFrom =
227           Result.Nodes.getNodeAs<clang::CXXRecordDecl>("derivedFrom")) {
228 
229     if (const auto *Templ =
230             llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(
231                 DerivedFrom)) {
232 
233       const auto &TArgs = Templ->getTemplateArgs();
234 
235       SmallString<256> TArgsString;
236       llvm::raw_svector_ostream OS(TArgsString);
237       OS << DerivedFrom->getName() << '<';
238 
239       clang::PrintingPolicy PPol(Result.Context->getLangOpts());
240       PPol.TerseOutput = true;
241 
242       for (unsigned I = 0; I < TArgs.size(); ++I) {
243         if (I > 0)
244           OS << ", ";
245         TArgs.get(I).getAsType().print(OS, PPol);
246       }
247       OS << '>';
248 
249       ClassInheritance[ClassName] = TArgsString.str().str();
250     } else {
251       ClassInheritance[ClassName] = DerivedFrom->getName().str();
252     }
253   }
254 
255   if (const auto *Templ = ASTClass->getDescribedClassTemplate()) {
256     if (auto *TParams = Templ->getTemplateParameters()) {
257       for (const auto &TParam : *TParams) {
258         CD.TemplateParms.push_back(TParam->getName().str());
259       }
260     }
261   }
262 
263   ClassEntries[ClassName] = CD;
264   ClassesInClade[CladeName].push_back(ClassName);
265 }
266