diff --git a/src/compile_error.rs b/src/compile_error.rs index ba8d09e..b4def32 100644 --- a/src/compile_error.rs +++ b/src/compile_error.rs @@ -198,6 +198,9 @@ impl Display for CompileError<'_> { parameter )?; } + ParsingRecursionDepthExceeded => { + write!(f, "Parsing recursion depth exceeded")?; + } RequiredParameterFollowsDefaultParameter { parameter } => { write!( f, diff --git a/src/compile_error_kind.rs b/src/compile_error_kind.rs index 062659b..a425e24 100644 --- a/src/compile_error_kind.rs +++ b/src/compile_error_kind.rs @@ -74,6 +74,7 @@ pub(crate) enum CompileErrorKind<'src> { ParameterShadowsVariable { parameter: &'src str, }, + ParsingRecursionDepthExceeded, RequiredParameterFollowsDefaultParameter { parameter: &'src str, }, diff --git a/src/parser.rs b/src/parser.rs index 9f77de4..fd42452 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -32,6 +32,8 @@ pub(crate) struct Parser<'tokens, 'src> { next: usize, /// Current expected tokens expected: BTreeSet, + /// Current recursion depth + depth: u8, } impl<'tokens, 'src> Parser<'tokens, 'src> { @@ -46,6 +48,7 @@ impl<'tokens, 'src> Parser<'tokens, 'src> { next: 0, expected: BTreeSet::new(), tokens, + depth: 0, } } @@ -391,19 +394,31 @@ impl<'tokens, 'src> Parser<'tokens, 'src> { /// Parse an expression, e.g. `1 + 2` fn parse_expression(&mut self) -> CompileResult<'src, Expression<'src>> { - if self.accepted_keyword(Keyword::If)? { - self.parse_conditional() + if self.depth == if cfg!(windows) { 64 } else { 255 } { + return Err(CompileError { + token: self.next()?, + kind: CompileErrorKind::ParsingRecursionDepthExceeded, + }); + } + + self.depth += 1; + + let expression = if self.accepted_keyword(Keyword::If)? { + self.parse_conditional()? } else { let value = self.parse_value()?; if self.accepted(Plus)? { let lhs = Box::new(value); let rhs = Box::new(self.parse_expression()?); - Ok(Expression::Concatenation { lhs, rhs }) + Expression::Concatenation { lhs, rhs } } else { - Ok(value) + value } - } + }; + + self.depth -= 1; + Ok(expression) } /// Parse a conditional, e.g. `if a == b { "foo" } else { "bar" }` diff --git a/tests/lib.rs b/tests/lib.rs index 0827bba..671450b 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -62,6 +62,7 @@ mod positional_arguments; mod quiet; mod quote; mod readme; +mod recursion_limit; mod regexes; mod run; mod search; diff --git a/tests/recursion_limit.rs b/tests/recursion_limit.rs new file mode 100644 index 0000000..40782c3 --- /dev/null +++ b/tests/recursion_limit.rs @@ -0,0 +1,30 @@ +use super::*; + +#[test] +fn bugfix() { + let mut justfile = String::from("foo: (x "); + for _ in 0..500 { + justfile.push('('); + } + Test::new() + .justfile(&justfile) + .stderr(RECURSION_LIMIT_REACHED) + .status(EXIT_FAILURE) + .run(); +} + +#[cfg(not(windows))] +const RECURSION_LIMIT_REACHED: &str = " +error: Parsing recursion depth exceeded + | +1 | foo: (x (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + | ^ +"; + +#[cfg(windows)] +const RECURSION_LIMIT_REACHED: &str = " +error: Parsing recursion depth exceeded + | +1 | foo: (x (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + | ^ +";