check: mode-strict-because suppression (iter 19b)

Closes the 19a/19a.1/19b arc. Corpus signal from 19a.1 (5/65
fixtures fire over-strict-mode, all deliberate RC codegen-test
fixtures) justified shipping the suppress mechanism end-to-end.

Schema: Suppress { code, because } on FnDef. Pre-19b hashes
bit-identical via skip_serializing_if. Typechecker drops matching
diagnostics; empty 'because' is Error severity; wrong codes are
silent no-ops (open-set registry).

Form-A: (suppress (code "...") (because "...")) clause, parser
+ printer round-trip clean. Form-B: '// @suppress <code>: <reason>'
above the doc string, lossless contract metadata.

5 RC fixtures migrated (.ail.json + .ailx + .prose.txt). Corpus
signal: 5/65 -> 0/65. Lint still fires on any future fn that's
accidentally over-strict without an authored reason.

Test counts: ailang-check 55->61, ailang-core 26->28, ailang-surface
21->26, ailang-prose 49->52, e2e 70 unchanged.

Known debt: .ailx comment headers lost on regen (ail render's
contract excludes comments); parse_suppress_attr accepts
duplicate code/because attrs without diagnose (bounded by canonical
print order).
This commit is contained in:
2026-05-08 19:36:04 +02:00
parent 9f3f10ce6e
commit 50b68267fe
30 changed files with 942 additions and 304 deletions
+8
View File
@@ -152,6 +152,7 @@ fn body_errors_accumulate_across_defs() {
],
tail: false,
},
suppress: vec![],
doc: None,
});
let bad_b = Def::Fn(FnDef {
@@ -167,6 +168,7 @@ fn body_errors_accumulate_across_defs() {
body: Term::Var {
name: "this_does_not_exist".into(),
},
suppress: vec![],
doc: None,
});
@@ -222,6 +224,7 @@ fn use_after_consume_on_own_param_is_reported() {
},
params: vec!["ys".into()],
body: Term::Lit { lit: Literal::Int { value: 0 } },
suppress: vec![],
doc: None,
});
@@ -256,6 +259,7 @@ fn use_after_consume_on_own_param_is_reported() {
],
tail: false,
},
suppress: vec![],
doc: None,
});
@@ -347,6 +351,7 @@ fn consume_while_borrowed_in_sibling_arg_is_reported() {
},
params: vec!["a".into(), "b".into()],
body: Term::Lit { lit: Literal::Int { value: 0 } },
suppress: vec![],
doc: None,
});
@@ -368,6 +373,7 @@ fn consume_while_borrowed_in_sibling_arg_is_reported() {
],
tail: false,
},
suppress: vec![],
doc: None,
});
@@ -505,6 +511,7 @@ fn reuse_as_happy_path_in_map_inc_is_linearity_clean() {
ty: map_inc_ty,
params: vec!["xs".into()],
body: map_inc_body,
suppress: vec![],
doc: None,
});
@@ -615,6 +622,7 @@ fn reuse_as_shape_mismatch_is_reported_on_cons_to_nil() {
ty: f_ty,
params: vec!["xs".into()],
body,
suppress: vec![],
doc: None,
});