From 52c4dfd8c3f93502245296205298d86169693a6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Mon, 25 Aug 2025 01:21:09 +0200 Subject: [PATCH] parser: reject `extends` below top-level, too --- askama_parser/src/node.rs | 45 ++++++++++--------- .../tests/ui/blocks_below_top_level.stderr | 4 +- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/askama_parser/src/node.rs b/askama_parser/src/node.rs index 27d1797d..37ad661e 100644 --- a/askama_parser/src/node.rs +++ b/askama_parser/src/node.rs @@ -38,8 +38,27 @@ pub enum Node<'a> { impl<'a: 'l, 'l> Node<'a> { pub(super) fn parse_template(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Vec>> { - let mut p = parse_with_unexpected_fallback(Self::many, unexpected_tag); - let nodes = p.parse_next(i)?; + let mut nodes = vec![]; + let mut allow_extends = true; + while let Some(node) = parse_with_unexpected_fallback( + opt(move |i: &mut _| Self::one(i, allow_extends)), + unexpected_tag, + ) + .parse_next(i)? + { + if allow_extends { + match &*node { + // Since comments don't impact generated code, we allow them before `extends`. + Node::Comment(_) => {} + // If it only contains whitespace characters, it's fine too. + Node::Lit(lit) if lit.val.is_empty() => {} + // Everything else must not come before an `extends` block. + _ => allow_extends = false, + } + } + nodes.push(node); + } + if !i.is_empty() { opt(unexpected_tag).parse_next(i)?; return cut_error!( @@ -53,27 +72,13 @@ impl<'a: 'l, 'l> Node<'a> { } fn many(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Vec>> { - let mut nb_nodes = 0; - - repeat(0.., move |i: &mut _| Self::parse_nodes(i, &mut nb_nodes)) - .map(|v: Vec<_>| v) - .parse_next(i) + repeat(0.., |i: &mut _| Self::one(i, false)).parse_next(i) } - fn parse_nodes( - i: &mut InputStream<'a, 'l>, - nb_nodes: &mut usize, - ) -> ParseResult<'a, Box> { + fn one(i: &mut InputStream<'a, 'l>, allow_extends: bool) -> ParseResult<'a, Box> { let node = alt((Lit::parse, Comment::parse, Self::expr, Self::parse)).parse_next(i)?; - match *node { - Self::Extends(..) if *nb_nodes > 0 => { - return cut_error!("`extends` block must come first in a template", node.span()); - } - // Since comments don't impact generated code, we allow them before `extends`. - Self::Comment(..) => {} - // If it only contains whitespace characters, it's fine too. - Self::Lit(ref lit) if lit.val.trim().is_empty() => {} - _ => *nb_nodes += 1, + if !allow_extends && let Node::Extends(node) = &*node { + return cut_error!("`extends` block must come first in a template", node.span()); } Ok(node) } diff --git a/testing/tests/ui/blocks_below_top_level.stderr b/testing/tests/ui/blocks_below_top_level.stderr index ce1a084f..d7e932c3 100644 --- a/testing/tests/ui/blocks_below_top_level.stderr +++ b/testing/tests/ui/blocks_below_top_level.stderr @@ -1,5 +1,5 @@ -error: `extends` blocks are not allowed below top level - --> MyTemplate1.txt:3:2 +error: `extends` block must come first in a template + --> :3:2 " extends \"bla.txt\" %}\n{% endblock %}\n" --> tests/ui/blocks_below_top_level.rs:4:21 |