From ac1da8f4b57290a67240973a7d6172cfbf5680a8 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 7 Feb 2025 15:20:56 -0300 Subject: [PATCH] fix: avoid stack overflow on many comments in a row (#7325) --- compiler/noirc_frontend/src/lexer/lexer.rs | 26 +++++++++++++++------- compiler/noirc_frontend/src/tests.rs | 6 +++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index 771af3daba0..ef5706b4d49 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -103,11 +103,28 @@ impl<'a> Lexer<'a> { } fn next_token(&mut self) -> SpannedTokenResult { + if !self.skip_comments { + return self.next_token_without_checking_comments(); + } + + // Read tokens and skip comments. This is done like this to avoid recursion + // and hitting stack overflow when there are many comments in a row. + loop { + let token = self.next_token_without_checking_comments()?; + if matches!(token.token(), Token::LineComment(_, None) | Token::BlockComment(_, None)) { + continue; + } + return Ok(token); + } + } + + /// Reads the next token, which might be a comment token (these aren't skipped in this method) + fn next_token_without_checking_comments(&mut self) -> SpannedTokenResult { match self.next_char() { Some(x) if Self::is_code_whitespace(x) => { let spanned = self.eat_whitespace(x); if self.skip_whitespaces { - self.next_token() + self.next_token_without_checking_comments() } else { Ok(spanned) } @@ -755,10 +772,6 @@ impl<'a> Lexer<'a> { return Err(LexerErrorKind::NonAsciiComment { span }); } - if doc_style.is_none() && self.skip_comments { - return self.next_token(); - } - Ok(Token::LineComment(comment, doc_style).into_span(start, self.position)) } @@ -804,9 +817,6 @@ impl<'a> Lexer<'a> { return Err(LexerErrorKind::NonAsciiComment { span }); } - if doc_style.is_none() && self.skip_comments { - return self.next_token(); - } Ok(Token::BlockComment(content, doc_style).into_span(start, self.position)) } else { let span = Span::inclusive(start, self.position); diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index cda6c267ec7..40e9f778f07 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -4364,3 +4364,9 @@ fn errors_on_if_without_else_type_mismatch() { }; assert!(matches!(**err, TypeCheckError::TypeMismatch { .. })); } + +#[test] +fn does_not_stack_overflow_on_many_comments_in_a_row() { + let src = "//\n".repeat(10_000); + assert_no_errors(&src); +}