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:
@@ -405,10 +405,8 @@ impl<'a> Parser<'a> {
|
||||
loop {
|
||||
match self.peek_head_ident() {
|
||||
Some("doc") => {
|
||||
if doc.is_some() {
|
||||
return Err(self.duplicate_clause_err("data-def", &format!("data `{name}`"), "doc"));
|
||||
}
|
||||
doc = Some(self.parse_doc()?);
|
||||
let subj = format!("data `{name}`");
|
||||
self.set_once(&mut doc, "data-def", &subj, "doc", |p| p.parse_doc())?;
|
||||
}
|
||||
Some("ctor") => {
|
||||
ctors.push(self.parse_ctor()?);
|
||||
@@ -559,41 +557,29 @@ impl<'a> Parser<'a> {
|
||||
loop {
|
||||
match self.peek_head_ident() {
|
||||
Some("doc") => {
|
||||
if doc.is_some() {
|
||||
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "doc"));
|
||||
}
|
||||
doc = Some(self.parse_doc()?);
|
||||
let subj = format!("fn `{name}`");
|
||||
self.set_once(&mut doc, "fn-def", &subj, "doc", |p| p.parse_doc())?;
|
||||
}
|
||||
Some("export") => {
|
||||
if export.is_some() {
|
||||
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "export"));
|
||||
}
|
||||
export = Some(self.parse_export()?);
|
||||
let subj = format!("fn `{name}`");
|
||||
self.set_once(&mut export, "fn-def", &subj, "export", |p| p.parse_export())?;
|
||||
}
|
||||
Some("suppress") => suppress.push(self.parse_suppress_attr()?),
|
||||
Some("type") => {
|
||||
if ty.is_some() {
|
||||
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "type"));
|
||||
}
|
||||
ty = Some(self.parse_type_attr()?);
|
||||
let subj = format!("fn `{name}`");
|
||||
self.set_once(&mut ty, "fn-def", &subj, "type", |p| p.parse_type_attr())?;
|
||||
}
|
||||
Some("params") => {
|
||||
if params.is_some() {
|
||||
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "params"));
|
||||
}
|
||||
params = Some(self.parse_params_attr()?);
|
||||
let subj = format!("fn `{name}`");
|
||||
self.set_once(&mut params, "fn-def", &subj, "params", |p| p.parse_params_attr())?;
|
||||
}
|
||||
Some("body") => {
|
||||
if body.is_some() {
|
||||
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "body"));
|
||||
}
|
||||
body = Some(self.parse_body_attr()?);
|
||||
let subj = format!("fn `{name}`");
|
||||
self.set_once(&mut body, "fn-def", &subj, "body", |p| p.parse_body_attr())?;
|
||||
}
|
||||
Some("intrinsic") => {
|
||||
if body.is_some() {
|
||||
return Err(self.duplicate_clause_err("fn-def", &format!("fn `{name}`"), "body"));
|
||||
}
|
||||
body = Some(self.parse_intrinsic_attr()?);
|
||||
let subj = format!("fn `{name}`");
|
||||
self.set_once(&mut body, "fn-def", &subj, "body", |p| p.parse_intrinsic_attr())?;
|
||||
}
|
||||
Some(other) => {
|
||||
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> {
|
||||
self.expect_lparen("type-attr")?;
|
||||
self.expect_keyword("type")?;
|
||||
@@ -772,22 +781,16 @@ impl<'a> Parser<'a> {
|
||||
loop {
|
||||
match self.peek_head_ident() {
|
||||
Some("doc") => {
|
||||
if doc.is_some() {
|
||||
return Err(self.duplicate_clause_err("const-def", &format!("const `{name}`"), "doc"));
|
||||
}
|
||||
doc = Some(self.parse_doc()?);
|
||||
let subj = format!("const `{name}`");
|
||||
self.set_once(&mut doc, "const-def", &subj, "doc", |p| p.parse_doc())?;
|
||||
}
|
||||
Some("type") => {
|
||||
if ty.is_some() {
|
||||
return Err(self.duplicate_clause_err("const-def", &format!("const `{name}`"), "type"));
|
||||
}
|
||||
ty = Some(self.parse_type_attr()?);
|
||||
let subj = format!("const `{name}`");
|
||||
self.set_once(&mut ty, "const-def", &subj, "type", |p| p.parse_type_attr())?;
|
||||
}
|
||||
Some("body") => {
|
||||
if value.is_some() {
|
||||
return Err(self.duplicate_clause_err("const-def", &format!("const `{name}`"), "body"));
|
||||
}
|
||||
value = Some(self.parse_body_attr()?);
|
||||
let subj = format!("const `{name}`");
|
||||
self.set_once(&mut value, "const-def", &subj, "body", |p| p.parse_body_attr())?;
|
||||
}
|
||||
Some(other) => {
|
||||
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
|
||||
@@ -834,25 +837,22 @@ impl<'a> Parser<'a> {
|
||||
loop {
|
||||
match self.peek_head_ident() {
|
||||
Some("param") => {
|
||||
if param.is_some() {
|
||||
return Err(self.duplicate_clause_err("class-def", &format!("class `{name}`"), "param"));
|
||||
}
|
||||
self.expect_lparen("class.param")?;
|
||||
self.expect_keyword("param")?;
|
||||
param = Some(self.expect_ident("class param ident")?);
|
||||
self.expect_rparen("class.param")?;
|
||||
let subj = format!("class `{name}`");
|
||||
self.set_once(&mut param, "class-def", &subj, "param", |p| {
|
||||
p.expect_lparen("class.param")?;
|
||||
p.expect_keyword("param")?;
|
||||
let v = p.expect_ident("class param ident")?;
|
||||
p.expect_rparen("class.param")?;
|
||||
Ok(v)
|
||||
})?;
|
||||
}
|
||||
Some("superclass") => {
|
||||
if superclass.is_some() {
|
||||
return Err(self.duplicate_clause_err("class-def", &format!("class `{name}`"), "superclass"));
|
||||
}
|
||||
superclass = Some(self.parse_superclass()?);
|
||||
let subj = format!("class `{name}`");
|
||||
self.set_once(&mut superclass, "class-def", &subj, "superclass", |p| p.parse_superclass())?;
|
||||
}
|
||||
Some("doc") => {
|
||||
if doc.is_some() {
|
||||
return Err(self.duplicate_clause_err("class-def", &format!("class `{name}`"), "doc"));
|
||||
}
|
||||
doc = Some(self.parse_doc()?);
|
||||
let subj = format!("class `{name}`");
|
||||
self.set_once(&mut doc, "class-def", &subj, "doc", |p| p.parse_doc())?;
|
||||
}
|
||||
Some("method") => {
|
||||
methods.push(self.parse_class_method()?);
|
||||
@@ -911,16 +911,12 @@ impl<'a> Parser<'a> {
|
||||
loop {
|
||||
match self.peek_head_ident() {
|
||||
Some("type") => {
|
||||
if ty.is_some() {
|
||||
return Err(self.duplicate_clause_err("class.method", &format!("method `{name}`"), "type"));
|
||||
}
|
||||
ty = Some(self.parse_type_attr()?);
|
||||
let subj = format!("method `{name}`");
|
||||
self.set_once(&mut ty, "class.method", &subj, "type", |p| p.parse_type_attr())?;
|
||||
}
|
||||
Some("default") => {
|
||||
if default.is_some() {
|
||||
return Err(self.duplicate_clause_err("class.method", &format!("method `{name}`"), "default"));
|
||||
}
|
||||
default = Some(self.parse_default_attr()?);
|
||||
let subj = format!("method `{name}`");
|
||||
self.set_once(&mut default, "class.method", &subj, "default", |p| p.parse_default_attr())?;
|
||||
}
|
||||
Some(other) => {
|
||||
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
|
||||
@@ -964,25 +960,19 @@ impl<'a> Parser<'a> {
|
||||
loop {
|
||||
match self.peek_head_ident() {
|
||||
Some("class") => {
|
||||
if class.is_some() {
|
||||
return Err(self.duplicate_clause_err("instance-def", "instance", "class"));
|
||||
}
|
||||
self.expect_lparen("instance.class")?;
|
||||
self.expect_keyword("class")?;
|
||||
class = Some(self.expect_ident("instance class name")?);
|
||||
self.expect_rparen("instance.class")?;
|
||||
self.set_once(&mut class, "instance-def", "instance", "class", |p| {
|
||||
p.expect_lparen("instance.class")?;
|
||||
p.expect_keyword("class")?;
|
||||
let v = p.expect_ident("instance class name")?;
|
||||
p.expect_rparen("instance.class")?;
|
||||
Ok(v)
|
||||
})?;
|
||||
}
|
||||
Some("type") => {
|
||||
if type_.is_some() {
|
||||
return Err(self.duplicate_clause_err("instance-def", "instance", "type"));
|
||||
}
|
||||
type_ = Some(self.parse_type_attr()?);
|
||||
self.set_once(&mut type_, "instance-def", "instance", "type", |p| p.parse_type_attr())?;
|
||||
}
|
||||
Some("doc") => {
|
||||
if doc.is_some() {
|
||||
return Err(self.duplicate_clause_err("instance-def", "instance", "doc"));
|
||||
}
|
||||
doc = Some(self.parse_doc()?);
|
||||
self.set_once(&mut doc, "instance-def", "instance", "doc", |p| p.parse_doc())?;
|
||||
}
|
||||
Some("method") => {
|
||||
methods.push(self.parse_instance_method()?);
|
||||
@@ -1027,10 +1017,8 @@ impl<'a> Parser<'a> {
|
||||
loop {
|
||||
match self.peek_head_ident() {
|
||||
Some("body") => {
|
||||
if body.is_some() {
|
||||
return Err(self.duplicate_clause_err("instance.method", &format!("instance method `{name}`"), "body"));
|
||||
}
|
||||
body = Some(self.parse_body_attr()?);
|
||||
let subj = format!("instance method `{name}`");
|
||||
self.set_once(&mut body, "instance.method", &subj, "body", |p| p.parse_body_attr())?;
|
||||
}
|
||||
Some(other) => {
|
||||
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
|
||||
|
||||
Reference in New Issue
Block a user