From 58d3938fb7631c3ab74b7bf48a21240face3a265 Mon Sep 17 00:00:00 2001 From: René Kijewski Date: Wed, 9 Nov 2022 02:30:03 +0100 Subject: Fail to parse if keyword is not longest ident This PR makes e.g. `{% leta = b %}` a parsing error. To the reader it would appear that `leta` should be a meaningful expression in Askama, which it is not. Before this PR, `leta` was tokenized as `let` + `a`. This PR makes the parser try to find the longest identifier at a parsing positions and only compare the outcome against the expected keyword. This is potentially a breaking change, because code that should always have been invalid will now fail to compile, when it was accepted before. --- askama_derive/src/parser.rs | 87 +++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/askama_derive/src/parser.rs b/askama_derive/src/parser.rs index dc163fa..650eabf 100644 --- a/askama_derive/src/parser.rs +++ b/askama_derive/src/parser.rs @@ -238,6 +238,17 @@ fn skip_till<'a, O>( } } +fn keyword<'a>(k: &'a str) -> impl FnMut(&'a str) -> IResult<&'a str, &'a str> { + move |i: &'a str| -> IResult<&'a str, &'a str> { + let (j, v) = identifier(i)?; + if k == v { + Ok((j, v)) + } else { + Err(nom::Err::Error(error_position!(i, ErrorKind::Tag))) + } + } +} + struct State<'a> { syntax: &'a Syntax<'a>, loop_depth: Cell, @@ -282,7 +293,7 @@ fn identifier_tail(s: &str) -> IResult<&str, &str> { } fn bool_lit(i: &str) -> IResult<&str, &str> { - alt((tag("false"), tag("true")))(i) + alt((keyword("false"), keyword("true")))(i) } fn expr_bool_lit(i: &str) -> IResult<&str, Expr<'_>> { @@ -442,7 +453,7 @@ fn target(i: &str) -> IResult<&str, Target<'_>> { let (i, path) = opt(path)(i)?; if let Some(path) = path { let i_before_matching_with = i; - let (i, _) = opt(ws(tag("with")))(i)?; + let (i, _) = opt(ws(keyword("with")))(i)?; let (i, is_unnamed_struct) = opt_opening_paren(i)?; if is_unnamed_struct { @@ -749,7 +760,7 @@ fn expr_node<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { fn block_call(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( opt(expr_handle_ws), - ws(tag("call")), + ws(keyword("call")), cut(tuple(( opt(tuple((ws(identifier), ws(tag("::"))))), ws(identifier), @@ -764,10 +775,10 @@ fn block_call(i: &str) -> IResult<&str, Node<'_>> { fn cond_if(i: &str) -> IResult<&str, CondTest<'_>> { let mut p = preceded( - ws(tag("if")), + ws(keyword("if")), cut(tuple(( opt(delimited( - ws(alt((tag("let"), tag("set")))), + ws(alt((keyword("let"), keyword("set")))), ws(target), ws(char('=')), )), @@ -782,7 +793,7 @@ fn cond_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Cond<'a>> { let mut p = tuple(( |i| tag_block_start(i, s), opt(expr_handle_ws), - ws(tag("else")), + ws(keyword("else")), cut(tuple(( opt(cond_if), opt(expr_handle_ws), @@ -807,7 +818,7 @@ fn block_if<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { cut(tuple(( |i| tag_block_start(i, s), opt(expr_handle_ws), - ws(tag("endif")), + ws(keyword("endif")), opt(expr_handle_ws), ))), ))), @@ -824,7 +835,7 @@ fn match_else_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, When<'a>> let mut p = tuple(( |i| tag_block_start(i, s), opt(expr_handle_ws), - ws(tag("else")), + ws(keyword("else")), cut(tuple(( opt(expr_handle_ws), |i| tag_block_end(i, s), @@ -839,7 +850,7 @@ fn when_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, When<'a>> { let mut p = tuple(( |i| tag_block_start(i, s), opt(expr_handle_ws), - ws(tag("when")), + ws(keyword("when")), cut(tuple(( ws(target), opt(expr_handle_ws), @@ -854,7 +865,7 @@ fn when_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, When<'a>> { fn block_match<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( opt(expr_handle_ws), - ws(tag("match")), + ws(keyword("match")), cut(tuple(( ws(expr_any), opt(expr_handle_ws), @@ -867,7 +878,7 @@ fn block_match<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { cut(tuple(( ws(|i| tag_block_start(i, s)), opt(expr_handle_ws), - ws(tag("endmatch")), + ws(keyword("endmatch")), opt(expr_handle_ws), ))), ))), @@ -887,7 +898,7 @@ fn block_match<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { fn block_let(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( opt(expr_handle_ws), - ws(alt((tag("let"), tag("set")))), + ws(alt((keyword("let"), keyword("set")))), cut(tuple(( ws(target), opt(tuple((ws(char('=')), ws(expr_any)))), @@ -914,10 +925,10 @@ fn parse_loop_content<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Vec(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { - let if_cond = preceded(ws(tag("if")), cut(ws(expr_any))); + let if_cond = preceded(ws(keyword("if")), cut(ws(expr_any))); let else_block = |i| { let mut p = preceded( - ws(tag("else")), + ws(keyword("else")), cut(tuple(( opt(expr_handle_ws), delimited( @@ -933,10 +944,10 @@ fn block_for<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { }; let mut p = tuple(( opt(expr_handle_ws), - ws(tag("for")), + ws(keyword("for")), cut(tuple(( ws(target), - ws(tag("in")), + ws(keyword("in")), cut(tuple(( ws(expr_any), opt(if_cond), @@ -948,7 +959,7 @@ fn block_for<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { |i| tag_block_start(i, s), opt(expr_handle_ws), opt(else_block), - ws(tag("endfor")), + ws(keyword("endfor")), opt(expr_handle_ws), ))), ))), @@ -974,14 +985,14 @@ fn block_for<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { } fn block_extends(i: &str) -> IResult<&str, Node<'_>> { - let (i, (_, name)) = tuple((ws(tag("extends")), ws(expr_str_lit)))(i)?; + let (i, (_, name)) = tuple((ws(keyword("extends")), ws(expr_str_lit)))(i)?; Ok((i, Node::Extends(name))) } fn block_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut start = tuple(( opt(expr_handle_ws), - ws(tag("block")), + ws(keyword("block")), cut(tuple((ws(identifier), opt(expr_handle_ws), |i| { tag_block_end(i, s) }))), @@ -993,8 +1004,8 @@ fn block_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { cut(tuple(( |i| tag_block_start(i, s), opt(expr_handle_ws), - ws(tag("endblock")), - cut(tuple((opt(ws(tag(name))), opt(expr_handle_ws)))), + ws(keyword("endblock")), + cut(tuple((opt(ws(keyword(name))), opt(expr_handle_ws)))), ))), ))); let (i, (contents, (_, pws2, _, (_, nws2)))) = end(i)?; @@ -1008,7 +1019,7 @@ fn block_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { fn block_include(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( opt(expr_handle_ws), - ws(tag("include")), + ws(keyword("include")), cut(pair(ws(str_lit), opt(expr_handle_ws))), )); let (i, (pws, _, (name, nws))) = p(i)?; @@ -1018,10 +1029,10 @@ fn block_include(i: &str) -> IResult<&str, Node<'_>> { fn block_import(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( opt(expr_handle_ws), - ws(tag("import")), + ws(keyword("import")), cut(tuple(( ws(str_lit), - ws(tag("as")), + ws(keyword("as")), cut(pair(ws(identifier), opt(expr_handle_ws))), ))), )); @@ -1032,7 +1043,7 @@ fn block_import(i: &str) -> IResult<&str, Node<'_>> { fn block_macro<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut start = tuple(( opt(expr_handle_ws), - ws(tag("macro")), + ws(keyword("macro")), cut(tuple(( ws(identifier), ws(parameters), @@ -1047,8 +1058,8 @@ fn block_macro<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { cut(tuple(( |i| tag_block_start(i, s), opt(expr_handle_ws), - ws(tag("endmacro")), - cut(tuple((opt(ws(tag(name))), opt(expr_handle_ws)))), + ws(keyword("endmacro")), + cut(tuple((opt(ws(keyword(name))), opt(expr_handle_ws)))), ))), ))); let (i, (contents, (_, pws2, _, (_, nws2)))) = end(i)?; @@ -1073,14 +1084,14 @@ fn block_raw<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let endraw = tuple(( |i| tag_block_start(i, s), opt(expr_handle_ws), - ws(tag("endraw")), + ws(keyword("endraw")), opt(expr_handle_ws), peek(|i| tag_block_end(i, s)), )); let mut p = tuple(( opt(expr_handle_ws), - ws(tag("raw")), + ws(keyword("raw")), cut(tuple(( opt(expr_handle_ws), |i| tag_block_end(i, s), @@ -1099,7 +1110,11 @@ fn block_raw<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { } fn break_statement<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { - let mut p = tuple((opt(expr_handle_ws), ws(tag("break")), opt(expr_handle_ws))); + let mut p = tuple(( + opt(expr_handle_ws), + ws(keyword("break")), + opt(expr_handle_ws), + )); let (j, (pws, _, nws)) = p(i)?; if s.loop_depth.get() == 0 { return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))); @@ -1110,7 +1125,7 @@ fn break_statement<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> fn continue_statement<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( opt(expr_handle_ws), - ws(tag("continue")), + ws(keyword("continue")), opt(expr_handle_ws), )); let (j, (pws, _, nws)) = p(i)?; @@ -1915,4 +1930,14 @@ mod tests { )], ); } + + #[test] + fn test_missing_space_after_kw() { + let syntax = Syntax::default(); + let err = super::parse("{%leta=b%}", &syntax).unwrap_err(); + assert!(matches!( + &*err.msg, + "unable to parse template:\n\n\"{%leta=b%}\"" + )); + } } -- cgit