Skip to content

Commit fc0cf41

Browse files
committed
Unrolled build for #161792 in rollup 162028
Rollup merge of #161792 - fmease:fix-reserved-prefixes-lint-diags, r=mejrs Fix and improve diagnostics for lint `rust_2021_prefixes_incompatible_syntax` On main, when encountering token sequences in Rust <2021 that would get interpreted as... 1. ...C string literals in Rust >=2021, 1. we report "*prefix `c` is unknown*" / "*prefix `cr` is unknown*" despite them obviously being known 2. we claim that they "*[are] a hard error in Rust 2021*" 2. ...raw lifetimes in Rust >=2021 like `'r#a`, we suggest splitting them *after* the hash (so `'r#` and `a`) which obviously doesn't fix the issue; they need to be split *before* the hash (so `'r` and `#a`) 3. ...unknown (reserved) prefixes in Rust >=2021, we report "*prefix […] is unknown*" but to be pedantic the sequence is *not* a prefix in the current edition, it's just a normal identifier; to be correct & precise we should thus say that they *will* be parsed as a prefix (in Rust >=2021) Lastly, unify / streamline all diagnostics pertaining to "reserved prefixes" (identifier/literal prefixes, hash prefixes (aka guarded string prefixes / multihashes)) --- Best reviewed commit by commit. > [!NOTE] > **Unblocks** PR #161775. <sub>(No LLM was or will be used by me during the entire creation process of this PR)</sub>
2 parents 9085017 + 3fca755 commit fc0cf41

16 files changed

Lines changed: 343 additions & 298 deletions

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3657,7 +3657,7 @@ declare_lint! {
36573657
Allow,
36583658
"identifiers that will be parsed as a prefix in Rust 2021",
36593659
@future_incompatible = FutureIncompatibleInfo {
3660-
reason: fcw!(EditionError 2021 "reserving-syntax"),
3660+
reason: fcw!(EditionSemanticsChange 2021 "reserving-syntax"),
36613661
};
36623662
crate_level_only
36633663
}

compiler/rustc_parse/src/diagnostics.rs

Lines changed: 9 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4584,19 +4584,6 @@ pub(crate) struct BreakWithLabelAndLoopSub {
45844584
pub right: Span,
45854585
}
45864586

4587-
#[derive(Diagnostic)]
4588-
#[diag("prefix `'r` is reserved")]
4589-
pub(crate) struct RawPrefix {
4590-
#[label("reserved prefix")]
4591-
pub label: Span,
4592-
#[suggestion(
4593-
"insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
4594-
code = " ",
4595-
applicability = "machine-applicable"
4596-
)]
4597-
pub suggestion: Span,
4598-
}
4599-
46004587
#[derive(Diagnostic)]
46014588
#[diag("unicode codepoint changing visible direction of text present in comment")]
46024589
#[note(
@@ -4638,40 +4625,18 @@ pub(crate) struct UnicodeTextFlowSuggestion {
46384625
}
46394626

46404627
#[derive(Diagnostic)]
4641-
#[diag("prefix `{$prefix}` is unknown")]
4642-
pub(crate) struct ReservedPrefix {
4643-
#[label("unknown prefix")]
4644-
pub label: Span,
4645-
#[suggestion(
4646-
"insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
4647-
code = " ",
4648-
applicability = "machine-applicable"
4649-
)]
4650-
pub suggestion: Span,
4651-
4652-
pub prefix: String,
4653-
}
4654-
4655-
#[derive(Diagnostic)]
4656-
#[diag("will be parsed as a guarded string in Rust 2024")]
4657-
pub(crate) struct ReservedStringLint {
4658-
#[suggestion(
4659-
"insert whitespace here to avoid this being parsed as a guarded string in Rust 2024",
4660-
code = " ",
4661-
applicability = "machine-applicable"
4662-
)]
4663-
pub suggestion: Span,
4664-
}
4665-
4666-
#[derive(Diagnostic)]
4667-
#[diag("reserved token in Rust 2024")]
4668-
pub(crate) struct ReservedMultihashLint {
4628+
#[diag("{$subject} is parsed as a {$kind} in Rust {$edition} and onward")]
4629+
pub(crate) struct ReservedPrefixLint {
4630+
pub subject: String,
4631+
pub kind: &'static str,
4632+
pub edition: Edition,
46694633
#[suggestion(
4670-
"insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024",
4634+
"consider inserting whitespace here to avoid this",
46714635
code = " ",
4672-
applicability = "machine-applicable"
4636+
applicability = "machine-applicable",
4637+
style = "verbose"
46734638
)]
4674-
pub suggestion: Span,
4639+
pub sugg: Span,
46754640
}
46764641

46774642
#[derive(Subdiagnostic)]

compiler/rustc_parse/src/lexer/mod.rs

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_lint_defs::builtin::{
1414
};
1515
use rustc_literal_escaper::{EscapeError, Mode, check_for_errors};
1616
use rustc_session::parse::ParseSess;
17+
use rustc_span::edition::Edition;
1718
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
1819
use tracing::debug;
1920

@@ -276,21 +277,30 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
276277
rustc_lexer::TokenKind::Literal {
277278
kind: kind @ (LiteralKind::CStr { .. } | LiteralKind::RawCStr { .. }),
278279
suffix_start: _,
279-
} if !self.mk_sp(start, self.pos).edition().at_least_rust_2021() => {
280-
let prefix_len = match kind {
281-
LiteralKind::CStr { .. } => 1,
282-
LiteralKind::RawCStr { .. } => 2,
280+
} if let span = self.mk_sp(start, self.pos) && !span.edition().at_least_rust_2021() => {
281+
let (prefix_len, kind) = match kind {
282+
LiteralKind::CStr { .. } => (1, "C string literal"),
283+
LiteralKind::RawCStr { .. } => (2, "raw C string literal"),
283284
_ => unreachable!(),
284285
};
285286

286-
// reset the state so that only the prefix ("c" or "cr")
287-
// was consumed.
288-
let lit_start = start + BytePos(prefix_len);
289-
self.pos = lit_start;
287+
// reset the state so that only the prefix ("c" or "cr") was consumed.
288+
self.pos = start + BytePos(prefix_len);
290289
self.cursor = Cursor::new(&str_before[prefix_len as usize..], FrontmatterAllowed::No);
291-
self.report_unknown_prefix(start);
292-
let prefix_span = self.mk_sp(start, lit_start);
293-
return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace);
290+
291+
self.psess.buffer_lint(
292+
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
293+
span,
294+
ast::CRATE_NODE_ID,
295+
crate::diagnostics::ReservedPrefixLint {
296+
subject: "this".into(),
297+
kind,
298+
edition: Edition::Edition2021,
299+
sugg: self.mk_sp(start, self.pos).shrink_to_hi(),
300+
},
301+
);
302+
303+
self.ident(start)
294304
}
295305
rustc_lexer::TokenKind::GuardedStrPrefix => {
296306
self.maybe_report_guarded_str(start, str_before)
@@ -333,9 +343,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
333343
self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1)));
334344

335345
let ident_start = start + BytePos(3);
336-
let prefix_span = self.mk_sp(start, ident_start);
337-
338-
if prefix_span.at_least_rust_2021() {
346+
if self.mk_sp(start, ident_start).at_least_rust_2021() {
339347
// If the raw lifetime is followed by \' then treat it a normal
340348
// lifetime followed by a \', which is to interpret it as a character
341349
// literal. In this case, it's always an invalid character literal
@@ -381,22 +389,23 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
381389

382390
token::Lifetime(sym, IdentIsRaw::Yes)
383391
} else {
384-
// Otherwise, this should be parsed like `'r`. Warn about it though.
392+
// Reset the state so we just lex the `'r`.
393+
self.pos = start + BytePos(2);
394+
self.cursor = Cursor::new(&str_before[2 as usize..], FrontmatterAllowed::No);
395+
396+
let prefix_span = self.mk_sp(start, self.pos);
385397
self.psess.buffer_lint(
386398
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
387399
prefix_span,
388400
ast::CRATE_NODE_ID,
389-
crate::diagnostics::RawPrefix {
390-
label: prefix_span,
391-
suggestion: prefix_span.shrink_to_hi()
392-
},
401+
crate::diagnostics::ReservedPrefixLint {
402+
subject: "`r`".into(),
403+
kind: "prefix",
404+
edition: Edition::Edition2021,
405+
sugg: prefix_span.shrink_to_hi(),
406+
}
393407
);
394408

395-
// Reset the state so we just lex the `'r`.
396-
let lt_start = start + BytePos(2);
397-
self.pos = lt_start;
398-
self.cursor = Cursor::new(&str_before[2 as usize..], FrontmatterAllowed::No);
399-
400409
let lifetime_name = nfc_normalize(self.str_from(start));
401410
token::Lifetime(lifetime_name, IdentIsRaw::No)
402411
}
@@ -1089,10 +1098,11 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
10891098
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
10901099
prefix_span,
10911100
ast::CRATE_NODE_ID,
1092-
crate::diagnostics::ReservedPrefix {
1093-
label: prefix_span,
1094-
suggestion: prefix_span.shrink_to_hi(),
1095-
prefix: prefix.to_string(),
1101+
crate::diagnostics::ReservedPrefixLint {
1102+
subject: format!("`{prefix}`"),
1103+
kind: "prefix",
1104+
edition: Edition::Edition2021,
1105+
sugg: prefix_span.shrink_to_hi(),
10961106
},
10971107
);
10981108
}
@@ -1167,18 +1177,15 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
11671177
})
11681178
} else {
11691179
// Before Rust 2024, only emit a lint for migration.
1170-
self.psess.dyn_buffer_lint(
1180+
self.psess.buffer_lint(
11711181
RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX,
11721182
span,
11731183
ast::CRATE_NODE_ID,
1174-
move |dcx, level| {
1175-
if is_string {
1176-
crate::diagnostics::ReservedStringLint { suggestion: space_span }
1177-
.into_diag(dcx, level)
1178-
} else {
1179-
crate::diagnostics::ReservedMultihashLint { suggestion: space_span }
1180-
.into_diag(dcx, level)
1181-
}
1184+
crate::diagnostics::ReservedPrefixLint {
1185+
subject: "this".into(),
1186+
kind: if is_string { "guarded string literal" } else { "reserved token" },
1187+
edition: Edition::Edition2024,
1188+
sugg: space_span,
11821189
},
11831190
);
11841191

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
//@ edition: 2015
1+
// Ensure that we parse `'r#lt` as three tokens pre Rust 2021.
2+
// Moreover, make sure we emit the relevant migration lint.
3+
4+
//@ edition: 2015..2021
25
//@ check-pass
3-
// Ensure that we parse `'r#lt` as three tokens in edition 2015.
46

5-
macro_rules! ed2015 {
7+
#![warn(rust_2021_prefixes_incompatible_syntax)]
8+
9+
macro_rules! check {
610
('r # lt) => {};
711
($lt:lifetime) => { compile_error!() };
812
}
913

10-
ed2015!('r#lt);
14+
check!('r#lt);
15+
//~^ WARNING parsed as a prefix in Rust 2021 and onward
16+
//~| WARNING this changes meaning in Rust 2021
1117

1218
fn main() {}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
warning: `r` is parsed as a prefix in Rust 2021 and onward
2+
--> $DIR/three-tokens.rs:14:8
3+
|
4+
LL | check!('r#lt);
5+
| ^^
6+
|
7+
= warning: this changes meaning in Rust 2021
8+
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/reserving-syntax.html>
9+
note: the lint level is defined here
10+
--> $DIR/three-tokens.rs:7:9
11+
|
12+
LL | #![warn(rust_2021_prefixes_incompatible_syntax)]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
help: consider inserting whitespace here to avoid this
15+
|
16+
LL | check!('r #lt);
17+
| +
18+
19+
warning: 1 warning emitted
20+

tests/ui/rfcs/rfc-3348-c-string-literals/edition-2015-2018-lexing.rs

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Prefixes including `c` as used by C string literals are only reserved in Rust 2021 and onward.
2+
// Exercise what happens pre Rust 2021 with C string literal "lookalikes".
3+
4+
//@ check-pass
5+
//@ edition: 2015..2021
6+
7+
#![warn(rust_2021_prefixes_incompatible_syntax)]
8+
9+
fn main() {
10+
// Make sure that pre Rust 2021 editions we continue to parse the snippet
11+
// `c"hello"` as an identifier followed by a (normal) string literal and
12+
// allow the code below to compile.
13+
//
14+
// issue: <https://github.com/rust-lang/rust/issues/113235>
15+
16+
// Moreover, make sure we emit the relevant edition migration lint with an appropriate
17+
// diagnostic (for a period of time we used to incorrectly state prefix `c` was unknown and
18+
// that the token sequence would unconditionally lead to a hard error in the next edition).
19+
20+
macro_rules! parse {
21+
(c $e:expr) => {
22+
$e
23+
};
24+
}
25+
26+
let _: &'static str = parse!(c"hello");
27+
//~^ WARNING parsed as a C string literal in Rust 2021 and onward
28+
//~| WARNING this changes meaning in Rust 2021
29+
30+
macro_rules! indifferent {
31+
($e:expr) => {};
32+
(c $e:expr) => {};
33+
}
34+
35+
indifferent!(c"...");
36+
//~^ WARNING parsed as a C string literal in Rust 2021 and onward
37+
//~| WARNING this changes meaning in Rust 2021
38+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
warning: this is parsed as a C string literal in Rust 2021 and onward
2+
--> $DIR/pre-2021-lexing.rs:26:34
3+
|
4+
LL | let _: &'static str = parse!(c"hello");
5+
| ^^^^^^^^
6+
|
7+
= warning: this changes meaning in Rust 2021
8+
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/reserving-syntax.html>
9+
note: the lint level is defined here
10+
--> $DIR/pre-2021-lexing.rs:7:9
11+
|
12+
LL | #![warn(rust_2021_prefixes_incompatible_syntax)]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
help: consider inserting whitespace here to avoid this
15+
|
16+
LL | let _: &'static str = parse!(c "hello");
17+
| +
18+
19+
warning: this is parsed as a C string literal in Rust 2021 and onward
20+
--> $DIR/pre-2021-lexing.rs:35:18
21+
|
22+
LL | indifferent!(c"...");
23+
| ^^^^^^
24+
|
25+
= warning: this changes meaning in Rust 2021
26+
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/reserving-syntax.html>
27+
help: consider inserting whitespace here to avoid this
28+
|
29+
LL | indifferent!(c "...");
30+
| +
31+
32+
warning: 2 warnings emitted
33+

tests/ui/rust-2021/reserved-prefixes-migration.fixed

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ macro_rules! m3 {
1414

1515
fn main() {
1616
m2!(z "hey");
17-
//~^ WARNING prefix `z` is unknown [rust_2021_prefixes_incompatible_syntax]
18-
//~| WARNING hard error in Rust 2021
17+
//~^ WARNING parsed as a prefix in Rust 2021 and onward [rust_2021_prefixes_incompatible_syntax]
18+
//~| WARNING changes meaning in Rust 2021
1919
m2!(prefix "hey");
20-
//~^ WARNING prefix `prefix` is unknown [rust_2021_prefixes_incompatible_syntax]
21-
//~| WARNING hard error in Rust 2021
20+
//~^ WARNING parsed as a prefix in Rust 2021 and onward [rust_2021_prefixes_incompatible_syntax]
21+
//~| WARNING changes meaning in Rust 2021
2222
m3!(hey #123);
23-
//~^ WARNING prefix `hey` is unknown [rust_2021_prefixes_incompatible_syntax]
24-
//~| WARNING hard error in Rust 2021
23+
//~^ WARNING parsed as a prefix in Rust 2021 and onward [rust_2021_prefixes_incompatible_syntax]
24+
//~| WARNING changes meaning in Rust 2021
2525
m3!(hey #hey);
26-
//~^ WARNING prefix `hey` is unknown [rust_2021_prefixes_incompatible_syntax]
27-
//~| WARNING hard error in Rust 2021
26+
//~^ WARNING parsed as a prefix in Rust 2021 and onward [rust_2021_prefixes_incompatible_syntax]
27+
//~| WARNING changes meaning in Rust 2021
2828
}
2929

3030
macro_rules! quote {
@@ -33,6 +33,6 @@ macro_rules! quote {
3333

3434
quote! {
3535
#name = #kind #value
36-
//~^ WARNING prefix `kind` is unknown [rust_2021_prefixes_incompatible_syntax]
37-
//~| WARNING hard error in Rust 2021
36+
//~^ WARNING parsed as a prefix in Rust 2021 and onward [rust_2021_prefixes_incompatible_syntax]
37+
//~| WARNING changes meaning in Rust 2021
3838
}

0 commit comments

Comments
 (0)