#![allow(clippy::unwrap_used, clippy::panic)]
use brink_format::{CountingFlags, DefinitionId, DefinitionTag};
use brink_ir::lir;
fn root_id() -> DefinitionId {
DefinitionId::new(DefinitionTag::Address, 1)
}
fn test_provenance() -> brink_ir::Provenance {
brink_ir::Provenance::synthetic(brink_ir::NodeClass::Stmt, rowan::TextRange::empty(0.into()))
}
fn s(kind: lir::StmtKind) -> lir::Stmt {
lir::Stmt::new(kind, test_provenance())
}
fn program_with_root_body(body: Vec<lir::Stmt>) -> lir::Program {
lir::Program {
root: lir::Container {
id: root_id(),
provenance: test_provenance(),
name: None,
kind: lir::ContainerKind::Root,
params: Vec::new(),
body,
children: Vec::new(),
counting_flags: CountingFlags::empty(),
temp_slot_count: 0,
labeled: false,
inline: false,
is_function: false,
local: false,
},
globals: Vec::new(),
lists: Vec::new(),
list_items: Vec::new(),
externals: Vec::new(),
name_table: Vec::new(),
struct_shapes: Vec::new(),
private_defs: Vec::new(),
aliases: Vec::new(),
file_paths: std::collections::BTreeMap::new(),
}
}
#[test]
fn well_formed_program_still_emits_successfully() {
let program = program_with_root_body(vec![s(lir::StmtKind::EndOfLine)]);
let story = brink_codegen_inkb::emit(&program);
assert!(story.is_ok(), "expected Ok, got {story:?}");
}
#[test]
fn break_in_a_well_formed_loop_still_emits_successfully() {
let program = program_with_root_body(vec![s(lir::StmtKind::LogicWhile(lir::LogicWhile {
condition: lir::ExprKind::Bool(true).at(test_provenance()),
body: vec![s(lir::StmtKind::LogicBreak)],
post: Vec::new(),
}))]);
let story = brink_codegen_inkb::emit(&program);
assert!(story.is_ok(), "expected Ok, got {story:?}");
}
#[test]
fn out_of_loop_logic_break_is_a_hard_codegen_error() {
let program = program_with_root_body(vec![s(lir::StmtKind::LogicBreak)]);
let err = brink_codegen_inkb::emit(&program)
.expect_err("an out-of-loop LogicBreak must not silently compile");
let message = err.to_string();
assert!(
message.contains("break") && message.contains("586"),
"error message should name the construct and reference #586: {message}"
);
}
#[test]
fn out_of_loop_logic_continue_is_a_hard_codegen_error() {
let program = program_with_root_body(vec![s(lir::StmtKind::LogicContinue)]);
let err = brink_codegen_inkb::emit(&program)
.expect_err("an out-of-loop LogicContinue must not silently compile");
let message = err.to_string();
assert!(
message.contains("continue") && message.contains("586"),
"error message should name the construct and reference #586: {message}"
);
}
#[test]
fn out_of_loop_break_after_a_sibling_loop_still_errors() {
let program = program_with_root_body(vec![
s(lir::StmtKind::LogicWhile(lir::LogicWhile {
condition: lir::ExprKind::Bool(true).at(test_provenance()),
body: vec![s(lir::StmtKind::EndOfLine)],
post: Vec::new(),
})),
s(lir::StmtKind::LogicBreak),
]);
let err = brink_codegen_inkb::emit(&program)
.expect_err("a break after (not inside) a loop must still error");
assert!(err.to_string().contains("break"));
}