Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3657,7 +3657,7 @@ declare_lint! {
Allow,
"identifiers that will be parsed as a prefix in Rust 2021",
@future_incompatible = FutureIncompatibleInfo {
reason: fcw!(EditionError 2021 "reserving-syntax"),
reason: fcw!(EditionSemanticsChange 2021 "reserving-syntax"),
Comment thread
mejrs marked this conversation as resolved.
};
crate_level_only
}
Expand Down
53 changes: 9 additions & 44 deletions compiler/rustc_parse/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4584,19 +4584,6 @@ pub(crate) struct BreakWithLabelAndLoopSub {
pub right: Span,
}

#[derive(Diagnostic)]
#[diag("prefix `'r` is reserved")]
pub(crate) struct RawPrefix {
#[label("reserved prefix")]
pub label: Span,
#[suggestion(
"insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
code = " ",
applicability = "machine-applicable"
)]
pub suggestion: Span,
}

#[derive(Diagnostic)]
#[diag("unicode codepoint changing visible direction of text present in comment")]
#[note(
Expand Down Expand Up @@ -4638,40 +4625,18 @@ pub(crate) struct UnicodeTextFlowSuggestion {
}

#[derive(Diagnostic)]
#[diag("prefix `{$prefix}` is unknown")]
pub(crate) struct ReservedPrefix {
#[label("unknown prefix")]
pub label: Span,
#[suggestion(
"insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
code = " ",
applicability = "machine-applicable"
)]
pub suggestion: Span,

pub prefix: String,
}

#[derive(Diagnostic)]
#[diag("will be parsed as a guarded string in Rust 2024")]
pub(crate) struct ReservedStringLint {
#[suggestion(
"insert whitespace here to avoid this being parsed as a guarded string in Rust 2024",
code = " ",
applicability = "machine-applicable"
)]
pub suggestion: Span,
}

#[derive(Diagnostic)]
#[diag("reserved token in Rust 2024")]
pub(crate) struct ReservedMultihashLint {
#[diag("{$subject} is parsed as a {$kind} in Rust {$edition} and onward")]
pub(crate) struct ReservedPrefixLint {
pub subject: String,
pub kind: &'static str,
pub edition: Edition,
#[suggestion(
"insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024",
"consider inserting whitespace here to avoid this",
code = " ",
applicability = "machine-applicable"
applicability = "machine-applicable",
style = "verbose"
)]
pub suggestion: Span,
pub sugg: Span,
}

#[derive(Subdiagnostic)]
Expand Down
81 changes: 44 additions & 37 deletions compiler/rustc_parse/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use rustc_lint_defs::builtin::{
};
use rustc_literal_escaper::{EscapeError, Mode, check_for_errors};
use rustc_session::parse::ParseSess;
use rustc_span::edition::Edition;
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
use tracing::debug;

Expand Down Expand Up @@ -276,21 +277,30 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
rustc_lexer::TokenKind::Literal {
kind: kind @ (LiteralKind::CStr { .. } | LiteralKind::RawCStr { .. }),
suffix_start: _,
} if !self.mk_sp(start, self.pos).edition().at_least_rust_2021() => {
let prefix_len = match kind {
LiteralKind::CStr { .. } => 1,
LiteralKind::RawCStr { .. } => 2,
} if let span = self.mk_sp(start, self.pos) && !span.edition().at_least_rust_2021() => {
let (prefix_len, kind) = match kind {
LiteralKind::CStr { .. } => (1, "C string literal"),
LiteralKind::RawCStr { .. } => (2, "raw C string literal"),
_ => unreachable!(),
};

// reset the state so that only the prefix ("c" or "cr")
// was consumed.
let lit_start = start + BytePos(prefix_len);
self.pos = lit_start;
// reset the state so that only the prefix ("c" or "cr") was consumed.
self.pos = start + BytePos(prefix_len);
self.cursor = Cursor::new(&str_before[prefix_len as usize..], FrontmatterAllowed::No);
self.report_unknown_prefix(start);
let prefix_span = self.mk_sp(start, lit_start);
return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace);

self.psess.buffer_lint(
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
span,
ast::CRATE_NODE_ID,
crate::diagnostics::ReservedPrefixLint {
subject: "this".into(),
kind,
edition: Edition::Edition2021,
sugg: self.mk_sp(start, self.pos).shrink_to_hi(),
},
);

self.ident(start)
}
rustc_lexer::TokenKind::GuardedStrPrefix => {
self.maybe_report_guarded_str(start, str_before)
Expand Down Expand Up @@ -333,9 +343,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1)));

let ident_start = start + BytePos(3);
let prefix_span = self.mk_sp(start, ident_start);

if prefix_span.at_least_rust_2021() {
if self.mk_sp(start, ident_start).at_least_rust_2021() {
// If the raw lifetime is followed by \' then treat it a normal
// lifetime followed by a \', which is to interpret it as a character
// literal. In this case, it's always an invalid character literal
Expand Down Expand Up @@ -381,22 +389,23 @@ impl<'psess, 'src> Lexer<'psess, 'src> {

token::Lifetime(sym, IdentIsRaw::Yes)
} else {
// Otherwise, this should be parsed like `'r`. Warn about it though.
// Reset the state so we just lex the `'r`.
self.pos = start + BytePos(2);
self.cursor = Cursor::new(&str_before[2 as usize..], FrontmatterAllowed::No);

let prefix_span = self.mk_sp(start, self.pos);
self.psess.buffer_lint(
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
prefix_span,
ast::CRATE_NODE_ID,
crate::diagnostics::RawPrefix {
label: prefix_span,
suggestion: prefix_span.shrink_to_hi()
},
crate::diagnostics::ReservedPrefixLint {
subject: "`r`".into(),
kind: "prefix",
edition: Edition::Edition2021,
sugg: prefix_span.shrink_to_hi(),
}
);

// Reset the state so we just lex the `'r`.
let lt_start = start + BytePos(2);
self.pos = lt_start;
self.cursor = Cursor::new(&str_before[2 as usize..], FrontmatterAllowed::No);

let lifetime_name = nfc_normalize(self.str_from(start));
token::Lifetime(lifetime_name, IdentIsRaw::No)
}
Expand Down Expand Up @@ -1089,10 +1098,11 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
prefix_span,
ast::CRATE_NODE_ID,
crate::diagnostics::ReservedPrefix {
label: prefix_span,
suggestion: prefix_span.shrink_to_hi(),
prefix: prefix.to_string(),
crate::diagnostics::ReservedPrefixLint {
subject: format!("`{prefix}`"),
kind: "prefix",
edition: Edition::Edition2021,
sugg: prefix_span.shrink_to_hi(),
},
);
}
Expand Down Expand Up @@ -1167,18 +1177,15 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
})
} else {
// Before Rust 2024, only emit a lint for migration.
self.psess.dyn_buffer_lint(
self.psess.buffer_lint(
RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX,
span,
ast::CRATE_NODE_ID,
move |dcx, level| {
if is_string {
crate::diagnostics::ReservedStringLint { suggestion: space_span }
.into_diag(dcx, level)
} else {
crate::diagnostics::ReservedMultihashLint { suggestion: space_span }
.into_diag(dcx, level)
}
crate::diagnostics::ReservedPrefixLint {
subject: "this".into(),
kind: if is_string { "guarded string literal" } else { "reserved token" },
edition: Edition::Edition2024,
sugg: space_span,
},
);

Expand Down
14 changes: 10 additions & 4 deletions tests/ui/lifetimes/raw/three-tokens.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
//@ edition: 2015
// Ensure that we parse `'r#lt` as three tokens pre Rust 2021.
// Moreover, make sure we emit the relevant migration lint.

//@ edition: 2015..2021
//@ check-pass
// Ensure that we parse `'r#lt` as three tokens in edition 2015.

macro_rules! ed2015 {
#![warn(rust_2021_prefixes_incompatible_syntax)]

macro_rules! check {
('r # lt) => {};
($lt:lifetime) => { compile_error!() };
}

ed2015!('r#lt);
check!('r#lt);
//~^ WARNING parsed as a prefix in Rust 2021 and onward
//~| WARNING this changes meaning in Rust 2021

fn main() {}
20 changes: 20 additions & 0 deletions tests/ui/lifetimes/raw/three-tokens.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
warning: `r` is parsed as a prefix in Rust 2021 and onward
--> $DIR/three-tokens.rs:14:8
|
LL | check!('r#lt);
| ^^
|
= warning: this changes meaning in Rust 2021
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/reserving-syntax.html>
note: the lint level is defined here
--> $DIR/three-tokens.rs:7:9
|
LL | #![warn(rust_2021_prefixes_incompatible_syntax)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider inserting whitespace here to avoid this
|
LL | check!('r #lt);
| +

warning: 1 warning emitted

This file was deleted.

38 changes: 38 additions & 0 deletions tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Prefixes including `c` as used by C string literals are only reserved in Rust 2021 and onward.
// Exercise what happens pre Rust 2021 with C string literal "lookalikes".

//@ check-pass
//@ edition: 2015..2021

#![warn(rust_2021_prefixes_incompatible_syntax)]

fn main() {
// Make sure that pre Rust 2021 editions we continue to parse the snippet
// `c"hello"` as an identifier followed by a (normal) string literal and
// allow the code below to compile.
//
// issue: <https://github.com/rust-lang/rust/issues/113235>

// Moreover, make sure we emit the relevant edition migration lint with an appropriate
// diagnostic (for a period of time we used to incorrectly state prefix `c` was unknown and
// that the token sequence would unconditionally lead to a hard error in the next edition).

macro_rules! parse {
(c $e:expr) => {
$e
};
}

let _: &'static str = parse!(c"hello");
//~^ WARNING parsed as a C string literal in Rust 2021 and onward
//~| WARNING this changes meaning in Rust 2021

macro_rules! indifferent {
($e:expr) => {};
(c $e:expr) => {};
}

indifferent!(c"...");
//~^ WARNING parsed as a C string literal in Rust 2021 and onward
//~| WARNING this changes meaning in Rust 2021
}
33 changes: 33 additions & 0 deletions tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
warning: this is parsed as a C string literal in Rust 2021 and onward
--> $DIR/pre-2021-lexing.rs:26:34
|
LL | let _: &'static str = parse!(c"hello");
| ^^^^^^^^
|
= warning: this changes meaning in Rust 2021
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/reserving-syntax.html>
note: the lint level is defined here
--> $DIR/pre-2021-lexing.rs:7:9
|
LL | #![warn(rust_2021_prefixes_incompatible_syntax)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider inserting whitespace here to avoid this
|
LL | let _: &'static str = parse!(c "hello");
| +

warning: this is parsed as a C string literal in Rust 2021 and onward
--> $DIR/pre-2021-lexing.rs:35:18
|
LL | indifferent!(c"...");
| ^^^^^^
|
= warning: this changes meaning in Rust 2021
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/reserving-syntax.html>
help: consider inserting whitespace here to avoid this
|
LL | indifferent!(c "...");
| +

warning: 2 warnings emitted

20 changes: 10 additions & 10 deletions tests/ui/rust-2021/reserved-prefixes-migration.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ macro_rules! m3 {

fn main() {
m2!(z "hey");
//~^ WARNING prefix `z` is unknown [rust_2021_prefixes_incompatible_syntax]
//~| WARNING hard error in Rust 2021
//~^ WARNING parsed as a prefix in Rust 2021 and onward [rust_2021_prefixes_incompatible_syntax]
//~| WARNING changes meaning in Rust 2021
m2!(prefix "hey");
//~^ WARNING prefix `prefix` is unknown [rust_2021_prefixes_incompatible_syntax]
//~| WARNING hard error in Rust 2021
//~^ WARNING parsed as a prefix in Rust 2021 and onward [rust_2021_prefixes_incompatible_syntax]
//~| WARNING changes meaning in Rust 2021
m3!(hey #123);
//~^ WARNING prefix `hey` is unknown [rust_2021_prefixes_incompatible_syntax]
//~| WARNING hard error in Rust 2021
//~^ WARNING parsed as a prefix in Rust 2021 and onward [rust_2021_prefixes_incompatible_syntax]
//~| WARNING changes meaning in Rust 2021
m3!(hey #hey);
//~^ WARNING prefix `hey` is unknown [rust_2021_prefixes_incompatible_syntax]
//~| WARNING hard error in Rust 2021
//~^ WARNING parsed as a prefix in Rust 2021 and onward [rust_2021_prefixes_incompatible_syntax]
//~| WARNING changes meaning in Rust 2021
}

macro_rules! quote {
Expand All @@ -33,6 +33,6 @@ macro_rules! quote {

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