refactor(surface): collapse the optional-clause duplicate-check onto set_once

Every definition parser repeated the same triplet per optional
attribute — `if slot.is_some() { return duplicate_clause_err(...) }
slot = Some(parse_X()?)`. Factor the check-then-assign into one
helper, set_once(slot, production, subject, clause, value), invoked as
`set_once(&mut doc, prod, subj, "doc", self.parse_doc()?)?`. The match
on peek_head_ident() and the per-clause production/subject/clause
arguments are unchanged, so every duplicate-clause error string stays
byte-identical (the *_rejects_duplicate_*_clause tests pin them).

The helper checks is_some() before the parser runs, so a duplicate
clause still reports at the offending clause's position. Arms that do
more than a plain check+assign (the bool flags drop-iterative/kernel,
the param-in nested map, the suppress/method Vec pushes) are left
inline.
This commit is contained in:
2026-06-02 02:29:10 +02:00
parent 72503c40fe
commit 5cd2a964e3
+70 -82
View File
@@ -405,10 +405,8 @@ impl<'a> Parser<'a> {
loop { loop {
match self.peek_head_ident() { match self.peek_head_ident() {
Some("doc") => { Some("doc") => {
if doc.is_some() { let subj = format!("data `{name}`");
return Err(self.duplicate_clause_err("data-def", &format!("data `{name}`"), "doc")); self.set_once(&mut doc, "data-def", &subj, "doc", |p| p.parse_doc())?;
}
doc = Some(self.parse_doc()?);
} }
Some("ctor") => { Some("ctor") => {
ctors.push(self.parse_ctor()?); ctors.push(self.parse_ctor()?);
@@ -559,41 +557,29 @@ impl<'a> Parser<'a> {
loop { loop {
match self.peek_head_ident() { match self.peek_head_ident() {
Some("doc") => { Some("doc") => {
if doc.is_some() { let subj = format!("fn `{name}`");
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "doc")); self.set_once(&mut doc, "fn-def", &subj, "doc", |p| p.parse_doc())?;
}
doc = Some(self.parse_doc()?);
} }
Some("export") => { Some("export") => {
if export.is_some() { let subj = format!("fn `{name}`");
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "export")); self.set_once(&mut export, "fn-def", &subj, "export", |p| p.parse_export())?;
}
export = Some(self.parse_export()?);
} }
Some("suppress") => suppress.push(self.parse_suppress_attr()?), Some("suppress") => suppress.push(self.parse_suppress_attr()?),
Some("type") => { Some("type") => {
if ty.is_some() { let subj = format!("fn `{name}`");
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "type")); self.set_once(&mut ty, "fn-def", &subj, "type", |p| p.parse_type_attr())?;
}
ty = Some(self.parse_type_attr()?);
} }
Some("params") => { Some("params") => {
if params.is_some() { let subj = format!("fn `{name}`");
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "params")); self.set_once(&mut params, "fn-def", &subj, "params", |p| p.parse_params_attr())?;
}
params = Some(self.parse_params_attr()?);
} }
Some("body") => { Some("body") => {
if body.is_some() { let subj = format!("fn `{name}`");
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "body")); self.set_once(&mut body, "fn-def", &subj, "body", |p| p.parse_body_attr())?;
}
body = Some(self.parse_body_attr()?);
} }
Some("intrinsic") => { Some("intrinsic") => {
if body.is_some() { let subj = format!("fn `{name}`");
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "body")); self.set_once(&mut body, "fn-def", &subj, "body", |p| p.parse_intrinsic_attr())?;
}
body = Some(self.parse_intrinsic_attr()?);
} }
Some(other) => { Some(other) => {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0); let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
@@ -722,6 +708,29 @@ impl<'a> Parser<'a> {
} }
} }
/// Fill an optional-clause slot exactly once. If `slot` already holds
/// a value, the clause is a duplicate and this returns
/// [`Self::duplicate_clause_err`] (computed against the *current*
/// cursor position, i.e. before `parse` runs — so the diagnostic
/// points at the offending second clause). Otherwise `parse` is
/// invoked and its result stored. The parse runs only on the
/// non-duplicate path, matching the inline `is_some` / dup-err /
/// assign shape it replaces.
fn set_once<T>(
&mut self,
slot: &mut Option<T>,
production: &'static str,
subject: &str,
clause: &'static str,
parse: impl FnOnce(&mut Self) -> Result<T, ParseError>,
) -> Result<(), ParseError> {
if slot.is_some() {
return Err(self.duplicate_clause_err(production, subject, clause));
}
*slot = Some(parse(self)?);
Ok(())
}
fn parse_type_attr(&mut self) -> Result<Type, ParseError> { fn parse_type_attr(&mut self) -> Result<Type, ParseError> {
self.expect_lparen("type-attr")?; self.expect_lparen("type-attr")?;
self.expect_keyword("type")?; self.expect_keyword("type")?;
@@ -772,22 +781,16 @@ impl<'a> Parser<'a> {
loop { loop {
match self.peek_head_ident() { match self.peek_head_ident() {
Some("doc") => { Some("doc") => {
if doc.is_some() { let subj = format!("const `{name}`");
return Err(self.duplicate_clause_err("const-def", &format!("const `{name}`"), "doc")); self.set_once(&mut doc, "const-def", &subj, "doc", |p| p.parse_doc())?;
}
doc = Some(self.parse_doc()?);
} }
Some("type") => { Some("type") => {
if ty.is_some() { let subj = format!("const `{name}`");
return Err(self.duplicate_clause_err("const-def", &format!("const `{name}`"), "type")); self.set_once(&mut ty, "const-def", &subj, "type", |p| p.parse_type_attr())?;
}
ty = Some(self.parse_type_attr()?);
} }
Some("body") => { Some("body") => {
if value.is_some() { let subj = format!("const `{name}`");
return Err(self.duplicate_clause_err("const-def", &format!("const `{name}`"), "body")); self.set_once(&mut value, "const-def", &subj, "body", |p| p.parse_body_attr())?;
}
value = Some(self.parse_body_attr()?);
} }
Some(other) => { Some(other) => {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0); let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
@@ -834,25 +837,22 @@ impl<'a> Parser<'a> {
loop { loop {
match self.peek_head_ident() { match self.peek_head_ident() {
Some("param") => { Some("param") => {
if param.is_some() { let subj = format!("class `{name}`");
return Err(self.duplicate_clause_err("class-def", &format!("class `{name}`"), "param")); self.set_once(&mut param, "class-def", &subj, "param", |p| {
} p.expect_lparen("class.param")?;
self.expect_lparen("class.param")?; p.expect_keyword("param")?;
self.expect_keyword("param")?; let v = p.expect_ident("class param ident")?;
param = Some(self.expect_ident("class param ident")?); p.expect_rparen("class.param")?;
self.expect_rparen("class.param")?; Ok(v)
})?;
} }
Some("superclass") => { Some("superclass") => {
if superclass.is_some() { let subj = format!("class `{name}`");
return Err(self.duplicate_clause_err("class-def", &format!("class `{name}`"), "superclass")); self.set_once(&mut superclass, "class-def", &subj, "superclass", |p| p.parse_superclass())?;
}
superclass = Some(self.parse_superclass()?);
} }
Some("doc") => { Some("doc") => {
if doc.is_some() { let subj = format!("class `{name}`");
return Err(self.duplicate_clause_err("class-def", &format!("class `{name}`"), "doc")); self.set_once(&mut doc, "class-def", &subj, "doc", |p| p.parse_doc())?;
}
doc = Some(self.parse_doc()?);
} }
Some("method") => { Some("method") => {
methods.push(self.parse_class_method()?); methods.push(self.parse_class_method()?);
@@ -911,16 +911,12 @@ impl<'a> Parser<'a> {
loop { loop {
match self.peek_head_ident() { match self.peek_head_ident() {
Some("type") => { Some("type") => {
if ty.is_some() { let subj = format!("method `{name}`");
return Err(self.duplicate_clause_err("class.method", &format!("method `{name}`"), "type")); self.set_once(&mut ty, "class.method", &subj, "type", |p| p.parse_type_attr())?;
}
ty = Some(self.parse_type_attr()?);
} }
Some("default") => { Some("default") => {
if default.is_some() { let subj = format!("method `{name}`");
return Err(self.duplicate_clause_err("class.method", &format!("method `{name}`"), "default")); self.set_once(&mut default, "class.method", &subj, "default", |p| p.parse_default_attr())?;
}
default = Some(self.parse_default_attr()?);
} }
Some(other) => { Some(other) => {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0); let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
@@ -964,25 +960,19 @@ impl<'a> Parser<'a> {
loop { loop {
match self.peek_head_ident() { match self.peek_head_ident() {
Some("class") => { Some("class") => {
if class.is_some() { self.set_once(&mut class, "instance-def", "instance", "class", |p| {
return Err(self.duplicate_clause_err("instance-def", "instance", "class")); p.expect_lparen("instance.class")?;
} p.expect_keyword("class")?;
self.expect_lparen("instance.class")?; let v = p.expect_ident("instance class name")?;
self.expect_keyword("class")?; p.expect_rparen("instance.class")?;
class = Some(self.expect_ident("instance class name")?); Ok(v)
self.expect_rparen("instance.class")?; })?;
} }
Some("type") => { Some("type") => {
if type_.is_some() { self.set_once(&mut type_, "instance-def", "instance", "type", |p| p.parse_type_attr())?;
return Err(self.duplicate_clause_err("instance-def", "instance", "type"));
}
type_ = Some(self.parse_type_attr()?);
} }
Some("doc") => { Some("doc") => {
if doc.is_some() { self.set_once(&mut doc, "instance-def", "instance", "doc", |p| p.parse_doc())?;
return Err(self.duplicate_clause_err("instance-def", "instance", "doc"));
}
doc = Some(self.parse_doc()?);
} }
Some("method") => { Some("method") => {
methods.push(self.parse_instance_method()?); methods.push(self.parse_instance_method()?);
@@ -1027,10 +1017,8 @@ impl<'a> Parser<'a> {
loop { loop {
match self.peek_head_ident() { match self.peek_head_ident() {
Some("body") => { Some("body") => {
if body.is_some() { let subj = format!("instance method `{name}`");
return Err(self.duplicate_clause_err("instance.method", &format!("instance method `{name}`"), "body")); self.set_once(&mut body, "instance.method", &subj, "body", |p| p.parse_body_attr())?;
}
body = Some(self.parse_body_attr()?);
} }
Some(other) => { Some(other) => {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0); let pos = self.peek().map(|t| t.span.start).unwrap_or(0);