diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs index 13c7bd474..7fdba4bd3 100644 --- a/src/syntax/parser.rs +++ b/src/syntax/parser.rs @@ -739,7 +739,7 @@ fn with_paren(p: &mut Parser, allow_destructuring: bool) { let m = p.marker(); let mut kind = collection(p, true); if p.at(SyntaxKind::Arrow) { - validate_params(p, m); + validate_params_at(p, m); p.wrap(m, SyntaxKind::Params); p.assert(SyntaxKind::Arrow); code_expr(p); @@ -747,7 +747,7 @@ fn with_paren(p: &mut Parser, allow_destructuring: bool) { } else if p.at(SyntaxKind::Eq) && kind != SyntaxKind::Parenthesized { // TODO: add warning if p.at(SyntaxKind::Eq) && kind == SyntaxKind::Parenthesized - validate_destruct_pattern(p, m, false); + validate_pattern_at(p, m, false); p.wrap(m, SyntaxKind::Destructuring); p.assert(SyntaxKind::Eq); code_expr(p); @@ -755,9 +755,11 @@ fn with_paren(p: &mut Parser, allow_destructuring: bool) { } match kind { - SyntaxKind::Array => validate_array(p, m), - SyntaxKind::Dict => validate_dict(p, m), - SyntaxKind::Parenthesized => validate_parenthesized(p, m), + SyntaxKind::Array if !allow_destructuring => validate_array_at(p, m), + SyntaxKind::Dict if !allow_destructuring => validate_dict_at(p, m), + SyntaxKind::Parenthesized if !allow_destructuring => { + validate_parenthesized_at(p, m) + } SyntaxKind::Destructuring if !allow_destructuring => { invalidate_destructuring(p, m) } @@ -914,7 +916,7 @@ fn args(p: &mut Parser) { let m = p.marker(); if p.at(SyntaxKind::LeftParen) { collection(p, false); - validate_args(p, m); + validate_args_at(p, m); } while p.directly_at(SyntaxKind::LeftBracket) { @@ -934,7 +936,7 @@ fn pattern(p: &mut Parser) -> PatternKind { let m = p.marker(); if p.at(SyntaxKind::LeftParen) { let kind = collection(p, false); - validate_destruct_pattern(p, m, true); + validate_pattern_at(p, m, true); if kind == SyntaxKind::Parenthesized { PatternKind::Ident @@ -963,7 +965,7 @@ fn let_binding(p: &mut Parser) { if closure { let m3 = p.marker(); collection(p, false); - validate_params(p, m3); + validate_params_at(p, m3); p.wrap(m3, SyntaxKind::Params); } } @@ -1114,36 +1116,51 @@ fn return_stmt(p: &mut Parser) { p.wrap(m, SyntaxKind::FuncReturn); } -fn validate_parenthesized(p: &mut Parser, m: Marker) { +fn validate_parenthesized_at(p: &mut Parser, m: Marker) { for child in p.post_process(m) { let kind = child.kind(); - if kind == SyntaxKind::Underscore { - child.convert_to_error(eco_format!( - "expected expression, found {}", - kind.name() - )); + match kind { + SyntaxKind::Array => validate_array(child.children_mut().iter_mut()), + SyntaxKind::Dict => validate_dict(child.children_mut().iter_mut()), + SyntaxKind::Underscore => { + child.convert_to_error(eco_format!( + "expected expression, found {}", + kind.name() + )); + } + _ => {} } } } -fn validate_array(p: &mut Parser, m: Marker) { - for child in p.post_process(m) { +fn validate_array_at(p: &mut Parser, m: Marker) { + validate_array(p.post_process(m)) +} + +fn validate_array<'a>(children: impl Iterator) { + for child in children { let kind = child.kind(); - if kind == SyntaxKind::Named - || kind == SyntaxKind::Keyed - || kind == SyntaxKind::Underscore - { - child.convert_to_error(eco_format!( - "expected expression, found {}", - kind.name() - )); + match kind { + SyntaxKind::Array => validate_array(child.children_mut().iter_mut()), + SyntaxKind::Dict => validate_dict(child.children_mut().iter_mut()), + SyntaxKind::Named | SyntaxKind::Keyed | SyntaxKind::Underscore => { + child.convert_to_error(eco_format!( + "expected expression, found {}", + kind.name() + )); + } + _ => {} } } } -fn validate_dict(p: &mut Parser, m: Marker) { +fn validate_dict_at(p: &mut Parser, m: Marker) { + validate_dict(p.post_process(m)) +} + +fn validate_dict<'a>(children: impl Iterator) { let mut used = HashSet::new(); - for child in p.post_process(m) { + for child in children { match child.kind() { SyntaxKind::Named | SyntaxKind::Keyed => { let Some(first) = child.children_mut().first_mut() else { continue }; @@ -1172,7 +1189,7 @@ fn validate_dict(p: &mut Parser, m: Marker) { } } -fn validate_params(p: &mut Parser, m: Marker) { +fn validate_params_at(p: &mut Parser, m: Marker) { let mut used_spread = false; let mut used = HashSet::new(); for child in p.post_process(m) { @@ -1238,7 +1255,7 @@ fn validate_params(p: &mut Parser, m: Marker) { } } -fn validate_args(p: &mut Parser, m: Marker) { +fn validate_args_at(p: &mut Parser, m: Marker) { let mut used = HashSet::new(); for child in p.post_process(m) { if child.kind() == SyntaxKind::Named { @@ -1256,7 +1273,7 @@ fn validate_args(p: &mut Parser, m: Marker) { } } -fn validate_destruct_pattern(p: &mut Parser, m: Marker, forbid_expressions: bool) { +fn validate_pattern_at(p: &mut Parser, m: Marker, forbid_expressions: bool) { let mut used = HashSet::new(); validate_pattern(p.post_process(m), &mut used, forbid_expressions); } diff --git a/tests/typ/bugs/parameter-pattern.typ b/tests/typ/bugs/parameter-pattern.typ new file mode 100644 index 000000000..31b07f2ca --- /dev/null +++ b/tests/typ/bugs/parameter-pattern.typ @@ -0,0 +1,5 @@ +// Test that underscore works in parameter patterns. +// Ref: false + +--- +#test((1, 2, 3).zip((1, 2, 3)).map(((_, x)) => x), (1, 2, 3))