Skip to main content

cinrs_core/
dump.rs

1//! A deterministic, human readable dump of the AST.
2//!
3//! Used by the snapshot tests and handy when debugging the parser. Source
4//! ranges are deliberately left out: they would drown the interesting
5//! structure, and span behaviour is covered by its own tests.
6
7use crate::ast::*;
8use crate::lex::{FloatSuffix, LongKind};
9
10/// Renders a whole translation unit.
11pub fn dump_translation_unit(unit: &TranslationUnit) -> String {
12    let mut d = Dumper::new(unit);
13    d.line("translation-unit");
14    d.indent += 1;
15    for item in &unit.items {
16        d.external_decl(item);
17    }
18    d.out
19}
20
21/// Renders a single expression (useful for focused tests).
22///
23/// The unit is needed because a type inside the expression may name a tag
24/// specifier, which lives in [`TranslationUnit::records`].
25pub fn dump_expr(unit: &TranslationUnit, expr: &Expr) -> String {
26    let mut d = Dumper::new(unit);
27    d.expr(expr);
28    d.out
29}
30
31/// Renders a type the way the dump does.
32pub fn type_to_string(unit: &TranslationUnit, ty: &Type) -> String {
33    let mut s = String::new();
34    if ty.qualifiers.is_const {
35        s.push_str("const ");
36    }
37    if ty.qualifiers.is_volatile {
38        s.push_str("volatile ");
39    }
40    if ty.qualifiers.is_restrict {
41        s.push_str("restrict ");
42    }
43    if ty.qualifiers.is_atomic {
44        s.push_str("_Atomic ");
45    }
46    match &ty.kind {
47        TypeKind::Void => s.push_str("void"),
48        TypeKind::Bool => s.push_str("_Bool"),
49        TypeKind::Char(None) => s.push_str("char"),
50        TypeKind::Char(Some(Sign::Signed)) => s.push_str("signed char"),
51        TypeKind::Char(Some(Sign::Unsigned)) => s.push_str("unsigned char"),
52        TypeKind::Int { sign, size } => {
53            if *sign == Sign::Unsigned {
54                s.push_str("unsigned ");
55            }
56            s.push_str(match size {
57                IntSize::Short => "short",
58                IntSize::Int => "int",
59                IntSize::Long => "long",
60                IntSize::LongLong => "long long",
61                IntSize::Int128 => "__int128",
62            });
63        }
64        TypeKind::Float(size) => s.push_str(float_size(*size)),
65        TypeKind::Complex(size) => {
66            s.push_str(float_size(*size));
67            s.push_str(" _Complex");
68        }
69        TypeKind::Imaginary(size) => {
70            s.push_str(float_size(*size));
71            s.push_str(" _Imaginary");
72        }
73        TypeKind::Pointer(inner) => {
74            s.push_str("ptr<");
75            s.push_str(&type_to_string(unit, inner));
76            s.push('>');
77        }
78        TypeKind::Array {
79            elem,
80            size,
81            qualifiers,
82            is_static,
83        } => {
84            s.push_str("array<");
85            s.push_str(&type_to_string(unit, elem));
86            s.push_str(", ");
87            s.push_str(&array_size(size));
88            if *is_static {
89                s.push_str(", static");
90            }
91            if qualifiers.any() {
92                s.push_str(", ");
93                if qualifiers.is_const {
94                    s.push_str("const ");
95                }
96                if qualifiers.is_volatile {
97                    s.push_str("volatile ");
98                }
99                if qualifiers.is_restrict {
100                    s.push_str("restrict ");
101                }
102                s.pop();
103            }
104            s.push('>');
105        }
106        TypeKind::Function(ft) => {
107            s.push_str("fn(");
108            if !ft.has_prototype {
109                if ft.kr_names.is_empty() {
110                    s.push_str("<unspecified>");
111                } else {
112                    s.push_str("<K&R> ");
113                    let names: Vec<&str> = ft.kr_names.iter().map(|n| n.name.as_str()).collect();
114                    s.push_str(&names.join(", "));
115                }
116            } else if ft.params.is_empty() && !ft.variadic {
117                s.push_str("void");
118            } else {
119                let params: Vec<String> = ft
120                    .params
121                    .iter()
122                    .map(|p| match &p.name {
123                        Some(n) => format!("{}: {}", n.name, type_to_string(unit, &p.ty)),
124                        None => type_to_string(unit, &p.ty),
125                    })
126                    .collect();
127                s.push_str(&params.join(", "));
128                if ft.variadic {
129                    if !params.is_empty() {
130                        s.push_str(", ");
131                    }
132                    s.push_str("...");
133                }
134            }
135            s.push_str(") -> ");
136            s.push_str(&type_to_string(unit, &ft.ret));
137        }
138        TypeKind::Record(id) => {
139            let r = unit.record(*id);
140            s.push_str(r.kind.as_str());
141            s.push(' ');
142            match &r.name {
143                Some(n) => s.push_str(&n.name),
144                None => s.push_str("<anonymous>"),
145            }
146            if r.fields.is_some() {
147                s.push_str(" {…}");
148            }
149        }
150        TypeKind::Enum(id) => {
151            let e = unit.enum_spec(*id);
152            s.push_str("enum ");
153            match &e.name {
154                Some(n) => s.push_str(&n.name),
155                None => s.push_str("<anonymous>"),
156            }
157            if e.enumerators.is_some() {
158                s.push_str(" {…}");
159            }
160        }
161        TypeKind::Typedef(id) => {
162            s.push_str("typedef-name ");
163            s.push_str(&id.name);
164        }
165        TypeKind::Typeof { id, unqual } => {
166            s.push_str(if *unqual { "typeof_unqual<" } else { "typeof<" });
167            match unit.typeof_operand(*id) {
168                TypeofOperand::Expr(_) => s.push_str("expr"),
169                TypeofOperand::Type(name) => s.push_str(&type_to_string(unit, &name.ty)),
170            }
171            s.push('>');
172        }
173        TypeKind::Auto => s.push_str("auto"),
174        TypeKind::Error => s.push_str("<error>"),
175    }
176    s
177}
178
179fn float_size(size: FloatSize) -> &'static str {
180    match size {
181        FloatSize::Float => "float",
182        FloatSize::Double => "double",
183        FloatSize::LongDouble => "long double",
184    }
185}
186
187fn array_size(size: &ArraySize) -> String {
188    match size {
189        ArraySize::Unspecified => "?".to_owned(),
190        ArraySize::Star => "*".to_owned(),
191        ArraySize::Expr(e) => match &e.kind {
192            ExprKind::Int(lit) => lit.value.to_string(),
193            _ => "expr".to_owned(),
194        },
195    }
196}
197
198struct Dumper<'a> {
199    /// The unit being dumped, which the tag specifiers are looked up in.
200    unit: &'a TranslationUnit,
201    out: String,
202    indent: usize,
203}
204
205impl<'a> Dumper<'a> {
206    fn new(unit: &'a TranslationUnit) -> Self {
207        Self {
208            unit,
209            out: String::new(),
210            indent: 0,
211        }
212    }
213
214    /// The rendering of a type, in this unit.
215    fn ty(&self, ty: &Type) -> String {
216        type_to_string(self.unit, ty)
217    }
218
219    fn line(&mut self, text: impl AsRef<str>) {
220        for _ in 0..self.indent {
221            self.out.push_str("  ");
222        }
223        self.out.push_str(text.as_ref());
224        self.out.push('\n');
225    }
226
227    /// Emits `label` and runs `f` one level deeper.
228    fn under(&mut self, label: &str, f: impl FnOnce(&mut Self)) {
229        self.line(label);
230        self.indent += 1;
231        f(self);
232        self.indent -= 1;
233    }
234
235    // -- declarations -------------------------------------------------------
236
237    fn external_decl(&mut self, item: &ExternalDecl) {
238        match item {
239            ExternalDecl::Function(f) => self.function_def(f),
240            ExternalDecl::Decl(d) => self.decl(d),
241            ExternalDecl::StaticAssert(sa) => self.static_assert(sa),
242        }
243    }
244
245    fn static_assert(&mut self, sa: &StaticAssert) {
246        let label = match &sa.message {
247            Some(message) => format!("static-assert {message}"),
248            None => "static-assert".to_owned(),
249        };
250        self.under(&label, |d| d.expr(&sa.cond));
251    }
252
253    fn function_def(&mut self, f: &FunctionDef) {
254        let mut header = format!("function '{}' : {}", f.name.name, self.ty(&f.ty));
255        if let Some(storage) = &f.specifiers.storage {
256            header.push_str(&format!(" [{}]", storage.node.as_str()));
257        }
258        if f.specifiers.inline {
259            header.push_str(" [inline]");
260        }
261        self.line(header);
262        self.indent += 1;
263        self.type_body(&f.specifiers.base);
264        for kr in &f.kr_decls {
265            self.under("kr-param-decl", |d| d.decl_inner(kr));
266        }
267        self.under("body", |d| {
268            for item in &f.body.items {
269                d.block_item(item);
270            }
271        });
272        self.indent -= 1;
273    }
274
275    fn decl(&mut self, decl: &Decl) {
276        self.line("declaration");
277        self.indent += 1;
278        self.decl_inner(decl);
279        self.indent -= 1;
280    }
281
282    fn decl_inner(&mut self, decl: &Decl) {
283        let mut header = format!("specifiers: {}", self.ty(&decl.specifiers.base));
284        if let Some(storage) = &decl.specifiers.storage {
285            header.push_str(&format!(" [{}]", storage.node.as_str()));
286        }
287        if decl.specifiers.inline {
288            header.push_str(" [inline]");
289        }
290        self.line(header);
291        self.type_body(&decl.specifiers.base);
292        for d in &decl.declarators {
293            let name = d.name.as_ref().map_or("<abstract>", |n| n.name.as_str());
294            self.line(format!("declarator '{}' : {}", name, self.ty(&d.ty)));
295            if let Some(init) = &d.init {
296                self.indent += 1;
297                self.under("init", |dd| dd.initializer(init));
298                self.indent -= 1;
299            }
300        }
301    }
302
303    /// Dumps the members of a `struct`/`union`/`enum` specifier that has a
304    /// body, so that bit-fields and enumerator values are visible.
305    fn type_body(&mut self, ty: &Type) {
306        match &ty.kind {
307            TypeKind::Record(id) => {
308                let r = self.unit.record(*id);
309                let Some(fields) = &r.fields else { return };
310                let label = format!(
311                    "{} '{}' members",
312                    r.kind.as_str(),
313                    r.name.as_ref().map_or("<anonymous>", |n| n.name.as_str())
314                );
315                self.under(&label, |d| {
316                    for f in fields {
317                        let name = f.name.as_ref().map_or("<anonymous>", |n| n.name.as_str());
318                        let mut line = format!("field '{}' : {}", name, d.ty(&f.ty));
319                        if let Some(w) = &f.bit_width {
320                            if let ExprKind::Int(lit) = &w.kind {
321                                line.push_str(&format!(" : {}", lit.value));
322                            } else {
323                                line.push_str(" : <expr>");
324                            }
325                        }
326                        d.line(line);
327                        d.indent += 1;
328                        d.type_body(&f.specifiers.base);
329                        d.indent -= 1;
330                    }
331                    for assert in &r.asserts {
332                        d.static_assert(assert);
333                    }
334                });
335            }
336            TypeKind::Enum(id) => {
337                let e = self.unit.enum_spec(*id);
338                let Some(enumerators) = &e.enumerators else {
339                    return;
340                };
341                let label = format!(
342                    "enum '{}' enumerators",
343                    e.name.as_ref().map_or("<anonymous>", |n| n.name.as_str())
344                );
345                self.under(&label, |d| {
346                    for en in enumerators {
347                        match &en.value {
348                            Some(v) => {
349                                d.under(&format!("enumerator '{}'", en.name.name), |dd| dd.expr(v))
350                            }
351                            None => d.line(format!("enumerator '{}'", en.name.name)),
352                        }
353                    }
354                });
355            }
356            _ => {}
357        }
358    }
359
360    fn initializer(&mut self, init: &Initializer) {
361        match &init.kind {
362            InitializerKind::Expr(e) => self.expr(e),
363            InitializerKind::List(items) => self.under("init-list", |d| {
364                for item in items {
365                    let label = element_label(&item.designators);
366                    d.under(&label, |dd| dd.initializer(&item.init));
367                }
368            }),
369        }
370    }
371
372    // -- statements ---------------------------------------------------------
373
374    fn block_item(&mut self, item: &BlockItem) {
375        match item {
376            BlockItem::Decl(d) => self.decl(d),
377            BlockItem::Stmt(s) => self.stmt(s),
378            BlockItem::StaticAssert(sa) => self.static_assert(sa),
379            BlockItem::NestedFunction(f) => self.function_def(f),
380        }
381    }
382
383    fn stmt(&mut self, stmt: &Stmt) {
384        match &stmt.kind {
385            StmtKind::Labeled { label, body } => {
386                self.under(&format!("label '{}'", label.name), |d| d.stmt(body));
387            }
388            StmtKind::Case { value, upper, body } => self.under("case", |d| {
389                d.under("value", |dd| dd.expr(value));
390                if let Some(upper) = upper {
391                    d.under("upto", |dd| dd.expr(upper));
392                }
393                d.stmt(body);
394            }),
395            StmtKind::Default { body } => self.under("default", |d| d.stmt(body)),
396            StmtKind::Compound(block) => self.under("block", |d| {
397                for item in &block.items {
398                    d.block_item(item);
399                }
400            }),
401            StmtKind::Expr(None) => self.line("null-stmt"),
402            StmtKind::Expr(Some(e)) => self.under("expr-stmt", |d| d.expr(e)),
403            StmtKind::If {
404                cond,
405                then_branch,
406                else_branch,
407            } => self.under("if", |d| {
408                d.under("cond", |dd| dd.expr(cond));
409                d.under("then", |dd| dd.stmt(then_branch));
410                if let Some(e) = else_branch {
411                    d.under("else", |dd| dd.stmt(e));
412                }
413            }),
414            StmtKind::Switch { cond, body } => self.under("switch", |d| {
415                d.under("cond", |dd| dd.expr(cond));
416                d.under("body", |dd| dd.stmt(body));
417            }),
418            StmtKind::While { cond, body } => self.under("while", |d| {
419                d.under("cond", |dd| dd.expr(cond));
420                d.under("body", |dd| dd.stmt(body));
421            }),
422            StmtKind::DoWhile { body, cond } => self.under("do-while", |d| {
423                d.under("body", |dd| dd.stmt(body));
424                d.under("cond", |dd| dd.expr(cond));
425            }),
426            StmtKind::For {
427                init,
428                cond,
429                step,
430                body,
431            } => self.under("for", |d| {
432                match init {
433                    ForInit::None => d.line("init: <none>"),
434                    ForInit::Expr(e) => d.under("init", |dd| dd.expr(e)),
435                    ForInit::Decl(decl) => d.under("init", |dd| dd.decl(decl)),
436                    ForInit::StaticAssert(sa) => d.under("init", |dd| dd.static_assert(sa)),
437                }
438                match cond {
439                    Some(e) => d.under("cond", |dd| dd.expr(e)),
440                    None => d.line("cond: <none>"),
441                }
442                match step {
443                    Some(e) => d.under("step", |dd| dd.expr(e)),
444                    None => d.line("step: <none>"),
445                }
446                d.under("body", |dd| dd.stmt(body));
447            }),
448            StmtKind::Goto(label) => self.line(format!("goto '{}'", label.name)),
449            StmtKind::GotoPtr(target) => self.under("goto-ptr", |d| d.expr(target)),
450            StmtKind::Continue => self.line("continue"),
451            StmtKind::Break => self.line("break"),
452            StmtKind::Return(None) => self.line("return"),
453            StmtKind::Return(Some(e)) => self.under("return", |d| d.expr(e)),
454            StmtKind::Error => self.line("<error-stmt>"),
455        }
456    }
457
458    // -- expressions --------------------------------------------------------
459
460    fn expr(&mut self, expr: &Expr) {
461        match &expr.kind {
462            ExprKind::Ident(id) => self.line(format!("ident '{}'", id.name)),
463            ExprKind::Int(lit) => {
464                let mut s = format!("int {}", lit.value);
465                if lit.unsigned {
466                    s.push_str(" unsigned");
467                }
468                match lit.long {
469                    LongKind::None => {}
470                    LongKind::Long => s.push_str(" long"),
471                    LongKind::LongLong => s.push_str(" long-long"),
472                }
473                self.line(s);
474            }
475            ExprKind::Float(lit) => {
476                let mut s = format!("float {}", lit.value);
477                match lit.suffix {
478                    FloatSuffix::None => {}
479                    FloatSuffix::Float => s.push_str(" f"),
480                    FloatSuffix::LongDouble => s.push_str(" l"),
481                }
482                self.line(s);
483            }
484            ExprKind::Char(lit) => {
485                self.line(format!(
486                    "char {}{} = {}",
487                    lit.kind.prefix(),
488                    lit.text,
489                    lit.value
490                ));
491            }
492            ExprKind::Str(lit) => {
493                self.line(format!(
494                    "string {}{} ({} elements)",
495                    lit.kind.prefix(),
496                    lit.text,
497                    lit.values.len()
498                ));
499            }
500            ExprKind::Unary { op, operand } => {
501                self.under(&format!("unary '{}'", op.as_str()), |d| d.expr(operand));
502            }
503            ExprKind::Binary { op, lhs, rhs } => {
504                self.under(&format!("binary '{}'", op.as_str()), |d| {
505                    d.expr(lhs);
506                    d.expr(rhs);
507                });
508            }
509            ExprKind::Assign { op, lhs, rhs } => {
510                let label = match op {
511                    Some(op) => format!("assign '{}='", op.as_str()),
512                    None => "assign '='".to_owned(),
513                };
514                self.under(&label, |d| {
515                    d.expr(lhs);
516                    d.expr(rhs);
517                });
518            }
519            ExprKind::Conditional {
520                cond,
521                then_expr,
522                else_expr,
523            } => self.under("conditional", |d| {
524                d.under("cond", |dd| dd.expr(cond));
525                if let Some(then_expr) = then_expr {
526                    d.under("then", |dd| dd.expr(then_expr));
527                }
528                d.under("else", |dd| dd.expr(else_expr));
529            }),
530            ExprKind::Comma { lhs, rhs } => self.under("comma", |d| {
531                d.expr(lhs);
532                d.expr(rhs);
533            }),
534            ExprKind::Call { callee, args } => self.under("call", |d| {
535                d.under("callee", |dd| dd.expr(callee));
536                for arg in args {
537                    d.under("arg", |dd| dd.expr(arg));
538                }
539            }),
540            ExprKind::Member { base, arrow, field } => {
541                let op = if *arrow { "->" } else { "." };
542                self.under(&format!("member '{}{}'", op, field.name), |d| d.expr(base));
543            }
544            ExprKind::Index { base, index } => self.under("index", |d| {
545                d.expr(base);
546                d.expr(index);
547            }),
548            ExprKind::PostIncDec { op, operand } => {
549                self.under(&format!("postfix '{}'", op.as_str()), |d| d.expr(operand));
550            }
551            ExprKind::PreIncDec { op, operand } => {
552                self.under(&format!("prefix '{}'", op.as_str()), |d| d.expr(operand));
553            }
554            ExprKind::Cast { ty, expr } => {
555                self.under(&format!("cast to {}", self.ty(&ty.ty)), |d| d.expr(expr));
556            }
557            ExprKind::LabelAddr(label) => self.line(format!("label-addr '{}'", label.name)),
558            ExprKind::SizeofExpr(inner) => self.under("sizeof-expr", |d| d.expr(inner)),
559            ExprKind::SizeofType(ty) => {
560                self.line(format!("sizeof-type {}", self.ty(&ty.ty)));
561            }
562            ExprKind::AlignofExpr(inner) => self.under("alignof-expr", |d| d.expr(inner)),
563            ExprKind::AlignofType(ty) => {
564                self.line(format!("alignof-type {}", self.ty(&ty.ty)));
565            }
566            ExprKind::Generic {
567                controlling,
568                assocs,
569            } => self.under("generic", |d| {
570                d.under("controlling", |dd| dd.expr(controlling));
571                for assoc in assocs {
572                    let label = match &assoc.ty {
573                        Some(ty) => format!("assoc {}", d.ty(&ty.ty)),
574                        None => "assoc default".to_owned(),
575                    };
576                    d.under(&label, |dd| dd.expr(&assoc.value));
577                }
578            }),
579            ExprKind::Bool(value) => self.line(if *value { "true" } else { "false" }),
580            ExprKind::Nullptr => self.line("nullptr"),
581            ExprKind::VaArg { ap, ty } => {
582                self.under(&format!("va_arg {}", self.ty(&ty.ty)), |d| d.expr(ap));
583            }
584            ExprKind::OffsetOf { ty, member, path } => {
585                let rest = element_label(path);
586                let rest = rest.strip_prefix("element").unwrap_or(&rest);
587                self.line(format!(
588                    "offsetof {} .{}{rest}",
589                    self.ty(&ty.ty),
590                    member.name
591                ));
592            }
593            ExprKind::CompoundLiteral { ty, init } => {
594                self.under(&format!("compound-literal {}", self.ty(&ty.ty)), |d| {
595                    for item in init {
596                        let label = element_label(&item.designators);
597                        d.under(&label, |dd| dd.initializer(&item.init));
598                    }
599                })
600            }
601            ExprKind::StmtExpr(block) => self.under("stmt-expr", |d| {
602                for item in &block.items {
603                    d.block_item(item);
604                }
605            }),
606            ExprKind::TypesCompatible { lhs, rhs } => self.line(format!(
607                "types-compatible {} {}",
608                self.ty(&lhs.ty),
609                self.ty(&rhs.ty)
610            )),
611            ExprKind::ChooseExpr {
612                cond,
613                then_expr,
614                else_expr,
615            } => self.under("choose-expr", |d| {
616                d.under("cond", |dd| dd.expr(cond));
617                d.under("then", |dd| dd.expr(then_expr));
618                d.under("else", |dd| dd.expr(else_expr));
619            }),
620            ExprKind::ComplexPart { real, operand } => {
621                let name = if *real { "__real__" } else { "__imag__" };
622                self.under(name, |d| d.expr(operand));
623            }
624            ExprKind::Error => self.line("<error-expr>"),
625        }
626    }
627}
628
629/// The label one element of an initialiser list is dumped under.
630fn element_label(designators: &[Designator]) -> String {
631    let mut label = String::from("element");
632    let index = |e: &Expr| match &e.kind {
633        ExprKind::Int(lit) => lit.value.to_string(),
634        _ => "expr".to_owned(),
635    };
636    for designator in designators {
637        match designator {
638            Designator::Field(f) => label.push_str(&format!(" .{}", f.name)),
639            Designator::Index(e) => label.push_str(&format!(" [{}]", index(e))),
640            Designator::Range(low, high) => {
641                label.push_str(&format!(" [{} ... {}]", index(low), index(high)));
642            }
643        }
644    }
645    label
646}