mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-27 13:00:57 +00:00
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:
parent
802d980a47
commit
4b8bd45844
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
4
testing/templates/fuzzed-recursion-mul-deref.txt
Normal file
4
testing/templates/fuzzed-recursion-mul-deref.txt
Normal file
File diff suppressed because one or more lines are too long
@ -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 = "
|
||||
|
@ -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")]
|
||||
|
9
testing/tests/ui/fuzzed_recursion_depth_mul_deref.rs
Normal file
9
testing/tests/ui/fuzzed_recursion_depth_mul_deref.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use rinja::Template;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "fuzzed-recursion-mul-deref.txt")]
|
||||
struct Filtered {
|
||||
s: &'static str,
|
||||
}
|
||||
|
||||
fn main() {}
|
7
testing/tests/ui/fuzzed_recursion_depth_mul_deref.stderr
Normal file
7
testing/tests/ui/fuzzed_recursion_depth_mul_deref.stderr
Normal 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")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
Loading…
x
Reference in New Issue
Block a user