diff --git a/askama_parser/src/expr.rs b/askama_parser/src/expr.rs index 11b6a7be..e70a8042 100644 --- a/askama_parser/src/expr.rs +++ b/askama_parser/src/expr.rs @@ -12,8 +12,8 @@ use winnow::token::take_until; use crate::node::CondTest; use crate::{ CharLit, ErrorContext, Level, Num, ParseErr, ParseResult, PathOrIdentifier, Span, StrLit, - StrPrefix, WithSpan, char_lit, filter, identifier, keyword, num_lit, path_or_identifier, - skip_ws0, skip_ws1, str_lit, ws, + StrPrefix, WithSpan, char_lit, filter, identifier, keyword, not_suffix_with_hash, num_lit, + path_or_identifier, skip_ws0, skip_ws1, str_lit, ws, }; macro_rules! expr_prec_layer { @@ -774,9 +774,9 @@ impl<'a> Suffix<'a> { // let some_other = alt(( // literals - Expr::char.value(Token::SomeOther), - Expr::str.value(Token::SomeOther), - Expr::num.value(Token::SomeOther), + char_lit.value(Token::SomeOther), + str_lit.value(Token::SomeOther), + num_lit.value(Token::SomeOther), // keywords + (raw) identifiers + raw strings identifier_or_prefixed_string.value(Token::SomeOther), // lifetimes @@ -886,6 +886,7 @@ impl<'a> Suffix<'a> { return Err(winnow::error::ErrMode::Cut(ErrorContext::new(msg, prefix))); } + not_suffix_with_hash(i)?; Ok(()) } else if hashes == 0 { // a simple identifier diff --git a/askama_parser/src/lib.rs b/askama_parser/src/lib.rs index 05f0e893..e69db776 100644 --- a/askama_parser/src/lib.rs +++ b/askama_parser/src/lib.rs @@ -680,9 +680,20 @@ fn str_lit<'a>(i: &mut &'a str) -> ParseResult<'a, StrLit<'a>> { return Err(winnow::error::ErrMode::Cut(ErrorContext::new(msg, start))); } + not_suffix_with_hash(i)?; Ok(lit) } +fn not_suffix_with_hash<'a>(i: &mut &'a str) -> ParseResult<'a, ()> { + if let Some(suffix) = opt((identifier, '#').take()).parse_next(i)? { + return Err(winnow::error::ErrMode::Cut(ErrorContext::new( + "you are missing a space to separate two string literals", + suffix, + ))); + } + Ok(()) +} + fn str_lit_without_prefix<'a>(i: &mut &'a str) -> ParseResult<'a> { let start = *i; let lit = str_lit.parse_next(i)?; diff --git a/fuzzing/fuzz/artifacts/derive/clusterfuzz-testcase-minimized-derive-5353647120908288 b/fuzzing/fuzz/artifacts/derive/clusterfuzz-testcase-minimized-derive-5353647120908288 new file mode 100644 index 00000000..fb3ccafb --- /dev/null +++ b/fuzzing/fuzz/artifacts/derive/clusterfuzz-testcase-minimized-derive-5353647120908288 @@ -0,0 +1 @@ +ÿÿÿ {{ l ! { """" "" ""r#" """"""#"""""""""" }}} ÿ }o Ë \ No newline at end of file diff --git a/testing/tests/ui/raw-prefix.rs b/testing/tests/ui/raw-prefix.rs index 8c7388fb..857826fe 100644 --- a/testing/tests/ui/raw-prefix.rs +++ b/testing/tests/ui/raw-prefix.rs @@ -105,4 +105,19 @@ struct MacroCallReservedPrefix2; #[template(path = "macro-call-raw-string-many-hashes.html")] struct MacroCallManyHashes; +// Need a space between (guarded) string literal and next prefixed string literal. +// Regression test for . + +#[derive(Template)] +#[template(source = r##"{{ z!(r""r#""#) }}"##, ext = "txt")] +struct UnseparatedPrefixedStrings1; + +#[derive(Template)] +#[template(source = r##"{{ z!(r#""#x#"") }}"##, ext = "txt")] +struct UnseparatedPrefixedStrings2; + +#[derive(Template)] +#[template(source = r##"{{ z!(c""r#""#) }}"##, ext = "txt")] +struct UnseparatedPrefixedStrings3; + fn main() {} diff --git a/testing/tests/ui/raw-prefix.stderr b/testing/tests/ui/raw-prefix.stderr index 929a33b3..195d7847 100644 --- a/testing/tests/ui/raw-prefix.stderr +++ b/testing/tests/ui/raw-prefix.stderr @@ -109,3 +109,27 @@ error: a maximum of 255 hashes `#` are allowed with raw strings | 105 | #[template(path = "macro-call-raw-string-many-hashes.html")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you are missing a space to separate two string literals + --> :1:9 + "r#\"\"#) }}" + --> tests/ui/raw-prefix.rs:112:21 + | +112 | #[template(source = r##"{{ z!(r""r#""#) }}"##, ext = "txt")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you are missing a space to separate two string literals + --> :1:11 + "x#\"\") }}" + --> tests/ui/raw-prefix.rs:116:21 + | +116 | #[template(source = r##"{{ z!(r#""#x#"") }}"##, ext = "txt")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you are missing a space to separate two string literals + --> :1:9 + "r#\"\"#) }}" + --> tests/ui/raw-prefix.rs:120:21 + | +120 | #[template(source = r##"{{ z!(c""r#""#) }}"##, ext = "txt")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^