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:
@@ -183,6 +183,41 @@ pub struct FnDef {
|
||||
/// Optional source-level documentation string.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub doc: Option<String>,
|
||||
/// Iter 19b: structured-diagnostic suppressions opted into for this
|
||||
/// fn. Each entry names a diagnostic code and an author-asserted
|
||||
/// reason. Currently the only consumer is `over-strict-mode`
|
||||
/// (Iter 19a / 19a.1) but the mechanism is generic across codes.
|
||||
/// `because` must be non-empty — the typechecker emits
|
||||
/// `empty-suppress-reason` (Error) otherwise.
|
||||
///
|
||||
/// Serialised with `skip_serializing_if = "Vec::is_empty"` so
|
||||
/// every pre-19b fixture's canonical-JSON hash stays bit-identical.
|
||||
/// The same additive-schema pattern is used by [`TypeDef::vars`]
|
||||
/// (Iter 13a) and [`Type::Con::args`] (Iter 13a).
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub suppress: Vec<Suppress>,
|
||||
}
|
||||
|
||||
/// Iter 19b: one entry in [`FnDef::suppress`]. Marks a structured
|
||||
/// diagnostic the author has consciously decided to allow on this
|
||||
/// def, with a mandatory reason.
|
||||
///
|
||||
/// `because` is non-empty by schema rule — the typechecker emits
|
||||
/// `empty-suppress-reason` (Error severity) when it is empty or
|
||||
/// whitespace-only, and a wrong/unknown `code` simply matches no
|
||||
/// diagnostic and therefore suppresses nothing (the original
|
||||
/// diagnostic still fires unmasked).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Suppress {
|
||||
/// The diagnostic code being suppressed (e.g.
|
||||
/// `"over-strict-mode"`). Matched against
|
||||
/// [`crate::SCHEMA`]-side codes; an unknown code suppresses
|
||||
/// nothing but is not itself an error (the diagnostic registry
|
||||
/// is open-set).
|
||||
pub code: String,
|
||||
/// The author's stated reason. Must be non-empty — the
|
||||
/// typechecker emits `empty-suppress-reason` (Error) otherwise.
|
||||
pub because: String,
|
||||
}
|
||||
|
||||
/// A constant (top-level binding to a value).
|
||||
|
||||
Reference in New Issue
Block a user