parser: reject extends below top-level, too

This commit is contained in:
René Kijewski 2025-08-25 01:21:09 +02:00
parent b744cab2be
commit 52c4dfd8c3
2 changed files with 27 additions and 22 deletions

View File

@ -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<Box<Self>>> {
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<Box<Self>>> {
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<Self>> {
fn one(i: &mut InputStream<'a, 'l>, allow_extends: bool) -> ParseResult<'a, Box<Self>> {
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)
}

View File

@ -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
--> <source attribute>:3:2
" extends \"bla.txt\" %}\n{% endblock %}\n"
--> tests/ui/blocks_below_top_level.rs:4:21
|