diff --git a/rinja_derive/src/heritage.rs b/rinja_derive/src/heritage.rs index 0c60f0d4..e9f72342 100644 --- a/rinja_derive/src/heritage.rs +++ b/rinja_derive/src/heritage.rs @@ -76,26 +76,25 @@ impl Context<'_> { while let Some(nodes) = nested.pop() { for n in nodes { match n { - Node::Extends(e) if top => match extends { - Some(_) => { - return Err(CompileError::no_file_info("multiple extend blocks found")) + Node::Extends(e) => { + ensure_top(top, e, path, parsed, "extends")?; + if extends.is_some() { + return Err(CompileError::new( + "multiple extend blocks found", + Some(FileInfo::of(e, path, parsed)), + )); } - None => { - extends = Some(config.find_template(e.path, Some(path))?); - } - }, - Node::Macro(m) if top => { + extends = Some(config.find_template(e.path, Some(path))?); + } + Node::Macro(m) => { + ensure_top(top, m, path, parsed, "macro")?; macros.insert(m.name, &**m); } - Node::Import(import) if top => { + Node::Import(import) => { + ensure_top(top, import, path, parsed, "import")?; let path = config.find_template(import.path, Some(path))?; imports.insert(import.scope, path); } - Node::Extends(_) | Node::Macro(_) | Node::Import(_) if !top => { - return Err(CompileError::no_file_info( - "extends, macro or import blocks not allowed below top level", - )); - } Node::BlockDef(b) => { blocks.insert(b.name, &**b); nested.push(&b.nodes); @@ -132,16 +131,26 @@ impl Context<'_> { } pub(crate) fn generate_error(&self, msg: &str, node: &WithSpan<'_, T>) -> CompileError { - match self.path { - Some(path) => CompileError::new( - msg, - Some(FileInfo::new( - path, - Some(self.parsed.source()), - Some(node.span()), - )), - ), - None => CompileError::new(msg, None), - } + CompileError::new( + msg, + self.path.map(|path| FileInfo::of(node, path, self.parsed)), + ) + } +} + +fn ensure_top( + top: bool, + node: &WithSpan<'_, T>, + path: &Path, + parsed: &Parsed, + kind: &str, +) -> Result<(), CompileError> { + if top { + Ok(()) + } else { + Err(CompileError::new( + format!("`{kind}` blocks are not allowed below top level"), + Some(FileInfo::of(node, path, parsed)), + )) } } diff --git a/rinja_derive/src/lib.rs b/rinja_derive/src/lib.rs index e3d99204..d8df4886 100644 --- a/rinja_derive/src/lib.rs +++ b/rinja_derive/src/lib.rs @@ -16,7 +16,7 @@ use config::{read_config_file, Config}; use generator::{Generator, MapChain}; use heritage::{Context, Heritage}; use input::{Print, TemplateArgs, TemplateInput}; -use parser::{generate_error_info, strip_common, ErrorInfo, ParseError}; +use parser::{generate_error_info, strip_common, ErrorInfo, ParseError, Parsed, WithSpan}; use proc_macro2::{Span, TokenStream}; #[cfg(not(feature = "__standalone"))] @@ -138,7 +138,7 @@ struct CompileError { } impl CompileError { - fn new(msg: S, file_info: Option>) -> Self { + fn new(msg: S, file_info: Option>) -> Self { let msg = match file_info { Some(file_info) => format!("{msg}{file_info}"), None => msg.to_string(), @@ -178,23 +178,31 @@ impl From for CompileError { } } -struct FileInfo<'a, 'b, 'c> { +struct FileInfo<'a> { path: &'a Path, - source: Option<&'b str>, - node_source: Option<&'c str>, + source: Option<&'a str>, + node_source: Option<&'a str>, } -impl<'a, 'b, 'c> FileInfo<'a, 'b, 'c> { - fn new(path: &'a Path, source: Option<&'b str>, node_source: Option<&'c str>) -> Self { +impl<'a> FileInfo<'a> { + fn new(path: &'a Path, source: Option<&'a str>, node_source: Option<&'a str>) -> Self { Self { path, source, node_source, } } + + fn of(node: &WithSpan<'a, T>, path: &'a Path, parsed: &'a Parsed) -> Self { + Self { + path, + source: Some(parsed.source()), + node_source: Some(node.span()), + } + } } -impl<'a, 'b, 'c> fmt::Display for FileInfo<'a, 'b, 'c> { +impl fmt::Display for FileInfo<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match (self.source, self.node_source) { (Some(source), Some(node_source)) => { diff --git a/rinja_parser/src/lib.rs b/rinja_parser/src/lib.rs index e217f7d4..9b5d3484 100644 --- a/rinja_parser/src/lib.rs +++ b/rinja_parser/src/lib.rs @@ -147,7 +147,7 @@ impl<'a, T> WithSpan<'a, T> { Self { inner, span } } - pub fn span(&self) -> &str { + pub fn span(&self) -> &'a str { self.span } } diff --git a/testing/tests/ui/blocks_below_top_level.rs b/testing/tests/ui/blocks_below_top_level.rs new file mode 100644 index 00000000..7969875c --- /dev/null +++ b/testing/tests/ui/blocks_below_top_level.rs @@ -0,0 +1,29 @@ +use rinja::Template; + +#[derive(Template)] +#[template(source = r#" +{% block bla %} +{% extends "bla.txt" %} +{% endblock %} +"#, ext = "txt")] +struct MyTemplate1; + +#[derive(Template)] +#[template(source = r#" +{% block bla %} +{% macro bla() %} +{% endmacro %} +{% endblock %} +"#, ext = "txt")] +struct MyTemplate2; + +#[derive(Template)] +#[template(source = r#" +{% block bla %} +{% import "bla.txt" as blue %} +{% endblock %} +"#, ext = "txt")] +struct MyTemplate3; + +fn main() { +} diff --git a/testing/tests/ui/blocks_below_top_level.stderr b/testing/tests/ui/blocks_below_top_level.stderr new file mode 100644 index 00000000..740388fd --- /dev/null +++ b/testing/tests/ui/blocks_below_top_level.stderr @@ -0,0 +1,29 @@ +error: `extends` blocks are not allowed below top level + --> MyTemplate1.txt:3:2 + " extends \"bla.txt\" %}\n{% endblock %}\n" + --> tests/ui/blocks_below_top_level.rs:3:10 + | +3 | #[derive(Template)] + | ^^^^^^^^ + | + = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `macro` blocks are not allowed below top level + --> MyTemplate2.txt:3:2 + " macro bla() %}\n{% endmacro %}\n{% endblo"... + --> tests/ui/blocks_below_top_level.rs:11:10 + | +11 | #[derive(Template)] + | ^^^^^^^^ + | + = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `import` blocks are not allowed below top level + --> MyTemplate3.txt:3:2 + " import \"bla.txt\" as blue %}\n{% endblock"... + --> tests/ui/blocks_below_top_level.rs:20:10 + | +20 | #[derive(Template)] + | ^^^^^^^^ + | + = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/testing/tests/ui/multiple_extends.rs b/testing/tests/ui/multiple_extends.rs new file mode 100644 index 00000000..f731d159 --- /dev/null +++ b/testing/tests/ui/multiple_extends.rs @@ -0,0 +1,10 @@ +use rinja::Template; + +#[derive(Template)] +#[template(source = r#" +{% extends "let.html" %} +{% extends "foo.html" %} +"#, ext = "txt")] +struct MyTemplate4; + +fn main() {} diff --git a/testing/tests/ui/multiple_extends.stderr b/testing/tests/ui/multiple_extends.stderr new file mode 100644 index 00000000..87834926 --- /dev/null +++ b/testing/tests/ui/multiple_extends.stderr @@ -0,0 +1,9 @@ +error: multiple extend blocks found + --> MyTemplate4.txt:3:2 + " extends \"foo.html\" %}\n" + --> tests/ui/multiple_extends.rs:3:10 + | +3 | #[derive(Template)] + | ^^^^^^^^ + | + = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)