parser: use drop guards to track nesting level

Not for all operations the nesting level was incremented when needed
and/or the un-incremented nesting level was used in subfunction calls.
Binary operators such as `*` did not properly increment the nesting
level.

This PR changes `Level` in such a way that it can be used to keep track
of the nesting level when used in a loop. It is now impossible to
accidentally refer to an old nesting level value.

Resolves <https://issues.oss-fuzz.com/issues/385256115>.
This commit is contained in:
René Kijewski 2024-12-25 11:50:20 +01:00
parent 802d980a47
commit 4b8bd45844
10 changed files with 229 additions and 164 deletions

View File

@ -9,7 +9,8 @@ extend-exclude = [
"fuzzing/fuzz/corpus/",
"target/",
"rinja_parser/tests/*.txt",
# fillter texts
"testing/templates/fuzzed-*",
# filler texts
"rinja/benches/strings.inc",
# too many false positives
"testing/tests/gen_ws_tests.py",

View File

@ -18,16 +18,18 @@ use crate::{
macro_rules! expr_prec_layer {
( $name:ident, $inner:ident, $op:expr ) => {
fn $name(i: &mut &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
let level = level.nest(i)?;
fn $name(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let mut level_guard = level.guard();
let start = *i;
let left = Self::$inner(i, level)?;
let right = repeat(0.., (ws($op), |i: &mut _| Self::$inner(i, level)))
.map(|v: Vec<_>| v)
.parse_next(i)?;
Ok(right.into_iter().fold(left, |left, (op, right)| {
WithSpan::new(Self::BinOp(op, Box::new(left), Box::new(right)), start)
}))
let mut expr = Self::$inner(i, level)?;
let mut i_before = *i;
let mut right = opt((ws($op), |i: &mut _| Self::$inner(i, level)));
while let Some((op, right)) = right.parse_next(i)? {
level_guard.nest(i_before)?;
i_before = *i;
expr = WithSpan::new(Self::BinOp(op, Box::new(expr), Box::new(right)), start);
}
Ok(expr)
}
};
}
@ -136,10 +138,10 @@ pub enum Expr<'a> {
impl<'a> Expr<'a> {
pub(super) fn arguments(
i: &mut &'a str,
level: Level,
level: Level<'_>,
is_template_macro: bool,
) -> ParseResult<'a, Vec<WithSpan<'a, Self>>> {
let level = level.nest(i)?;
let _level_guard = level.nest(i)?;
let mut named_arguments = HashSet::new();
let start = *i;
@ -186,7 +188,7 @@ impl<'a> Expr<'a> {
fn named_argument(
i: &mut &'a str,
level: Level,
level: Level<'_>,
named_arguments: &mut HashSet<&'a str>,
start: &'a str,
is_template_macro: bool,
@ -197,7 +199,6 @@ impl<'a> Expr<'a> {
return fail.parse_next(i);
}
let level = level.nest(i)?;
let (argument, _, value) = (identifier, ws('='), move |i: &mut _| {
Self::parse(i, level, false)
})
@ -217,10 +218,10 @@ impl<'a> Expr<'a> {
pub(super) fn parse(
i: &mut &'a str,
level: Level,
level: Level<'_>,
allow_underscore: bool,
) -> ParseResult<'a, WithSpan<'a, Self>> {
let level = level.nest(i)?;
let _level_guard = level.nest(i)?;
let start = Span::from(*i);
let range_right = move |i: &mut _| {
(
@ -257,10 +258,10 @@ impl<'a> Expr<'a> {
expr_prec_layer!(shifts, addsub, alt((">>", "<<")));
expr_prec_layer!(addsub, concat, alt(("+", "-")));
fn concat(i: &mut &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
fn concat(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn concat_expr<'a>(
i: &mut &'a str,
level: Level,
level: Level<'_>,
) -> ParseResult<'a, Option<WithSpan<'a, Expr<'a>>>> {
let ws1 = |i: &mut _| opt(skip_ws1).parse_next(i);
@ -295,7 +296,7 @@ impl<'a> Expr<'a> {
expr_prec_layer!(muldivmod, is_as, alt(("*", "/", "%")));
fn is_as(i: &mut &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
fn is_as(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
let lhs = Self::filtered(i, level)?;
let before_keyword = *i;
@ -358,10 +359,13 @@ impl<'a> Expr<'a> {
Ok(WithSpan::new(ctor(var_name), start))
}
fn filtered(i: &mut &'a str, mut level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
fn filtered(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let mut level_guard = level.guard();
let start = *i;
let mut res = Self::prefix(i, level)?;
while let Some((name, args)) = opt(|i: &mut _| filter(i, &mut level)).parse_next(i)? {
while let Some((name, args)) = opt(|i: &mut _| filter(i, level)).parse_next(i)? {
level_guard.nest(i)?;
let mut arguments = args.unwrap_or_else(|| Vec::with_capacity(1));
arguments.insert(0, res);
@ -370,28 +374,30 @@ impl<'a> Expr<'a> {
Ok(res)
}
fn prefix(i: &mut &'a str, mut level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
let nested = level.nest(i)?;
fn prefix(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
let (ops, mut expr) = (
repeat(0.., ws(alt(("!", "-", "*", "&")))).map(|v: Vec<_>| v),
|i: &mut _| Suffix::parse(i, nested),
)
.parse_next(i)?;
// This is a rare place where we create recursion in the parsed AST
// without recursing the parser call stack. However, this can lead
// to stack overflows in drop glue when the AST is very deep.
let mut level_guard = level.guard();
let mut ops = vec![];
let mut i_before = *i;
while let Some(op) = opt(ws(alt(("!", "-", "*", "&")))).parse_next(i)? {
level_guard.nest(i_before)?;
ops.push(op);
i_before = *i;
}
let mut expr = Suffix::parse(i, level)?;
for op in ops.iter().rev() {
// This is a rare place where we create recursion in the parsed AST
// without recursing the parser call stack. However, this can lead
// to stack overflows in drop glue when the AST is very deep.
level = level.nest(i)?;
expr = WithSpan::new(Self::Unary(op, Box::new(expr)), start);
}
Ok(expr)
}
fn single(i: &mut &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
let level = level.nest(i)?;
fn single(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
alt((
Self::num,
Self::str,
@ -403,8 +409,7 @@ impl<'a> Expr<'a> {
.parse_next(i)
}
fn group(i: &mut &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
let level = level.nest(i)?;
fn group(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
let expr = preceded(ws('('), opt(|i: &mut _| Self::parse(i, level, true))).parse_next(i)?;
let Some(expr) = expr else {
@ -434,9 +439,8 @@ impl<'a> Expr<'a> {
Ok(WithSpan::new(Self::Tuple(exprs), start))
}
fn array(i: &mut &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
fn array(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
let level = level.nest(i)?;
let array = preceded(
ws('['),
cut_err(terminated(
@ -556,32 +560,36 @@ enum Suffix<'a> {
}
impl<'a> Suffix<'a> {
fn parse(i: &mut &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Expr<'a>>> {
let level = level.nest(i)?;
fn parse(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Expr<'a>>> {
let mut level_guard = level.guard();
let mut expr = Expr::single(i, level)?;
let mut right = opt(alt((
Self::attr,
|i: &mut _| Self::index(i, level),
|i: &mut _| Self::call(i, level),
Self::r#try,
Self::r#macro,
)));
loop {
let before_suffix = *i;
let suffix = opt(alt((
Self::attr,
|i: &mut _| Self::index(i, level),
|i: &mut _| Self::call(i, level),
Self::r#try,
Self::r#macro,
)))
.parse_next(i)?;
let suffix = right.parse_next(i)?;
let Some(suffix) = suffix else {
break;
};
level_guard.nest(before_suffix)?;
match suffix {
Some(Self::Attr(attr)) => {
Self::Attr(attr) => {
expr = WithSpan::new(Expr::Attr(expr.into(), attr), before_suffix)
}
Some(Self::Index(index)) => {
Self::Index(index) => {
expr = WithSpan::new(Expr::Index(expr.into(), index.into()), before_suffix);
}
Some(Self::Call(args)) => {
Self::Call(args) => {
expr = WithSpan::new(Expr::Call(expr.into(), args), before_suffix)
}
Some(Self::Try) => expr = WithSpan::new(Expr::Try(expr.into()), before_suffix),
Some(Self::MacroCall(args)) => match expr.inner {
Self::Try => expr = WithSpan::new(Expr::Try(expr.into()), before_suffix),
Self::MacroCall(args) => match expr.inner {
Expr::Path(path) => {
expr = WithSpan::new(Expr::RustMacro(path, args), before_suffix)
}
@ -596,7 +604,6 @@ impl<'a> Suffix<'a> {
.cut());
}
},
None => break,
}
}
Ok(expr)
@ -665,8 +672,7 @@ impl<'a> Suffix<'a> {
.parse_next(i)
}
fn index(i: &mut &'a str, level: Level) -> ParseResult<'a, Self> {
let level = level.nest(i)?;
fn index(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> {
preceded(
ws('['),
cut_err(terminated(
@ -678,8 +684,7 @@ impl<'a> Suffix<'a> {
.parse_next(i)
}
fn call(i: &mut &'a str, level: Level) -> ParseResult<'a, Self> {
let level = level.nest(i)?;
fn call(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> {
(move |i: &mut _| Expr::arguments(i, level, false))
.map(Self::Call)
.parse_next(i)

View File

@ -111,7 +111,13 @@ impl<'a> Ast<'a> {
syntax: &Syntax<'_>,
) -> Result<Self, ParseError> {
let start = src;
match Node::parse_template(&mut src, &State::new(syntax)) {
let level = Cell::new(Level::MAX_DEPTH);
let state = State {
syntax,
loop_depth: Cell::new(0),
level: Level(&level),
};
match Node::parse_template(&mut src, &state) {
Ok(nodes) if src.is_empty() => Ok(Self { nodes }),
Ok(_) | Err(winnow::error::ErrMode::Incomplete(_)) => unreachable!(),
Err(
@ -728,34 +734,13 @@ fn path_or_identifier<'a>(i: &mut &'a str) -> ParseResult<'a, PathOrIdentifier<'
}
}
struct State<'a> {
syntax: &'a Syntax<'a>,
struct State<'a, 'l> {
syntax: &'l Syntax<'a>,
loop_depth: Cell<usize>,
level: Cell<Level>,
level: Level<'l>,
}
impl<'a> State<'a> {
fn new(syntax: &'a Syntax<'a>) -> State<'a> {
State {
syntax,
loop_depth: Cell::new(0),
level: Cell::new(Level::default()),
}
}
fn nest<'b, T, F: Parser<&'b str, T, ErrorContext<'b>>>(
&self,
i: &mut &'b str,
mut callback: F,
) -> ParseResult<'b, T> {
let prev_level = self.level.get();
let level = prev_level.nest(i)?;
self.level.set(level);
let ret = callback.parse_next(i);
self.level.set(prev_level);
ret
}
impl State<'_, '_> {
fn tag_block_start<'i>(&self, i: &mut &'i str) -> ParseResult<'i, ()> {
self.syntax.block_start.value(()).parse_next(i)
}
@ -960,35 +945,87 @@ impl<'a> SyntaxBuilder<'a> {
}
}
#[derive(Clone, Copy, Default)]
pub(crate) struct Level(u8);
/// The nesting level of nodes and expressions.
///
/// The level counts down from [`Level::MAX_DEPTH`] to 0. Once the value would reach below 0,
/// [`Level::nest()`] / [`LevelGuard::nest()`] will return an error. The same [`Level`] instance is
/// shared across all usages in a [`Parsed::new()`] / [`Ast::from_str()`] call, using a reference
/// to an interior mutable counter.
#[derive(Debug, Clone, Copy)]
struct Level<'l>(&'l Cell<usize>);
impl Level {
fn nest(self, i: &str) -> ParseResult<'_, Level> {
if self.0 >= Self::MAX_DEPTH {
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
"your template code is too deeply nested, or last expression is too complex",
i,
)));
impl Level<'_> {
const MAX_DEPTH: usize = 128;
/// Acquire a [`LevelGuard`] without decrementing the counter, to be used with loops.
fn guard(&self) -> LevelGuard<'_> {
LevelGuard {
level: *self,
count: 0,
}
Ok(Level(self.0 + 1))
}
const MAX_DEPTH: u8 = 128;
/// Decrement the remaining level counter, and return a [`LevelGuard`] that increments it again
/// when it's dropped.
fn nest<'a>(&self, i: &'a str) -> ParseResult<'a, LevelGuard<'_>> {
if let Some(new_level) = self.0.get().checked_sub(1) {
self.0.set(new_level);
Ok(LevelGuard {
level: *self,
count: 1,
})
} else {
Err(Self::_fail(i))
}
}
#[cold]
#[inline(never)]
fn _fail(i: &str) -> ParseErr<'_> {
winnow::error::ErrMode::Cut(ErrorContext::new(
"your template code is too deeply nested, or the last expression is too complex",
i,
))
}
}
/// Used to keep track how often [`LevelGuard::nest()`] was called and to re-increment the
/// remaining level counter when it is dropped / falls out of scope.
#[must_use]
struct LevelGuard<'l> {
level: Level<'l>,
count: usize,
}
impl Drop for LevelGuard<'_> {
fn drop(&mut self) {
self.level.0.set(self.level.0.get() + self.count);
}
}
impl LevelGuard<'_> {
/// Used to decrement the level multiple times, e.g. for every iteration of a loop.
fn nest<'a>(&mut self, i: &'a str) -> ParseResult<'a, ()> {
if let Some(new_level) = self.level.0.get().checked_sub(1) {
self.level.0.set(new_level);
self.count += 1;
Ok(())
} else {
Err(Level::_fail(i))
}
}
}
fn filter<'a>(
i: &mut &'a str,
level: &mut Level,
level: Level<'_>,
) -> ParseResult<'a, (&'a str, Option<Vec<WithSpan<'a, Expr<'a>>>>)> {
let start = *i;
let _ = ws(('|', not('|'))).parse_next(i)?;
ws(('|', not('|'))).parse_next(i)?;
*level = level.nest(start)?;
let _level_guard = level.nest(i)?;
cut_err((
ws(identifier),
opt(|i: &mut _| Expr::arguments(i, *level, false)),
opt(|i: &mut _| Expr::arguments(i, level, false)),
))
.parse_next(i)
}

View File

@ -37,7 +37,7 @@ pub enum Node<'a> {
}
impl<'a> Node<'a> {
pub(super) fn parse_template(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Vec<Self>> {
pub(super) fn parse_template(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Vec<Self>> {
let start = *i;
let result = match (|i: &mut _| Self::many(i, s)).parse_next(i) {
Ok(result) => result,
@ -68,7 +68,7 @@ impl<'a> Node<'a> {
Ok(result)
}
fn many(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Vec<Self>> {
fn many(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Vec<Self>> {
repeat(
0..,
alt((
@ -82,7 +82,7 @@ impl<'a> Node<'a> {
.parse_next(i)
}
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
let mut start = *i;
let tag = preceded(
|i: &mut _| s.tag_block_start(i),
@ -108,8 +108,8 @@ impl<'a> Node<'a> {
_ => return fail.parse_next(&mut start),
};
let node = s.nest(i, |i: &mut _| func(i, s))?;
let _level_guard = s.level.nest(i)?;
let node = func(i, s)?;
let closed = cut_node(
None,
alt((
@ -124,7 +124,7 @@ impl<'a> Node<'a> {
}
}
fn r#break(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
fn r#break(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
let mut p = (
opt(Whitespace::parse),
ws(keyword("break")),
@ -142,7 +142,7 @@ impl<'a> Node<'a> {
Ok(Self::Break(WithSpan::new(Ws(pws, nws), start)))
}
fn r#continue(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
fn r#continue(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
let mut p = (
opt(Whitespace::parse),
ws(keyword("continue")),
@ -160,15 +160,16 @@ impl<'a> Node<'a> {
Ok(Self::Continue(WithSpan::new(Ws(pws, nws), start)))
}
fn expr(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
fn expr(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
let start = *i;
let level = s.level;
let (pws, expr) = preceded(
|i: &mut _| s.tag_expr_start(i),
cut_node(
None,
(
opt(Whitespace::parse),
ws(|i: &mut _| Expr::parse(i, s.level.get(), false)),
ws(|i: &mut _| Expr::parse(i, level, false)),
),
),
)
@ -237,7 +238,7 @@ fn cut_node<'a, O>(
}
}
fn unexpected_tag<'a>(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, ()> {
fn unexpected_tag<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, ()> {
(
|i: &mut _| s.tag_block_start(i),
opt(Whitespace::parse),
@ -270,7 +271,7 @@ pub struct When<'a> {
}
impl<'a> When<'a> {
fn r#else(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn r#else(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let mut p = (
|i: &mut _| s.tag_block_start(i),
opt(Whitespace::parse),
@ -298,7 +299,7 @@ impl<'a> When<'a> {
}
#[allow(clippy::self_named_constructors)]
fn when(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn when(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
let endwhen = ws((
delimited(
@ -367,7 +368,7 @@ pub struct Cond<'a> {
}
impl<'a> Cond<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
let (_, pws, cond, nws, _, nodes) = (
|i: &mut _| s.tag_block_start(i),
@ -405,7 +406,7 @@ pub struct CondTest<'a> {
}
impl<'a> CondTest<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
preceded(
ws(keyword("if")),
cut_node(Some("if"), |i: &mut _| Self::parse_cond(i, s)),
@ -413,7 +414,7 @@ impl<'a> CondTest<'a> {
.parse_next(i)
}
fn parse_cond(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
fn parse_cond(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
let (target, expr) = (
opt(delimited(
ws(alt((keyword("let"), keyword("set")))),
@ -422,7 +423,7 @@ impl<'a> CondTest<'a> {
)),
ws(|i: &mut _| {
let start = *i;
let mut expr = Expr::parse(i, s.level.get(), false)?;
let mut expr = Expr::parse(i, s.level, false)?;
if let Expr::BinOp(_, _, ref mut right) = expr.inner {
if matches!(right.inner, Expr::Var("set" | "let")) {
*i = right.span.as_suffix_of(start).unwrap();
@ -485,7 +486,7 @@ impl FromStr for Whitespace {
fn check_block_start<'a>(
i: &mut &'a str,
start: &'a str,
s: &State<'_>,
s: &State<'_, '_>,
node: &str,
expected: &str,
) -> ParseResult<'a, ()> {
@ -511,8 +512,8 @@ pub struct Loop<'a> {
}
impl<'a> Loop<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn content<'a>(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Vec<Node<'a>>> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn content<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Vec<Node<'a>>> {
s.enter_loop();
let result = (|i: &mut _| Node::many(i, s)).parse_next(i);
s.leave_loop();
@ -524,7 +525,7 @@ impl<'a> Loop<'a> {
ws(keyword("if")),
cut_node(
Some("for-if"),
ws(|i: &mut _| Expr::parse(i, s.level.get(), true)),
ws(|i: &mut _| Expr::parse(i, s.level, true)),
),
);
@ -580,7 +581,7 @@ impl<'a> Loop<'a> {
cut_node(
Some("for"),
(
ws(|i: &mut _| Expr::parse(i, s.level.get(), true)),
ws(|i: &mut _| Expr::parse(i, s.level, true)),
opt(if_cond),
opt(Whitespace::parse),
|i: &mut _| s.tag_block_end(i),
@ -633,8 +634,8 @@ fn check_duplicated_name<'a>(
}
impl<'a> Macro<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let level = s.level.get();
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let level = s.level;
#[allow(clippy::type_complexity)]
let parameters = |i: &mut _| -> ParseResult<
'_,
@ -762,8 +763,7 @@ pub struct FilterBlock<'a> {
}
impl<'a> FilterBlock<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let mut level = s.level.get();
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start_s = *i;
let mut start = (
opt(Whitespace::parse),
@ -772,10 +772,10 @@ impl<'a> FilterBlock<'a> {
Some("filter"),
(
ws(identifier),
opt(|i: &mut _| Expr::arguments(i, s.level.get(), false)),
opt(|i: &mut _| Expr::arguments(i, s.level, false)),
repeat(0.., |i: &mut _| {
let start = *i;
filter(i, &mut level).map(|(name, params)| (name, params, start))
filter(i, s.level).map(|(name, params)| (name, params, start))
})
.map(|v: Vec<_>| v),
ws(empty),
@ -875,7 +875,7 @@ pub struct Call<'a> {
}
impl<'a> Call<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
let mut p = (
opt(Whitespace::parse),
@ -885,9 +885,7 @@ impl<'a> Call<'a> {
(
opt((ws(identifier), ws("::"))),
ws(identifier),
opt(ws(|nested: &mut _| {
Expr::arguments(nested, s.level.get(), true)
})),
opt(ws(|nested: &mut _| Expr::arguments(nested, s.level, true))),
opt(Whitespace::parse),
),
),
@ -916,7 +914,7 @@ pub struct Match<'a> {
}
impl<'a> Match<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
let mut p = (
opt(Whitespace::parse),
@ -924,7 +922,7 @@ impl<'a> Match<'a> {
cut_node(
Some("match"),
(
ws(|i: &mut _| Expr::parse(i, s.level.get(), false)),
ws(|i: &mut _| Expr::parse(i, s.level, false)),
opt(Whitespace::parse),
|i: &mut _| s.tag_block_end(i),
cut_node(
@ -988,7 +986,7 @@ pub struct BlockDef<'a> {
}
impl<'a> BlockDef<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start_s = *i;
let mut start = (
opt(Whitespace::parse),
@ -1068,7 +1066,7 @@ pub struct Lit<'a> {
}
impl<'a> Lit<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
not(eof).parse_next(i)?;
@ -1115,7 +1113,7 @@ pub struct Raw<'a> {
}
impl<'a> Raw<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
let endraw = (
|i: &mut _| s.tag_block_start(i),
@ -1155,7 +1153,7 @@ pub struct Let<'a> {
}
impl<'a> Let<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
let mut p = (
opt(Whitespace::parse),
@ -1166,7 +1164,7 @@ impl<'a> Let<'a> {
ws(|i: &mut _| Target::parse(i, s)),
opt(preceded(
ws('='),
ws(|i: &mut _| Expr::parse(i, s.level.get(), false)),
ws(|i: &mut _| Expr::parse(i, s.level, false)),
)),
opt(Whitespace::parse),
),
@ -1217,7 +1215,7 @@ pub struct If<'a> {
}
impl<'a> If<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
let start = *i;
let mut p = (
opt(Whitespace::parse),
@ -1323,14 +1321,14 @@ pub struct Comment<'a> {
}
impl<'a> Comment<'a> {
fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
#[derive(Debug, Clone, Copy)]
enum Tag {
Open,
Close,
}
fn tag<'a>(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Tag> {
fn tag<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Tag> {
alt((
(|i: &mut _| s.tag_comment_start(i)).value(Tag::Open),
(|i: &mut _| s.tag_comment_end(i)).value(Tag::Close),
@ -1338,7 +1336,7 @@ impl<'a> Comment<'a> {
.parse_next(i)
}
fn content<'a>(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a> {
fn content<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a> {
let mut depth = 0usize;
let start = *i;
loop {

View File

@ -27,22 +27,26 @@ pub enum Target<'a> {
impl<'a> Target<'a> {
/// Parses multiple targets with `or` separating them
pub(super) fn parse(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
separated(
1..,
|i: &mut _| s.nest(i, |i: &mut _| Self::parse_one(i, s)),
ws("or"),
)
.map(|v: Vec<_>| v)
.map(|mut opts| match opts.len() {
1 => opts.pop().unwrap(),
_ => Self::OrChain(opts),
})
.parse_next(i)
pub(super) fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
let _level_guard = s.level.nest(i)?;
let mut p = opt(preceded(ws(keyword("or")), |i: &mut _| {
Self::parse_one(i, s)
}));
let target = Self::parse_one(i, s)?;
let Some(snd_target) = p.parse_next(i)? else {
return Ok(target);
};
let mut targets = vec![target, snd_target];
while let Some(target) = p.parse_next(i)? {
targets.push(target);
}
Ok(Self::OrChain(targets))
}
/// Parses a single target without an `or`, unless it is wrapped in parentheses.
fn parse_one(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
fn parse_one(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
let mut opt_opening_paren = opt(ws('(')).map(|o| o.is_some());
let mut opt_opening_brace = opt(ws('{')).map(|o| o.is_some());
let mut opt_opening_bracket = opt(ws('[')).map(|o| o.is_some());
@ -130,11 +134,11 @@ impl<'a> Target<'a> {
.parse_next(i)
}
fn unnamed(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
fn unnamed(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
alt((Self::rest, |i: &mut _| Self::parse(i, s))).parse_next(i)
}
fn named(i: &mut &'a str, s: &State<'_>) -> ParseResult<'a, (&'a str, Self)> {
fn named(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, (&'a str, Self)> {
let start = *i;
let rest = opt(Self::rest.with_taken()).parse_next(i)?;
if let Some(rest) = rest {

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
error: your template code is too deeply nested, or last expression is too complex
--> <source attribute>:14:42
"%}{%if 1%}{%if 1%}{%if 1%}{%if 1%}{%if 1%}\n {%if 1%}{%if 1%}{%if 1%}{%if 1%}{"...
error: your template code is too deeply nested, or the last expression is too complex
--> <source attribute>:15:58
"%}{%if 1%}{%if 1%}{%if 1%}\n {%if 1%}{%if 1%}{%if 1%}{%if 1%}{%if 1%}{%if 1%}{"...
--> tests/ui/excessive_nesting.rs:5:14
|
5 | source = "

View File

@ -1,6 +1,6 @@
error: your template code is too deeply nested, or last expression is too complex
--> testing/templates/filter-recursion.html:1:255
"|A|AA|A|A|A|A|AA|A|A|A|A|AA|A|A|A|A|AA|A|A|A|A|AA|A|A|A|A|AA|A|A|A||A|A|AA|A|A|A"...
error: your template code is too deeply nested, or the last expression is too complex
--> testing/templates/filter-recursion.html:1:275
"|A|A|AA|A|A|A|A|AA|A|A|A|A|AA|A|A|A|A|AA|A|A|A||A|A|AA|A|A|A|A|AA|A|A|A|A|AA|A|A"...
--> tests/ui/filter-recursion.rs:4:19
|
4 | #[template(path = "filter-recursion.html")]

View File

@ -0,0 +1,9 @@
use rinja::Template;
#[derive(Template)]
#[template(path = "fuzzed-recursion-mul-deref.txt")]
struct Filtered {
s: &'static str,
}
fn main() {}

View File

@ -0,0 +1,7 @@
error: your template code is too deeply nested, or the last expression is too complex
--> testing/templates/fuzzed-recursion-mul-deref.txt:2:486
"!**y**false|yz***y**false|z**yz**s**fa*galse|iz**!**y**false|yz***y**f*!**y**fal"...
--> tests/ui/fuzzed_recursion_depth_mul_deref.rs:4:19
|
4 | #[template(path = "fuzzed-recursion-mul-deref.txt")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^