1use std::collections::{HashMap, HashSet};
41use std::mem;
42
43use rucc_ast::{self as ast, ForInit, StorageClass};
44use rucc_base::Symbol;
45use rucc_diag::{Diagnostic, Span};
46use rucc_types::{IntegerInfo, TypeId, is_integer, is_pointer, is_void};
47
48use crate::check::Checker;
49use crate::check::expr::Target;
50use crate::eval;
51use crate::expr::{Category, Expr, ExprId, ExprKind};
52use crate::stmt::{Case, Stmt, StmtId};
53use crate::tast::{Const, Label, LabelId};
54
55#[derive(Debug)]
57pub(in crate::check) struct Body {
58 ret: TypeId,
60 at: Span,
63 labels: HashMap<Symbol, Labelled>,
65 shadowed: Vec<(Symbol, Option<Labelled>)>,
68 blocks: Vec<usize>,
70 switches: Vec<Switch>,
72 loops: usize,
74 undeclared: HashSet<Symbol>,
78}
79
80#[derive(Debug, Clone, Copy)]
82struct Labelled {
83 id: LabelId,
85 defined: Option<Span>,
87 at: Span,
89}
90
91#[derive(Debug)]
93struct Switch {
94 ty: TypeId,
96 range: Option<IntegerInfo>,
101 cases: Vec<Case>,
103 spans: Vec<Span>,
105 labels: Vec<StmtId>,
108 default: Option<(StmtId, Span)>,
110}
111
112impl Checker<'_> {
113 pub fn check_stmt(&mut self, ret: TypeId, id: ast::StmtId) -> StmtId {
119 let previous = self.open_body(ret, Span::DUMMY);
120 let stmt = self.stmt(id);
121 self.close_body(previous);
122 stmt
123 }
124
125 pub(in crate::check) fn stmt(&mut self, id: ast::StmtId) -> StmtId {
127 let span = self.ast.stmt_span(id);
128 let node = match self.ast[id] {
129 ast::Stmt::Error => Stmt::Error,
130 ast::Stmt::Empty => Stmt::Empty,
131 ast::Stmt::Expr(value) => {
132 let value = self.expr(value);
133 Stmt::Expr(self.value(value))
134 }
135 ast::Stmt::Decl(decl) => Stmt::Decls(self.check_decl(decl)),
136 ast::Stmt::Compound(body) => Stmt::Block(self.block(body)),
137 ast::Stmt::If { cond, then, otherwise } => {
138 let cond = self.controlling(cond);
139 let then = self.stmt(then);
140 Stmt::If { cond, then, otherwise: otherwise.map(|id| self.stmt(id)) }
141 }
142 ast::Stmt::Switch { scrutinee, body } => self.switch(scrutinee, body),
143 ast::Stmt::While { cond, body } => {
144 let cond = self.controlling(cond);
145 Stmt::While { cond, body: self.loop_body(body) }
146 }
147 ast::Stmt::DoWhile { body, cond } => {
148 let body = self.loop_body(body);
149 Stmt::DoWhile { body, cond: self.controlling(cond) }
150 }
151 ast::Stmt::For { init, cond, step, body } => self.for_loop(init, cond, step, body),
152 ast::Stmt::Goto(name) => Stmt::Goto(self.label(name, span)),
153 ast::Stmt::GotoExpr(target) => self.computed_goto(target),
154 ast::Stmt::Continue => self.continue_stmt(span),
155 ast::Stmt::Break => self.break_stmt(span),
156 ast::Stmt::Return(value) => self.return_stmt(value, span),
157 ast::Stmt::Label { name, body, .. } => self.labelled(name, body, span),
158 ast::Stmt::Case { lo, hi, body } => self.case(lo, hi, body, span),
159 ast::Stmt::Default { body } => self.default(body, span),
160 ast::Stmt::LocalLabels(names) => {
161 self.local_labels(names, span);
162 Stmt::Empty
163 }
164 ast::Stmt::Asm(_) => {
165 self.statement_unsupported("an assembler statement", span);
166 Stmt::Error
167 }
168 };
169 let stmt = self.tast.stmt(node, span);
170 if matches!(node, Stmt::Case { .. }) {
174 if let Some(switch) = self.switches() {
175 switch.labels.push(stmt);
176 }
177 }
178 stmt
179 }
180
181 pub(in crate::check) fn stmt_expr(&mut self, id: ast::StmtId, span: Span) -> ExprId {
189 let stmt = self.stmt(id);
190 let ty = match self.tast[stmt] {
191 Stmt::Block(body) => match self.tast[body].last() {
192 Some(&last) => match self.tast[last] {
193 Stmt::Expr(value) => self.tast[value].ty,
194 _ => self.types.void(),
195 },
196 None => self.types.void(),
197 },
198 _ => self.types.void(),
199 };
200 self.tast.expr(Expr::new(ExprKind::StmtExpr(stmt), ty, Category::Rvalue), span)
201 }
202
203 pub(in crate::check) fn label_addr(&mut self, name: Symbol, span: Span) -> ExprId {
208 let label = self.label(name, span);
209 let ty = self.types.pointer(self.types.void());
210 self.tast.expr(Expr::new(ExprKind::LabelAddr(label), ty, Category::Rvalue), span)
211 }
212
213 pub(in crate::check) fn open_body(&mut self, ret: TypeId, at: Span) -> Option<Body> {
218 let body = Body {
219 ret,
220 at,
221 labels: HashMap::new(),
222 shadowed: Vec::new(),
223 blocks: Vec::new(),
224 switches: Vec::new(),
225 loops: 0,
226 undeclared: HashSet::new(),
227 };
228 self.body.replace(body)
229 }
230
231 pub(in crate::check) fn first_undeclared_use(&mut self, name: Symbol) -> bool {
237 match &mut self.body {
238 Some(body) => body.undeclared.insert(name),
239 None => true,
240 }
241 }
242
243 pub(in crate::check) fn close_body(&mut self, previous: Option<Body>) {
245 let Some(body) = mem::replace(&mut self.body, previous) else {
246 return;
247 };
248 let mut undefined: Vec<Labelled> =
251 body.labels.into_values().filter(|label| label.defined.is_none()).collect();
252 undefined.sort_by_key(|label| label.at.lo);
253 for label in undefined {
254 self.undefined_label(label);
255 }
256 }
257
258 pub(in crate::check) fn body_block(&mut self, body: ast::StmtId) -> StmtId {
264 let span = self.ast.stmt_span(body);
265 let ast::Stmt::Compound(list) = self.ast[body] else {
266 return self.stmt(body);
267 };
268 let list = self.statements(list);
269 self.tast.stmt(Stmt::Block(list), span)
270 }
271
272 fn block(&mut self, body: ast::StmtList) -> crate::stmt::StmtList {
274 self.scopes.push();
275 let list = self.statements(body);
276 self.scopes.pop();
277 list
278 }
279
280 fn statements(&mut self, body: ast::StmtList) -> crate::stmt::StmtList {
282 if let Some(state) = self.body.as_mut() {
283 let mark = state.shadowed.len();
284 state.blocks.push(mark);
285 }
286 let ids = self.ast[body].to_vec();
287 let mut stmts = Vec::with_capacity(ids.len());
288 for id in ids {
289 stmts.push(self.stmt(id));
290 }
291 self.end_block();
292 self.tast.add_stmt_refs(&stmts)
293 }
294
295 fn end_block(&mut self) {
297 let Some(body) = self.body.as_mut() else {
298 return;
299 };
300 let Some(mark) = body.blocks.pop() else {
301 return;
302 };
303 let mut gone = Vec::new();
304 while body.shadowed.len() > mark {
305 let (name, previous) = body.shadowed.pop().expect("a saved binding");
306 let local = match previous {
307 Some(previous) => body.labels.insert(name, previous),
308 None => body.labels.remove(&name),
309 };
310 if let Some(local) = local {
311 if local.defined.is_none() {
312 gone.push(local);
313 }
314 }
315 }
316 gone.sort_by_key(|label| label.at.lo);
317 for label in gone {
318 self.undefined_label(label);
319 }
320 }
321
322 fn loop_body(&mut self, body: ast::StmtId) -> StmtId {
324 if let Some(state) = self.body.as_mut() {
325 state.loops += 1;
326 }
327 let body = self.stmt(body);
328 if let Some(state) = self.body.as_mut() {
329 state.loops -= 1;
330 }
331 body
332 }
333
334 fn for_loop(
336 &mut self,
337 init: ForInit,
338 cond: Option<ast::ExprId>,
339 step: Option<ast::ExprId>,
340 body: ast::StmtId,
341 ) -> Stmt {
342 self.scopes.push();
345 let init = match init {
346 ForInit::None => None,
347 ForInit::Expr(value) => {
348 let span = self.ast.expr_span(value);
349 let value = self.expr(value);
350 let value = self.value(value);
351 Some(self.tast.stmt(Stmt::Expr(value), span))
352 }
353 ForInit::Decl(decl) => {
354 let span = self.ast.decl_span(decl);
355 let decls = self.check_decl(decl);
356 self.check_loop_declaration(decl);
357 Some(self.tast.stmt(Stmt::Decls(decls), span))
358 }
359 };
360 let cond = cond.map(|cond| self.controlling(cond));
361 let step = step.map(|step| {
362 let step = self.expr(step);
363 self.value(step)
364 });
365 let body = self.loop_body(body);
366 self.scopes.pop();
367 Stmt::For { init, cond, step, body }
368 }
369
370 fn check_loop_declaration(&mut self, decl: ast::DeclId) {
381 if !self.cx.pedantic {
382 return;
383 }
384 let ast::Decl::Var { specs, declarators } = self.ast[decl] else {
385 return;
386 };
387 let specs = self.ast[specs];
388 let word = match specs.storage {
389 _ if specs.is_typedef() => "non-variable",
390 Some(StorageClass::Static) => "static variable",
391 Some(StorageClass::Extern) => "'extern' variable",
392 _ => return,
393 };
394 let ast = self.ast;
395 for &item in &ast[declarators] {
396 let node = ast[item.declarator];
397 let Some(name) = node.name else { continue };
398 let spelled = self.text(name).to_owned();
399 self.report(
400 Diagnostic::warning(
401 format!("declaration of {word} '{spelled}' in 'for' loop initial declaration"),
402 node.name_span,
403 )
404 .with_code("E0619"),
405 );
406 }
407 }
408
409 fn switch(&mut self, scrutinee: ast::ExprId, body: ast::StmtId) -> Stmt {
411 let at = self.ast.expr_span(scrutinee);
412 let cond = self.expr(scrutinee);
413 let cond = self.value(cond);
414 let range = eval::int_shape(&self.types, self.tast[cond].ty, self.cx.target);
418 let cond = self.conv().promote(cond);
419 let ty = self.tast[cond].ty;
420 let cond = if self.is_poisoned(cond) || is_integer(&self.types, ty) {
421 cond
422 } else {
423 self.report(Diagnostic::error("switch quantity not an integer", at).with_code("E0620"));
424 self.poison(at)
425 };
426 let ty = if is_integer(&self.types, ty) { ty } else { self.int() };
430 if let Some(state) = self.body.as_mut() {
431 state.switches.push(Switch {
432 ty,
433 range,
434 cases: Vec::new(),
435 spans: Vec::new(),
436 labels: Vec::new(),
437 default: None,
438 });
439 }
440 let body = self.stmt(body);
441 let Some(switch) = self.body.as_mut().and_then(|state| state.switches.pop()) else {
442 return Stmt::Error;
443 };
444 let cases = self.tast.add_cases(&switch.cases);
445 for &labelled in &switch.labels {
446 let Stmt::Case { case: entry, body } = self.tast[labelled] else {
447 continue;
448 };
449 let case = cases.iter().nth(entry.index()).expect("a case for every label");
453 self.tast.set_stmt(labelled, Stmt::Case { case, body });
454 }
455 Stmt::Switch { cond, body, cases, default: switch.default.map(|(stmt, _)| stmt) }
456 }
457
458 fn case(
460 &mut self,
461 lo: ast::ExprId,
462 hi: Option<ast::ExprId>,
463 body: Option<ast::StmtId>,
464 span: Span,
465 ) -> Stmt {
466 let entry = self.enter_case(lo, hi, span);
471 let body = self.labelled_body(body, span);
472 let Some(entry) = entry else {
473 return Stmt::Error;
474 };
475 self.switches().expect("a switch").cases[entry].body = body;
476 Stmt::Case { case: rucc_base::Idx::from_usize(entry), body }
481 }
482
483 fn enter_case(
486 &mut self,
487 lo: ast::ExprId,
488 hi: Option<ast::ExprId>,
489 span: Span,
490 ) -> Option<usize> {
491 if self.body.as_ref().is_none_or(|state| state.switches.is_empty()) {
492 self.report(
493 Diagnostic::error("case label not within a switch statement", span)
494 .with_code("E0621"),
495 );
496 return None;
497 }
498 let low = self.case_value(lo, span)?;
499 let high = match hi {
500 Some(hi) => self.case_value(hi, span)?,
501 None => low,
502 };
503 if high < low {
504 self.report(Diagnostic::warning("empty range specified", span).with_code("E0622"));
505 return None;
506 }
507 if let Some(at) = self.overlapping_case(low, high) {
508 self.report(
509 Diagnostic::error("duplicate case value", span)
510 .with_code("E0623")
511 .note("previously used here".to_owned(), at),
512 );
513 return None;
514 }
515 let switch = self.switches().expect("a switch");
516 let entry = switch.cases.len();
517 switch.cases.push(Case { low, high, body: rucc_base::Idx::from_usize(0) });
520 switch.spans.push(span);
521 Some(entry)
522 }
523
524 fn case_value(&mut self, value: ast::ExprId, span: Span) -> Option<i128> {
526 let at = self.ast.expr_span(value);
527 let value = self.expr(value);
528 let value = self.value(value);
529 let folded = match self.eval_integer(value) {
530 Ok(folded) => folded,
531 Err(failed) => {
532 if !failed.poisoned {
533 self.report(
534 Diagnostic::error("case label does not reduce to an integer constant", at)
535 .with_code("E0624"),
536 );
537 }
538 return None;
539 }
540 };
541 let switch = self.switches()?;
542 let (ty, range) = (switch.ty, switch.range);
543 if let Some(range) = range {
544 if eval::overflows(Const::Int(folded), range) {
545 self.report(
546 Diagnostic::warning("case label value exceeds maximum value for type", span)
547 .with_code("E0625"),
548 );
549 }
550 }
551 let info = eval::int_shape(&self.types, ty, self.cx.target)?;
552 Some(eval::narrowed(Const::Int(folded), info))
553 }
554
555 fn overlapping_case(&mut self, low: i128, high: i128) -> Option<Span> {
557 let switch = self.switches()?;
558 switch
559 .cases
560 .iter()
561 .position(|case| case.low <= high && low <= case.high)
562 .map(|index| switch.spans[index])
563 }
564
565 fn default(&mut self, body: Option<ast::StmtId>, span: Span) -> Stmt {
567 let body = self.labelled_body(body, span);
568 if self.body.as_ref().is_none_or(|state| state.switches.is_empty()) {
569 self.report(
570 Diagnostic::error("'default' label not within a switch statement", span)
571 .with_code("E0626"),
572 );
573 return Stmt::Error;
574 }
575 if let Some((_, at)) = self.switches().expect("a switch").default {
576 self.report(
577 Diagnostic::error("multiple default labels in one switch", span)
578 .with_code("E0627")
579 .note("this is the first default label".to_owned(), at),
580 );
581 return Stmt::Error;
582 }
583 self.switches().expect("a switch").default = Some((body, span));
584 Stmt::Default { body }
585 }
586
587 fn labelled(&mut self, name: Symbol, body: Option<ast::StmtId>, span: Span) -> Stmt {
589 let body = self.labelled_body(body, span);
590 let label = self.label(name, span);
591 let defined = self.body.as_ref().and_then(|state| state.labels[&name].defined);
592 if let Some(at) = defined {
593 let spelled = self.text(name).to_owned();
594 self.report(
595 Diagnostic::error(format!("duplicate label '{spelled}'"), span)
596 .with_code("E0628")
597 .note(format!("previous definition of '{spelled}' with type 'void'"), at),
598 );
599 return Stmt::Error;
600 }
601 if let Some(state) = self.body.as_mut() {
602 state.labels.entry(name).and_modify(|known| known.defined = Some(span));
603 }
604 self.tast.define_label(label, body);
605 Stmt::Label { label, body }
606 }
607
608 fn labelled_body(&mut self, body: Option<ast::StmtId>, span: Span) -> StmtId {
610 match body {
611 Some(body) => self.stmt(body),
612 None => self.tast.stmt(Stmt::Empty, span),
613 }
614 }
615
616 fn local_labels(&mut self, names: ast::SymbolList, span: Span) {
618 let ast = self.ast;
619 for &name in &ast[names] {
620 let id = self.tast.add_label(Label { name, stmt: None });
621 let local = Labelled { id, defined: None, at: span };
622 if let Some(state) = self.body.as_mut() {
623 let previous = state.labels.insert(name, local);
624 state.shadowed.push((name, previous));
625 }
626 }
627 }
628
629 fn label(&mut self, name: Symbol, span: Span) -> LabelId {
631 if let Some(known) = self.body.as_ref().and_then(|state| state.labels.get(&name)) {
632 return known.id;
633 }
634 let id = self.tast.add_label(Label { name, stmt: None });
635 if let Some(state) = self.body.as_mut() {
636 state.labels.insert(name, Labelled { id, defined: None, at: span });
637 }
638 id
639 }
640
641 fn undefined_label(&mut self, label: Labelled) {
648 let name = self.tast[label.id].name;
649 let spelled = self.text(name).to_owned();
650 self.report(
651 Diagnostic::error(format!("label '{spelled}' used but not defined"), label.at)
652 .with_code("E0629"),
653 );
654 }
655
656 fn computed_goto(&mut self, target: ast::ExprId) -> Stmt {
658 let at = self.ast.expr_span(target);
659 let target = self.expr(target);
660 let target = self.value(target);
661 if self.is_poisoned(target) {
662 return Stmt::Error;
663 }
664 let ty = self.tast[target].ty;
665 if !is_pointer(&self.types, ty) && !is_integer(&self.types, ty) {
668 self.report(
669 Diagnostic::error("computed goto must be pointer type", at).with_code("E0630"),
670 );
671 return Stmt::Error;
672 }
673 let void = self.types.pointer(self.types.void());
674 let target = self.conv().to_type(target, void);
675 Stmt::IndirectGoto(target)
676 }
677
678 fn break_stmt(&mut self, span: Span) -> Stmt {
680 let inside =
681 self.body.as_ref().is_some_and(|state| state.loops > 0 || !state.switches.is_empty());
682 if inside {
683 return Stmt::Break;
684 }
685 self.report(
686 Diagnostic::error("break statement not within loop or switch", span).with_code("E0631"),
687 );
688 Stmt::Error
689 }
690
691 fn continue_stmt(&mut self, span: Span) -> Stmt {
693 if self.body.as_ref().is_some_and(|state| state.loops > 0) {
694 return Stmt::Continue;
695 }
696 self.report(
697 Diagnostic::error("continue statement not within a loop", span).with_code("E0632"),
698 );
699 Stmt::Error
700 }
701
702 fn return_stmt(&mut self, value: Option<ast::ExprId>, span: Span) -> Stmt {
709 let Some((ret, at)) = self.body.as_ref().map(|state| (state.ret, state.at)) else {
710 return Stmt::Return(None);
711 };
712 let void = is_void(&self.types, ret);
713 let Some(value) = value else {
714 if !void {
715 self.report(
716 Diagnostic::error(
717 "'return' with no value, in function returning non-void",
718 span,
719 )
720 .with_code("E0633")
721 .note("declared here".to_owned(), at),
722 );
723 }
724 return Stmt::Return(None);
725 };
726 let where_from = self.ast.expr_span(value);
727 let value = self.expr(value);
728 let value = self.value(value);
729 if !void {
730 return Stmt::Return(Some(self.assign_to(ret, value, where_from, Target::Return)));
731 }
732 if !is_void(&self.types, self.tast[value].ty) && !self.is_poisoned(value) {
735 self.report(
736 Diagnostic::error("'return' with a value, in function returning void", where_from)
737 .with_code("E0634")
738 .note("declared here".to_owned(), at),
739 );
740 }
741 let value = self.conv().to_void(value);
742 Stmt::Return(Some(value))
743 }
744
745 fn controlling(&mut self, cond: ast::ExprId) -> ExprId {
747 let span = self.ast.expr_span(cond);
748 let cond = self.expr(cond);
749 self.condition(cond, span)
750 }
751
752 fn switches(&mut self) -> Option<&mut Switch> {
754 self.body.as_mut()?.switches.last_mut()
755 }
756
757 fn statement_unsupported(&mut self, what: &str, span: Span) {
759 self.report(
760 Diagnostic::error(format!("{what} is not supported yet"), span).with_code("E0519"),
761 );
762 }
763}
764
765#[cfg(test)]
766mod tests {
767 use rucc_ast::{
768 AttrList, Builtin, BuiltinSet, DeclSpecs, DeclSpecsId, Declarator, DeclaratorId, Derived,
769 TypeSpec,
770 };
771 use rucc_base::Interner;
772 use rucc_lex::{IntConstant, IntConstantType, Remarks};
773 use rucc_session::Std;
774 use rucc_target::{TargetInfo, Triple};
775 use rucc_types::IntKind;
776
777 use super::*;
778 use crate::check::Context;
779 use crate::print::Printer;
780
781 struct Fixture {
787 ast: rucc_ast::Ast,
788 names: Interner,
789 target: TargetInfo,
790 }
791
792 impl Fixture {
793 fn new() -> Fixture {
794 let target =
795 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
796 Fixture { ast: rucc_ast::Ast::new(), names: Interner::new(), target }
797 }
798
799 fn name(&mut self, text: &str) -> Symbol {
800 self.names.intern(text)
801 }
802
803 fn int(&mut self, value: u128) -> ast::ExprId {
804 let ty = IntConstantType::Standard(IntKind::Int);
805 let id = self.ast.add_int(IntConstant { value, ty, remarks: Remarks::default() });
806 self.ast.expr(ast::Expr::Int(id), Span::DUMMY)
807 }
808
809 fn use_name(&mut self, text: &str) -> ast::ExprId {
810 let name = self.name(text);
811 self.ast.expr(ast::Expr::Name(name), Span::DUMMY)
812 }
813
814 fn keywords(&mut self, written: &[BuiltinSet]) -> DeclSpecsId {
816 let mut builtin = Builtin::NONE;
817 for &keyword in written {
818 builtin = builtin.add(keyword).expect("a keyword written once");
819 }
820 let mut specs = DeclSpecs::empty(Span::DUMMY);
821 specs.ty = TypeSpec::Builtin(builtin);
822 self.ast.add_specs(specs)
823 }
824
825 fn int_specs(&mut self) -> DeclSpecsId {
827 self.keywords(&[BuiltinSet::INT])
828 }
829
830 fn declarator(&mut self, name: Option<&str>, derived: &[Derived]) -> DeclaratorId {
831 let name = name.map(|text| self.name(text));
832 let derived = self.ast.add_derived_list(derived);
833 self.ast.add_declarator(Declarator {
834 name,
835 name_span: Span::DUMMY,
836 derived,
837 span: Span::DUMMY,
838 })
839 }
840
841 fn local(&mut self, specs: DeclSpecsId, name: &str) -> ast::DeclId {
843 let declarator = self.declarator(Some(name), &[]);
844 let item = ast::InitDeclarator {
845 declarator,
846 init: None,
847 asm_label: None,
848 attrs: AttrList::EMPTY,
849 span: Span::DUMMY,
850 };
851 let declarators = self.ast.add_init_declarator_list(&[item]);
852 self.ast.decl(ast::Decl::Var { specs, declarators }, Span::DUMMY)
853 }
854
855 fn cast(&mut self, specs: DeclSpecsId, value: ast::ExprId) -> ast::ExprId {
857 let declarator = self.declarator(None, &[]);
858 let ty = self.ast.add_type_name(ast::TypeName { specs, declarator, span: Span::DUMMY });
859 self.ast.expr(ast::Expr::Cast { ty, operand: value }, Span::DUMMY)
860 }
861
862 fn stmt(&mut self, stmt: ast::Stmt) -> ast::StmtId {
863 self.ast.stmt(stmt, Span::DUMMY)
864 }
865
866 fn block(&mut self, body: &[ast::StmtId]) -> ast::StmtId {
868 let body = self.ast.add_stmt_list(body);
869 self.stmt(ast::Stmt::Compound(body))
870 }
871
872 fn expr_stmt(&mut self, value: ast::ExprId) -> ast::StmtId {
874 self.stmt(ast::Stmt::Expr(value))
875 }
876
877 fn labelled(&mut self, text: &str, body: Option<ast::StmtId>) -> ast::StmtId {
879 let name = self.name(text);
880 self.stmt(ast::Stmt::Label { name, body, attrs: AttrList::EMPTY })
881 }
882
883 fn goto(&mut self, text: &str) -> ast::StmtId {
885 let name = self.name(text);
886 self.stmt(ast::Stmt::Goto(name))
887 }
888
889 fn local_labels(&mut self, names: &[&str]) -> ast::StmtId {
891 let names: Vec<Symbol> = names.iter().map(|text| self.name(text)).collect();
892 let names = self.ast.add_symbol_list(&names);
893 self.stmt(ast::Stmt::LocalLabels(names))
894 }
895
896 fn case(&mut self, lo: u128, hi: Option<u128>, body: Option<ast::StmtId>) -> ast::StmtId {
898 let lo = self.int(lo);
899 let hi = hi.map(|hi| self.int(hi));
900 self.stmt(ast::Stmt::Case { lo, hi, body })
901 }
902
903 fn switch(&mut self, scrutinee: ast::ExprId, body: &[ast::StmtId]) -> ast::StmtId {
905 let body = self.block(body);
906 self.stmt(ast::Stmt::Switch { scrutinee, body })
907 }
908
909 fn checker(&self) -> Checker<'_> {
910 Checker::new(&self.ast, Context::new(&self.names, &self.target, Std::C23))
911 }
912 }
913
914 fn dump(checker: &Checker<'_>, id: StmtId) -> String {
916 let mut printer = Printer::new(&checker.tast, &checker.types, checker.cx.names);
917 printer.stmt(id);
918 printer.finish()
919 }
920
921 fn messages(checker: &Checker<'_>) -> Vec<String> {
923 checker
924 .errors
925 .diagnostics()
926 .iter()
927 .flat_map(|d| {
928 std::iter::once(d.message.clone())
929 .chain(d.children.iter().map(|n| n.message.clone()))
930 })
931 .collect()
932 }
933
934 fn message(checker: &Checker<'_>) -> String {
936 let mut reported = messages(checker);
937 assert_eq!(reported.len(), 1, "expected exactly one diagnostic, got {reported:?}");
938 reported.pop().expect("one message")
939 }
940
941 fn reported(checker: &Checker<'_>) -> Vec<String> {
945 checker
946 .errors
947 .diagnostics()
948 .iter()
949 .map(|d| format!("{}: {}", d.severity.as_str(), d.message))
950 .collect()
951 }
952
953 #[test]
954 fn a_block_is_a_scope_and_a_name_declared_in_one_is_gone_after_it() {
955 let mut f = Fixture::new();
956 let specs = f.int_specs();
957 let declared = f.local(specs, "x");
958 let declared = f.stmt(ast::Stmt::Decl(declared));
959 let inner = f.block(&[declared]);
960 let use_x = f.use_name("x");
961 let after = f.expr_stmt(use_x);
962 let outer = f.block(&[inner, after]);
963
964 let mut c = f.checker();
965 let void = c.types.void();
966 c.check_stmt(void, outer);
967
968 assert_eq!(message(&c), "'x' undeclared (first use in this function)");
969 }
970
971 #[test]
972 fn a_name_nobody_declared_is_reported_once_per_function_and_not_once_per_use() {
973 let mut f = Fixture::new();
977 let first = f.use_name("nope");
978 let first = f.expr_stmt(first);
979 let second = f.use_name("nope");
980 let second = f.expr_stmt(second);
981 let body = f.block(&[first, second]);
982
983 let mut c = f.checker();
984 let void = c.types.void();
985 let previous = c.open_body(void, Span::DUMMY);
986 c.check_stmt(void, body);
987 c.close_body(previous);
988
989 assert_eq!(message(&c), "'nope' undeclared (first use in this function)");
990 }
991
992 #[test]
993 fn an_expression_statement_holds_the_value_and_not_a_conversion_of_it_to_void() {
994 let mut f = Fixture::new();
995 let one = f.int(1);
996 let stmt = f.expr_stmt(one);
997
998 let mut c = f.checker();
999 let void = c.types.void();
1000 let id = c.check_stmt(void, stmt);
1001
1002 assert_eq!(dump(&c, id), "expr\n const 1 : int\n");
1003 assert!(c.errors.is_empty());
1004 }
1005
1006 #[test]
1007 fn a_statement_expression_has_the_type_of_its_last_statement() {
1008 let mut f = Fixture::new();
1009 let one = f.int(1);
1010 let inner = f.expr_stmt(one);
1011 let body = f.block(&[inner]);
1012 let value = f.ast.expr(ast::Expr::StmtExpr(body), Span::DUMMY);
1013 let stmt = f.expr_stmt(value);
1014
1015 let mut c = f.checker();
1016 let void = c.types.void();
1017 let id = c.check_stmt(void, stmt);
1018
1019 assert_eq!(
1020 dump(&c, id),
1021 "expr\n stmt-expr : int\n block\n expr\n const 1 : int\n"
1022 );
1023 assert!(c.errors.is_empty());
1024 }
1025
1026 #[test]
1027 fn a_statement_expression_that_ends_in_something_else_is_void() {
1028 let mut f = Fixture::new();
1029 let body = f.block(&[]);
1030 let value = f.ast.expr(ast::Expr::StmtExpr(body), Span::DUMMY);
1031 let stmt = f.expr_stmt(value);
1032
1033 let mut c = f.checker();
1034 let void = c.types.void();
1035 let id = c.check_stmt(void, stmt);
1036
1037 assert_eq!(dump(&c, id), "expr\n stmt-expr : void\n block\n");
1038 assert!(c.errors.is_empty());
1039 }
1040
1041 #[test]
1042 fn the_declaration_in_a_for_clause_scopes_to_the_loop_and_not_to_what_follows() {
1043 let mut f = Fixture::new();
1044 let specs = f.int_specs();
1045 let declared = f.local(specs, "i");
1046 let empty = f.stmt(ast::Stmt::Empty);
1047 let loop_stmt = f.stmt(ast::Stmt::For {
1048 init: ForInit::Decl(declared),
1049 cond: None,
1050 step: None,
1051 body: empty,
1052 });
1053 let use_i = f.use_name("i");
1054 let after = f.expr_stmt(use_i);
1055 let outer = f.block(&[loop_stmt, after]);
1056
1057 let mut c = f.checker();
1058 let void = c.types.void();
1059 c.check_stmt(void, outer);
1060
1061 assert_eq!(message(&c), "'i' undeclared (first use in this function)");
1062 }
1063
1064 #[test]
1065 fn a_static_in_a_for_clause_is_accepted_and_only_pedantic_says_anything_about_it() {
1066 let mut f = Fixture::new();
1067 let mut specs = DeclSpecs::empty(Span::DUMMY);
1068 let builtin = Builtin::NONE.add(BuiltinSet::INT).expect("a keyword written once");
1069 specs.ty = TypeSpec::Builtin(builtin);
1070 specs.storage = Some(StorageClass::Static);
1071 let specs = f.ast.add_specs(specs);
1072 let declared = f.local(specs, "i");
1073 let empty = f.stmt(ast::Stmt::Empty);
1074 let loop_stmt = f.stmt(ast::Stmt::For {
1075 init: ForInit::Decl(declared),
1076 cond: None,
1077 step: None,
1078 body: empty,
1079 });
1080
1081 let mut c = f.checker();
1082 let void = c.types.void();
1083 c.check_stmt(void, loop_stmt);
1084 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1085
1086 let mut c = f.checker();
1087 c.cx.pedantic = true;
1088 let void = c.types.void();
1089 c.check_stmt(void, loop_stmt);
1090 assert_eq!(
1091 reported(&c),
1092 ["warning: declaration of static variable 'i' in 'for' loop initial declaration"]
1093 );
1094 }
1095
1096 #[test]
1097 fn continue_needs_a_loop_and_is_not_satisfied_by_a_switch() {
1098 let mut f = Fixture::new();
1099 let one = f.int(1);
1100 let go_on = f.stmt(ast::Stmt::Continue);
1101 let case = f.stmt(ast::Stmt::Case { lo: one, hi: None, body: Some(go_on) });
1102 let scrutinee = f.int(0);
1103 let switch = f.switch(scrutinee, &[case]);
1104
1105 let mut c = f.checker();
1106 let void = c.types.void();
1107 c.check_stmt(void, switch);
1108
1109 assert_eq!(message(&c), "continue statement not within a loop");
1110 }
1111
1112 #[test]
1113 fn break_is_satisfied_by_a_switch_and_reported_where_there_is_neither() {
1114 let mut f = Fixture::new();
1115 let stop = f.stmt(ast::Stmt::Break);
1116 let scrutinee = f.int(0);
1117 let switch = f.switch(scrutinee, &[stop]);
1118 let loose = f.stmt(ast::Stmt::Break);
1119
1120 let mut c = f.checker();
1121 let void = c.types.void();
1122 c.check_stmt(void, switch);
1123 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1124
1125 let mut c = f.checker();
1126 let void = c.types.void();
1127 c.check_stmt(void, loose);
1128 assert_eq!(message(&c), "break statement not within loop or switch");
1129 }
1130
1131 #[test]
1132 fn a_goto_resolves_to_a_label_the_function_defines_further_down() {
1133 let mut f = Fixture::new();
1134 let jump = f.goto("done");
1135 let empty = f.stmt(ast::Stmt::Empty);
1136 let target = f.labelled("done", Some(empty));
1137 let body = f.block(&[jump, target]);
1138
1139 let mut c = f.checker();
1140 let void = c.types.void();
1141 let id = c.check_stmt(void, body);
1142
1143 assert_eq!(dump(&c, id), "block\n goto #0 done\n label #0 done\n empty\n");
1144 assert!(c.errors.is_empty());
1145 }
1146
1147 #[test]
1148 fn a_label_that_is_jumped_to_and_never_defined_is_reported_at_the_jump() {
1149 let mut f = Fixture::new();
1150 let jump = f.goto("away");
1151 let body = f.block(&[jump]);
1152
1153 let mut c = f.checker();
1154 let void = c.types.void();
1155 c.check_stmt(void, body);
1156
1157 assert_eq!(message(&c), "label 'away' used but not defined");
1158 }
1159
1160 #[test]
1161 fn the_address_of_a_label_is_a_use_of_it_and_not_a_definition() {
1162 let mut f = Fixture::new();
1163 let away = f.name("away");
1164 let value = f.ast.expr(ast::Expr::LabelAddr(away), Span::DUMMY);
1165 let stmt = f.expr_stmt(value);
1166
1167 let mut c = f.checker();
1168 let void = c.types.void();
1169 let id = c.check_stmt(void, stmt);
1170
1171 assert_eq!(dump(&c, id), "expr\n label-addr #0 away : void *\n");
1172 assert_eq!(message(&c), "label 'away' used but not defined");
1173 }
1174
1175 #[test]
1176 fn one_label_defined_twice_is_an_error_that_points_at_the_first() {
1177 let mut f = Fixture::new();
1178 let first = f.labelled("here", None);
1179 let second = f.labelled("here", None);
1180 let body = f.block(&[first, second]);
1181
1182 let mut c = f.checker();
1183 let void = c.types.void();
1184 c.check_stmt(void, body);
1185
1186 assert_eq!(
1187 messages(&c),
1188 ["duplicate label 'here'", "previous definition of 'here' with type 'void'",]
1189 );
1190 }
1191
1192 #[test]
1193 fn a_local_label_is_undone_when_its_block_ends_so_two_blocks_may_declare_one_name() {
1194 let mut f = Fixture::new();
1195 let sibling = |f: &mut Fixture| {
1196 let declared = f.local_labels(&["done"]);
1197 let jump = f.goto("done");
1198 let target = f.labelled("done", None);
1199 f.block(&[declared, jump, target])
1200 };
1201 let first = sibling(&mut f);
1202 let second = sibling(&mut f);
1203 let body = f.block(&[first, second]);
1204
1205 let mut c = f.checker();
1206 let void = c.types.void();
1207 let id = c.check_stmt(void, body);
1208
1209 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1210 assert_eq!(
1211 dump(&c, id),
1212 "block\n block\n empty\n goto #0 done\n label #0 done\n empty\n \
1213 block\n empty\n goto #1 done\n label #1 done\n empty\n"
1214 );
1215 }
1216
1217 #[test]
1218 fn a_local_label_that_nothing_defines_is_reported_when_its_block_ends() {
1219 let mut f = Fixture::new();
1220 let declared = f.local_labels(&["done"]);
1221 let jump = f.goto("done");
1222 let inner = f.block(&[declared, jump]);
1223 let target = f.labelled("done", None);
1224 let body = f.block(&[inner, target]);
1225
1226 let mut c = f.checker();
1227 let void = c.types.void();
1228 c.check_stmt(void, body);
1229
1230 assert_eq!(message(&c), "label 'done' used but not defined");
1231 }
1232
1233 #[test]
1234 fn a_computed_goto_wants_something_that_could_be_an_address() {
1235 let mut f = Fixture::new();
1236 let specs = f.keywords(&[BuiltinSet::DOUBLE]);
1237 let zero = f.int(0);
1238 let target = f.cast(specs, zero);
1239 let stmt = f.stmt(ast::Stmt::GotoExpr(target));
1240
1241 let mut c = f.checker();
1242 let void = c.types.void();
1243 c.check_stmt(void, stmt);
1244
1245 assert_eq!(message(&c), "computed goto must be pointer type");
1246 }
1247
1248 #[test]
1249 fn a_switch_on_something_that_is_not_an_integer_is_an_error() {
1250 let mut f = Fixture::new();
1251 let specs = f.keywords(&[BuiltinSet::DOUBLE]);
1252 let zero = f.int(0);
1253 let scrutinee = f.cast(specs, zero);
1254 let switch = f.switch(scrutinee, &[]);
1255
1256 let mut c = f.checker();
1257 let void = c.types.void();
1258 c.check_stmt(void, switch);
1259
1260 assert_eq!(message(&c), "switch quantity not an integer");
1261 }
1262
1263 #[test]
1264 fn the_cases_of_a_switch_are_one_table_in_the_order_they_were_written() {
1265 let mut f = Fixture::new();
1266 let first = f.case(1, None, None);
1267 let second = f.case(4, Some(6), None);
1268 let default = f.stmt(ast::Stmt::Default { body: None });
1269 let scrutinee = f.int(0);
1270 let switch = f.switch(scrutinee, &[first, second, default]);
1271
1272 let mut c = f.checker();
1273 let void = c.types.void();
1274 let id = c.check_stmt(void, switch);
1275
1276 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1277 assert_eq!(
1278 dump(&c, id),
1279 "switch\n cond\n const 0 : int\n cases\n case #0 1\n case #1 4 ... 6\n \
1280 default\n body\n block\n case #0\n empty\n case #1\n \
1281 empty\n default\n empty\n"
1282 );
1283 }
1284
1285 #[test]
1286 fn two_labels_on_one_statement_are_in_the_table_the_way_round_they_were_written() {
1287 let mut f = Fixture::new();
1290 let inner = f.case(2, None, None);
1291 let outer = f.case(1, None, Some(inner));
1292 let scrutinee = f.int(0);
1293 let switch = f.switch(scrutinee, &[outer]);
1294
1295 let mut c = f.checker();
1296 let void = c.types.void();
1297 let id = c.check_stmt(void, switch);
1298
1299 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1300 assert_eq!(
1301 dump(&c, id),
1302 "switch\n cond\n const 0 : int\n cases\n case #0 1\n case #1 2\n body\n \
1303 block\n case #0\n case #1\n empty\n"
1304 );
1305 }
1306
1307 #[test]
1308 fn a_case_that_covers_a_value_an_earlier_one_covers_is_a_duplicate() {
1309 let mut f = Fixture::new();
1310 let first = f.case(1, Some(3), None);
1311 let second = f.case(2, None, None);
1312 let scrutinee = f.int(0);
1313 let switch = f.switch(scrutinee, &[first, second]);
1314
1315 let mut c = f.checker();
1316 let void = c.types.void();
1317 c.check_stmt(void, switch);
1318
1319 assert_eq!(messages(&c), ["duplicate case value", "previously used here"]);
1320 }
1321
1322 #[test]
1323 fn a_case_outside_a_switch_is_an_error_and_so_is_a_default() {
1324 let mut f = Fixture::new();
1325 let case = f.case(1, None, None);
1326 let default = f.stmt(ast::Stmt::Default { body: None });
1327 let body = f.block(&[case, default]);
1328
1329 let mut c = f.checker();
1330 let void = c.types.void();
1331 c.check_stmt(void, body);
1332
1333 assert_eq!(
1334 messages(&c),
1335 [
1336 "case label not within a switch statement",
1337 "'default' label not within a switch statement",
1338 ]
1339 );
1340 }
1341
1342 #[test]
1343 fn a_case_label_that_is_not_a_constant_is_an_error() {
1344 let mut f = Fixture::new();
1345 let specs = f.int_specs();
1346 let declared = f.local(specs, "n");
1347 let declared = f.stmt(ast::Stmt::Decl(declared));
1348 let use_n = f.use_name("n");
1349 let case = f.stmt(ast::Stmt::Case { lo: use_n, hi: None, body: None });
1350 let scrutinee = f.int(0);
1351 let switch = f.switch(scrutinee, &[case]);
1352 let body = f.block(&[declared, switch]);
1353
1354 let mut c = f.checker();
1355 let void = c.types.void();
1356 c.check_stmt(void, body);
1357
1358 assert_eq!(message(&c), "case label does not reduce to an integer constant");
1359 }
1360
1361 #[test]
1362 fn a_case_range_that_runs_backwards_is_empty() {
1363 let mut f = Fixture::new();
1364 let case = f.case(6, Some(4), None);
1365 let scrutinee = f.int(0);
1366 let switch = f.switch(scrutinee, &[case]);
1367
1368 let mut c = f.checker();
1369 let void = c.types.void();
1370 c.check_stmt(void, switch);
1371
1372 assert_eq!(reported(&c), ["warning: empty range specified"]);
1373 }
1374
1375 #[test]
1376 fn a_case_is_measured_against_the_type_that_was_written_and_not_the_promoted_one() {
1377 let mut f = Fixture::new();
1378 let specs = f.keywords(&[BuiltinSet::CHAR]);
1379 let zero = f.int(0);
1380 let scrutinee = f.cast(specs, zero);
1381 let case = f.case(300, None, None);
1382 let switch = f.switch(scrutinee, &[case]);
1383
1384 let mut c = f.checker();
1385 let void = c.types.void();
1386 c.check_stmt(void, switch);
1387
1388 assert_eq!(reported(&c), ["warning: case label value exceeds maximum value for type"]);
1389 }
1390
1391 #[test]
1392 fn two_defaults_in_one_switch_are_an_error_that_points_at_the_first() {
1393 let mut f = Fixture::new();
1394 let first = f.stmt(ast::Stmt::Default { body: None });
1395 let second = f.stmt(ast::Stmt::Default { body: None });
1396 let scrutinee = f.int(0);
1397 let switch = f.switch(scrutinee, &[first, second]);
1398
1399 let mut c = f.checker();
1400 let void = c.types.void();
1401 c.check_stmt(void, switch);
1402
1403 assert_eq!(
1404 messages(&c),
1405 ["multiple default labels in one switch", "this is the first default label"]
1406 );
1407 }
1408
1409 #[test]
1410 fn a_nested_switch_keeps_its_cases_to_itself() {
1411 let mut f = Fixture::new();
1412 let inner_case = f.case(1, None, None);
1413 let inner_scrutinee = f.int(0);
1414 let inner = f.switch(inner_scrutinee, &[inner_case]);
1415 let outer_case = f.case(1, None, Some(inner));
1416 let outer_scrutinee = f.int(0);
1417 let outer = f.switch(outer_scrutinee, &[outer_case]);
1418
1419 let mut c = f.checker();
1420 let void = c.types.void();
1421 let id = c.check_stmt(void, outer);
1422
1423 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1424 assert_eq!(
1425 dump(&c, id),
1426 "switch\n cond\n const 0 : int\n cases\n case #1 1\n body\n block\n \
1427 case #1\n switch\n cond\n const 0 : int\n \
1428 cases\n case #0 1\n body\n block\n case \
1429 #0\n empty\n"
1430 );
1431 }
1432
1433 #[test]
1434 fn a_bare_return_from_a_function_that_promised_a_value_is_an_error() {
1435 let mut f = Fixture::new();
1436 let stmt = f.stmt(ast::Stmt::Return(None));
1437
1438 let mut c = f.checker();
1439 let int = c.int();
1440 c.check_stmt(int, stmt);
1441
1442 assert_eq!(reported(&c), ["error: 'return' with no value, in function returning non-void"]);
1443 assert_eq!(messages(&c).len(), 2, "the note is attached to it");
1444 }
1445
1446 #[test]
1447 fn a_value_returned_from_a_function_returning_void_is_an_error() {
1448 let mut f = Fixture::new();
1449 let one = f.int(1);
1450 let stmt = f.stmt(ast::Stmt::Return(Some(one)));
1451
1452 let mut c = f.checker();
1453 let void = c.types.void();
1454 c.check_stmt(void, stmt);
1455
1456 assert_eq!(reported(&c), ["error: 'return' with a value, in function returning void"]);
1457 }
1458
1459 #[test]
1460 fn a_void_value_returned_from_a_function_returning_void_is_what_a_wrapper_writes() {
1461 let mut f = Fixture::new();
1462 let specs = f.keywords(&[BuiltinSet::VOID]);
1463 let one = f.int(1);
1464 let value = f.cast(specs, one);
1465 let stmt = f.stmt(ast::Stmt::Return(Some(value)));
1466
1467 let mut c = f.checker();
1468 let void = c.types.void();
1469 c.check_stmt(void, stmt);
1470
1471 assert!(c.errors.is_empty(), "got {:?}", messages(&c));
1472 }
1473
1474 #[test]
1475 fn a_returned_value_is_converted_to_the_return_type() {
1476 let mut f = Fixture::new();
1477 let one = f.int(1);
1478 let stmt = f.stmt(ast::Stmt::Return(Some(one)));
1479
1480 let mut c = f.checker();
1481 let long = c.types.int(IntKind::Long);
1482 let id = c.check_stmt(long, stmt);
1483
1484 assert_eq!(dump(&c, id), "return\n convert arithmetic : long\n const 1 : int\n");
1485 assert!(c.errors.is_empty());
1486 }
1487}