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