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