From 5199e528dc0cb391bfa88451a2802edc11f65a66 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 11 Mar 2025 09:37:37 -0300 Subject: [PATCH] fix: allow method call after block, if and match --- .../src/parser/parser/expression.rs | 19 +++++++++ .../src/parser/parser/function.rs | 42 ++++++++++++++++++- .../src/parser/parser/statement.rs | 37 +++++++++------- 3 files changed, 82 insertions(+), 16 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 672d9428d9a..4b51c6219ff 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -147,6 +147,25 @@ impl Parser<'_> { self.parse_index(atom, start_location) } + pub(super) fn parse_member_accesses_or_method_calls_after_expression( + &mut self, + mut atom: Expression, + start_location: Location, + ) -> Expression { + let mut parsed; + + loop { + (atom, parsed) = self.parse_member_access_or_method_call(atom, start_location); + if parsed { + continue; + } else { + break; + } + } + + atom + } + /// CallExpression = Atom CallArguments fn parse_call(&mut self, atom: Expression, start_location: Location) -> (Expression, bool) { if let Some(call_arguments) = self.parse_call_arguments() { diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index caf2cdeb1c3..9fb4154f649 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -324,8 +324,8 @@ fn empty_body() -> BlockExpression { mod tests { use crate::{ ast::{ - IntegerBitSize, ItemVisibility, NoirFunction, Signedness, UnresolvedTypeData, - Visibility, + ExpressionKind, IntegerBitSize, ItemVisibility, NoirFunction, Signedness, + StatementKind, UnresolvedTypeData, Visibility, }, parse_program_with_dummy_file, parser::{ @@ -570,4 +570,42 @@ mod tests { UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::SixtyFour) ); } + + #[test] + fn parses_block_followed_by_call() { + let src = "fn foo() { { 1 }.bar() }"; + let noir_function = parse_function_no_error(src); + let statements = &noir_function.def.body.statements; + assert_eq!(statements.len(), 1); + + let StatementKind::Expression(expr) = &statements[0].kind else { + panic!("Expected expression statement"); + }; + + let ExpressionKind::MethodCall(call) = &expr.kind else { + panic!("Expected method call expression"); + }; + + assert!(matches!(call.object.kind, ExpressionKind::Block(_))); + assert_eq!(call.method_name.to_string(), "bar"); + } + + #[test] + fn parses_if_followed_by_call() { + let src = "fn foo() { if 1 { 2 } else { 3 }.bar() }"; + let noir_function = parse_function_no_error(src); + let statements = &noir_function.def.body.statements; + assert_eq!(statements.len(), 1); + + let StatementKind::Expression(expr) = &statements[0].kind else { + panic!("Expected expression statement"); + }; + + let ExpressionKind::MethodCall(call) = &expr.kind else { + panic!("Expected method call expression"); + }; + + assert!(matches!(call.object.kind, ExpressionKind::If(_))); + assert_eq!(call.method_name.to_string(), "bar"); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 600ddec43c9..579ece91b99 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -146,21 +146,12 @@ impl Parser<'_> { return Some(StatementKind::While(while_)); } - if let Some(kind) = self.parse_if_expr() { - let location = self.location_since(start_location); - return Some(StatementKind::Expression(Expression { kind, location })); - } - - if let Some(kind) = self.parse_match_expr() { + if let Some(kind) = self.parse_block_like() { let location = self.location_since(start_location); - return Some(StatementKind::Expression(Expression { kind, location })); - } - - if let Some(block) = self.parse_block() { - return Some(StatementKind::Expression(Expression { - kind: ExpressionKind::Block(block), - location: self.location_since(start_location), - })); + let expression = Expression { kind, location }; + let expression = self + .parse_member_accesses_or_method_calls_after_expression(expression, start_location); + return Some(StatementKind::Expression(expression)); } if let Some(token) = self.eat_kind(TokenKind::InternedLValue) { @@ -214,6 +205,24 @@ impl Parser<'_> { Some(StatementKind::Expression(expression)) } + /// Parses an expression that looks like a block (ends with '}'): + /// `{ ... }`, `if { ... }` and `match { ... }`. + fn parse_block_like(&mut self) -> Option { + if let Some(kind) = self.parse_if_expr() { + return Some(kind); + } + + if let Some(kind) = self.parse_match_expr() { + return Some(kind); + } + + if let Some(block) = self.parse_block() { + return Some(ExpressionKind::Block(block)); + } + + None + } + fn next_is_op_assign(&mut self) -> Option { let start_location = self.current_token_location; let operator = if self.next_is(Token::Assign) {