diff options
author | René Kijewski <kijewski@library.vetmed.fu-berlin.de> | 2021-07-30 15:54:38 +0200 |
---|---|---|
committer | Dirkjan Ochtman <dirkjan@ochtman.nl> | 2021-07-30 16:36:59 +0200 |
commit | 4123ec37a2a58248433b03765ebfb2d55155a2ae (patch) | |
tree | b79fea8a4a97b01413d97e3283041dfc02df39ee | |
parent | 268d8250fb0a9cdcbbd760bdf39424ed02fd1920 (diff) | |
download | askama-4123ec37a2a58248433b03765ebfb2d55155a2ae.tar.gz askama-4123ec37a2a58248433b03765ebfb2d55155a2ae.tar.bz2 askama-4123ec37a2a58248433b03765ebfb2d55155a2ae.zip |
Better error messages using cuts
Diffstat (limited to '')
-rw-r--r-- | askama_shared/src/parser.rs | 317 | ||||
-rw-r--r-- | testing/tests/ui/typo_in_keyword.rs | 11 | ||||
-rw-r--r-- | testing/tests/ui/typo_in_keyword.stderr | 8 |
3 files changed, 205 insertions, 131 deletions
diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs index a965025..957c778 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -1,7 +1,7 @@ use nom::branch::alt; use nom::bytes::complete::{escaped, is_not, tag, take_until}; use nom::character::complete::{anychar, char, digit1}; -use nom::combinator::{complete, map, opt, recognize, value}; +use nom::combinator::{complete, cut, map, opt, recognize, value}; use nom::error::{Error, ParseError}; use nom::multi::{fold_many0, many0, many1, separated_list0, separated_list1}; use nom::sequence::{delimited, pair, preceded, terminated, tuple}; @@ -387,13 +387,13 @@ fn target(i: &[u8]) -> IResult<&[u8], Target<'_>> { } let mut targets = vec![first_target]; - let (i, _) = tuple(( + let (i, _) = cut(tuple(( fold_many0(preceded(ws(tag(",")), target), (), |_, target| { targets.push(target); }), opt(ws(tag(","))), - ws(tag(")")), - ))(i)?; + ws(cut(tag(")"))), + )))(i)?; return Ok((i, Target::Tuple(Vec::new(), targets))); } @@ -408,8 +408,8 @@ fn target(i: &[u8]) -> IResult<&[u8], Target<'_>> { let (i, targets) = alt(( map(tag(")"), |_| Vec::new()), terminated( - separated_list1(ws(tag(",")), target), - pair(opt(ws(tag(","))), ws(tag(")"))), + cut(separated_list1(ws(tag(",")), target)), + pair(opt(ws(tag(","))), ws(cut(tag(")")))), ), ))(i)?; return Ok((i, Target::Tuple(path, targets))); @@ -420,8 +420,8 @@ fn target(i: &[u8]) -> IResult<&[u8], Target<'_>> { let (i, targets) = alt(( map(tag("}"), |_| Vec::new()), terminated( - separated_list1(ws(tag(",")), named_target), - pair(opt(ws(tag(","))), ws(tag("}"))), + cut(separated_list1(ws(tag(",")), named_target)), + pair(opt(ws(tag(","))), ws(cut(tag("}")))), ), ))(i)?; return Ok((i, Target::Struct(path, targets))); @@ -664,12 +664,11 @@ fn expr_any(i: &[u8]) -> IResult<&[u8], Expr<'_>> { fn expr_node<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> { let mut p = tuple(( |i| tag_expr_start(i, s), - opt(tag("-")), - ws(expr_any), - opt(tag("-")), - |i| tag_expr_end(i, s), + cut(tuple((opt(tag("-")), ws(expr_any), opt(tag("-")), |i| { + tag_expr_end(i, s) + }))), )); - let (i, (_, pws, expr, nws, _)) = p(i)?; + let (i, (_, (pws, expr, nws, _))) = p(i)?; Ok((i, Node::Expr(Ws(pws.is_some(), nws.is_some()), expr))) } @@ -677,12 +676,14 @@ fn block_call(i: &[u8]) -> IResult<&[u8], Node<'_>> { let mut p = tuple(( opt(tag("-")), ws(tag("call")), - opt(tuple((ws(identifier), ws(tag("::"))))), - ws(identifier), - ws(arguments), - opt(tag("-")), + cut(tuple(( + opt(tuple((ws(identifier), ws(tag("::"))))), + ws(identifier), + ws(arguments), + opt(tag("-")), + ))), )); - let (i, (pws, _, scope, name, args, nws)) = p(i)?; + let (i, (pws, _, (scope, name, args, nws))) = p(i)?; let scope = scope.map(|(scope, _)| scope); Ok(( i, @@ -691,23 +692,19 @@ fn block_call(i: &[u8]) -> IResult<&[u8], Node<'_>> { } fn cond_if(i: &[u8]) -> IResult<&[u8], CondTest<'_>> { - let mut p = tuple(( + let mut p = preceded( ws(tag("if")), - opt(tuple(( - ws(alt((tag("let"), tag("set")))), - ws(target), - ws(tag("=")), + cut(tuple(( + opt(delimited( + ws(alt((tag("let"), tag("set")))), + ws(target), + ws(tag("=")), + )), + ws(expr_any), ))), - ws(expr_any), - )); - let (i, (_, dest, expr)) = p(i)?; - Ok(( - i, - CondTest { - target: dest.map(|(_, target, _)| target), - expr, - }, - )) + ); + let (i, (target, expr)) = p(i)?; + Ok((i, CondTest { target, expr })) } fn cond_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Cond<'a>> { @@ -715,12 +712,14 @@ fn cond_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Cond<'a>> |i| tag_block_start(i, s), opt(tag("-")), ws(tag("else")), - opt(cond_if), - opt(tag("-")), - |i| tag_block_end(i, s), - |i| parse_template(i, s), + cut(tuple(( + opt(cond_if), + opt(tag("-")), + |i| tag_block_end(i, s), + cut(|i| parse_template(i, s)), + ))), )); - let (i, (_, pws, _, cond, nws, _, block)) = p(i)?; + let (i, (_, pws, _, (cond, nws, _, block))) = p(i)?; Ok((i, (Ws(pws.is_some(), nws.is_some()), cond, block))) } @@ -728,16 +727,22 @@ fn block_if<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> { let mut p = tuple(( opt(tag("-")), cond_if, - opt(tag("-")), - |i| tag_block_end(i, s), - |i| parse_template(i, s), - many0(|i| cond_block(i, s)), - |i| tag_block_start(i, s), - opt(tag("-")), - ws(tag("endif")), - opt(tag("-")), + cut(tuple(( + opt(tag("-")), + |i| tag_block_end(i, s), + cut(tuple(( + |i| parse_template(i, s), + many0(|i| cond_block(i, s)), + cut(tuple(( + |i| tag_block_start(i, s), + opt(tag("-")), + ws(tag("endif")), + opt(tag("-")), + ))), + ))), + ))), )); - let (i, (pws1, cond, nws1, _, block, elifs, _, pws2, _, nws2)) = p(i)?; + let (i, (pws1, cond, (nws1, _, (block, elifs, (_, pws2, _, nws2))))) = p(i)?; let mut res = vec![(Ws(pws1.is_some(), nws1.is_some()), Some(cond), block)]; res.extend(elifs); @@ -749,11 +754,13 @@ fn match_else_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Whe |i| tag_block_start(i, s), opt(tag("-")), ws(tag("else")), - opt(tag("-")), - |i| tag_block_end(i, s), - |i| parse_template(i, s), + cut(tuple(( + opt(tag("-")), + |i| tag_block_end(i, s), + cut(|i| parse_template(i, s)), + ))), )); - let (i, (_, pws, _, nws, _, block)) = p(i)?; + let (i, (_, pws, _, (nws, _, block))) = p(i)?; Ok(( i, (Ws(pws.is_some(), nws.is_some()), Target::Name("_"), block), @@ -765,12 +772,14 @@ fn when_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], When<'a>> |i| tag_block_start(i, s), opt(tag("-")), ws(tag("when")), - ws(target), - opt(tag("-")), - |i| tag_block_end(i, s), - |i| parse_template(i, s), + cut(tuple(( + ws(target), + opt(tag("-")), + |i| tag_block_end(i, s), + cut(|i| parse_template(i, s)), + ))), )); - let (i, (_, pws, _, target, nws, _, block)) = p(i)?; + let (i, (_, pws, _, (target, nws, _, block))) = p(i)?; Ok((i, (Ws(pws.is_some(), nws.is_some()), target, block))) } @@ -778,18 +787,26 @@ fn block_match<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a> let mut p = tuple(( opt(tag("-")), ws(tag("match")), - ws(expr_any), - opt(tag("-")), - |i| tag_block_end(i, s), - opt(|i| take_content(i, s)), - many1(|i| when_block(i, s)), - opt(|i| match_else_block(i, s)), - ws(|i| tag_block_start(i, s)), - opt(tag("-")), - ws(tag("endmatch")), - opt(tag("-")), + cut(tuple(( + ws(expr_any), + opt(tag("-")), + |i| tag_block_end(i, s), + cut(tuple(( + opt(|i| take_content(i, s)), + many1(|i| when_block(i, s)), + cut(tuple(( + opt(|i| match_else_block(i, s)), + cut(tuple(( + ws(|i| tag_block_start(i, s)), + opt(tag("-")), + ws(tag("endmatch")), + opt(tag("-")), + ))), + ))), + ))), + ))), )); - let (i, (pws1, _, expr, nws1, _, inter, arms, else_arm, _, pws2, _, nws2)) = p(i)?; + let (i, (pws1, _, (expr, nws1, _, (inter, arms, (else_arm, (_, pws2, _, nws2)))))) = p(i)?; let mut arms = arms; if let Some(arm) = else_arm { @@ -828,11 +845,13 @@ fn block_let(i: &[u8]) -> IResult<&[u8], Node<'_>> { let mut p = tuple(( opt(tag("-")), ws(alt((tag("let"), tag("set")))), - ws(target), - opt(tuple((ws(tag("=")), ws(expr_any)))), - opt(tag("-")), + cut(tuple(( + ws(target), + opt(tuple((ws(tag("=")), ws(expr_any)))), + opt(tag("-")), + ))), )); - let (i, (pws, _, var, val, nws)) = p(i)?; + let (i, (pws, _, (var, val, nws))) = p(i)?; Ok(( i, @@ -848,18 +867,26 @@ fn block_for<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> let mut p = tuple(( opt(tag("-")), ws(tag("for")), - ws(target), - ws(tag("in")), - ws(expr_any), - opt(tag("-")), - |i| tag_block_end(i, s), - |i| parse_template(i, s), - |i| tag_block_start(i, s), - opt(tag("-")), - ws(tag("endfor")), - opt(tag("-")), + cut(tuple(( + ws(target), + ws(tag("in")), + cut(tuple(( + ws(expr_any), + opt(tag("-")), + |i| tag_block_end(i, s), + cut(tuple(( + |i| parse_template(i, s), + cut(tuple(( + |i| tag_block_start(i, s), + opt(tag("-")), + ws(tag("endfor")), + opt(tag("-")), + ))), + ))), + ))), + ))), )); - let (i, (pws1, _, var, _, iter, nws1, _, block, _, pws2, _, nws2)) = p(i)?; + let (i, (pws1, _, (var, _, (iter, nws1, _, (block, (_, pws2, _, nws2)))))) = p(i)?; Ok(( i, Node::Loop( @@ -881,21 +908,22 @@ fn block_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a> let mut start = tuple(( opt(tag("-")), ws(tag("block")), - ws(identifier), - opt(tag("-")), - |i| tag_block_end(i, s), - |i| parse_template(i, s), + cut(tuple((ws(identifier), opt(tag("-")), |i| { + tag_block_end(i, s) + }))), )); - let (i, (pws1, _, name, nws1, _, contents)) = start(i)?; + let (i, (pws1, _, (name, nws1, _))) = start(i)?; - let mut end = tuple(( - |i| tag_block_start(i, s), - opt(tag("-")), - ws(tag("endblock")), - opt(ws(tag(name))), - opt(tag("-")), - )); - let (i, (_, pws2, _, _, nws2)) = end(i)?; + let mut end = cut(tuple(( + |i| parse_template(i, s), + cut(tuple(( + |i| tag_block_start(i, s), + opt(tag("-")), + ws(tag("endblock")), + cut(tuple((opt(ws(tag(name))), opt(tag("-"))))), + ))), + ))); + let (i, (contents, (_, pws2, _, (_, nws2)))) = end(i)?; Ok(( i, @@ -912,10 +940,9 @@ fn block_include(i: &[u8]) -> IResult<&[u8], Node<'_>> { let mut p = tuple(( opt(tag("-")), ws(tag("include")), - ws(expr_str_lit), - opt(tag("-")), + cut(pair(ws(expr_str_lit), opt(tag("-")))), )); - let (i, (pws, _, name, nws)) = p(i)?; + let (i, (pws, _, (name, nws))) = p(i)?; Ok(( i, Node::Include( @@ -932,12 +959,13 @@ fn block_import(i: &[u8]) -> IResult<&[u8], Node<'_>> { let mut p = tuple(( opt(tag("-")), ws(tag("import")), - ws(expr_str_lit), - ws(tag("as")), - ws(identifier), - opt(tag("-")), + cut(tuple(( + ws(expr_str_lit), + ws(tag("as")), + cut(pair(ws(identifier), opt(tag("-")))), + ))), )); - let (i, (pws, _, name, _, scope, nws)) = p(i)?; + let (i, (pws, _, (name, _, (scope, nws)))) = p(i)?; Ok(( i, Node::Import( @@ -955,18 +983,24 @@ fn block_macro<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a> let mut p = tuple(( opt(tag("-")), ws(tag("macro")), - ws(identifier), - ws(parameters), - opt(tag("-")), - |i| tag_block_end(i, s), - |i| parse_template(i, s), - |i| tag_block_start(i, s), - opt(tag("-")), - ws(tag("endmacro")), - opt(tag("-")), + cut(tuple(( + ws(identifier), + ws(parameters), + opt(tag("-")), + |i| tag_block_end(i, s), + cut(tuple(( + |i| parse_template(i, s), + cut(tuple(( + |i| tag_block_start(i, s), + opt(tag("-")), + ws(tag("endmacro")), + opt(tag("-")), + ))), + ))), + ))), )); - let (i, (pws1, _, name, params, nws1, _, contents, _, pws2, _, nws2)) = p(i)?; + let (i, (pws1, _, (name, params, nws1, _, (contents, (_, pws2, _, nws2))))) = p(i)?; if name == "super" { panic!("invalid macro name 'super'"); } @@ -989,16 +1023,18 @@ fn block_raw<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> let mut p = tuple(( opt(tag("-")), ws(tag("raw")), - opt(tag("-")), - |i| tag_block_end(i, s), - take_until("{% endraw %}"), - |i| tag_block_start(i, s), - opt(tag("-")), - ws(tag("endraw")), - opt(tag("-")), + cut(tuple(( + opt(tag("-")), + |i| tag_block_end(i, s), + take_until("{% endraw %}"), + |i| tag_block_start(i, s), + opt(tag("-")), + ws(tag("endraw")), + opt(tag("-")), + ))), )); - let (i, (pws1, _, nws1, _, contents, _, pws2, _, nws2)) = p(i)?; + let (i, (pws1, _, (nws1, _, contents, _, pws2, _, nws2))) = p(i)?; let str_contents = str::from_utf8(contents).unwrap(); Ok(( i, @@ -1026,7 +1062,7 @@ fn block_node<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> |i| block_macro(i, s), |i| block_raw(i, s), )), - |i| tag_block_end(i, s), + cut(|i| tag_block_end(i, s)), )); let (i, (_, contents, _)) = p(i)?; Ok((i, contents)) @@ -1053,11 +1089,13 @@ fn block_comment_body<'a>(mut i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8 fn block_comment<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> { let mut p = tuple(( |i| tag_comment_start(i, s), - opt(tag("-")), - |i| block_comment_body(i, s), - |i| tag_comment_end(i, s), + cut(tuple(( + opt(tag("-")), + |i| block_comment_body(i, s), + |i| tag_comment_end(i, s), + ))), )); - let (i, (_, pws, tail, _)) = p(i)?; + let (i, (_, (pws, tail, _))) = p(i)?; Ok((i, Node::Comment(Ws(pws.is_some(), tail.ends_with(b"-"))))) } @@ -1099,12 +1137,29 @@ pub fn parse<'a>(src: &'a str, syntax: &'a Syntax<'a>) -> Result<Vec<Node<'a>>, Ok(res) } } - Err(nom::Err::Error(err)) => { - Err(format!("problems parsing template source: {:?}", err).into()) - } - Err(nom::Err::Failure(err)) => { - Err(format!("problems parsing template source: {:?}", err).into()) + + Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => { + let nom::error::Error { input, .. } = err; + let offset = src.len() - input.len(); + let (source_before, source_after) = src.split_at(offset); + + let source_after = match source_after.char_indices().enumerate().take(41).last() { + Some((40, (i, _))) => format!("{:?}...", &source_after[..i]), + _ => format!("{:?}", source_after), + }; + + let (row, last_line) = source_before.lines().enumerate().last().unwrap(); + let column = last_line.chars().count(); + + let msg = format!( + "problems parsing template source at row {}, column {} near:\n{}", + row + 1, + column, + source_after, + ); + Err(msg.into()) } + Err(nom::Err::Incomplete(_)) => Err("parsing incomplete".into()), } } diff --git a/testing/tests/ui/typo_in_keyword.rs b/testing/tests/ui/typo_in_keyword.rs new file mode 100644 index 0000000..acbf5d7 --- /dev/null +++ b/testing/tests/ui/typo_in_keyword.rs @@ -0,0 +1,11 @@ +use askama::Template; + +#[derive(Template)] +#[template( + source = "{%for i in 1..=10%}{{i}}{%endfo%}\n1234567890123456789012345678901234567890", + ext = "txt" +)] +struct MyTemplate; + +fn main() { +} diff --git a/testing/tests/ui/typo_in_keyword.stderr b/testing/tests/ui/typo_in_keyword.stderr new file mode 100644 index 0000000..545476e --- /dev/null +++ b/testing/tests/ui/typo_in_keyword.stderr @@ -0,0 +1,8 @@ +error: problems parsing template source at row 1, column 26 near: +"endfo%}\n12345678901234567890123456789012"... + --> $DIR/typo_in_keyword.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) |