1
//! HANDLER-CASE — try_table-based condition recovery. Compile + validate
2
//! surface; runtime evaluation (the catch actually firing and clauses
3
//! returning values) is covered in `nms/tests/handler_case_runtime.rs`.
4

            
5
use super::common::{compile_and_validate, compile_expect_error, wrap_with_runtime_i32};
6

            
7
#[test]
8
1
fn handler_case_single_clause_validates() {
9
1
    compile_and_validate(r#"(handler-case (error 'oops "x") (oops (e) 7))"#);
10
1
}
11

            
12
#[test]
13
1
fn handler_case_no_throw_body_validates() {
14
1
    compile_and_validate("(handler-case 42 (oops (e) 7))");
15
1
}
16

            
17
#[test]
18
1
fn handler_case_multi_clause_validates() {
19
1
    compile_and_validate(r#"(handler-case (error 'two "x") (one (e) 1) (two (e) 2))"#);
20
1
}
21

            
22
#[test]
23
1
fn handler_case_catch_all_validates() {
24
1
    compile_and_validate(r#"(handler-case (error 'whatever "x") (t (e) 99))"#);
25
1
}
26

            
27
#[test]
28
1
fn handler_case_unmatched_propagates_validates() {
29
    // No matching clause + no `t`: re-raises. Still a valid module.
30
1
    compile_and_validate(r#"(handler-case (error 'nope "x") (other (e) 1))"#);
31
1
}
32

            
33
#[test]
34
1
fn handler_case_error_code_accessor_validates() {
35
1
    compile_and_validate(r#"(handler-case (error 'x "m") (x (e) (error-code e)))"#);
36
1
}
37

            
38
#[test]
39
1
fn handler_case_error_message_accessor_validates() {
40
1
    compile_and_validate(r#"(handler-case (error 'x "m") (x (e) (error-message e)))"#);
41
1
}
42

            
43
#[test]
44
1
fn handler_case_no_clauses_validates() {
45
    // Zero clauses degenerates to "evaluate body; any raise propagates".
46
1
    compile_and_validate(r#"(handler-case (error 'x "m"))"#);
47
1
}
48

            
49
#[test]
50
1
fn handler_case_body_and_clause_types_must_agree() {
51
    // Body falls through with a Ratio; the clause yields a StringRef — the
52
    // two reachable exit types conflict, a structured compile error.
53
1
    let err = compile_expect_error(&wrap_with_runtime_i32(
54
1
        r#"(handler-case (if (= IDX 0) 11/10 12/10) (oops (e) "str"))"#,
55
1
    ));
56
1
    assert!(
57
1
        err.contains("HANDLER-CASE") && err.contains("conflicting result types"),
58
        "expected a conflicting-result-types error, got: {err}"
59
    );
60
1
}
61

            
62
#[test]
63
1
fn handler_case_catch_all_must_be_last() {
64
1
    let err = compile_expect_error(r#"(handler-case (error 'x "m") (t (e) 1) (x (e) 2))"#);
65
1
    assert!(
66
1
        err.contains("handler-case") && err.contains("must be last"),
67
        "expected a t-must-be-last error, got: {err}"
68
    );
69
1
}
70

            
71
#[test]
72
1
fn handler_case_clause_binding_must_be_single_var() {
73
1
    let err = compile_expect_error(r#"(handler-case (error 'x "m") (x (a b) 1))"#);
74
1
    assert!(
75
1
        err.contains("handler-case") && err.contains("single (var)"),
76
        "expected a clause-binding-shape error, got: {err}"
77
    );
78
1
}
79

            
80
#[test]
81
1
fn error_code_rejects_non_condition_arg() {
82
    // The accessor is typed: a Ratio is not an EntityRef(Condition).
83
1
    let err = compile_expect_error("(error-code 5)");
84
1
    assert!(
85
1
        err.contains("ERROR-CODE") || err.contains("condition"),
86
        "expected a type error for ERROR-CODE on a non-condition, got: {err}"
87
    );
88
1
}
89

            
90
#[test]
91
1
fn return_from_inside_handler_case_body_resolves() {
92
    // A `(return-from)` inside a handler-case body must resolve its relative
93
    // `br` target through the +3 wrapper frames the handler-case opens.
94
1
    compile_and_validate(r#"(block done (handler-case (return-from done 1) (x (e) 2)))"#);
95
1
}