diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 49f3e14ea64..8e98adc0a7b 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -196,7 +196,10 @@ impl<'a> Parser<'a> { UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() }; (visibility, typ) } else { - (self.parse_visibility(), self.parse_type_or_error()) + ( + self.parse_visibility(), + self.parse_type_or_error_with_recovery(&[Token::Comma, Token::RightParen]), + ) }; Param { visibility, pattern, typ, location: self.location_since(start_location) } @@ -306,7 +309,10 @@ fn empty_body() -> BlockExpression { #[cfg(test)] mod tests { use crate::{ - ast::{ItemVisibility, NoirFunction, UnresolvedTypeData, Visibility}, + ast::{ + IntegerBitSize, ItemVisibility, NoirFunction, Signedness, UnresolvedTypeData, + Visibility, + }, parse_program_with_dummy_file, parser::{ parser::tests::{ @@ -520,4 +526,34 @@ mod tests { let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::MissingParametersForFunctionDefinition)); } + + #[test] + fn parse_function_with_keyword_before_type() { + let src = " + fn foo(x: mut i32, y: i64) {} + ^^^ + "; + let (src, span) = get_source_with_error_span(src); + let (mut module, errors) = parse_program_with_dummy_file(&src); + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected a type but found 'mut'"); + + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Function(noir_function) = item.kind else { + panic!("Expected function"); + }; + + let params = noir_function.parameters(); + assert_eq!(params.len(), 2); + + assert_eq!( + params[0].typ.typ, + UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo) + ); + assert_eq!( + params[1].typ.typ, + UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::SixtyFour) + ); + } } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 4bac19cfd38..14e7614f044 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -19,6 +19,26 @@ impl<'a> Parser<'a> { } } + /// Tries to parse a type. If the current token doesn't denote a type and it's not + /// one of `stop_tokens`, try to parse a type starting from the next token (and so on). + pub(crate) fn parse_type_or_error_with_recovery( + &mut self, + stop_tokens: &[Token], + ) -> UnresolvedType { + loop { + let typ = self.parse_type_or_error(); + if typ.typ != UnresolvedTypeData::Error { + return typ; + } + + if self.at_eof() || stop_tokens.contains(self.token.token()) { + return typ; + } + + self.bump(); + } + } + pub(crate) fn parse_type(&mut self) -> Option { let start_location = self.current_token_location; let typ = self.parse_unresolved_type_data()?;