Skip to content

Commit

Permalink
feat: better error message when keyword is found instead of type in p… (
Browse files Browse the repository at this point in the history
#7501)

Co-authored-by: Tom French <[email protected]>
  • Loading branch information
asterite and TomAFrench authored Feb 24, 2025
1 parent 4aba428 commit 43ca11a
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 2 deletions.
40 changes: 38 additions & 2 deletions compiler/noirc_frontend/src/parser/parser/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand Down Expand Up @@ -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::{
Expand Down Expand Up @@ -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)
);
}
}
20 changes: 20 additions & 0 deletions compiler/noirc_frontend/src/parser/parser/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<UnresolvedType> {
let start_location = self.current_token_location;
let typ = self.parse_unresolved_type_data()?;
Expand Down

1 comment on commit 43ca11a

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Test Suite Duration'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.20.

Benchmark suite Current: 43ca11a Previous: 4aba428 Ratio
noir-lang_noir_json_parser_ 11 s 9 s 1.22

This comment was automatically generated by workflow using github-action-benchmark.

CC: @TomAFrench

Please sign in to comment.