Lines
100 %
Functions
Branches
//! HANDLER-CASE — try_table-based condition recovery. Compile + validate
//! surface; runtime evaluation (the catch actually firing and clauses
//! returning values) is covered in `nms/tests/handler_case_runtime.rs`.
use super::common::{compile_and_validate, compile_expect_error, wrap_with_runtime_i32};
#[test]
fn handler_case_single_clause_validates() {
compile_and_validate(r#"(handler-case (error 'oops "x") (oops (e) 7))"#);
}
fn handler_case_no_throw_body_validates() {
compile_and_validate("(handler-case 42 (oops (e) 7))");
fn handler_case_multi_clause_validates() {
compile_and_validate(r#"(handler-case (error 'two "x") (one (e) 1) (two (e) 2))"#);
fn handler_case_catch_all_validates() {
compile_and_validate(r#"(handler-case (error 'whatever "x") (t (e) 99))"#);
fn handler_case_unmatched_propagates_validates() {
// No matching clause + no `t`: re-raises. Still a valid module.
compile_and_validate(r#"(handler-case (error 'nope "x") (other (e) 1))"#);
fn handler_case_error_code_accessor_validates() {
compile_and_validate(r#"(handler-case (error 'x "m") (x (e) (error-code e)))"#);
fn handler_case_error_message_accessor_validates() {
compile_and_validate(r#"(handler-case (error 'x "m") (x (e) (error-message e)))"#);
fn handler_case_no_clauses_validates() {
// Zero clauses degenerates to "evaluate body; any raise propagates".
compile_and_validate(r#"(handler-case (error 'x "m"))"#);
fn handler_case_body_and_clause_types_must_agree() {
// Body falls through with a Ratio; the clause yields a StringRef — the
// two reachable exit types conflict, a structured compile error.
let err = compile_expect_error(&wrap_with_runtime_i32(
r#"(handler-case (if (= IDX 0) 11/10 12/10) (oops (e) "str"))"#,
));
assert!(
err.contains("HANDLER-CASE") && err.contains("conflicting result types"),
"expected a conflicting-result-types error, got: {err}"
);
fn handler_case_catch_all_must_be_last() {
let err = compile_expect_error(r#"(handler-case (error 'x "m") (t (e) 1) (x (e) 2))"#);
err.contains("handler-case") && err.contains("must be last"),
"expected a t-must-be-last error, got: {err}"
fn handler_case_clause_binding_must_be_single_var() {
let err = compile_expect_error(r#"(handler-case (error 'x "m") (x (a b) 1))"#);
err.contains("handler-case") && err.contains("single (var)"),
"expected a clause-binding-shape error, got: {err}"
fn error_code_rejects_non_condition_arg() {
// The accessor is typed: a Ratio is not an EntityRef(Condition).
let err = compile_expect_error("(error-code 5)");
err.contains("ERROR-CODE") || err.contains("condition"),
"expected a type error for ERROR-CODE on a non-condition, got: {err}"
fn return_from_inside_handler_case_body_resolves() {
// A `(return-from)` inside a handler-case body must resolve its relative
// `br` target through the +3 wrapper frames the handler-case opens.
compile_and_validate(r#"(block done (handler-case (return-from done 1) (x (e) 2)))"#);