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