xref: /openbsd-src/gnu/llvm/clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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