From 79e7331ccc7be9a767fc932244ca5fbda077dbd8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 2 Jul 2024 21:35:41 +0200 Subject: [PATCH 1/5] Generate better errors for top level nodes --- rinja_derive/src/heritage.rs | 48 +++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/rinja_derive/src/heritage.rs b/rinja_derive/src/heritage.rs index 0c60f0d4e..578d99f5d 100644 --- a/rinja_derive/src/heritage.rs +++ b/rinja_derive/src/heritage.rs @@ -78,7 +78,12 @@ impl Context<'_> { match n { Node::Extends(e) if top => match extends { Some(_) => { - return Err(CompileError::no_file_info("multiple extend blocks found")) + return Err(generate_error( + path, + parsed.source(), + "multiple extend blocks found", + e, + )) } None => { extends = Some(config.find_template(e.path, Some(path))?); @@ -91,9 +96,28 @@ impl Context<'_> { 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::Extends(e) if !top => { + return Err(generate_error( + path, + parsed.source(), + "extends blocks are not allowed below top level", + e, + )); + } + Node::Macro(e) if !top => { + return Err(generate_error( + path, + parsed.source(), + "macro blocks are not allowed below top level", + e, + )); + } + Node::Import(e) if !top => { + return Err(generate_error( + path, + parsed.source(), + "import blocks are not allowed below top level", + e, )); } Node::BlockDef(b) => { @@ -133,15 +157,15 @@ 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()), - )), - ), + Some(path) => generate_error(path, self.parsed.source(), msg, node), None => CompileError::new(msg, None), } } } + +fn generate_error(path: &Path, source: &str, msg: &str, node: &WithSpan<'_, T>) -> CompileError { + CompileError::new( + msg, + Some(FileInfo::new(path, Some(source), Some(node.span()))), + ) +} From 27e12f083bb94e07f83612fa97b93a3e6ca05798 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 2 Jul 2024 21:35:53 +0200 Subject: [PATCH 2/5] Add UI tests for top level nodes --- testing/tests/ui/blocks_below_top_level.rs | 29 +++++++++++++++++++ .../tests/ui/blocks_below_top_level.stderr | 29 +++++++++++++++++++ testing/tests/ui/multiple_extends.rs | 10 +++++++ testing/tests/ui/multiple_extends.stderr | 9 ++++++ 4 files changed, 77 insertions(+) create mode 100644 testing/tests/ui/blocks_below_top_level.rs create mode 100644 testing/tests/ui/blocks_below_top_level.stderr create mode 100644 testing/tests/ui/multiple_extends.rs create mode 100644 testing/tests/ui/multiple_extends.stderr 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 000000000..7969875c9 --- /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 000000000..3466eb346 --- /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 000000000..f731d159a --- /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 000000000..878349269 --- /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) From bf20db307e5243603a9bfd7f905cdff4300bec78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Wed, 3 Jul 2024 00:26:03 +0200 Subject: [PATCH 3/5] =?UTF-8?q?`FileInfo<'a,=20'b,=20'c>`=20=E2=86=92=20`F?= =?UTF-8?q?ileInfo<'a>`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rinja_derive/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rinja_derive/src/lib.rs b/rinja_derive/src/lib.rs index e3d99204d..b3d1f1283 100644 --- a/rinja_derive/src/lib.rs +++ b/rinja_derive/src/lib.rs @@ -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,14 +178,14 @@ 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, @@ -194,7 +194,7 @@ impl<'a, 'b, 'c> FileInfo<'a, 'b, 'c> { } } -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)) => { From 20dc1a0139fd67f800913f4fcdd69fa4ad4fc35f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Wed, 3 Jul 2024 00:48:09 +0200 Subject: [PATCH 4/5] Fix lifetime of WithSpan::span() --- rinja_parser/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rinja_parser/src/lib.rs b/rinja_parser/src/lib.rs index a7ffb1957..d3ddd812a 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 } } From d4b363b9a46205d7afbeac689cc628b15a5ef542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Wed, 3 Jul 2024 01:14:53 +0200 Subject: [PATCH 5/5] Dedup top-level check in heritage --- rinja_derive/src/heritage.rs | 77 ++++++++----------- rinja_derive/src/lib.rs | 10 ++- .../tests/ui/blocks_below_top_level.stderr | 6 +- 3 files changed, 43 insertions(+), 50 deletions(-) diff --git a/rinja_derive/src/heritage.rs b/rinja_derive/src/heritage.rs index 578d99f5d..e9f723420 100644 --- a/rinja_derive/src/heritage.rs +++ b/rinja_derive/src/heritage.rs @@ -76,50 +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(generate_error( - path, - parsed.source(), + Node::Extends(e) => { + ensure_top(top, e, path, parsed, "extends")?; + if extends.is_some() { + return Err(CompileError::new( "multiple extend blocks found", - e, - )) + 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(e) if !top => { - return Err(generate_error( - path, - parsed.source(), - "extends blocks are not allowed below top level", - e, - )); - } - Node::Macro(e) if !top => { - return Err(generate_error( - path, - parsed.source(), - "macro blocks are not allowed below top level", - e, - )); - } - Node::Import(e) if !top => { - return Err(generate_error( - path, - parsed.source(), - "import blocks are not allowed below top level", - e, - )); - } Node::BlockDef(b) => { blocks.insert(b.name, &**b); nested.push(&b.nodes); @@ -156,16 +131,26 @@ impl Context<'_> { } pub(crate) fn generate_error(&self, msg: &str, node: &WithSpan<'_, T>) -> CompileError { - match self.path { - Some(path) => generate_error(path, self.parsed.source(), msg, node), - None => CompileError::new(msg, None), - } + CompileError::new( + msg, + self.path.map(|path| FileInfo::of(node, path, self.parsed)), + ) } } -fn generate_error(path: &Path, source: &str, msg: &str, node: &WithSpan<'_, T>) -> CompileError { - CompileError::new( - msg, - Some(FileInfo::new(path, Some(source), Some(node.span()))), - ) +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 b3d1f1283..d8df48867 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"))] @@ -192,6 +192,14 @@ impl<'a> FileInfo<'a> { 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 fmt::Display for FileInfo<'_> { diff --git a/testing/tests/ui/blocks_below_top_level.stderr b/testing/tests/ui/blocks_below_top_level.stderr index 3466eb346..740388fd1 100644 --- a/testing/tests/ui/blocks_below_top_level.stderr +++ b/testing/tests/ui/blocks_below_top_level.stderr @@ -1,4 +1,4 @@ -error: extends blocks are not allowed below top level +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 @@ -8,7 +8,7 @@ error: extends blocks are not allowed below top level | = 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 +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 @@ -18,7 +18,7 @@ error: macro blocks are not allowed below top level | = 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 +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