use super::*;
use crate::diagnostic_codes::Code;
fn throws_mismatches(source: &str) -> Vec<String> {
check_source(source)
.into_iter()
.filter(|d| d.code == Code::ThrowsTypeMismatch)
.map(|d| d.message)
.collect()
}
#[test]
fn throwing_the_declared_type_checks_clean() {
let diags = throws_mismatches(
r#"fn parse(s: string) throws string {
throw "bad input"
}"#,
);
assert!(
diags.is_empty(),
"throwing the declared type must not raise TYP-026: {diags:?}"
);
}
#[test]
fn throwing_an_undeclared_type_errors() {
let diags = throws_mismatches(
r"fn parse(s: string) throws string {
throw 42
}",
);
assert_eq!(
diags.len(),
1,
"throwing a type outside the declared set must raise exactly one TYP-026: {diags:?}"
);
}
#[test]
fn union_throws_set_covers_each_member() {
let diags = throws_mismatches(
r#"fn parse(s: string) throws string | int {
if s == "" {
throw 42
}
throw "bad"
}"#,
);
assert!(
diags.is_empty(),
"each member of a union throws set must be allowed: {diags:?}"
);
}
#[test]
fn missing_throws_clause_is_unconstrained() {
let diags = throws_mismatches(
r"fn parse(s: string) {
throw 42
}",
);
assert!(
diags.is_empty(),
"an unannotated callable must never be throws-checked: {diags:?}"
);
}
#[test]
fn pipeline_throws_clause_is_enforced() {
let diags = throws_mismatches(
r"pipeline run(task) throws string {
throw 42
}",
);
assert_eq!(
diags.len(),
1,
"a pipeline that throws outside its declared set must raise TYP-026: {diags:?}"
);
}
#[test]
fn caught_error_is_excluded_from_the_declared_channel() {
let diags = throws_mismatches(
r"enum Parse { Bad }
enum Net { Down }
fn f() throws Net {
try {
throw Parse.Bad
} catch (e: Parse) {
throw Net.Down
}
}",
);
assert!(
diags.is_empty(),
"an error handled by its catch clause must not count against `throws`: {diags:?}"
);
}
#[test]
fn uncaught_error_escapes_and_must_be_declared() {
let diags = throws_mismatches(
r"enum Parse { Bad }
enum Net { Down }
fn f() throws Parse {
try {
throw Net.Down
} catch (e: Parse) {
}
}",
);
assert_eq!(
diags.len(),
1,
"an error the catch does not cover must escape and raise TYP-026: {diags:?}"
);
}
#[test]
fn catch_all_absorbs_every_body_error() {
let diags = throws_mismatches(
r"enum Net { Down }
fn f() throws Net {
try {
throw Net.Down
} catch {
}
}",
);
assert!(
diags.is_empty(),
"a catch-all must absorb every body error: {diags:?}"
);
}
#[test]
fn finally_body_throw_escapes() {
let diags = throws_mismatches(
r"enum Parse { Bad }
enum Net { Down }
fn f() throws Parse {
try {
throw Parse.Bad
} catch (e: Parse) {
} finally {
throw Net.Down
}
}",
);
assert_eq!(
diags.len(),
1,
"a throw in `finally` must escape and be checked against `throws`: {diags:?}"
);
}