From 59c06c7155b99eef2c2457f9bfeeb1eded780667 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 17 Sep 2022 10:19:37 +0200 Subject: [PATCH] Improve support for nested errors (#119) --- examples/error/Cargo.toml | 2 +- examples/error/README.md | 27 ++++++-- examples/error/src/main.rs | 34 ++++++---- minijinja/src/compiler/codegen.rs | 5 ++ minijinja/src/compiler/instructions.rs | 11 ++++ minijinja/src/error.rs | 36 ++++++++-- minijinja/src/value/mod.rs | 2 +- minijinja/src/vm/mod.rs | 62 +++++++++++++----- .../tests/inputs/err_bad_basic_block.txt | 4 ++ minijinja/tests/inputs/err_bad_block.txt | 4 ++ minijinja/tests/inputs/err_bad_super.txt | 7 ++ minijinja/tests/inputs/err_in_include.txt | 5 ++ minijinja/tests/inputs/refs/a_plus_b.txt | 1 + .../tests/inputs/refs/bad_basic_block.txt | 4 ++ ...est_templates__vm@block_super_err.txt.snap | 4 +- .../test_templates__vm@debug.txt.snap | 2 + ...st_templates__vm@err_bad_addition.txt.snap | 3 +- ...templates__vm@err_bad_basic_block.txt.snap | 44 +++++++++++++ .../test_templates__vm@err_bad_block.txt.snap | 40 ++++++++++++ .../test_templates__vm@err_bad_call.txt.snap | 3 +- ...test_templates__vm@err_bad_filter.txt.snap | 3 +- ...es__vm@err_bad_nested_subtraction.txt.snap | 3 +- .../test_templates__vm@err_bad_super.txt.snap | 62 ++++++++++++++++++ .../test_templates__vm@err_bad_test.txt.snap | 3 +- ...plates__vm@err_bad_test_arguments.txt.snap | 3 +- ...test_templates__vm@err_in_include.txt.snap | 65 +++++++++++++++++++ ..._templates__vm@err_undefined_attr.txt.snap | 3 +- ..._templates__vm@err_undefined_item.txt.snap | 3 +- ...tes__vm@err_undefined_nested_attr.txt.snap | 3 +- ...templates__vm@include_choice_none.txt.snap | 4 +- ...est_templates__vm@include_missing.txt.snap | 4 +- ..._templates__vm@loop_bad_unpacking.txt.snap | 11 ++-- ...__vm@loop_bad_unpacking_wrong_len.txt.snap | 9 ++- ...plates__vm@loop_over_non_iterable.txt.snap | 3 +- minijinja/tests/test_templates.rs | 23 +++++-- 35 files changed, 417 insertions(+), 85 deletions(-) create mode 100644 minijinja/tests/inputs/err_bad_basic_block.txt create mode 100644 minijinja/tests/inputs/err_bad_block.txt create mode 100644 minijinja/tests/inputs/err_bad_super.txt create mode 100644 minijinja/tests/inputs/err_in_include.txt create mode 100644 minijinja/tests/inputs/refs/a_plus_b.txt create mode 100644 minijinja/tests/inputs/refs/bad_basic_block.txt create mode 100644 minijinja/tests/snapshots/test_templates__vm@err_bad_basic_block.txt.snap create mode 100644 minijinja/tests/snapshots/test_templates__vm@err_bad_block.txt.snap create mode 100644 minijinja/tests/snapshots/test_templates__vm@err_bad_super.txt.snap create mode 100644 minijinja/tests/snapshots/test_templates__vm@err_in_include.txt.snap diff --git a/examples/error/Cargo.toml b/examples/error/Cargo.toml index ed4bf99a..05e38d39 100644 --- a/examples/error/Cargo.toml +++ b/examples/error/Cargo.toml @@ -6,4 +6,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -minijinja = { path = "../../minijinja" } +minijinja = { path = "../../minijinja", features = ["internal_debug"] } diff --git a/examples/error/README.md b/examples/error/README.md index fc18feec..22caf33a 100644 --- a/examples/error/README.md +++ b/examples/error/README.md @@ -5,23 +5,38 @@ template debuggability ```console $ cargo run -Template Failed Rendering: - impossible operation: tried to use + operator on unsupported types number and string (in hello.txt:8) +Error rendering template: could not render an included template: happend in "include.txt" (in hello.txt:8) ---------------------------- Template Source ----------------------------- 5 | {% with foo = 42 %} 6 | {{ range(10) }} 7 | {{ other_seq|join(" ") }} - 8 > Hello {{ item_squared + bar }}! + 8 > {% include "include.txt" %} + i ^^^^^^^^^^^^^^^^^^^^^ could not render an included template 9 | {% endwith %} 10 | {% endwith %} 11 | {% endfor %} -------------------------------------------------------------------------- Referenced variables: { - bar: "test", - item_squared: 4, - other_seq: [...], + other_seq: [ + 0, + 1, + 2, + 3, + 4, + ], range: minijinja::functions::builtins::range, foo: 42, } -------------------------------------------------------------------------- + +caused by: impossible operation: tried to use + operator on unsupported types number and string (in include.txt:1) +---------------------------- Template Source ----------------------------- + 1 > Hello {{ item_squared + bar }}! + i ^^^^^^^^^^^^^^^^^^ impossible operation +-------------------------------------------------------------------------- +Referenced variables: { + bar: "test", + item_squared: 4, +} +-------------------------------------------------------------------------- ``` diff --git a/examples/error/src/main.rs b/examples/error/src/main.rs index 31f08006..84d7299b 100644 --- a/examples/error/src/main.rs +++ b/examples/error/src/main.rs @@ -1,9 +1,10 @@ use minijinja::{context, Environment}; -fn main() { +fn execute() -> Result<(), minijinja::Error> { let mut env = Environment::new(); env.set_debug(true); - if let Err(err) = env.add_template( + env.add_template("include.txt", "Hello {{ item_squared + bar }}!")?; + env.add_template( "hello.txt", r#" first line @@ -12,29 +13,34 @@ fn main() { {% with foo = 42 %} {{ range(10) }} {{ other_seq|join(" ") }} - Hello {{ item_squared + bar }}! + {% include "include.txt" %} {% endwith %} {% endwith %} {% endfor %} last line "#, - ) { - eprintln!("Template Failed Parsing:"); - eprintln!(" {:#}", err); - std::process::exit(1); - } + )?; let template = env.get_template("hello.txt").unwrap(); let ctx = context! { seq => vec![2, 4, 8], other_seq => (0..5).collect::>(), bar => "test" }; - match template.render(&ctx) { - Ok(result) => println!("{}", result), - Err(err) => { - eprintln!("Template Failed Rendering:"); - eprintln!(" {:#}", err); - std::process::exit(1); + println!("{}", template.render(&ctx)?); + Ok(()) +} + +fn main() { + if let Err(err) = execute() { + eprintln!("Error rendering template: {:#}", err); + + let mut err = &err as &dyn std::error::Error; + while let Some(next_err) = err.source() { + eprintln!(); + eprintln!("caused by: {:#}", next_err); + err = next_err; } + + std::process::exit(1); } } diff --git a/minijinja/src/compiler/codegen.rs b/minijinja/src/compiler/codegen.rs index 545ea536..235027ff 100644 --- a/minijinja/src/compiler/codegen.rs +++ b/minijinja/src/compiler/codegen.rs @@ -244,7 +244,9 @@ impl<'source> CodeGenerator<'source> { ast::Stmt::Include(include) => { self.set_line_from_span(include.span()); self.compile_expr(&include.name)?; + self.push_span(include.span()); self.add(Instruction::Include(include.ignore_missing)); + self.pop_span(); } ast::Stmt::AutoEscape(auto_escape) => { self.set_line_from_span(auto_escape.span()); @@ -312,7 +314,9 @@ impl<'source> CodeGenerator<'source> { if let ast::Expr::Call(call) = &expr.expr { if let ast::Expr::Var(var) = &call.expr { if var.id == "super" && call.args.is_empty() { + self.push_span(call.span()); self.add(Instruction::FastSuper); + self.pop_span(); return Ok(()); } if var.id == "loop" && call.args.len() == 1 { @@ -526,6 +530,7 @@ impl<'source> CodeGenerator<'source> { self.sc_bool(matches!(c.op, ast::BinOpKind::ScAnd)); self.compile_expr(&c.right)?; self.end_sc_bool(); + self.pop_span(); return Ok(()); } ast::BinOpKind::Add => Instruction::Add, diff --git a/minijinja/src/compiler/instructions.rs b/minijinja/src/compiler/instructions.rs index 462c8a3b..9c5df19e 100644 --- a/minijinja/src/compiler/instructions.rs +++ b/minijinja/src/compiler/instructions.rs @@ -262,6 +262,17 @@ impl<'source> Instructions<'source> { pub fn add_with_line(&mut self, instr: Instruction<'source>, line: usize) -> usize { let rv = self.add(instr); self.add_line_record(rv, line); + + // if we follow up to a valid span with no more span, clear it out + #[cfg(feature = "debug")] + { + if self.span_infos.last().map_or(false, |x| x.span.is_some()) { + self.span_infos.push(SpanInfo { + first_instruction: rv as u32, + span: None, + }); + } + } rv } diff --git a/minijinja/src/error.rs b/minijinja/src/error.rs index c826bc84..aaf0e411 100644 --- a/minijinja/src/error.rs +++ b/minijinja/src/error.rs @@ -5,7 +5,12 @@ use std::fmt; /// /// If debug mode is enabled a template error contains additional debug /// information that can be displayed by formatting an error with the -/// alternative formatting (``format!("{:#}", err)``). +/// alternative formatting (``format!("{:#}", err)``). That information +/// is also shown for the [`Debug`] display where the extended information +/// is hidden when the alternative formatting is used. +/// +/// Since MiniJinja takes advantage of chained errors it's recommended +/// to render the entire chain to better understand the causes. /// /// # Example /// @@ -18,8 +23,14 @@ use std::fmt; /// match template.render(ctx) { /// Ok(result) => println!("{}", result), /// Err(err) => { -/// eprintln!("Could not render template:"); -/// eprintln!(" {:#}", err); +/// eprintln!("Could not render template: {:#}", err); +/// // render causes as well +/// let mut err = &err as &dyn std::error::Error; +/// while let Some(next_err) = err.source() { +/// eprintln!(); +/// eprintln!("caused by: {:#}", next_err); +/// err = next_err; +/// } /// } /// } /// ``` @@ -57,10 +68,12 @@ impl fmt::Debug for Error { // error struct dump. #[cfg(feature = "debug")] { - if let Some(info) = self.debug_info() { - writeln!(f)?; - render_debug_info(f, self.kind, self.line(), self.span, info)?; - writeln!(f)?; + if !f.alternate() { + if let Some(info) = self.debug_info() { + writeln!(f)?; + render_debug_info(f, self.kind, self.line(), self.span, info)?; + writeln!(f)?; + } } } @@ -97,6 +110,12 @@ pub enum ErrorKind { UndefinedError, /// Impossible to serialize this value. BadSerialization, + /// An error happened in an include. + BadInclude, + /// An error happened in a super block. + EvalBlock, + /// Unable to unpack a value. + CannotUnpack, /// Failed writing output. WriteFailure, } @@ -117,6 +136,9 @@ impl ErrorKind { ErrorKind::BadEscape => "bad string escape", ErrorKind::UndefinedError => "undefined value", ErrorKind::BadSerialization => "could not serialize to internal format", + ErrorKind::BadInclude => "could not render an included template", + ErrorKind::EvalBlock => "could not render block", + ErrorKind::CannotUnpack => "cannot unpack", ErrorKind::WriteFailure => "failed to write output", } } diff --git a/minijinja/src/value/mod.rs b/minijinja/src/value/mod.rs index 54daf32d..6107c2a3 100644 --- a/minijinja/src/value/mod.rs +++ b/minijinja/src/value/mod.rs @@ -575,7 +575,7 @@ impl Value { ValueRepr::Seq(ref v) => Ok(&v[..]), _ => Err(Error::new( ErrorKind::ImpossibleOperation, - "value is not a list", + format!("value of type {} is not a sequence", self.kind()), )), } } diff --git a/minijinja/src/vm/mod.rs b/minijinja/src/vm/mod.rs index 1660ec4c..b2663b58 100644 --- a/minijinja/src/vm/mod.rs +++ b/minijinja/src/vm/mod.rs @@ -280,7 +280,27 @@ impl<'env> Vm<'env> { state.current_block = Some(name); if let Some(layers) = state.blocks.get(name) { let instructions = layers.first().unwrap(); - try_ctx!(self.sub_eval(state, out, instructions, state.blocks.clone())); + let referenced_template = match instructions.name() { + name if name != state.instructions.name() => Some(name), + _ => None, + }; + try_ctx!(self + .sub_eval(state, out, instructions, state.blocks.clone()) + .map_err(|err| { + Error::new( + ErrorKind::EvalBlock, + match referenced_template { + Some(template) => format!( + "happend in replaced block \"{}\" of \"{}\"", + name, template + ), + None => { + format!("happend in local block \"{}\"", name) + } + }, + ) + .with_source(err) + })); } else { bail!(Error::new( ErrorKind::ImpossibleOperation, @@ -429,7 +449,14 @@ impl<'env> Vm<'env> { } let original_escape = state.auto_escape; state.auto_escape = tmpl.initial_auto_escape(); - self.sub_eval(state, out, instructions, referenced_blocks)?; + self.sub_eval(state, out, instructions, referenced_blocks) + .map_err(|err| { + Error::new( + ErrorKind::BadInclude, + format!("happend in \"{}\"", instructions.name()), + ) + .with_source(err) + })?; state.auto_escape = original_escape; return Ok(()); } @@ -476,7 +503,10 @@ impl<'env> Vm<'env> { if capture { out.begin_capture(); } - self.sub_eval(state, out, instructions, state.blocks.clone())?; + self.sub_eval(state, out, instructions, state.blocks.clone()) + .map_err(|err| { + Error::new(ErrorKind::EvalBlock, "happend in super block").with_source(err) + })?; if capture { Ok(out.end_capture(state.auto_escape)) } else { @@ -584,18 +614,14 @@ impl<'env> Vm<'env> { fn unpack_list(&self, stack: &mut Stack, count: &usize) -> Result<(), Error> { let top = stack.pop(); - let v = top.as_slice().map_err(|e| { - Error::new( - ErrorKind::ImpossibleOperation, - "cannot unpack: not a sequence", - ) - .with_source(e) - })?; + let v = top + .as_slice() + .map_err(|e| Error::new(ErrorKind::CannotUnpack, "not a sequence").with_source(e))?; if v.len() != *count { return Err(Error::new( - ErrorKind::ImpossibleOperation, + ErrorKind::CannotUnpack, format!( - "cannot unpack: sequence of wrong length (expected {}, got {})", + "sequence of wrong length (expected {}, got {})", *count, v.len() ), @@ -632,11 +658,15 @@ impl<'env> Vm<'env> { } fn process_err(mut err: Error, pc: usize, state: &State) -> Error { - if let Some(span) = state.instructions.get_span(pc) { - err.set_filename_and_span(state.instructions.name(), span); - } else if let Some(lineno) = state.instructions.get_line(pc) { - err.set_filename_and_line(state.instructions.name(), lineno); + // only attach line information if the error does not have line info yet. + if err.line().is_none() { + if let Some(span) = state.instructions.get_span(pc) { + err.set_filename_and_span(state.instructions.name(), span); + } else if let Some(lineno) = state.instructions.get_line(pc) { + err.set_filename_and_line(state.instructions.name(), lineno); + } } + // only attach debug info if we don't have one yet and we are in debug mode. #[cfg(feature = "debug")] { if state.env.debug() && err.debug_info.is_none() { diff --git a/minijinja/tests/inputs/err_bad_basic_block.txt b/minijinja/tests/inputs/err_bad_basic_block.txt new file mode 100644 index 00000000..82eea324 --- /dev/null +++ b/minijinja/tests/inputs/err_bad_basic_block.txt @@ -0,0 +1,4 @@ +{} +--- +{% extends "bad_basic_block.txt" %} +{% block title %}My Title{% endblock %} diff --git a/minijinja/tests/inputs/err_bad_block.txt b/minijinja/tests/inputs/err_bad_block.txt new file mode 100644 index 00000000..26211d80 --- /dev/null +++ b/minijinja/tests/inputs/err_bad_block.txt @@ -0,0 +1,4 @@ +{} +--- +{% extends "simple_layout.txt" %} +{% block title %}{{ missing_function() }}{% endblock %} diff --git a/minijinja/tests/inputs/err_bad_super.txt b/minijinja/tests/inputs/err_bad_super.txt new file mode 100644 index 00000000..c4458865 --- /dev/null +++ b/minijinja/tests/inputs/err_bad_super.txt @@ -0,0 +1,7 @@ +{} +--- +{% extends "bad_basic_block.txt" %} +{% block title %}My Title{% endblock %} +{% block body %} + Changed stuff goes here. {{ super() }} +{% endblock %} diff --git a/minijinja/tests/inputs/err_in_include.txt b/minijinja/tests/inputs/err_in_include.txt new file mode 100644 index 00000000..2b2cc67e --- /dev/null +++ b/minijinja/tests/inputs/err_in_include.txt @@ -0,0 +1,5 @@ +{"seq": [1, 2, 3], "b": []} +--- +{% for a in seq %} + This fails in the include: {% include "a_plus_b.txt" %} +{% endfor %} \ No newline at end of file diff --git a/minijinja/tests/inputs/refs/a_plus_b.txt b/minijinja/tests/inputs/refs/a_plus_b.txt new file mode 100644 index 00000000..3d8fc0ac --- /dev/null +++ b/minijinja/tests/inputs/refs/a_plus_b.txt @@ -0,0 +1 @@ +This template adds b to a: {{ a + b }} \ No newline at end of file diff --git a/minijinja/tests/inputs/refs/bad_basic_block.txt b/minijinja/tests/inputs/refs/bad_basic_block.txt new file mode 100644 index 00000000..40d2a8c0 --- /dev/null +++ b/minijinja/tests/inputs/refs/bad_basic_block.txt @@ -0,0 +1,4 @@ +{% block title %}default title{% endblock %} +{% block body %} + {{ missing_function() }} +{% endblock %} diff --git a/minijinja/tests/snapshots/test_templates__vm@block_super_err.txt.snap b/minijinja/tests/snapshots/test_templates__vm@block_super_err.txt.snap index e6714d2b..b96fefcb 100644 --- a/minijinja/tests/snapshots/test_templates__vm@block_super_err.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@block_super_err.txt.snap @@ -13,11 +13,11 @@ Error { line: 1, } +impossible operation: cannot super outside of block (in block_super_err.txt:1) ---------------------------- Template Source ----------------------------- 1 > {{ super() }} + i ^^^^^^^ impossible operation -------------------------------------------------------------------------- Referenced variables: -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap b/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap index 05372bf0..b2890e5f 100644 --- a/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap @@ -72,6 +72,8 @@ State { "urlencode", ], templates: [ + "a_plus_b.txt", + "bad_basic_block.txt", "debug.txt", "simple_include.txt", "simple_layout.txt", diff --git a/minijinja/tests/snapshots/test_templates__vm@err_bad_addition.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_bad_addition.txt.snap index d55f41c7..17e7c36f 100644 --- a/minijinja/tests/snapshots/test_templates__vm@err_bad_addition.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@err_bad_addition.txt.snap @@ -13,6 +13,7 @@ Error { line: 1, } +impossible operation: tried to use + operator on unsupported types sequence and number (in err_bad_addition.txt:1) ---------------------------- Template Source ----------------------------- 1 > {{ [1, 2] + 23 }} i ^^^^^^^^^^^ impossible operation @@ -20,5 +21,3 @@ Error { Referenced variables: -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@err_bad_basic_block.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_bad_basic_block.txt.snap new file mode 100644 index 00000000..439a2b94 --- /dev/null +++ b/minijinja/tests/snapshots/test_templates__vm@err_bad_basic_block.txt.snap @@ -0,0 +1,44 @@ +--- +source: minijinja/tests/test_templates.rs +description: "{% extends \"bad_basic_block.txt\" %}\n{% block title %}My Title{% endblock %}" +info: {} +input_file: minijinja/tests/inputs/err_bad_basic_block.txt +--- +!!!ERROR!!! + +Error { + kind: EvalBlock, + detail: "happend in local block \"body\"", + name: "bad_basic_block.txt", + line: 2, + source: Error { + kind: UnknownFunction, + detail: "missing_function is unknown", + name: "bad_basic_block.txt", + line: 3, + }, +} + +could not render block: happend in local block "body" (in bad_basic_block.txt:2) +---------------------------- Template Source ----------------------------- + 1 | {% block title %}default title{% endblock %} + 2 > {% block body %} + 3 | {{ missing_function() }} + 4 | {% endblock %} +-------------------------------------------------------------------------- +Referenced variables: +-------------------------------------------------------------------------- + +caused by: unknown function: missing_function is unknown (in bad_basic_block.txt:3) +---------------------------- Template Source ----------------------------- + 1 | {% block title %}default title{% endblock %} + 2 | {% block body %} + 3 > {{ missing_function() }} + i ^^^^^^^^^^^^^^^^^^ unknown function + 4 | {% endblock %} +-------------------------------------------------------------------------- +Referenced variables: { + missing_function: Undefined, +} +-------------------------------------------------------------------------- + diff --git a/minijinja/tests/snapshots/test_templates__vm@err_bad_block.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_bad_block.txt.snap new file mode 100644 index 00000000..0a3b0f58 --- /dev/null +++ b/minijinja/tests/snapshots/test_templates__vm@err_bad_block.txt.snap @@ -0,0 +1,40 @@ +--- +source: minijinja/tests/test_templates.rs +description: "{% extends \"simple_layout.txt\" %}\n{% block title %}{{ missing_function() }}{% endblock %}" +info: {} +input_file: minijinja/tests/inputs/err_bad_block.txt +--- +!!!ERROR!!! + +Error { + kind: EvalBlock, + detail: "happend in replaced block \"title\" of \"err_bad_block.txt\"", + name: "simple_layout.txt", + line: 1, + source: Error { + kind: UnknownFunction, + detail: "missing_function is unknown", + name: "err_bad_block.txt", + line: 2, + }, +} + +could not render block: happend in replaced block "title" of "err_bad_block.txt" (in simple_layout.txt:1) +---------------------------- Template Source ----------------------------- + 1 > {% block title %}default title{% endblock %} + 2 | {% block body %}default body{% endblock %} +-------------------------------------------------------------------------- +Referenced variables: +-------------------------------------------------------------------------- + +caused by: unknown function: missing_function is unknown (in err_bad_block.txt:2) +---------------------------- Template Source ----------------------------- + 1 | {% extends "simple_layout.txt" %} + 2 > {% block title %}{{ missing_function() }}{% endblock %} + i ^^^^^^^^^^^^^^^^^^ unknown function +-------------------------------------------------------------------------- +Referenced variables: { + missing_function: Undefined, +} +-------------------------------------------------------------------------- + diff --git a/minijinja/tests/snapshots/test_templates__vm@err_bad_call.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_bad_call.txt.snap index e8b4cd35..379caa1d 100644 --- a/minijinja/tests/snapshots/test_templates__vm@err_bad_call.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@err_bad_call.txt.snap @@ -13,6 +13,7 @@ Error { line: 1, } +unknown function: an_unknown_function is unknown (in err_bad_call.txt:1) ---------------------------- Template Source ----------------------------- 1 > This is {{ an_unknown_function() }}! i ^^^^^^^^^^^^^^^^^^^^^ unknown function @@ -22,5 +23,3 @@ Referenced variables: { } -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@err_bad_filter.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_bad_filter.txt.snap index 10a0351e..c94ddd87 100644 --- a/minijinja/tests/snapshots/test_templates__vm@err_bad_filter.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@err_bad_filter.txt.snap @@ -13,6 +13,7 @@ Error { line: 1, } +impossible operation: object is not iterable (in err_bad_filter.txt:1) ---------------------------- Template Source ----------------------------- 1 > {% for item in 42|slice(4) %} i ^^^^^^^^ impossible operation @@ -22,5 +23,3 @@ Error { Referenced variables: -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@err_bad_nested_subtraction.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_bad_nested_subtraction.txt.snap index cbd66194..bed560a2 100644 --- a/minijinja/tests/snapshots/test_templates__vm@err_bad_nested_subtraction.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@err_bad_nested_subtraction.txt.snap @@ -17,6 +17,7 @@ Error { line: 2, } +impossible operation: tried to use - operator on unsupported types number and sequence (in err_bad_nested_subtraction.txt:2) ---------------------------- Template Source ----------------------------- 1 | {% for item in seq %} 2 > {{ ((item + 4) * (3 - [])) + 4 - 2 }} @@ -44,5 +45,3 @@ Referenced variables: { } -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@err_bad_super.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_bad_super.txt.snap new file mode 100644 index 00000000..b848580e --- /dev/null +++ b/minijinja/tests/snapshots/test_templates__vm@err_bad_super.txt.snap @@ -0,0 +1,62 @@ +--- +source: minijinja/tests/test_templates.rs +description: "{% extends \"bad_basic_block.txt\" %}\n{% block title %}My Title{% endblock %}\n{% block body %}\n Changed stuff goes here. {{ super() }}\n{% endblock %}" +info: {} +input_file: minijinja/tests/inputs/err_bad_super.txt +--- +!!!ERROR!!! + +Error { + kind: EvalBlock, + detail: "happend in replaced block \"body\" of \"err_bad_super.txt\"", + name: "bad_basic_block.txt", + line: 2, + source: Error { + kind: EvalBlock, + detail: "happend in super block", + name: "err_bad_super.txt", + line: 4, + source: Error { + kind: UnknownFunction, + detail: "missing_function is unknown", + name: "bad_basic_block.txt", + line: 3, + }, + }, +} + +could not render block: happend in replaced block "body" of "err_bad_super.txt" (in bad_basic_block.txt:2) +---------------------------- Template Source ----------------------------- + 1 | {% block title %}default title{% endblock %} + 2 > {% block body %} + 3 | {{ missing_function() }} + 4 | {% endblock %} +-------------------------------------------------------------------------- +Referenced variables: +-------------------------------------------------------------------------- + +caused by: could not render block: happend in super block (in err_bad_super.txt:4) +---------------------------- Template Source ----------------------------- + 1 | {% extends "bad_basic_block.txt" %} + 2 | {% block title %}My Title{% endblock %} + 3 | {% block body %} + 4 > Changed stuff goes here. {{ super() }} + i ^^^^^^^ could not render block + 5 | {% endblock %} +-------------------------------------------------------------------------- +Referenced variables: +-------------------------------------------------------------------------- + +caused by: unknown function: missing_function is unknown (in bad_basic_block.txt:3) +---------------------------- Template Source ----------------------------- + 1 | {% block title %}default title{% endblock %} + 2 | {% block body %} + 3 > {{ missing_function() }} + i ^^^^^^^^^^^^^^^^^^ unknown function + 4 | {% endblock %} +-------------------------------------------------------------------------- +Referenced variables: { + missing_function: Undefined, +} +-------------------------------------------------------------------------- + diff --git a/minijinja/tests/snapshots/test_templates__vm@err_bad_test.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_bad_test.txt.snap index 53cc3955..c9562fed 100644 --- a/minijinja/tests/snapshots/test_templates__vm@err_bad_test.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@err_bad_test.txt.snap @@ -17,6 +17,7 @@ Error { line: 2, } +unknown test: test reallyEven is unknown (in err_bad_test.txt:2) ---------------------------- Template Source ----------------------------- 1 | {% for item in seq %} 2 > {% if item is reallyEven %} @@ -46,5 +47,3 @@ Referenced variables: { } -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@err_bad_test_arguments.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_bad_test_arguments.txt.snap index c8faa577..15ee89f7 100644 --- a/minijinja/tests/snapshots/test_templates__vm@err_bad_test_arguments.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@err_bad_test_arguments.txt.snap @@ -16,6 +16,7 @@ Error { line: 2, } +too many arguments (in err_bad_test_arguments.txt:2) ---------------------------- Template Source ----------------------------- 1 | {% for item in seq %} 2 > {% if item is even(42) %} @@ -45,5 +46,3 @@ Referenced variables: { } -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@err_in_include.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_in_include.txt.snap new file mode 100644 index 00000000..801c3696 --- /dev/null +++ b/minijinja/tests/snapshots/test_templates__vm@err_in_include.txt.snap @@ -0,0 +1,65 @@ +--- +source: minijinja/tests/test_templates.rs +description: "{% for a in seq %}\n This fails in the include: {% include \"a_plus_b.txt\" %}\n{% endfor %}" +info: + b: [] + seq: + - 1 + - 2 + - 3 +input_file: minijinja/tests/inputs/err_in_include.txt +--- +!!!ERROR!!! + +Error { + kind: BadInclude, + detail: "happend in \"a_plus_b.txt\"", + name: "err_in_include.txt", + line: 2, + source: Error { + kind: ImpossibleOperation, + detail: "tried to use + operator on unsupported types number and sequence", + name: "a_plus_b.txt", + line: 1, + }, +} + +could not render an included template: happend in "a_plus_b.txt" (in err_in_include.txt:2) +---------------------------- Template Source ----------------------------- + 1 | {% for a in seq %} + 2 > This fails in the include: {% include "a_plus_b.txt" %} + i ^^^^^^^^^^^^^^^^^^^^^^ could not render an included template + 3 | {% endfor %} +-------------------------------------------------------------------------- +Referenced variables: { + a: 1, + loop: LoopState { + index0: 0, + index: 1, + length: 3, + revindex: 3, + revindex0: 2, + first: true, + last: false, + depth: 1, + depth0: 0, + }, + seq: [ + 1, + 2, + 3, + ], +} +-------------------------------------------------------------------------- + +caused by: impossible operation: tried to use + operator on unsupported types number and sequence (in a_plus_b.txt:1) +---------------------------- Template Source ----------------------------- + 1 > This template adds b to a: {{ a + b }} + i ^^^^^ impossible operation +-------------------------------------------------------------------------- +Referenced variables: { + b: [], + a: 1, +} +-------------------------------------------------------------------------- + diff --git a/minijinja/tests/snapshots/test_templates__vm@err_undefined_attr.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_undefined_attr.txt.snap index 09abe68e..b5ec9ecc 100644 --- a/minijinja/tests/snapshots/test_templates__vm@err_undefined_attr.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@err_undefined_attr.txt.snap @@ -12,6 +12,7 @@ Error { line: 1, } +undefined value (in err_undefined_attr.txt:1) ---------------------------- Template Source ----------------------------- 1 > {{ undefined_value.attr }} i ^^^^^^^^^^^^^^^^^^^^ undefined value @@ -21,5 +22,3 @@ Referenced variables: { } -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@err_undefined_item.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_undefined_item.txt.snap index 2b4b6842..f58716bf 100644 --- a/minijinja/tests/snapshots/test_templates__vm@err_undefined_item.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@err_undefined_item.txt.snap @@ -16,6 +16,7 @@ Error { line: 1, } +undefined value (in err_undefined_item.txt:1) ---------------------------- Template Source ----------------------------- 1 > {{ seq[42][23] }} i ^^^^^^^^ undefined value @@ -29,5 +30,3 @@ Referenced variables: { } -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@err_undefined_nested_attr.txt.snap b/minijinja/tests/snapshots/test_templates__vm@err_undefined_nested_attr.txt.snap index ece3e7c2..85f81598 100644 --- a/minijinja/tests/snapshots/test_templates__vm@err_undefined_nested_attr.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@err_undefined_nested_attr.txt.snap @@ -16,6 +16,7 @@ Error { line: 2, } +undefined value (in err_undefined_nested_attr.txt:2) ---------------------------- Template Source ----------------------------- 1 | {{ seq.whatever }} 2 > {{ seq.whatever.else }} @@ -30,5 +31,3 @@ Referenced variables: { } -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@include_choice_none.txt.snap b/minijinja/tests/snapshots/test_templates__vm@include_choice_none.txt.snap index 002e923c..47004ed0 100644 --- a/minijinja/tests/snapshots/test_templates__vm@include_choice_none.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@include_choice_none.txt.snap @@ -13,13 +13,13 @@ Error { line: 2, } +template not found: tried to include one of multiple templates, none of which existed ["missing_template1.txt", "missing_template2.txt"] (in include_choice_none.txt:2) ---------------------------- Template Source ----------------------------- 1 | Before 2 > {% include ["missing_template1.txt", "missing_template2.txt"] %} + i ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ template not found 3 | After -------------------------------------------------------------------------- Referenced variables: -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@include_missing.txt.snap b/minijinja/tests/snapshots/test_templates__vm@include_missing.txt.snap index 20f518dd..36d7d076 100644 --- a/minijinja/tests/snapshots/test_templates__vm@include_missing.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@include_missing.txt.snap @@ -14,9 +14,11 @@ Error { line: 2, } +template not found: tried to include non-existing template "missing_template.txt" (in include_missing.txt:2) ---------------------------- Template Source ----------------------------- 1 | Before 2 > {% include template %} + i ^^^^^^^^^^^^^^^^ template not found 3 | After -------------------------------------------------------------------------- Referenced variables: { @@ -24,5 +26,3 @@ Referenced variables: { } -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@loop_bad_unpacking.txt.snap b/minijinja/tests/snapshots/test_templates__vm@loop_bad_unpacking.txt.snap index 4825ee61..6b712caa 100644 --- a/minijinja/tests/snapshots/test_templates__vm@loop_bad_unpacking.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@loop_bad_unpacking.txt.snap @@ -11,20 +11,21 @@ input_file: minijinja/tests/inputs/loop_bad_unpacking.txt !!!ERROR!!! Error { - kind: ImpossibleOperation, - detail: "cannot unpack: not a sequence", + kind: CannotUnpack, + detail: "not a sequence", name: "loop_bad_unpacking.txt", line: 2, source: Error { kind: ImpossibleOperation, - detail: "value is not a list", + detail: "value of type number is not a sequence", }, } +cannot unpack: not a sequence (in loop_bad_unpacking.txt:2) ---------------------------- Template Source ----------------------------- 1 | @@ -49,5 +50,5 @@ Referenced variables: { } -------------------------------------------------------------------------- - +caused by: impossible operation: value of type number is not a sequence diff --git a/minijinja/tests/snapshots/test_templates__vm@loop_bad_unpacking_wrong_len.txt.snap b/minijinja/tests/snapshots/test_templates__vm@loop_bad_unpacking_wrong_len.txt.snap index 07605730..8787473b 100644 --- a/minijinja/tests/snapshots/test_templates__vm@loop_bad_unpacking_wrong_len.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@loop_bad_unpacking_wrong_len.txt.snap @@ -17,16 +17,17 @@ input_file: minijinja/tests/inputs/loop_bad_unpacking_wrong_len.txt !!!ERROR!!! Error { - kind: ImpossibleOperation, - detail: "cannot unpack: sequence of wrong length (expected 2, got 3)", + kind: CannotUnpack, + detail: "sequence of wrong length (expected 2, got 3)", name: "loop_bad_unpacking_wrong_len.txt", line: 2, } +cannot unpack: sequence of wrong length (expected 2, got 3) (in loop_bad_unpacking_wrong_len.txt:2) ---------------------------- Template Source ----------------------------- 1 | @@ -63,5 +64,3 @@ Referenced variables: { } -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/snapshots/test_templates__vm@loop_over_non_iterable.txt.snap b/minijinja/tests/snapshots/test_templates__vm@loop_over_non_iterable.txt.snap index 7dcacb12..c09ee6b3 100644 --- a/minijinja/tests/snapshots/test_templates__vm@loop_over_non_iterable.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@loop_over_non_iterable.txt.snap @@ -14,6 +14,7 @@ Error { line: 1, } +impossible operation: object is not iterable (in loop_over_non_iterable.txt:1) ---------------------------- Template Source ----------------------------- 1 > [{% for item in seq %}{% endfor %}] -------------------------------------------------------------------------- @@ -23,5 +24,3 @@ Referenced variables: { } -------------------------------------------------------------------------- - - diff --git a/minijinja/tests/test_templates.rs b/minijinja/tests/test_templates.rs index e2cf9947..2a17cfb8 100644 --- a/minijinja/tests/test_templates.rs +++ b/minijinja/tests/test_templates.rs @@ -1,4 +1,5 @@ use std::collections::BTreeMap; +use std::fmt::Write; use std::fs; use minijinja::{context, Environment, Error, State}; @@ -38,11 +39,25 @@ fn test_vm() { env.add_template(filename, content).unwrap(); let template = env.get_template(filename).unwrap(); - let mut rendered = match template.render(&ctx) { - Ok(rendered) => rendered, - Err(err) => format!("!!!ERROR!!!\n\n{:#?}\n", err), + let rendered = match template.render(&ctx) { + Ok(mut rendered) => { + rendered.push('\n'); + rendered + } + Err(err) => { + let mut rendered = format!("!!!ERROR!!!\n\n{:#?}\n\n", err); + + writeln!(rendered, "{:#}", err).unwrap(); + let mut err = &err as &dyn std::error::Error; + while let Some(next_err) = err.source() { + writeln!(rendered).unwrap(); + writeln!(rendered, "caused by: {:#}", next_err).unwrap(); + err = next_err; + } + + rendered + } }; - rendered.push('\n'); insta::with_settings!({ info => &ctx,