mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-27 13:00:57 +00:00
parser: better messages for (group)
This commit is contained in:
parent
c0d6f0f0fc
commit
98ad5d5b3f
@ -571,33 +571,86 @@ impl<'a> Expr<'a> {
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = ***i;
|
||||
let expr = preceded(ws('('), opt(|i: &mut _| Self::parse(i, level, true))).parse_next(i)?;
|
||||
let Some(expr) = expr else {
|
||||
let _ = ')'.parse_next(i)?;
|
||||
return Ok(WithSpan::new(Box::new(Self::Tuple(vec![])), start, i));
|
||||
};
|
||||
ws('(').parse_next(i)?;
|
||||
Self::group_actually(i, level)
|
||||
}
|
||||
|
||||
let comma = ws(opt(peek(','))).parse_next(i)?;
|
||||
if comma.is_none() {
|
||||
let _ = ')'.parse_next(i)?;
|
||||
return Ok(WithSpan::new(Box::new(Self::Group(expr)), start, i));
|
||||
// `Self::group()` is quite big. Let's only put it on the stack if needed.
|
||||
#[inline(never)]
|
||||
fn group_actually(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let (expr, span) = cut_err(|i: &mut _| Self::group_actually_inner(i, level))
|
||||
.with_taken()
|
||||
.parse_next(i)?;
|
||||
Ok(WithSpan::new_with_full(expr, span.trim_ascii()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn group_actually_inner(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, Box<Self>> {
|
||||
enum GroupResult<'a> {
|
||||
Tuple(WithSpan<'a, Box<Expr<'a>>>),
|
||||
Expr(Box<Expr<'a>>),
|
||||
Err(&'static str),
|
||||
}
|
||||
|
||||
let (expr, comma, closing) = (
|
||||
opt(|i: &mut _| Self::parse(i, level, true)),
|
||||
ws(opt(','.take())),
|
||||
opt(')'),
|
||||
)
|
||||
.parse_next(i)?;
|
||||
|
||||
let result = match (expr, comma, closing) {
|
||||
// `(expr,`
|
||||
(Some(expr), Some(_), None) => GroupResult::Tuple(expr),
|
||||
// `()`
|
||||
(None, None, Some(_)) => GroupResult::Expr(Box::new(Self::Tuple(vec![]))),
|
||||
// `(expr)`
|
||||
(Some(expr), None, Some(_)) => GroupResult::Expr(Box::new(Self::Group(expr))),
|
||||
// `(expr,)`
|
||||
(Some(expr), Some(_), Some(_)) => GroupResult::Expr(Box::new(Self::Tuple(vec![expr]))),
|
||||
// `(`
|
||||
(None, None, None) => GroupResult::Err("expected closing `)` or an expression"),
|
||||
// `(expr`
|
||||
(Some(_), None, None) => GroupResult::Err("expected `,` or `)`"),
|
||||
// `(,`
|
||||
(None, Some(span), _) => return cut_error!("stray comma after opening `(`", span),
|
||||
};
|
||||
let expr = match result {
|
||||
GroupResult::Tuple(expr) => expr,
|
||||
GroupResult::Expr(expr) => return Ok(expr),
|
||||
GroupResult::Err(msg) => return cut_error!(msg, ***i),
|
||||
};
|
||||
|
||||
let mut exprs = vec![expr];
|
||||
repeat(
|
||||
0..,
|
||||
preceded(',', ws(|i: &mut _| Self::parse(i, level, true))),
|
||||
)
|
||||
.fold(
|
||||
|| (),
|
||||
|(), expr| {
|
||||
exprs.push(expr);
|
||||
let collect_items = opt(separated(
|
||||
1..,
|
||||
|i: &mut _| {
|
||||
exprs.push(Self::parse(i, level, true)?);
|
||||
Ok(())
|
||||
},
|
||||
ws(','),
|
||||
)
|
||||
.parse_next(i)?;
|
||||
let _ = (ws(opt(',')), ')').parse_next(i)?;
|
||||
Ok(WithSpan::new(Box::new(Self::Tuple(exprs)), start, i))
|
||||
.map(|()| ()));
|
||||
|
||||
let ((items, comma, close), span) = cut_err((collect_items, ws(opt(',')), opt(')')))
|
||||
.with_taken()
|
||||
.parse_next(i)?;
|
||||
let msg = if items.is_none() {
|
||||
"expected `)` or an expression"
|
||||
} else if close.is_some() {
|
||||
return Ok(Box::new(Self::Tuple(exprs)));
|
||||
} else if comma.is_some() {
|
||||
"expected `)` or an expression"
|
||||
} else {
|
||||
"expected `,` or `)`"
|
||||
};
|
||||
cut_error!(msg, span)
|
||||
}
|
||||
|
||||
fn array(
|
||||
|
75
testing/tests/ui/groups.rs
Normal file
75
testing/tests/ui/groups.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use askama::Template;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = ( %}"#, ext = "html")]
|
||||
struct TruncatedInput;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (a, %}"#, ext = "html")]
|
||||
struct TruncatedInput2;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (a, b %}"#, ext = "html")]
|
||||
struct TruncatedInput3;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (a, b, %}"#, ext = "html")]
|
||||
struct TruncatedInput4;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (() %}"#, ext = "html")]
|
||||
struct MoreOpenThanClose;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = ((()) %}"#, ext = "html")]
|
||||
struct MoreOpenThanClose2;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (,) %}"#, ext = "html")]
|
||||
struct ExtraneousComma;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (,,) %}"#, ext = "html")]
|
||||
struct ExtraneousComma2;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (a,,) %}"#, ext = "html")]
|
||||
struct ExtraneousComma3;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (a, b,,) %}"#, ext = "html")]
|
||||
struct ExtraneousComma4;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (, %}"#, ext = "html")]
|
||||
struct NoCloseAndExtraneousComma;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (,, %}"#, ext = "html")]
|
||||
struct NoCloseAndExtraneousComma2;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (a,, %}"#, ext = "html")]
|
||||
struct NoCloseAndExtraneousComma3;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (a, b,, %}"#, ext = "html")]
|
||||
struct NoCloseAndExtraneousComma4;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (a b) %}"#, ext = "html")]
|
||||
struct MissingComma;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (a b c) %}"#, ext = "html")]
|
||||
struct MissingComma2;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (a b %}"#, ext = "html")]
|
||||
struct NoCloseAndMissingComma;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{% let test = (a b c %}"#, ext = "html")]
|
||||
struct NoCloseAndMissingComma2;
|
||||
|
||||
fn main() {}
|
143
testing/tests/ui/groups.stderr
Normal file
143
testing/tests/ui/groups.stderr
Normal file
@ -0,0 +1,143 @@
|
||||
error: expected closing `)` or an expression
|
||||
--> <source attribute>:1:16
|
||||
"%}"
|
||||
--> tests/ui/groups.rs:4:21
|
||||
|
|
||||
4 | #[template(source = r#"{% let test = ( %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `)` or an expression
|
||||
--> <source attribute>:1:18
|
||||
"%}"
|
||||
--> tests/ui/groups.rs:8:21
|
||||
|
|
||||
8 | #[template(source = r#"{% let test = (a, %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `,` or `)`
|
||||
--> <source attribute>:1:18
|
||||
"b %}"
|
||||
--> tests/ui/groups.rs:12:21
|
||||
|
|
||||
12 | #[template(source = r#"{% let test = (a, b %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `)` or an expression
|
||||
--> <source attribute>:1:18
|
||||
"b, %}"
|
||||
--> tests/ui/groups.rs:16:21
|
||||
|
|
||||
16 | #[template(source = r#"{% let test = (a, b, %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `,` or `)`
|
||||
--> <source attribute>:1:18
|
||||
"%}"
|
||||
--> tests/ui/groups.rs:20:21
|
||||
|
|
||||
20 | #[template(source = r#"{% let test = (() %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `,` or `)`
|
||||
--> <source attribute>:1:20
|
||||
"%}"
|
||||
--> tests/ui/groups.rs:24:21
|
||||
|
|
||||
24 | #[template(source = r#"{% let test = ((()) %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: stray comma after opening `(`
|
||||
--> <source attribute>:1:15
|
||||
",) %}"
|
||||
--> tests/ui/groups.rs:28:21
|
||||
|
|
||||
28 | #[template(source = r#"{% let test = (,) %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: stray comma after opening `(`
|
||||
--> <source attribute>:1:15
|
||||
",,) %}"
|
||||
--> tests/ui/groups.rs:32:21
|
||||
|
|
||||
32 | #[template(source = r#"{% let test = (,,) %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `)` or an expression
|
||||
--> <source attribute>:1:17
|
||||
",) %}"
|
||||
--> tests/ui/groups.rs:36:21
|
||||
|
|
||||
36 | #[template(source = r#"{% let test = (a,,) %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `)` or an expression
|
||||
--> <source attribute>:1:18
|
||||
"b,,) %}"
|
||||
--> tests/ui/groups.rs:40:21
|
||||
|
|
||||
40 | #[template(source = r#"{% let test = (a, b,,) %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: stray comma after opening `(`
|
||||
--> <source attribute>:1:15
|
||||
", %}"
|
||||
--> tests/ui/groups.rs:44:21
|
||||
|
|
||||
44 | #[template(source = r#"{% let test = (, %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: stray comma after opening `(`
|
||||
--> <source attribute>:1:15
|
||||
",, %}"
|
||||
--> tests/ui/groups.rs:48:21
|
||||
|
|
||||
48 | #[template(source = r#"{% let test = (,, %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `)` or an expression
|
||||
--> <source attribute>:1:17
|
||||
", %}"
|
||||
--> tests/ui/groups.rs:52:21
|
||||
|
|
||||
52 | #[template(source = r#"{% let test = (a,, %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `)` or an expression
|
||||
--> <source attribute>:1:18
|
||||
"b,, %}"
|
||||
--> tests/ui/groups.rs:56:21
|
||||
|
|
||||
56 | #[template(source = r#"{% let test = (a, b,, %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `,` or `)`
|
||||
--> <source attribute>:1:17
|
||||
"b) %}"
|
||||
--> tests/ui/groups.rs:60:21
|
||||
|
|
||||
60 | #[template(source = r#"{% let test = (a b) %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `,` or `)`
|
||||
--> <source attribute>:1:17
|
||||
"b c) %}"
|
||||
--> tests/ui/groups.rs:64:21
|
||||
|
|
||||
64 | #[template(source = r#"{% let test = (a b c) %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `,` or `)`
|
||||
--> <source attribute>:1:17
|
||||
"b %}"
|
||||
--> tests/ui/groups.rs:68:21
|
||||
|
|
||||
68 | #[template(source = r#"{% let test = (a b %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `,` or `)`
|
||||
--> <source attribute>:1:17
|
||||
"b c %}"
|
||||
--> tests/ui/groups.rs:72:21
|
||||
|
|
||||
72 | #[template(source = r#"{% let test = (a b c %}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
Loading…
x
Reference in New Issue
Block a user