parser: str lit must not be followed by suffix#

Resolves <https://issues.oss-fuzz.com/issues/426509683>.
This commit is contained in:
René Kijewski 2025-06-21 16:52:28 +02:00 committed by René Kijewski
parent 2e510b0862
commit 76b26cd5fa
5 changed files with 57 additions and 5 deletions

View File

@ -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> {
// <https://doc.rust-lang.org/reference/tokens.html>
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

View File

@ -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)?;

View File

@ -0,0 +1 @@
˙˙˙ {{ l ! { """" "" ""r#" """"""#"""""""""" }}} ˙ }o Ë

View File

@ -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 <https://issues.oss-fuzz.com/issues/426509683>.
#[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() {}

View File

@ -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
--> <source attribute>: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
--> <source attribute>: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
--> <source attribute>:1:9
"r#\"\"#) }}"
--> tests/ui/raw-prefix.rs:120:21
|
120 | #[template(source = r##"{{ z!(c""r#""#) }}"##, ext = "txt")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^