Skip to content

Commit

Permalink
Fail to parse if keyword is not longest ident
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Kijewski authored and djc committed Nov 9, 2022
1 parent 6f52d0e commit 58d3938
Showing 1 changed file with 56 additions and 31 deletions.
87 changes: 56 additions & 31 deletions askama_derive/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down Expand Up @@ -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<'_>> {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
Expand All @@ -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('=')),
)),
Expand All @@ -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),
Expand All @@ -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),
))),
))),
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -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),
))),
))),
Expand All @@ -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)))),
Expand All @@ -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(
Expand All @@ -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),
Expand All @@ -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),
))),
))),
Expand All @@ -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)
}))),
Expand All @@ -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)?;
Expand All @@ -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)?;
Expand All @@ -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))),
))),
));
Expand All @@ -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),
Expand All @@ -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)?;
Expand All @@ -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),
Expand All @@ -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)));
Expand All @@ -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)?;
Expand Down Expand Up @@ -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%}\""
));
}
}

0 comments on commit 58d3938

Please sign in to comment.