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