parser: recognize comments in macro calls

Fixes issue <https://issues.oss-fuzz.com/issues/425145246>
This commit is contained in:
René Kijewski 2025-06-16 19:16:51 +02:00 committed by René Kijewski
parent e329f98433
commit 568ced0c55
5 changed files with 225 additions and 1 deletions

View File

@ -4,9 +4,10 @@ use std::str;
use winnow::Parser;
use winnow::ascii::digit1;
use winnow::combinator::{
alt, cut_err, fail, not, opt, peek, preceded, repeat, separated, terminated,
alt, cut_err, empty, fail, not, opt, peek, preceded, repeat, separated, terminated,
};
use winnow::error::ParserError as _;
use winnow::token::take_until;
use crate::node::CondTest;
use crate::{
@ -780,6 +781,9 @@ impl<'a> Suffix<'a> {
identifier_or_prefixed_string.value(Token::SomeOther),
// lifetimes
('\'', identifier, not(peek('\''))).value(Token::SomeOther),
// comments
line_comment.value(Token::SomeOther),
block_comment.value(Token::SomeOther),
// punctuations
punctuation.value(Token::SomeOther),
hash,
@ -787,6 +791,46 @@ impl<'a> Suffix<'a> {
alt((open.map(Token::Open), close.map(Token::Close), some_other)).parse_next(i)
}
fn line_comment<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
let start = "//".parse_next(i)?;
let is_doc_comment = alt((
('/', not(peek('/'))).value(true),
'!'.value(true),
empty.value(false),
))
.parse_next(i)?;
if opt(take_until(.., '\n')).parse_next(i)?.is_none() {
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
format!(
"you are probably missing a line break to end {}comment",
if is_doc_comment { "doc " } else { "" }
),
start,
)));
}
Ok(())
}
fn block_comment<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
let start = "/*".parse_next(i)?;
let is_doc_comment = alt((
('*', not(peek('*'))).value(true),
'!'.value(true),
empty.value(false),
))
.parse_next(i)?;
if opt(take_until(.., "*/")).parse_next(i)?.is_none() {
return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
format!(
"missing `*/` to close block {}comment",
if is_doc_comment { "doc " } else { "" }
),
start,
)));
}
Ok(())
}
fn identifier_or_prefixed_string<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
// <https://doc.rust-lang.org/reference/tokens.html#r-lex.token.literal.str-raw.syntax>

View File

@ -1441,3 +1441,70 @@ fn macro_calls_can_have_raw_prefixes() {
)],
);
}
#[test]
fn macro_comments_in_macro_calls() {
// Related to issue <https://github.com/askama-rs/askama/issues/475>.
let syntax = Syntax::default();
assert!(Ast::from_str("{{ e!(// hello) }}", None, &syntax).is_err());
assert!(Ast::from_str("{{ e!(/// hello) }}", None, &syntax).is_err());
assert!(Ast::from_str("{{ e!(// hello)\n }}", None, &syntax).is_err());
assert!(Ast::from_str("{{ e!(/// hello)\n }}", None, &syntax).is_err());
assert_eq!(
Ast::from_str("{{ e!(// hello\n) }}", None, &syntax)
.unwrap()
.nodes,
vec![Node::Expr(
Ws(None, None),
WithSpan::no_span(Expr::RustMacro(vec!["e"], "// hello\n")),
)],
);
assert_eq!(
Ast::from_str("{{ e!(/// hello\n) }}", None, &syntax)
.unwrap()
.nodes,
vec![Node::Expr(
Ws(None, None),
WithSpan::no_span(Expr::RustMacro(vec!["e"], "/// hello\n")),
)],
);
assert_eq!(
Ast::from_str("{{ e!(//! hello\n) }}", None, &syntax)
.unwrap()
.nodes,
vec![Node::Expr(
Ws(None, None),
WithSpan::no_span(Expr::RustMacro(vec!["e"], "//! hello\n")),
)],
);
assert_eq!(
Ast::from_str("{{ e!(/* hello */) }}", None, &syntax)
.unwrap()
.nodes,
vec![Node::Expr(
Ws(None, None),
WithSpan::no_span(Expr::RustMacro(vec!["e"], "/* hello */")),
)],
);
assert_eq!(
Ast::from_str("{{ e!(/** hello */) }}", None, &syntax)
.unwrap()
.nodes,
vec![Node::Expr(
Ws(None, None),
WithSpan::no_span(Expr::RustMacro(vec!["e"], "/** hello */")),
)],
);
assert_eq!(
Ast::from_str("{{ e!(/*! hello */) }}", None, &syntax)
.unwrap()
.nodes,
vec![Node::Expr(
Ws(None, None),
WithSpan::no_span(Expr::RustMacro(vec!["e"], "/*! hello */")),
)],
);
}

View File

@ -0,0 +1 @@
˙˙˙{{e!{//}}}˙˙˙˙s˙

View File

@ -0,0 +1,41 @@
use askama::Template;
// Regression test for <https://issues.oss-fuzz.com/issues/425145246>.
#[derive(Template)]
#[template(ext = "html", source = "{{ e!(// hello) }}")]
struct LineComment;
#[derive(Template)]
#[template(ext = "html", source = "{{ e!(// hello)\n}}")]
struct LineComment2;
#[derive(Template)]
#[template(ext = "html", source = "{{ e!(/* hello) }}")]
struct BlockComment;
#[derive(Template)]
#[template(ext = "html", source = "{{ e!(/// hello) }}")]
struct OuterLineDoc;
#[derive(Template)]
#[template(ext = "html", source = "{{ e!(/// hello)\n}}")]
struct OuterLineDoc2;
#[derive(Template)]
#[template(ext = "html", source = "{{ e!(/** hello) }}")]
struct OuterBlockDoc;
#[derive(Template)]
#[template(ext = "html", source = "{{ e!(//! hello) }}")]
struct InnerLineDoc;
#[derive(Template)]
#[template(ext = "html", source = "{{ e!(//! hello)\n}}")]
struct InnerLineDoc2;
#[derive(Template)]
#[template(ext = "html", source = "{{ e!(/*! hello) }}")]
struct InnerBlockDoc;
fn main() {}

View File

@ -0,0 +1,71 @@
error: you are probably missing a line break to end comment
--> <source attribute>:1:6
"// hello) }}"
--> tests/ui/comments-in-macro-calls.rs:6:35
|
6 | #[template(ext = "html", source = "{{ e!(// hello) }}")]
| ^^^^^^^^^^^^^^^^^^^^
error: expected `)` but found `}`
--> <source attribute>:1:15
"}}"
--> tests/ui/comments-in-macro-calls.rs:10:35
|
10 | #[template(ext = "html", source = "{{ e!(// hello)\n}}")]
| ^^^^^^^^^^^^^^^^^^^^^
error: missing `*/` to close block comment
--> <source attribute>:1:6
"/* hello) }}"
--> tests/ui/comments-in-macro-calls.rs:14:35
|
14 | #[template(ext = "html", source = "{{ e!(/* hello) }}")]
| ^^^^^^^^^^^^^^^^^^^^
error: you are probably missing a line break to end doc comment
--> <source attribute>:1:6
"/// hello) }}"
--> tests/ui/comments-in-macro-calls.rs:18:35
|
18 | #[template(ext = "html", source = "{{ e!(/// hello) }}")]
| ^^^^^^^^^^^^^^^^^^^^^
error: expected `)` but found `}`
--> <source attribute>:1:16
"}}"
--> tests/ui/comments-in-macro-calls.rs:22:35
|
22 | #[template(ext = "html", source = "{{ e!(/// hello)\n}}")]
| ^^^^^^^^^^^^^^^^^^^^^^
error: missing `*/` to close block doc comment
--> <source attribute>:1:6
"/** hello) }}"
--> tests/ui/comments-in-macro-calls.rs:26:35
|
26 | #[template(ext = "html", source = "{{ e!(/** hello) }}")]
| ^^^^^^^^^^^^^^^^^^^^^
error: you are probably missing a line break to end doc comment
--> <source attribute>:1:6
"//! hello) }}"
--> tests/ui/comments-in-macro-calls.rs:30:35
|
30 | #[template(ext = "html", source = "{{ e!(//! hello) }}")]
| ^^^^^^^^^^^^^^^^^^^^^
error: expected `)` but found `}`
--> <source attribute>:1:16
"}}"
--> tests/ui/comments-in-macro-calls.rs:34:35
|
34 | #[template(ext = "html", source = "{{ e!(//! hello)\n}}")]
| ^^^^^^^^^^^^^^^^^^^^^^
error: missing `*/` to close block doc comment
--> <source attribute>:1:6
"/*! hello) }}"
--> tests/ui/comments-in-macro-calls.rs:38:35
|
38 | #[template(ext = "html", source = "{{ e!(/*! hello) }}")]
| ^^^^^^^^^^^^^^^^^^^^^