aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar René Kijewski <rene.kijewski@fu-berlin.de>2022-11-09 02:30:03 +0100
committerLibravatar Dirkjan Ochtman <dirkjan@ochtman.nl>2022-11-09 09:38:25 +0100
commit58d3938fb7631c3ab74b7bf48a21240face3a265 (patch)
treee0c740750a5bcdad788958178e6e866a8539bb3a
parent6f52d0eef5a3178e1ebf7d4c7e2cf397756f57eb (diff)
downloadaskama-58d3938fb7631c3ab74b7bf48a21240face3a265.tar.gz
askama-58d3938fb7631c3ab74b7bf48a21240face3a265.tar.bz2
askama-58d3938fb7631c3ab74b7bf48a21240face3a265.zip
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.
-rw-r--r--askama_derive/src/parser.rs87
1 files 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<usize>,
@@ -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<Nod
}
fn block_for<'a>(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%}\""
+ ));
+ }
}