Fix bugs with parenthesized expression parsing (#3505)

This commit is contained in:
Laurenz 2024-02-27 12:12:43 +01:00 committed by GitHub
parent 9646a132a8
commit 0aa9254356
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 26 additions and 5 deletions

View File

@ -746,7 +746,7 @@ fn code_primary(p: &mut Parser, atomic: bool) {
SyntaxKind::LeftBrace => code_block(p),
SyntaxKind::LeftBracket => content_block(p),
SyntaxKind::LeftParen => expr_with_paren(p),
SyntaxKind::LeftParen => expr_with_paren(p, atomic),
SyntaxKind::Dollar => equation(p),
SyntaxKind::Let => let_binding(p),
SyntaxKind::Set => set_rule(p),
@ -1020,13 +1020,16 @@ fn return_stmt(p: &mut Parser) {
}
/// An expression that starts with a parenthesis.
fn expr_with_paren(p: &mut Parser) {
fn expr_with_paren(p: &mut Parser, atomic: bool) {
// If we've seen this position before and have a memoized result, just use
// it. See below for more explanation about this memoization.
let start = p.current_start();
if let Some((range, end_point)) = p.memo.get(&start) {
p.nodes.extend(p.memo_arena[range.clone()].iter().cloned());
p.restore(end_point.clone());
if let Some((range, end_point)) = p.memo.get(&start).cloned() {
// Restore the end point first, so that it doesn't truncate our freshly
// pushed nodes. If the current length of `p.nodes` doesn't match what
// we had in the memoized run, this might otherwise happen.
p.restore(end_point);
p.nodes.extend(p.memo_arena[range].iter().cloned());
return;
}
@ -1038,6 +1041,9 @@ fn expr_with_paren(p: &mut Parser) {
// these are the most likely things. We can handle all of those in a single
// pass.
let kind = parenthesized_or_array_or_dict(p);
if atomic {
return;
}
// If, however, '=>' or '=' follows, we must backtrack and reparse as either
// a parameter list or a destructuring. To be able to do that, we created a
@ -1553,6 +1559,7 @@ impl<'s> Parser<'s> {
self.skip();
}
#[track_caller]
fn eat_and_get(&mut self) -> &mut SyntaxNode {
let offset = self.nodes.len();
self.save();
@ -1622,6 +1629,7 @@ impl<'s> Parser<'s> {
m.0 > 0 && self.nodes[m.0 - 1].kind().is_error()
}
#[track_caller]
fn post_process(&mut self, m: Marker) -> impl Iterator<Item = &mut SyntaxNode> {
self.nodes[m.0..]
.iter_mut()
@ -1762,6 +1770,7 @@ impl<'s> Parser<'s> {
/// Consume the given closing delimiter or produce an error for the matching
/// opening delimiter at `open`.
#[track_caller]
fn expect_closing_delimiter(&mut self, open: Marker, kind: SyntaxKind) {
if !self.eat_if(kind) {
self.nodes[open.0].convert_to_error("unclosed delimiter");

View File

@ -84,3 +84,15 @@
// Here, `a` is not duplicate, where it was previously identified as one.
#let f((a: b), (c,), a) = (a, b, c)
#test(f((a: 1), (2,), 3), (3, 1, 2))
---
// Ensure that we can't have non-atomic closures.
#let x = 1
#let c = [#(x) => (1, 2)]
#test(c.children.last(), [(1, 2)]))
---
// Ensure that we can't have non-atomic destructuring.
#let x = 1
#let c = [#() = ()]
#test(c.children.last(), [()])