From 52d4ef12a88ce0ead3cc5ac96b6778e3a33b3583 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 9 Dec 2025 15:54:26 -0600 Subject: [PATCH] fix(parser): Disallow CR in frontmatter T-lang came back on the stabilization PR asking for CR to be disallowed to leave room for all stray CRs to be rejected in the future. At that point, the test can remain but the implementation can be removed. If that plan does not go through, we'll need to re-evaluate - whether this is more lint-like and should defer to the calling tool that is managing the frontmatter - how much Rust should treat the frontmatter as Rust and apply the same grammar restrictions of "no stray CR" (like raw string literals) --- compiler/rustc_parse/messages.ftl | 3 ++- compiler/rustc_parse/src/errors.rs | 7 +++++++ compiler/rustc_parse/src/lexer/mod.rs | 10 +++++++++- tests/ui/frontmatter/content-cr.rs | 3 +-- tests/ui/frontmatter/content-cr.stderr | 8 ++++++++ 5 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 tests/ui/frontmatter/content-cr.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 449d0b964fd4..3f3300117460 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -98,6 +98,8 @@ parse_bare_cr = {$double_quotes -> } .escape = escape the character +parse_bare_cr_in_frontmatter = bare CR not allowed in frontmatter + parse_bare_cr_in_raw_string = bare CR not allowed in raw string parse_binder_and_polarity = `for<...>` binder not allowed with `{$polarity}` trait polarity modifier @@ -352,7 +354,6 @@ parse_frontmatter_length_mismatch = frontmatter close does not match the opening parse_frontmatter_too_many_dashes = too many `-` symbols: frontmatter openings may be delimited by up to 255 `-` symbols, but found {$len_opening} parse_frontmatter_unclosed = unclosed frontmatter .note = frontmatter opening here was not closed - parse_function_body_equals_expr = function body cannot be `= expression;` .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 42327c7e343d..4e789a321649 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -829,6 +829,13 @@ pub(crate) struct FrontmatterTooManyDashes { pub len_opening: usize, } +#[derive(Diagnostic)] +#[diag(parse_bare_cr_in_frontmatter)] +pub(crate) struct BareCrFrontmatter { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_leading_plus_not_supported)] pub(crate) struct LeadingPlusNotSupported { diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 4d0139e179f4..f9bf50de091a 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -614,8 +614,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> { }); } + let line_end = real_s.find('\n').unwrap_or(real_s.len()); if invalid_infostring { - let line_end = real_s.find('\n').unwrap_or(real_s.len()); let span = self.mk_sp( frontmatter_opening_end_pos, frontmatter_opening_pos + BytePos(line_end as u32), @@ -624,6 +624,14 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } let last_line_start = real_s.rfind('\n').map_or(0, |i| i + 1); + + let content = &real_s[line_end..last_line_start]; + if let Some(cr_offset) = content.find('\r') { + let cr_pos = start + BytePos((real_start + line_end + cr_offset) as u32); + let span = self.mk_sp(cr_pos, cr_pos + BytePos(1 as u32)); + self.dcx().emit_err(errors::BareCrFrontmatter { span }); + } + let last_line = &real_s[last_line_start..]; let last_line_trimmed = last_line.trim_start_matches(is_horizontal_whitespace); let last_line_start_pos = frontmatter_opening_pos + BytePos(last_line_start as u32); diff --git a/tests/ui/frontmatter/content-cr.rs b/tests/ui/frontmatter/content-cr.rs index 768282fc2b93..71bede928f37 100644 --- a/tests/ui/frontmatter/content-cr.rs +++ b/tests/ui/frontmatter/content-cr.rs @@ -1,10 +1,9 @@ --- -package.name = " " +package.name = " " # //~ ERROR bare CR not allowed in frontmatter package.description = "é" --- // ignore-tidy-cr -//@ check-pass #![feature(frontmatter)] diff --git a/tests/ui/frontmatter/content-cr.stderr b/tests/ui/frontmatter/content-cr.stderr new file mode 100644 index 000000000000..d59b899cc4c3 --- /dev/null +++ b/tests/ui/frontmatter/content-cr.stderr @@ -0,0 +1,8 @@ +error: bare CR not allowed in frontmatter + --> $DIR/content-cr.rs:2:17 + | +LL | package.name = "␍" # + | ^ + +error: aborting due to 1 previous error +