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    size.as_str()
181}
182
183fn array_size(size: &ArraySize) -> String {
184    match size {
185        ArraySize::Unspecified => "?".to_owned(),
186        ArraySize::Star => "*".to_owned(),
187        ArraySize::Expr(e) => match &e.kind {
188            ExprKind::Int(lit) => lit.value.to_string(),
189            _ => "expr".to_owned(),
190        },
191    }
192}
193
194struct Dumper<'a> {
195    /// The unit being dumped, which the tag specifiers are looked up in.
196    unit: &'a TranslationUnit,
197    out: String,
198    indent: usize,
199}
200
201impl<'a> Dumper<'a> {
202    fn new(unit: &'a TranslationUnit) -> Self {
203        Self {
204            unit,
205            out: String::new(),
206            indent: 0,
207        }
208    }
209
210    /// The rendering of a type, in this unit.
211    fn ty(&self, ty: &Type) -> String {
212        type_to_string(self.unit, ty)
213    }
214
215    fn line(&mut self, text: impl AsRef<str>) {
216        for _ in 0..self.indent {
217            self.out.push_str("  ");
218        }
219        self.out.push_str(text.as_ref());
220        self.out.push('\n');
221    }
222
223    /// Emits `label` and runs `f` one level deeper.
224    fn under(&mut self, label: &str, f: impl FnOnce(&mut Self)) {
225        self.line(label);
226        self.indent += 1;
227        f(self);
228        self.indent -= 1;
229    }
230
231    // -- declarations -------------------------------------------------------
232
233    fn external_decl(&mut self, item: &ExternalDecl) {
234        match item {
235            ExternalDecl::Function(f) => self.function_def(f),
236            ExternalDecl::Decl(d) => self.decl(d),
237            ExternalDecl::StaticAssert(sa) => self.static_assert(sa),
238        }
239    }
240
241    fn static_assert(&mut self, sa: &StaticAssert) {
242        let label = match &sa.message {
243            Some(message) => format!("static-assert {message}"),
244            None => "static-assert".to_owned(),
245        };
246        self.under(&label, |d| d.expr(&sa.cond));
247    }
248
249    fn function_def(&mut self, f: &FunctionDef) {
250        let mut header = format!("function '{}' : {}", f.name.name, self.ty(&f.ty));
251        if let Some(storage) = &f.specifiers.storage {
252            header.push_str(&format!(" [{}]", storage.node.as_str()));
253        }
254        if f.specifiers.inline {
255            header.push_str(" [inline]");
256        }
257        self.line(header);
258        self.indent += 1;
259        self.type_body(&f.specifiers.base);
260        for kr in &f.kr_decls {
261            self.under("kr-param-decl", |d| d.decl_inner(kr));
262        }
263        self.under("body", |d| {
264            for item in &f.body.items {
265                d.block_item(item);
266            }
267        });
268        self.indent -= 1;
269    }
270
271    fn decl(&mut self, decl: &Decl) {
272        self.line("declaration");
273        self.indent += 1;
274        self.decl_inner(decl);
275        self.indent -= 1;
276    }
277
278    fn decl_inner(&mut self, decl: &Decl) {
279        let mut header = format!("specifiers: {}", self.ty(&decl.specifiers.base));
280        if let Some(storage) = &decl.specifiers.storage {
281            header.push_str(&format!(" [{}]", storage.node.as_str()));
282        }
283        if decl.specifiers.inline {
284            header.push_str(" [inline]");
285        }
286        self.line(header);
287        self.type_body(&decl.specifiers.base);
288        for d in &decl.declarators {
289            let name = d.name.as_ref().map_or("<abstract>", |n| n.name.as_str());
290            self.line(format!("declarator '{}' : {}", name, self.ty(&d.ty)));
291            if let Some(init) = &d.init {
292                self.indent += 1;
293                self.under("init", |dd| dd.initializer(init));
294                self.indent -= 1;
295            }
296        }
297    }
298
299    /// Dumps the members of a `struct`/`union`/`enum` specifier that has a
300    /// body, so that bit-fields and enumerator values are visible.
301    fn type_body(&mut self, ty: &Type) {
302        match &ty.kind {
303            TypeKind::Record(id) => {
304                let r = self.unit.record(*id);
305                let Some(fields) = &r.fields else { return };
306                let label = format!(
307                    "{} '{}' members",
308                    r.kind.as_str(),
309                    r.name.as_ref().map_or("<anonymous>", |n| n.name.as_str())
310                );
311                self.under(&label, |d| {
312                    for f in fields {
313                        let name = f.name.as_ref().map_or("<anonymous>", |n| n.name.as_str());
314                        let mut line = format!("field '{}' : {}", name, d.ty(&f.ty));
315                        if let Some(w) = &f.bit_width {
316                            if let ExprKind::Int(lit) = &w.kind {
317                                line.push_str(&format!(" : {}", lit.value));
318                            } else {
319                                line.push_str(" : <expr>");
320                            }
321                        }
322                        d.line(line);
323                        d.indent += 1;
324                        d.type_body(&f.specifiers.base);
325                        d.indent -= 1;
326                    }
327                    for assert in &r.asserts {
328                        d.static_assert(assert);
329                    }
330                });
331            }
332            TypeKind::Enum(id) => {
333                let e = self.unit.enum_spec(*id);
334                let Some(enumerators) = &e.enumerators else {
335                    return;
336                };
337                let label = format!(
338                    "enum '{}' enumerators",
339                    e.name.as_ref().map_or("<anonymous>", |n| n.name.as_str())
340                );
341                self.under(&label, |d| {
342                    for en in enumerators {
343                        match &en.value {
344                            Some(v) => {
345                                d.under(&format!("enumerator '{}'", en.name.name), |dd| dd.expr(v))
346                            }
347                            None => d.line(format!("enumerator '{}'", en.name.name)),
348                        }
349                    }
350                });
351            }
352            _ => {}
353        }
354    }
355
356    fn initializer(&mut self, init: &Initializer) {
357        match &init.kind {
358            InitializerKind::Expr(e) => self.expr(e),
359            InitializerKind::List(items) => self.under("init-list", |d| {
360                for item in items {
361                    let label = element_label(&item.designators);
362                    d.under(&label, |dd| dd.initializer(&item.init));
363                }
364            }),
365        }
366    }
367
368    // -- statements ---------------------------------------------------------
369
370    fn block_item(&mut self, item: &BlockItem) {
371        match item {
372            BlockItem::Decl(d) => self.decl(d),
373            BlockItem::Stmt(s) => self.stmt(s),
374            BlockItem::StaticAssert(sa) => self.static_assert(sa),
375            BlockItem::NestedFunction(f) => self.function_def(f),
376        }
377    }
378
379    fn stmt(&mut self, stmt: &Stmt) {
380        match &stmt.kind {
381            StmtKind::Labeled { label, body } => {
382                self.under(&format!("label '{}'", label.name), |d| d.stmt(body));
383            }
384            StmtKind::Case { value, upper, body } => self.under("case", |d| {
385                d.under("value", |dd| dd.expr(value));
386                if let Some(upper) = upper {
387                    d.under("upto", |dd| dd.expr(upper));
388                }
389                d.stmt(body);
390            }),
391            StmtKind::Default { body } => self.under("default", |d| d.stmt(body)),
392            StmtKind::Compound(block) => self.under("block", |d| {
393                for item in &block.items {
394                    d.block_item(item);
395                }
396            }),
397            StmtKind::Expr(None) => self.line("null-stmt"),
398            StmtKind::Expr(Some(e)) => self.under("expr-stmt", |d| d.expr(e)),
399            StmtKind::If {
400                cond,
401                then_branch,
402                else_branch,
403            } => self.under("if", |d| {
404                d.under("cond", |dd| dd.expr(cond));
405                d.under("then", |dd| dd.stmt(then_branch));
406                if let Some(e) = else_branch {
407                    d.under("else", |dd| dd.stmt(e));
408                }
409            }),
410            StmtKind::Switch { cond, body } => self.under("switch", |d| {
411                d.under("cond", |dd| dd.expr(cond));
412                d.under("body", |dd| dd.stmt(body));
413            }),
414            StmtKind::While { cond, body } => self.under("while", |d| {
415                d.under("cond", |dd| dd.expr(cond));
416                d.under("body", |dd| dd.stmt(body));
417            }),
418            StmtKind::DoWhile { body, cond } => self.under("do-while", |d| {
419                d.under("body", |dd| dd.stmt(body));
420                d.under("cond", |dd| dd.expr(cond));
421            }),
422            StmtKind::For {
423                init,
424                cond,
425                step,
426                body,
427            } => self.under("for", |d| {
428                match init {
429                    ForInit::None => d.line("init: <none>"),
430                    ForInit::Expr(e) => d.under("init", |dd| dd.expr(e)),
431                    ForInit::Decl(decl) => d.under("init", |dd| dd.decl(decl)),
432                    ForInit::StaticAssert(sa) => d.under("init", |dd| dd.static_assert(sa)),
433                }
434                match cond {
435                    Some(e) => d.under("cond", |dd| dd.expr(e)),
436                    None => d.line("cond: <none>"),
437                }
438                match step {
439                    Some(e) => d.under("step", |dd| dd.expr(e)),
440                    None => d.line("step: <none>"),
441                }
442                d.under("body", |dd| dd.stmt(body));
443            }),
444            StmtKind::Goto(label) => self.line(format!("goto '{}'", label.name)),
445            StmtKind::GotoPtr(target) => self.under("goto-ptr", |d| d.expr(target)),
446            StmtKind::Continue => self.line("continue"),
447            StmtKind::Break => self.line("break"),
448            StmtKind::Return(None) => self.line("return"),
449            StmtKind::Return(Some(e)) => self.under("return", |d| d.expr(e)),
450            StmtKind::Asm(asm) => self.asm(asm),
451            StmtKind::Error => self.line("<error-stmt>"),
452        }
453    }
454
455    fn asm(&mut self, asm: &AsmStmt) {
456        let mut head = String::from(if asm.extended { "asm" } else { "asm basic" });
457        for (flag, name) in [
458            (asm.volatile, "volatile"),
459            (asm.inline, "inline"),
460            (asm.goto, "goto"),
461        ] {
462            if flag {
463                head.push(' ');
464                head.push_str(name);
465            }
466        }
467        self.under(&head, |d| {
468            d.line(format!("template {:?}", asm.template.node));
469            for (what, operands) in [("output", &asm.outputs), ("input", &asm.inputs)] {
470                for operand in operands {
471                    let mut line = format!("{what} {:?}", operand.constraint.node);
472                    if let Some(name) = &operand.name {
473                        line.push_str(&format!(" [{}]", name.name));
474                    }
475                    d.under(&line, |dd| dd.expr(&operand.expr));
476                }
477            }
478            for clobber in &asm.clobbers {
479                d.line(format!("clobber {:?}", clobber.node));
480            }
481            for label in &asm.labels {
482                d.line(format!("label '{}'", label.name));
483            }
484        });
485    }
486
487    // -- expressions --------------------------------------------------------
488
489    fn expr(&mut self, expr: &Expr) {
490        match &expr.kind {
491            ExprKind::Ident(id) => self.line(format!("ident '{}'", id.name)),
492            ExprKind::Int(lit) => {
493                let mut s = format!("int {}", lit.value);
494                if lit.unsigned {
495                    s.push_str(" unsigned");
496                }
497                match lit.long {
498                    LongKind::None => {}
499                    LongKind::Long => s.push_str(" long"),
500                    LongKind::LongLong => s.push_str(" long-long"),
501                }
502                self.line(s);
503            }
504            ExprKind::Float(lit) => {
505                let mut s = format!("float {}", lit.value);
506                match lit.suffix {
507                    FloatSuffix::None => {}
508                    FloatSuffix::Float => s.push_str(" f"),
509                    FloatSuffix::LongDouble => s.push_str(" l"),
510                }
511                self.line(s);
512            }
513            ExprKind::Char(lit) => {
514                self.line(format!(
515                    "char {}{} = {}",
516                    lit.kind.prefix(),
517                    lit.text,
518                    lit.value
519                ));
520            }
521            ExprKind::Str(lit) => {
522                self.line(format!(
523                    "string {}{} ({} elements)",
524                    lit.kind.prefix(),
525                    lit.text,
526                    lit.values.len()
527                ));
528            }
529            ExprKind::Unary { op, operand } => {
530                self.under(&format!("unary '{}'", op.as_str()), |d| d.expr(operand));
531            }
532            ExprKind::Binary { op, lhs, rhs } => {
533                self.under(&format!("binary '{}'", op.as_str()), |d| {
534                    d.expr(lhs);
535                    d.expr(rhs);
536                });
537            }
538            ExprKind::Assign { op, lhs, rhs } => {
539                let label = match op {
540                    Some(op) => format!("assign '{}='", op.as_str()),
541                    None => "assign '='".to_owned(),
542                };
543                self.under(&label, |d| {
544                    d.expr(lhs);
545                    d.expr(rhs);
546                });
547            }
548            ExprKind::Conditional {
549                cond,
550                then_expr,
551                else_expr,
552            } => self.under("conditional", |d| {
553                d.under("cond", |dd| dd.expr(cond));
554                if let Some(then_expr) = then_expr {
555                    d.under("then", |dd| dd.expr(then_expr));
556                }
557                d.under("else", |dd| dd.expr(else_expr));
558            }),
559            ExprKind::Comma { lhs, rhs } => self.under("comma", |d| {
560                d.expr(lhs);
561                d.expr(rhs);
562            }),
563            ExprKind::Call { callee, args } => self.under("call", |d| {
564                d.under("callee", |dd| dd.expr(callee));
565                for arg in args {
566                    d.under("arg", |dd| dd.expr(arg));
567                }
568            }),
569            ExprKind::Member { base, arrow, field } => {
570                let op = if *arrow { "->" } else { "." };
571                self.under(&format!("member '{}{}'", op, field.name), |d| d.expr(base));
572            }
573            ExprKind::Index { base, index } => self.under("index", |d| {
574                d.expr(base);
575                d.expr(index);
576            }),
577            ExprKind::PostIncDec { op, operand } => {
578                self.under(&format!("postfix '{}'", op.as_str()), |d| d.expr(operand));
579            }
580            ExprKind::PreIncDec { op, operand } => {
581                self.under(&format!("prefix '{}'", op.as_str()), |d| d.expr(operand));
582            }
583            ExprKind::Cast { ty, expr } => {
584                self.under(&format!("cast to {}", self.ty(&ty.ty)), |d| d.expr(expr));
585            }
586            ExprKind::LabelAddr(label) => self.line(format!("label-addr '{}'", label.name)),
587            ExprKind::SizeofExpr(inner) => self.under("sizeof-expr", |d| d.expr(inner)),
588            ExprKind::SizeofType(ty) => {
589                self.line(format!("sizeof-type {}", self.ty(&ty.ty)));
590            }
591            ExprKind::AlignofExpr(inner) => self.under("alignof-expr", |d| d.expr(inner)),
592            ExprKind::AlignofType(ty) => {
593                self.line(format!("alignof-type {}", self.ty(&ty.ty)));
594            }
595            ExprKind::Generic {
596                controlling,
597                assocs,
598            } => self.under("generic", |d| {
599                d.under("controlling", |dd| dd.expr(controlling));
600                for assoc in assocs {
601                    let label = match &assoc.ty {
602                        Some(ty) => format!("assoc {}", d.ty(&ty.ty)),
603                        None => "assoc default".to_owned(),
604                    };
605                    d.under(&label, |dd| dd.expr(&assoc.value));
606                }
607            }),
608            ExprKind::Bool(value) => self.line(if *value { "true" } else { "false" }),
609            ExprKind::Nullptr => self.line("nullptr"),
610            ExprKind::VaArg { ap, ty } => {
611                self.under(&format!("va_arg {}", self.ty(&ty.ty)), |d| d.expr(ap));
612            }
613            ExprKind::OffsetOf { ty, member, path } => {
614                let rest = element_label(path);
615                let rest = rest.strip_prefix("element").unwrap_or(&rest);
616                self.line(format!(
617                    "offsetof {} .{}{rest}",
618                    self.ty(&ty.ty),
619                    member.name
620                ));
621            }
622            ExprKind::CompoundLiteral { ty, init } => {
623                self.under(&format!("compound-literal {}", self.ty(&ty.ty)), |d| {
624                    for item in init {
625                        let label = element_label(&item.designators);
626                        d.under(&label, |dd| dd.initializer(&item.init));
627                    }
628                })
629            }
630            ExprKind::StmtExpr(block) => self.under("stmt-expr", |d| {
631                for item in &block.items {
632                    d.block_item(item);
633                }
634            }),
635            ExprKind::TypesCompatible { lhs, rhs } => self.line(format!(
636                "types-compatible {} {}",
637                self.ty(&lhs.ty),
638                self.ty(&rhs.ty)
639            )),
640            ExprKind::ChooseExpr {
641                cond,
642                then_expr,
643                else_expr,
644            } => self.under("choose-expr", |d| {
645                d.under("cond", |dd| dd.expr(cond));
646                d.under("then", |dd| dd.expr(then_expr));
647                d.under("else", |dd| dd.expr(else_expr));
648            }),
649            ExprKind::ComplexPart { real, operand } => {
650                let name = if *real { "__real__" } else { "__imag__" };
651                self.under(name, |d| d.expr(operand));
652            }
653            ExprKind::Error => self.line("<error-expr>"),
654        }
655    }
656}
657
658/// The label one element of an initialiser list is dumped under.
659fn element_label(designators: &[Designator]) -> String {
660    let mut label = String::from("element");
661    let index = |e: &Expr| match &e.kind {
662        ExprKind::Int(lit) => lit.value.to_string(),
663        _ => "expr".to_owned(),
664    };
665    for designator in designators {
666        match designator {
667            Designator::Field(f) => label.push_str(&format!(" .{}", f.name)),
668            Designator::Index(e) => label.push_str(&format!(" [{}]", index(e))),
669            Designator::Range(low, high) => {
670                label.push_str(&format!(" [{} ... {}]", index(low), index(high)));
671            }
672        }
673    }
674    label
675}