Skip to content

Commit

Permalink
feat: recover from '=' instead of ':' in struct constructor/pattern (#…
Browse files Browse the repository at this point in the history
…6236)

# Description

## Problem

Resolves #6215

## Summary

If we get '=' instead of ':' we error but continue parsing the field
value.

## Additional Context

None.

## Documentation

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
asterite authored Oct 8, 2024
1 parent c4273a0 commit 9a12f31
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 1 deletion.
34 changes: 34 additions & 0 deletions compiler/noirc_frontend/src/parser/parser/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,12 @@ impl<'a> Parser<'a> {
Some(if self.eat_colon() {
let expression = self.parse_expression_or_error();
(ident, expression)
} else if self.at(Token::Assign) {
// If we find '=' instead of ':', assume the user meant ':`, error and continue
self.expected_token(Token::Colon);
self.bump();
let expression = self.parse_expression_or_error();
(ident, expression)
} else {
(ident.clone(), ident.into())
})
Expand Down Expand Up @@ -1337,6 +1343,34 @@ mod tests {
assert_eq!(expr.to_string(), "2");
}

#[test]
fn parses_constructor_with_fields_recovers_if_assign_instead_of_colon() {
let src = "
Foo { x = 1, y }
^
";
let (src, span) = get_source_with_error_span(src);
let mut parser = Parser::for_str(&src);
let expr = parser.parse_expression_or_error();

let error = get_single_error(&parser.errors, span);
assert_eq!(error.to_string(), "Expected a : but found =");

let ExpressionKind::Constructor(mut constructor) = expr.kind else {
panic!("Expected constructor");
};
assert_eq!(constructor.typ.to_string(), "Foo");
assert_eq!(constructor.fields.len(), 2);

let (name, expr) = constructor.fields.remove(0);
assert_eq!(name.to_string(), "x");
assert_eq!(expr.to_string(), "1");

let (name, expr) = constructor.fields.remove(0);
assert_eq!(name.to_string(), "y");
assert_eq!(expr.to_string(), "y");
}

#[test]
fn parses_parses_if_true() {
let src = "if true { 1 }";
Expand Down
36 changes: 35 additions & 1 deletion compiler/noirc_frontend/src/parser/parser/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@ impl<'a> Parser<'a> {

Some(if self.eat_colon() {
(ident, self.parse_pattern_or_error())
} else if self.at(Token::Assign) {
// If we find '=' instead of ':', assume the user meant ':`, error and continue
self.expected_token(Token::Colon);
self.bump();
(ident, self.parse_pattern_or_error())
} else {
(ident.clone(), Pattern::Identifier(ident))
})
Expand Down Expand Up @@ -252,7 +257,8 @@ mod tests {
ast::Pattern,
parser::{
parser::tests::{
expect_no_errors, get_single_error_reason, get_source_with_error_span,
expect_no_errors, get_single_error, get_single_error_reason,
get_source_with_error_span,
},
Parser, ParserErrorReason,
},
Expand Down Expand Up @@ -342,6 +348,34 @@ mod tests {
assert_eq!(pattern.to_string(), "y");
}

#[test]
fn parses_struct_pattern_recovers_if_assign_instead_of_colon() {
let src = "
foo::Bar { x = one, y }
^
";
let (src, span) = get_source_with_error_span(src);
let mut parser = Parser::for_str(&src);
let pattern = parser.parse_pattern_or_error();

let error = get_single_error(&parser.errors, span);
assert_eq!(error.to_string(), "Expected a : but found =");

let Pattern::Struct(path, mut patterns, _) = pattern else {
panic!("Expected a struct pattern")
};
assert_eq!(path.to_string(), "foo::Bar");
assert_eq!(patterns.len(), 2);

let (ident, pattern) = patterns.remove(0);
assert_eq!(ident.to_string(), "x");
assert_eq!(pattern.to_string(), "one");

let (ident, pattern) = patterns.remove(0);
assert_eq!(ident.to_string(), "y");
assert_eq!(pattern.to_string(), "y");
}

#[test]
fn parses_unclosed_struct_pattern() {
let src = "foo::Bar { x";
Expand Down

0 comments on commit 9a12f31

Please sign in to comment.