1 | //===--- ASTDumper.cpp - Dumping implementation for ASTs ------------------===// |
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 | // This file implements the AST dump methods, which dump out the |
10 | // AST in a form that exposes type details and other fields. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "clang/AST/ASTDumper.h" |
15 | #include "clang/AST/ASTConcept.h" |
16 | #include "clang/AST/ASTContext.h" |
17 | #include "clang/AST/DeclLookups.h" |
18 | #include "clang/AST/JSONNodeDumper.h" |
19 | #include "clang/Basic/Builtins.h" |
20 | #include "clang/Basic/SourceManager.h" |
21 | #include "llvm/Support/raw_ostream.h" |
22 | |
23 | using namespace clang; |
24 | using namespace clang::comments; |
25 | |
26 | void ASTDumper::dumpInvalidDeclContext(const DeclContext *DC) { |
27 | NodeDumper.AddChild(DoAddChild: [=] { |
28 | if (!DC) { |
29 | ColorScope Color(OS, ShowColors, NullColor); |
30 | OS << "<<<NULL>>>" ; |
31 | return; |
32 | } |
33 | // An invalid DeclContext is one for which a dyn_cast() from a DeclContext |
34 | // pointer to a Decl pointer would fail an assertion or otherwise fall prey |
35 | // to undefined behavior as a result of an invalid associated DeclKind. |
36 | // Such invalidity is not supposed to happen of course, but, when it does, |
37 | // the information provided below is intended to provide some hints about |
38 | // what might have gone awry. |
39 | { |
40 | ColorScope Color(OS, ShowColors, DeclKindNameColor); |
41 | OS << "DeclContext" ; |
42 | } |
43 | NodeDumper.dumpPointer(Ptr: DC); |
44 | OS << " <" ; |
45 | { |
46 | ColorScope Color(OS, ShowColors, DeclNameColor); |
47 | OS << "unrecognized Decl kind " << (unsigned)DC->getDeclKind(); |
48 | } |
49 | OS << ">" ; |
50 | }); |
51 | } |
52 | |
53 | void ASTDumper::dumpLookups(const DeclContext *DC, bool DumpDecls) { |
54 | NodeDumper.AddChild(DoAddChild: [=] { |
55 | OS << "StoredDeclsMap " ; |
56 | NodeDumper.dumpBareDeclRef(D: cast<Decl>(Val: DC)); |
57 | |
58 | const DeclContext *Primary = DC->getPrimaryContext(); |
59 | if (Primary != DC) { |
60 | OS << " primary" ; |
61 | NodeDumper.dumpPointer(Ptr: cast<Decl>(Val: Primary)); |
62 | } |
63 | |
64 | bool HasUndeserializedLookups = Primary->hasExternalVisibleStorage(); |
65 | |
66 | auto Range = getDeserialize() |
67 | ? Primary->lookups() |
68 | : Primary->noload_lookups(/*PreserveInternalState=*/true); |
69 | for (auto I = Range.begin(), E = Range.end(); I != E; ++I) { |
70 | DeclarationName Name = I.getLookupName(); |
71 | DeclContextLookupResult R = *I; |
72 | |
73 | NodeDumper.AddChild(DoAddChild: [=] { |
74 | OS << "DeclarationName " ; |
75 | { |
76 | ColorScope Color(OS, ShowColors, DeclNameColor); |
77 | OS << '\'' << Name << '\''; |
78 | } |
79 | |
80 | for (DeclContextLookupResult::iterator RI = R.begin(), RE = R.end(); |
81 | RI != RE; ++RI) { |
82 | NodeDumper.AddChild(DoAddChild: [=] { |
83 | NodeDumper.dumpBareDeclRef(D: *RI); |
84 | |
85 | if (!(*RI)->isUnconditionallyVisible()) |
86 | OS << " hidden" ; |
87 | |
88 | // If requested, dump the redecl chain for this lookup. |
89 | if (DumpDecls) { |
90 | // Dump earliest decl first. |
91 | std::function<void(Decl *)> DumpWithPrev = [&](Decl *D) { |
92 | if (Decl *Prev = D->getPreviousDecl()) |
93 | DumpWithPrev(Prev); |
94 | Visit(D); |
95 | }; |
96 | DumpWithPrev(*RI); |
97 | } |
98 | }); |
99 | } |
100 | }); |
101 | } |
102 | |
103 | if (HasUndeserializedLookups) { |
104 | NodeDumper.AddChild(DoAddChild: [=] { |
105 | ColorScope Color(OS, ShowColors, UndeserializedColor); |
106 | OS << "<undeserialized lookups>" ; |
107 | }); |
108 | } |
109 | }); |
110 | } |
111 | |
112 | template <typename SpecializationDecl> |
113 | void ASTDumper::dumpTemplateDeclSpecialization(const SpecializationDecl *D, |
114 | bool DumpExplicitInst, |
115 | bool DumpRefOnly) { |
116 | bool DumpedAny = false; |
117 | for (const auto *RedeclWithBadType : D->redecls()) { |
118 | // FIXME: The redecls() range sometimes has elements of a less-specific |
119 | // type. (In particular, ClassTemplateSpecializationDecl::redecls() gives |
120 | // us TagDecls, and should give CXXRecordDecls). |
121 | auto *Redecl = cast<SpecializationDecl>(RedeclWithBadType); |
122 | switch (Redecl->getTemplateSpecializationKind()) { |
123 | case TSK_ExplicitInstantiationDeclaration: |
124 | case TSK_ExplicitInstantiationDefinition: |
125 | if (!DumpExplicitInst) |
126 | break; |
127 | [[fallthrough]]; |
128 | case TSK_Undeclared: |
129 | case TSK_ImplicitInstantiation: |
130 | if (DumpRefOnly) |
131 | NodeDumper.dumpDeclRef(D: Redecl); |
132 | else |
133 | Visit(Redecl); |
134 | DumpedAny = true; |
135 | break; |
136 | case TSK_ExplicitSpecialization: |
137 | break; |
138 | } |
139 | } |
140 | |
141 | // Ensure we dump at least one decl for each specialization. |
142 | if (!DumpedAny) |
143 | NodeDumper.dumpDeclRef(D); |
144 | } |
145 | |
146 | template <typename TemplateDecl> |
147 | void ASTDumper::dumpTemplateDecl(const TemplateDecl *D, bool DumpExplicitInst) { |
148 | dumpTemplateParameters(TPL: D->getTemplateParameters()); |
149 | |
150 | Visit(D->getTemplatedDecl()); |
151 | |
152 | if (GetTraversalKind() == TK_AsIs) { |
153 | for (const auto *Child : D->specializations()) |
154 | dumpTemplateDeclSpecialization(Child, DumpExplicitInst, |
155 | !D->isCanonicalDecl()); |
156 | } |
157 | } |
158 | |
159 | void ASTDumper::VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { |
160 | // FIXME: We don't add a declaration of a function template specialization |
161 | // to its context when it's explicitly instantiated, so dump explicit |
162 | // instantiations when we dump the template itself. |
163 | dumpTemplateDecl(D, DumpExplicitInst: true); |
164 | } |
165 | |
166 | void ASTDumper::VisitClassTemplateDecl(const ClassTemplateDecl *D) { |
167 | dumpTemplateDecl(D, DumpExplicitInst: false); |
168 | } |
169 | |
170 | void ASTDumper::VisitVarTemplateDecl(const VarTemplateDecl *D) { |
171 | dumpTemplateDecl(D, DumpExplicitInst: false); |
172 | } |
173 | |
174 | //===----------------------------------------------------------------------===// |
175 | // Type method implementations |
176 | //===----------------------------------------------------------------------===// |
177 | |
178 | void QualType::dump(const char *msg) const { |
179 | if (msg) |
180 | llvm::errs() << msg << ": " ; |
181 | dump(); |
182 | } |
183 | |
184 | LLVM_DUMP_METHOD void QualType::dump() const { |
185 | ASTDumper Dumper(llvm::errs(), /*ShowColors=*/false); |
186 | Dumper.Visit(T: *this); |
187 | } |
188 | |
189 | LLVM_DUMP_METHOD void QualType::dump(llvm::raw_ostream &OS, |
190 | const ASTContext &Context) const { |
191 | ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors()); |
192 | Dumper.Visit(T: *this); |
193 | } |
194 | |
195 | LLVM_DUMP_METHOD void Type::dump() const { QualType(this, 0).dump(); } |
196 | |
197 | LLVM_DUMP_METHOD void Type::dump(llvm::raw_ostream &OS, |
198 | const ASTContext &Context) const { |
199 | QualType(this, 0).dump(OS, Context); |
200 | } |
201 | |
202 | //===----------------------------------------------------------------------===// |
203 | // TypeLoc method implementations |
204 | //===----------------------------------------------------------------------===// |
205 | |
206 | LLVM_DUMP_METHOD void TypeLoc::dump() const { |
207 | ASTDumper(llvm::errs(), /*ShowColors=*/false).Visit(T: *this); |
208 | } |
209 | |
210 | LLVM_DUMP_METHOD void TypeLoc::dump(llvm::raw_ostream &OS, |
211 | const ASTContext &Context) const { |
212 | ASTDumper(OS, Context, Context.getDiagnostics().getShowColors()).Visit(T: *this); |
213 | } |
214 | |
215 | //===----------------------------------------------------------------------===// |
216 | // Decl method implementations |
217 | //===----------------------------------------------------------------------===// |
218 | |
219 | LLVM_DUMP_METHOD void Decl::dump() const { dump(Out&: llvm::errs()); } |
220 | |
221 | LLVM_DUMP_METHOD void Decl::dump(raw_ostream &OS, bool Deserialize, |
222 | ASTDumpOutputFormat Format) const { |
223 | ASTContext &Ctx = getASTContext(); |
224 | const SourceManager &SM = Ctx.getSourceManager(); |
225 | |
226 | if (ADOF_JSON == Format) { |
227 | JSONDumper P(OS, SM, Ctx, Ctx.getPrintingPolicy(), |
228 | &Ctx.getCommentCommandTraits()); |
229 | (void)Deserialize; // FIXME? |
230 | P.Visit(D: this); |
231 | } else { |
232 | ASTDumper P(OS, Ctx, Ctx.getDiagnostics().getShowColors()); |
233 | P.setDeserialize(Deserialize); |
234 | P.Visit(D: this); |
235 | } |
236 | } |
237 | |
238 | LLVM_DUMP_METHOD void Decl::dumpColor() const { |
239 | const ASTContext &Ctx = getASTContext(); |
240 | ASTDumper P(llvm::errs(), Ctx, /*ShowColors=*/true); |
241 | P.Visit(D: this); |
242 | } |
243 | |
244 | LLVM_DUMP_METHOD void DeclContext::dumpAsDecl() const { |
245 | dumpAsDecl(Ctx: nullptr); |
246 | } |
247 | |
248 | LLVM_DUMP_METHOD void DeclContext::dumpAsDecl(const ASTContext *Ctx) const { |
249 | // By design, DeclContext is required to be a base class of some class that |
250 | // derives from Decl. Thus, it should always be possible to dyn_cast() from |
251 | // a DeclContext pointer to a Decl pointer and Decl::castFromDeclContext() |
252 | // asserts that to be the case. Since this function is intended for use in a |
253 | // debugger, it performs an additional check in order to prevent a failed |
254 | // cast and assertion. If that check fails, then the (invalid) DeclContext |
255 | // is dumped with an indication of its invalidity. |
256 | if (hasValidDeclKind()) { |
257 | const auto *D = cast<Decl>(Val: this); |
258 | D->dump(); |
259 | } else { |
260 | // If an ASTContext is not available, a less capable ASTDumper is |
261 | // constructed for which color diagnostics are, regrettably, disabled. |
262 | ASTDumper P = Ctx ? ASTDumper(llvm::errs(), *Ctx, |
263 | Ctx->getDiagnostics().getShowColors()) |
264 | : ASTDumper(llvm::errs(), /*ShowColors*/ false); |
265 | P.dumpInvalidDeclContext(DC: this); |
266 | } |
267 | } |
268 | |
269 | LLVM_DUMP_METHOD void DeclContext::dumpLookups() const { |
270 | dumpLookups(OS&: llvm::errs()); |
271 | } |
272 | |
273 | LLVM_DUMP_METHOD void DeclContext::dumpLookups(raw_ostream &OS, |
274 | bool DumpDecls, |
275 | bool Deserialize) const { |
276 | const DeclContext *DC = this; |
277 | while (!DC->isTranslationUnit()) |
278 | DC = DC->getParent(); |
279 | const ASTContext &Ctx = cast<TranslationUnitDecl>(Val: DC)->getASTContext(); |
280 | ASTDumper P(OS, Ctx, Ctx.getDiagnostics().getShowColors()); |
281 | P.setDeserialize(Deserialize); |
282 | P.dumpLookups(DC: this, DumpDecls); |
283 | } |
284 | |
285 | //===----------------------------------------------------------------------===// |
286 | // Stmt method implementations |
287 | //===----------------------------------------------------------------------===// |
288 | |
289 | LLVM_DUMP_METHOD void Stmt::dump() const { |
290 | ASTDumper P(llvm::errs(), /*ShowColors=*/false); |
291 | P.Visit(Node: this); |
292 | } |
293 | |
294 | LLVM_DUMP_METHOD void Stmt::dump(raw_ostream &OS, |
295 | const ASTContext &Context) const { |
296 | ASTDumper P(OS, Context, Context.getDiagnostics().getShowColors()); |
297 | P.Visit(Node: this); |
298 | } |
299 | |
300 | LLVM_DUMP_METHOD void Stmt::dumpColor() const { |
301 | ASTDumper P(llvm::errs(), /*ShowColors=*/true); |
302 | P.Visit(Node: this); |
303 | } |
304 | |
305 | //===----------------------------------------------------------------------===// |
306 | // Comment method implementations |
307 | //===----------------------------------------------------------------------===// |
308 | |
309 | LLVM_DUMP_METHOD void Comment::() const { |
310 | const auto *FC = dyn_cast<FullComment>(Val: this); |
311 | if (!FC) |
312 | return; |
313 | ASTDumper Dumper(llvm::errs(), /*ShowColors=*/false); |
314 | Dumper.Visit(C: FC, FC); |
315 | } |
316 | |
317 | LLVM_DUMP_METHOD void Comment::(raw_ostream &OS, |
318 | const ASTContext &Context) const { |
319 | const auto *FC = dyn_cast<FullComment>(Val: this); |
320 | if (!FC) |
321 | return; |
322 | ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors()); |
323 | Dumper.Visit(C: FC, FC); |
324 | } |
325 | |
326 | LLVM_DUMP_METHOD void Comment::() const { |
327 | const auto *FC = dyn_cast<FullComment>(Val: this); |
328 | if (!FC) |
329 | return; |
330 | ASTDumper Dumper(llvm::errs(), /*ShowColors=*/true); |
331 | Dumper.Visit(C: FC, FC); |
332 | } |
333 | |
334 | //===----------------------------------------------------------------------===// |
335 | // APValue method implementations |
336 | //===----------------------------------------------------------------------===// |
337 | |
338 | LLVM_DUMP_METHOD void APValue::dump() const { |
339 | ASTDumper Dumper(llvm::errs(), /*ShowColors=*/false); |
340 | Dumper.Visit(Value: *this, /*Ty=*/QualType()); |
341 | } |
342 | |
343 | LLVM_DUMP_METHOD void APValue::dump(raw_ostream &OS, |
344 | const ASTContext &Context) const { |
345 | ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors()); |
346 | Dumper.Visit(Value: *this, /*Ty=*/Context.getPointerType(T: Context.CharTy)); |
347 | } |
348 | |
349 | //===----------------------------------------------------------------------===// |
350 | // ConceptReference method implementations |
351 | //===----------------------------------------------------------------------===// |
352 | |
353 | LLVM_DUMP_METHOD void ConceptReference::dump() const { |
354 | dump(llvm::errs()); |
355 | } |
356 | |
357 | LLVM_DUMP_METHOD void ConceptReference::dump(raw_ostream &OS) const { |
358 | auto &Ctx = getNamedConcept()->getASTContext(); |
359 | ASTDumper P(OS, Ctx, Ctx.getDiagnostics().getShowColors()); |
360 | P.Visit(R: this); |
361 | } |
362 | |
363 | //===----------------------------------------------------------------------===// |
364 | // TemplateName method implementations |
365 | //===----------------------------------------------------------------------===// |
366 | |
367 | // FIXME: These are actually using the TemplateArgument dumper, through |
368 | // an implicit conversion. The dump will claim this is a template argument, |
369 | // which is misleading. |
370 | |
371 | LLVM_DUMP_METHOD void TemplateName::dump() const { |
372 | ASTDumper Dumper(llvm::errs(), /*ShowColors=*/false); |
373 | Dumper.Visit(A: *this); |
374 | } |
375 | |
376 | LLVM_DUMP_METHOD void TemplateName::dump(llvm::raw_ostream &OS, |
377 | const ASTContext &Context) const { |
378 | ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors()); |
379 | Dumper.Visit(A: *this); |
380 | } |
381 | |
382 | //===----------------------------------------------------------------------===// |
383 | // TemplateArgument method implementations |
384 | //===----------------------------------------------------------------------===// |
385 | |
386 | LLVM_DUMP_METHOD void TemplateArgument::dump() const { |
387 | ASTDumper Dumper(llvm::errs(), /*ShowColors=*/false); |
388 | Dumper.Visit(A: *this); |
389 | } |
390 | |
391 | LLVM_DUMP_METHOD void TemplateArgument::dump(llvm::raw_ostream &OS, |
392 | const ASTContext &Context) const { |
393 | ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors()); |
394 | Dumper.Visit(A: *this); |
395 | } |
396 | |