1use rucc_ast::AsmQuals;
49use rucc_base::Interner;
50use rucc_types::{TypeKind, Types, spell};
51
52use crate::asm::{AsmId, AsmOperandList};
53use crate::decl::{DeclId, DeclKind, Definition, Linkage, StorageDuration, Visibility};
54use crate::expr::{Category, Expr, ExprId, ExprKind};
55use crate::stmt::{CaseId, Stmt, StmtId};
56use crate::tast::{Base, Const, LabelId, Tast};
57
58#[must_use]
60pub fn print(tast: &Tast, types: &Types, names: &Interner) -> String {
61 let mut printer = Printer::new(tast, types, names);
62 printer.unit();
63 printer.finish()
64}
65
66#[derive(Debug)]
71pub struct Printer<'a> {
72 tast: &'a Tast,
73 types: &'a Types,
74 names: &'a Interner,
75 out: String,
76 depth: usize,
77}
78
79impl<'a> Printer<'a> {
80 #[must_use]
82 pub fn new(tast: &'a Tast, types: &'a Types, names: &'a Interner) -> Printer<'a> {
83 Printer { tast, types, names, out: String::new(), depth: 0 }
84 }
85
86 #[must_use]
88 pub fn finish(self) -> String {
89 self.out
90 }
91
92 pub fn unit(&mut self) {
94 for &id in self.tast.top_level() {
95 self.decl(id);
96 }
97 }
98
99 pub fn decl(&mut self, id: DeclId) {
101 let node = &self.tast[id];
102 let mut head = format!("decl #{}", id.index());
103 if let Some(name) = node.name {
104 head.push(' ');
105 head.push_str(self.names.resolve(name));
106 }
107 head.push_str(" : ");
108 head.push_str(&spell(self.types, self.names, node.ty));
109 head.push_str(match node.kind {
110 DeclKind::Object => " object",
111 DeclKind::Function => " function",
112 });
113 head.push_str(match node.linkage {
114 Linkage::None => "",
115 Linkage::Internal => " internal",
116 Linkage::External => " external",
117 });
118 if node.kind == DeclKind::Object {
119 head.push_str(match node.duration {
120 StorageDuration::Static => " static",
121 StorageDuration::Thread => " thread",
122 StorageDuration::Automatic => " automatic",
123 });
124 }
125 head.push_str(match node.state {
126 Definition::Declared => " declared",
127 Definition::Tentative => " tentative",
128 Definition::Defined => " defined",
129 });
130 if node.constant {
131 head.push_str(" constexpr");
132 }
133 if let Some(align) = node.alignment {
134 head.push_str(&format!(" alignas {align}"));
135 }
136 if let Some(label) = node.asm_label {
137 head.push_str(&format!(" asm {}", self.tast[label].spell()));
138 }
139 if let Some(target) = node.alias {
140 head.push_str(&format!(" alias {}", self.tast[target].spell()));
141 }
142 if !node.inline.emits() {
145 head.push_str(" inline-definition");
146 }
147 if node.noreturn {
148 head.push_str(" noreturn");
149 }
150 match node.visibility {
153 None => {}
154 Some(Visibility::Default) => head.push_str(" default-visibility"),
155 Some(Visibility::Hidden) => head.push_str(" hidden"),
156 Some(Visibility::Protected) => head.push_str(" protected"),
157 }
158 self.line(&head);
159
160 if let Some(list) = node.init {
164 self.depth += 1;
165 self.line("init");
166 self.depth += 1;
167 let entries = self.tast[list].to_vec();
170 for entry in entries {
171 let mut at = format!("+{}", entry.offset);
172 if entry.is_bit_field() {
173 at.push_str(&format!(" bit {} width {}", entry.bit_offset, entry.bit_width));
174 }
175 self.line(&at);
176 self.depth += 1;
177 self.expr(entry.value);
178 self.depth -= 1;
179 }
180 self.depth -= 2;
181 }
182 let params = self.tast[id].params;
185 if !params.is_empty() {
186 self.depth += 1;
187 self.line("params");
188 self.depth += 1;
189 let params = self.tast[params].to_vec();
190 for param in params {
191 self.decl(param);
192 }
193 self.depth -= 2;
194 }
195 if let Some(body) = self.tast[id].body {
196 self.depth += 1;
197 self.line("body");
198 self.depth += 1;
199 self.stmt(body);
200 self.depth -= 2;
201 }
202 }
203
204 pub fn stmt(&mut self, id: StmtId) {
206 match self.tast[id] {
207 Stmt::Error => self.line("error"),
208 Stmt::Empty => self.line("empty"),
209 Stmt::Expr(value) => {
210 self.line("expr");
211 self.under(|p| p.expr(value));
212 }
213 Stmt::Block(body) => {
214 self.line("block");
215 self.depth += 1;
216 let body = self.tast[body].to_vec();
217 for stmt in body {
218 self.stmt(stmt);
219 }
220 self.depth -= 1;
221 }
222 Stmt::Decls(decls) => {
223 self.line("decls");
224 self.depth += 1;
225 let decls = self.tast[decls].to_vec();
226 for decl in decls {
227 self.decl(decl);
228 }
229 self.depth -= 1;
230 }
231 Stmt::If { cond, then, otherwise } => {
232 self.line("if");
233 self.depth += 1;
234 self.group("cond", |p| p.expr(cond));
235 self.group("then", |p| p.stmt(then));
236 if let Some(otherwise) = otherwise {
237 self.group("else", |p| p.stmt(otherwise));
238 }
239 self.depth -= 1;
240 }
241 Stmt::While { cond, body } => {
242 self.line("while");
243 self.depth += 1;
244 self.group("cond", |p| p.expr(cond));
245 self.group("body", |p| p.stmt(body));
246 self.depth -= 1;
247 }
248 Stmt::DoWhile { body, cond } => {
249 self.line("do-while");
250 self.depth += 1;
251 self.group("body", |p| p.stmt(body));
252 self.group("cond", |p| p.expr(cond));
253 self.depth -= 1;
254 }
255 Stmt::For { init, cond, step, body } => {
256 self.line("for");
257 self.depth += 1;
258 if let Some(init) = init {
259 self.group("init", |p| p.stmt(init));
260 }
261 if let Some(cond) = cond {
262 self.group("cond", |p| p.expr(cond));
263 }
264 if let Some(step) = step {
265 self.group("step", |p| p.expr(step));
266 }
267 self.group("body", |p| p.stmt(body));
268 self.depth -= 1;
269 }
270 Stmt::Switch { cond, body, cases, default } => {
271 self.line("switch");
272 self.depth += 1;
273 self.group("cond", |p| p.expr(cond));
274 self.line("cases");
275 self.depth += 1;
276 for index in cases.iter() {
277 self.case(index);
278 }
279 if default.is_some() {
280 self.line("default");
281 }
282 self.depth -= 1;
283 self.group("body", |p| p.stmt(body));
284 self.depth -= 1;
285 }
286 Stmt::Case { case, body } => {
289 self.line(&format!("case #{}", case.index()));
290 self.under(|p| p.stmt(body));
291 }
292 Stmt::Default { body } => {
293 self.line("default");
294 self.under(|p| p.stmt(body));
295 }
296 Stmt::Label { label, body } => {
297 let head = self.label(label);
298 self.line(&format!("label {head}"));
299 self.under(|p| p.stmt(body));
300 }
301 Stmt::Goto(label) => {
302 let target = self.label(label);
303 self.line(&format!("goto {target}"));
304 }
305 Stmt::IndirectGoto(target) => {
306 self.line("indirect-goto");
307 self.under(|p| p.expr(target));
308 }
309 Stmt::Asm(asm) => self.asm(asm),
310 Stmt::Break => self.line("break"),
311 Stmt::Continue => self.line("continue"),
312 Stmt::Return(None) => self.line("return"),
313 Stmt::Return(Some(value)) => {
314 self.line("return");
315 self.under(|p| p.expr(value));
316 }
317 }
318 }
319
320 fn asm(&mut self, id: AsmId) {
327 let node = self.tast[id];
328 let mut head = String::from("asm");
329 for (qual, name) in [
330 (AsmQuals::VOLATILE, " volatile"),
331 (AsmQuals::INLINE, " inline"),
332 (AsmQuals::GOTO, " goto"),
333 ] {
334 if node.quals.has(qual) {
335 head.push_str(name);
336 }
337 }
338 self.line(&head);
339 self.depth += 1;
340 self.line(&format!("template {}", self.tast[node.template].spell()));
341 self.asm_operands(node.outputs, "output");
342 self.asm_operands(node.inputs, "input");
343 for index in 0..self.tast[node.clobbers].len() {
344 let clobber = self.tast[node.clobbers][index];
345 self.line(&format!("clobber {}", self.tast[clobber].spell()));
346 }
347 for index in 0..self.tast[node.labels].len() {
348 let label = self.tast[node.labels][index];
349 let head = self.label(label);
350 self.line(&format!("label {head}"));
351 }
352 self.depth -= 1;
353 }
354
355 fn asm_operands(&mut self, list: AsmOperandList, what: &str) {
357 for index in 0..self.tast[list].len() {
358 let operand = self.tast[list][index];
359 let name = match operand.name {
360 Some(name) => format!(" [{}]", self.names.resolve(name)),
361 None => String::new(),
362 };
363 let memory = if operand.memory { " memory" } else { "" };
364 let constraint = self.tast[operand.constraint].spell();
365 self.line(&format!("{what}{name} {constraint}{memory}"));
366 self.under(|p| p.expr(operand.value));
367 }
368 }
369
370 pub fn expr(&mut self, id: ExprId) {
372 let node = self.tast[id];
373 let head = self.head(node);
374 let ty = spell(self.types, self.names, node.ty);
375 let category = match node.category {
376 Category::Rvalue => "",
377 Category::Lvalue => " lvalue",
378 Category::Bitfield => " bit-field",
379 Category::Function => " function",
380 };
381 self.line(&format!("{head} : {ty}{category}"));
382 self.depth += 1;
383 self.operands(node.kind);
384 self.depth -= 1;
385 }
386
387 fn head(&self, node: Expr) -> String {
389 match node.kind {
390 ExprKind::Error => "error".to_owned(),
391 ExprKind::Const(value) => match self.tast[value] {
392 Const::Int(value) => format!("const {value}"),
396 Const::Float(value) => format!("const {}", value.to_hex()),
397 Const::Address(address) => {
398 let base = match address.base {
399 Base::Decl(decl) => format!("decl #{}", decl.index()),
400 Base::Str(id) => format!("string {}", self.tast[id].spell()),
401 };
402 format!("const address {base} + {}", address.offset)
403 }
404 },
405 ExprKind::Str(value) => format!("string {}", self.tast[value].spell()),
406 ExprKind::Decl(decl) => {
407 let mut head = format!("decl #{}", decl.index());
408 if let Some(name) = self.tast[decl].name {
409 head.push(' ');
410 head.push_str(self.names.resolve(name));
411 }
412 head
413 }
414 ExprKind::Member { base, field } => {
415 let mut head = format!("member #{field}");
416 if let Some(name) = self.field_name(base, field) {
417 head.push(' ');
418 head.push_str(name);
419 }
420 head
421 }
422 ExprKind::Subscript { .. } => "subscript".to_owned(),
423 ExprKind::Call { .. } => "call".to_owned(),
424 ExprKind::Unary { op, .. } if op.is_postfix() => {
425 format!("unary post {}", op.spelling())
426 }
427 ExprKind::Unary { op, .. } => format!("unary {}", op.spelling()),
428 ExprKind::Binary { op, .. } => format!("binary {}", op.spelling()),
429 ExprKind::Assign { op, computation, .. } => {
432 let mut head = match op {
433 None => "assign =".to_owned(),
434 Some(op) => format!("assign {}=", op.spelling()),
435 };
436 if computation != node.ty {
437 let ty = spell(self.types, self.names, computation);
438 head.push_str(&format!(" in {ty}"));
439 }
440 head
441 }
442 ExprKind::Cond { .. } => "cond".to_owned(),
443 ExprKind::Comma { .. } => "comma".to_owned(),
444 ExprKind::Cast(_) => "cast".to_owned(),
445 ExprKind::Convert { kind, .. } => format!("convert {}", kind.as_str()),
446 ExprKind::CompoundLiteral(decl) => format!("compound-literal #{}", decl.index()),
447 ExprKind::StmtExpr(_) => "stmt-expr".to_owned(),
448 ExprKind::LabelAddr(label) => format!("label-addr {}", self.label(label)),
449 ExprKind::VaArg { .. } => "va-arg".to_owned(),
450 ExprKind::VaStart { .. } => "va-start".to_owned(),
451 ExprKind::VaEnd { .. } => "va-end".to_owned(),
452 ExprKind::VaCopy { .. } => "va-copy".to_owned(),
453 ExprKind::Classify { op, .. } => format!("classify {}", op.as_str()),
454 ExprKind::FpClassify { .. } => "fpclassify".to_owned(),
455 ExprKind::Sign { op, .. } => format!("sign {}", op.as_str()),
456 ExprKind::Abs { .. } => "abs".to_owned(),
457 ExprKind::ByteSwap { .. } => "bswap".to_owned(),
458 ExprKind::BitCount { count, .. } => format!("count {}", count.as_str()),
459 ExprKind::Overflow { op, at, .. } => {
460 format!("overflow {} at {}", op.as_str(), spell(self.types, self.names, at))
461 }
462 ExprKind::Atomic { op, order, .. } => {
463 format!("atomic {} {}", op.as_str(), order.as_str())
464 }
465 ExprKind::Unreachable => "unreachable".to_owned(),
466 }
467 }
468
469 fn operands(&mut self, kind: ExprKind) {
471 match kind {
472 ExprKind::Error
473 | ExprKind::Const(_)
474 | ExprKind::Str(_)
475 | ExprKind::Decl(_)
476 | ExprKind::LabelAddr(_)
477 | ExprKind::Unreachable => {}
478 ExprKind::CompoundLiteral(decl) => self.decl(decl),
481 ExprKind::StmtExpr(body) => self.stmt(body),
482 ExprKind::Member { base, .. }
483 | ExprKind::Cast(base)
484 | ExprKind::VaArg { list: base }
485 | ExprKind::VaStart { list: base }
486 | ExprKind::VaEnd { list: base }
487 | ExprKind::Convert { operand: base, .. }
488 | ExprKind::Abs { operand: base }
489 | ExprKind::ByteSwap { operand: base }
490 | ExprKind::BitCount { operand: base, .. }
491 | ExprKind::Unary { operand: base, .. } => self.expr(base),
492 ExprKind::Overflow { args, .. } | ExprKind::Atomic { args, .. } => {
493 let args = self.tast[args].to_vec();
494 for arg in args {
495 self.expr(arg);
496 }
497 }
498 ExprKind::Subscript { base: lhs, index: rhs }
499 | ExprKind::Binary { lhs, rhs, .. }
500 | ExprKind::Assign { lhs, rhs, .. }
501 | ExprKind::VaCopy { dst: lhs, src: rhs }
502 | ExprKind::Comma { lhs, rhs } => {
503 self.expr(lhs);
504 self.expr(rhs);
505 }
506 ExprKind::Call { callee, args } => {
507 self.expr(callee);
508 let args = self.tast[args].to_vec();
509 for arg in args {
510 self.expr(arg);
511 }
512 }
513 ExprKind::Cond { cond, then, otherwise } => {
514 self.expr(cond);
515 self.expr(then);
516 self.expr(otherwise);
517 }
518 ExprKind::Classify { lhs, rhs, .. } | ExprKind::Sign { lhs, rhs, .. } => {
519 self.expr(lhs);
520 if let Some(rhs) = rhs {
521 self.expr(rhs);
522 }
523 }
524 ExprKind::FpClassify { value, answers } => {
527 self.expr(value);
528 let answers = self.tast[answers].to_vec();
529 for answer in answers {
530 self.expr(answer);
531 }
532 }
533 }
534 }
535
536 fn case(&mut self, id: CaseId) {
538 let case = self.tast[id];
539 let head = if case.low == case.high {
540 format!("case #{} {}", id.index(), case.low)
541 } else {
542 format!("case #{} {} ... {}", id.index(), case.low, case.high)
543 };
544 self.line(&head);
545 }
546
547 fn label(&self, id: LabelId) -> String {
549 format!("#{} {}", id.index(), self.names.resolve(self.tast[id].name))
550 }
551
552 fn field_name(&self, base: ExprId, field: u32) -> Option<&'a str> {
558 let ty = self.types.canonical(self.tast[base].ty);
559 let TypeKind::Record(record) = self.types.kind(ty) else { return None };
560 let field = self.types.record_info(record).fields.get(field as usize)?;
561 Some(self.names.resolve(field.name?))
562 }
563
564 fn group(&mut self, name: &str, write: impl FnOnce(&mut Printer<'a>)) {
566 self.line(name);
567 self.under(write);
568 }
569
570 fn under(&mut self, write: impl FnOnce(&mut Printer<'a>)) {
572 self.depth += 1;
573 write(self);
574 self.depth -= 1;
575 }
576
577 fn line(&mut self, text: &str) {
579 for _ in 0..self.depth {
580 self.out.push_str(" ");
581 }
582 self.out.push_str(text);
583 self.out.push('\n');
584 }
585}
586
587#[cfg(test)]
588mod tests {
589 use rucc_ast::{BinaryOp, UnaryOp};
590 use rucc_diag::Span;
591 use rucc_types::{ArrayLen, IntKind};
592
593 use super::*;
594 use crate::decl::{Decl, DeclList, Emission, InitEntry};
595 use crate::expr::{Conversion, Expr};
596 use crate::stmt::Case;
597 use crate::tast::Label;
598
599 struct Fixture {
600 tast: Tast,
601 types: Types,
602 names: Interner,
603 }
604
605 impl Fixture {
606 fn new() -> Fixture {
607 Fixture { tast: Tast::new(), types: Types::new(), names: Interner::new() }
608 }
609
610 fn int(&self) -> rucc_types::TypeId {
611 self.types.int(IntKind::Int)
612 }
613
614 fn value(&mut self, kind: ExprKind, ty: rucc_types::TypeId) -> ExprId {
616 self.tast.expr(Expr::new(kind, ty, Category::Rvalue), Span::DUMMY)
617 }
618
619 fn constant(&mut self, value: i128, ty: rucc_types::TypeId) -> ExprId {
620 let id = self.tast.add_const(Const::Int(value));
621 self.value(ExprKind::Const(id), ty)
622 }
623
624 fn text(&self, write: impl FnOnce(&mut Printer<'_>)) -> String {
625 let mut printer = Printer::new(&self.tast, &self.types, &self.names);
626 write(&mut printer);
627 printer.finish()
628 }
629 }
630
631 #[test]
632 fn an_expression_carries_its_type_on_every_line() {
633 let mut f = Fixture::new();
634 let int = f.int();
635 let left = f.constant(1, int);
636 let right = f.constant(2, int);
637 let sum = f.value(ExprKind::Binary { op: BinaryOp::Add, lhs: left, rhs: right }, int);
638
639 assert_eq!(f.text(|p| p.expr(sum)), "binary + : int\n const 1 : int\n const 2 : int\n");
640 }
641
642 #[test]
643 fn a_conversion_is_what_the_dump_is_for() {
644 let mut f = Fixture::new();
645 let (char_type, long) = (f.types.int(IntKind::Char), f.types.int(IntKind::Long));
646 let object = f.tast.decl(object_decl(char_type), Span::DUMMY);
647 let name = f
648 .tast
649 .expr(Expr::new(ExprKind::Decl(object), char_type, Category::Lvalue), Span::DUMMY);
650 let read =
651 f.value(ExprKind::Convert { kind: Conversion::Lvalue, operand: name }, char_type);
652 let widened =
653 f.value(ExprKind::Convert { kind: Conversion::Arithmetic, operand: read }, long);
654
655 assert_eq!(
658 f.text(|p| p.expr(widened)),
659 "convert arithmetic : long\n convert lvalue : char\n decl #0 : char lvalue\n"
660 );
661 }
662
663 #[test]
664 fn a_category_is_written_and_an_rvalue_is_the_silent_one() {
665 let mut f = Fixture::new();
666 let int = f.int();
667 let object = f.tast.decl(object_decl(int), Span::DUMMY);
668 let name =
669 f.tast.expr(Expr::new(ExprKind::Decl(object), int, Category::Lvalue), Span::DUMMY);
670 let bits =
671 f.tast.expr(Expr::new(ExprKind::Decl(object), int, Category::Bitfield), Span::DUMMY);
672
673 assert_eq!(f.text(|p| p.expr(name)), "decl #0 : int lvalue\n");
674 assert_eq!(f.text(|p| p.expr(bits)), "decl #0 : int bit-field\n");
675 }
676
677 #[test]
678 fn a_postfix_operator_is_not_printed_as_the_prefix_one() {
679 let mut f = Fixture::new();
680 let int = f.int();
681 let one = f.constant(1, int);
682 let post = f.value(ExprKind::Unary { op: UnaryOp::PostInc, operand: one }, int);
683 let pre = f.value(ExprKind::Unary { op: UnaryOp::PreInc, operand: one }, int);
684
685 assert!(f.text(|p| p.expr(post)).starts_with("unary post ++"));
686 assert!(f.text(|p| p.expr(pre)).starts_with("unary ++ :"));
687 }
688
689 #[test]
690 fn a_compound_assignment_keeps_its_operator() {
691 let mut f = Fixture::new();
692 let int = f.int();
693 let one = f.constant(1, int);
694 let plain =
695 f.value(ExprKind::Assign { op: None, computation: int, lhs: one, rhs: one }, int);
696 let shl =
697 ExprKind::Assign { op: Some(BinaryOp::Shl), computation: int, lhs: one, rhs: one };
698 let compound = f.value(shl, int);
699
700 assert!(f.text(|p| p.expr(plain)).starts_with("assign = :"));
701 assert!(f.text(|p| p.expr(compound)).starts_with("assign <<= :"));
702 }
703
704 #[test]
705 fn a_case_is_a_reference_into_the_table_and_not_a_second_copy_of_it() {
706 let mut f = Fixture::new();
707 let int = f.int();
708 let cond = f.constant(0, int);
709 let empty = f.tast.stmt(Stmt::Empty, Span::DUMMY);
710 let cases = f.tast.add_cases(&[
711 Case { low: 1, high: 1, body: empty },
712 Case { low: 2, high: 9, body: empty },
713 ]);
714 let first = f.tast.stmt(
715 Stmt::Case { case: cases.iter().next().expect("a case"), body: empty },
716 Span::DUMMY,
717 );
718 let fallback = f.tast.stmt(Stmt::Default { body: empty }, Span::DUMMY);
719 let body = f.tast.add_stmt_refs(&[first, fallback]);
720 let body = f.tast.stmt(Stmt::Block(body), Span::DUMMY);
721 let switch =
722 f.tast.stmt(Stmt::Switch { cond, body, cases, default: Some(empty) }, Span::DUMMY);
723
724 assert_eq!(
725 f.text(|p| p.stmt(switch)),
726 "\
727switch
728 cond
729 const 0 : int
730 cases
731 case #0 1
732 case #1 2 ... 9
733 default
734 body
735 block
736 case #0
737 empty
738 default
739 empty
740"
741 );
742 }
743
744 #[test]
745 fn a_label_and_the_goto_that_reaches_it_carry_the_same_number() {
746 let mut f = Fixture::new();
747 let name = f.names.intern("done");
748 let label = f.tast.add_label(Label { name, stmt: None });
749 let empty = f.tast.stmt(Stmt::Empty, Span::DUMMY);
750 let target = f.tast.stmt(Stmt::Label { label, body: empty }, Span::DUMMY);
751 let jump = f.tast.stmt(Stmt::Goto(label), Span::DUMMY);
752 f.tast.define_label(label, target);
753
754 assert_eq!(f.text(|p| p.stmt(target)), "label #0 done\n empty\n");
755 assert_eq!(f.text(|p| p.stmt(jump)), "goto #0 done\n");
756 }
757
758 #[test]
759 fn a_declaration_says_what_it_is_and_an_empty_initializer_is_still_one() {
760 let mut f = Fixture::new();
761 let int = f.int();
762 let array = f.types.array(int, ArrayLen::Fixed(2));
763 let mut decl = object_decl(array);
764 decl.name = Some(f.names.intern("a"));
765 decl.linkage = Linkage::Internal;
766 decl.duration = StorageDuration::Static;
767 decl.alignment = Some(16);
768 decl.init = Some(f.tast.add_init_entries(&[]));
769 let id = f.tast.decl(decl, Span::DUMMY);
770
771 assert_eq!(
772 f.text(|p| p.decl(id)),
773 "decl #0 a : int[2] object internal static defined alignas 16\n init\n"
774 );
775 }
776
777 #[test]
778 fn an_initializer_prints_where_each_value_goes() {
779 let mut f = Fixture::new();
780 let int = f.int();
781 let array = f.types.array(int, ArrayLen::Fixed(2));
782 let one = f.constant(1, int);
783 let entries = f.tast.add_init_entries(&[
784 InitEntry::at(0, one),
785 InitEntry { offset: 4, value: one, bit_offset: 3, bit_width: 5 },
786 ]);
787 let mut decl = object_decl(array);
788 decl.init = Some(entries);
789 let id = f.tast.decl(decl, Span::DUMMY);
790
791 assert_eq!(
792 f.text(|p| p.decl(id)),
793 "\
794decl #0 : int[2] object automatic defined
795 init
796 +0
797 const 1 : int
798 +4 bit 3 width 5
799 const 1 : int
800"
801 );
802 }
803
804 #[test]
805 fn a_unit_is_its_declarations_in_order() {
806 let mut f = Fixture::new();
807 let int = f.int();
808 let first = f.tast.decl(object_decl(int), Span::DUMMY);
809 let second = f.tast.decl(object_decl(int), Span::DUMMY);
810 f.tast.add_top_level(first);
811 f.tast.add_top_level(second);
812
813 assert_eq!(
814 print(&f.tast, &f.types, &f.names),
815 "decl #0 : int object automatic defined\ndecl #1 : int object automatic defined\n"
816 );
817 }
818
819 fn object_decl(ty: rucc_types::TypeId) -> Decl {
820 Decl {
821 name: None,
822 ty,
823 kind: DeclKind::Object,
824 linkage: Linkage::None,
825 duration: StorageDuration::Automatic,
826 state: Definition::Defined,
827 alignment: None,
828 constant: false,
829 retained: false,
830 asm_label: None,
831 alias: None,
832 inline: Emission::Silent,
833 gnu_inline: false,
834 noreturn: false,
835 visibility: None,
836 init: None,
837 params: DeclList::EMPTY,
838 body: None,
839 }
840 }
841}