iter 22-tidy.5.1: parse_fn rejects duplicate doc/type/params/body clauses

This commit is contained in:
2026-05-10 04:27:19 +02:00
parent 2506180023
commit 9592478971
+140 -7
View File
@@ -484,19 +484,58 @@ impl<'a> Parser<'a> {
let mut suppress: Vec<Suppress> = Vec::new();
loop {
match self.peek_head_ident() {
Some("doc") => doc = Some(self.parse_doc()?),
Some("doc") => {
if doc.is_some() {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
return Err(ParseError::Production {
production: "fn-def",
message: format!(
"fn `{name}` has duplicate `(doc ...)` clause"
),
pos,
});
}
doc = Some(self.parse_doc()?);
}
Some("suppress") => suppress.push(self.parse_suppress_attr()?),
Some("type") => {
let t = self.parse_type_attr()?;
ty = Some(t);
if ty.is_some() {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
return Err(ParseError::Production {
production: "fn-def",
message: format!(
"fn `{name}` has duplicate `(type ...)` clause"
),
pos,
});
}
ty = Some(self.parse_type_attr()?);
}
Some("params") => {
let p = self.parse_params_attr()?;
params = Some(p);
if params.is_some() {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
return Err(ParseError::Production {
production: "fn-def",
message: format!(
"fn `{name}` has duplicate `(params ...)` clause"
),
pos,
});
}
params = Some(self.parse_params_attr()?);
}
Some("body") => {
let b = self.parse_body_attr()?;
body = Some(b);
if body.is_some() {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
return Err(ParseError::Production {
production: "fn-def",
message: format!(
"fn `{name}` has duplicate `(body ...)` clause"
),
pos,
});
}
body = Some(self.parse_body_attr()?);
}
Some(other) => {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
@@ -2260,4 +2299,98 @@ mod tests {
};
assert_eq!(i.doc.as_deref(), Some("instance for ints"));
}
/// Iter 22-tidy.5: `parse_fn` rejects a duplicate `(doc ...)`
/// clause with a `fn-def` production diagnostic naming the
/// offending fn. Pre-22-tidy.5, the second clause silently
/// overwrote the first; this test pins the strict shape.
#[test]
fn parse_fn_rejects_duplicate_doc_clause() {
let err = parse(
r#"
(module m
(fn f
(doc "first")
(doc "second")
(type (fn-type (params) (ret (con Unit))))
(params)
(body (do io/print_str "x"))))
"#,
)
.unwrap_err();
let msg = format!("{err:?}");
assert!(
msg.contains("fn `f` has duplicate `(doc ...)` clause"),
"expected duplicate-doc diagnostic, got: {msg}"
);
}
/// Iter 22-tidy.5: `parse_fn` rejects a duplicate `(type ...)`
/// clause with a `fn-def` production diagnostic naming the
/// offending fn.
#[test]
fn parse_fn_rejects_duplicate_type_clause() {
let err = parse(
r#"
(module m
(fn f
(type (fn-type (params) (ret (con Unit))))
(type (fn-type (params) (ret (con Unit))))
(params)
(body (do io/print_str "x"))))
"#,
)
.unwrap_err();
let msg = format!("{err:?}");
assert!(
msg.contains("fn `f` has duplicate `(type ...)` clause"),
"expected duplicate-type diagnostic, got: {msg}"
);
}
/// Iter 22-tidy.5: `parse_fn` rejects a duplicate `(params ...)`
/// clause with a `fn-def` production diagnostic naming the
/// offending fn.
#[test]
fn parse_fn_rejects_duplicate_params_clause() {
let err = parse(
r#"
(module m
(fn f
(type (fn-type (params) (ret (con Unit))))
(params)
(params)
(body (do io/print_str "x"))))
"#,
)
.unwrap_err();
let msg = format!("{err:?}");
assert!(
msg.contains("fn `f` has duplicate `(params ...)` clause"),
"expected duplicate-params diagnostic, got: {msg}"
);
}
/// Iter 22-tidy.5: `parse_fn` rejects a duplicate `(body ...)`
/// clause with a `fn-def` production diagnostic naming the
/// offending fn.
#[test]
fn parse_fn_rejects_duplicate_body_clause() {
let err = parse(
r#"
(module m
(fn f
(type (fn-type (params) (ret (con Unit))))
(params)
(body (do io/print_str "x"))
(body (do io/print_str "y"))))
"#,
)
.unwrap_err();
let msg = format!("{err:?}");
assert!(
msg.contains("fn `f` has duplicate `(body ...)` clause"),
"expected duplicate-body diagnostic, got: {msg}"
);
}
}