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)
This commit is contained in:
Ed Page 2025-12-09 15:54:26 -06:00
parent dd5539251c
commit 52d4ef12a8
5 changed files with 27 additions and 4 deletions

View file

@ -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 `;`

View file

@ -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 {

View file

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

View file

@ -1,10 +1,9 @@
---
package.name = " "
package.name = " " # //~ ERROR bare CR not allowed in frontmatter
package.description = "é"
---
// ignore-tidy-cr
//@ check-pass
#![feature(frontmatter)]

View file

@ -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