From 8f57684ea6b07e3549f841441109ce7230aa5459 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sun, 14 Apr 2024 17:37:45 -0600 Subject: [PATCH] Reserve guarded string literals (RFC 3593) --- compiler/rustc_lexer/src/cursor.rs | 1 + compiler/rustc_lexer/src/lib.rs | 92 ++++++++++- .../rustc_lint/src/context/diagnostics.rs | 14 ++ compiler/rustc_lint_defs/src/builtin.rs | 41 +++++ compiler/rustc_lint_defs/src/lib.rs | 1 + compiler/rustc_parse/messages.ftl | 4 + compiler/rustc_parse/src/errors.rs | 18 +++ compiler/rustc_parse/src/lexer/mod.rs | 64 +++++++- src/librustdoc/html/highlight.rs | 3 +- .../crates/parser/src/lexed_str.rs | 4 + .../src/server/rust_analyzer_span.rs | 1 + .../proc-macro-srv/src/server/token_id.rs | 1 + .../reserved-guarded-strings-macro-2021.rs | 20 +++ .../reserved-guarded-strings-macro-2024.rs | 21 +++ .../reserved-guarded-strings-lexing.rs | 22 +++ .../reserved-guarded-strings-lexing.stderr | 47 ++++++ .../reserved-guarded-strings-migration.fixed | 76 +++++++++ .../reserved-guarded-strings-migration.rs | 76 +++++++++ .../reserved-guarded-strings-migration.stderr | 124 +++++++++++++++ .../reserved-guarded-strings-via-macro-2.rs | 18 +++ ...eserved-guarded-strings-via-macro-2.stderr | 20 +++ .../reserved-guarded-strings-via-macro.rs | 12 ++ .../ui/rust-2024/reserved-guarded-strings.rs | 60 +++++++ .../rust-2024/reserved-guarded-strings.stderr | 146 ++++++++++++++++++ 24 files changed, 876 insertions(+), 10 deletions(-) create mode 100644 tests/ui/rust-2024/auxiliary/reserved-guarded-strings-macro-2021.rs create mode 100644 tests/ui/rust-2024/auxiliary/reserved-guarded-strings-macro-2024.rs create mode 100644 tests/ui/rust-2024/reserved-guarded-strings-lexing.rs create mode 100644 tests/ui/rust-2024/reserved-guarded-strings-lexing.stderr create mode 100644 tests/ui/rust-2024/reserved-guarded-strings-migration.fixed create mode 100644 tests/ui/rust-2024/reserved-guarded-strings-migration.rs create mode 100644 tests/ui/rust-2024/reserved-guarded-strings-migration.stderr create mode 100644 tests/ui/rust-2024/reserved-guarded-strings-via-macro-2.rs create mode 100644 tests/ui/rust-2024/reserved-guarded-strings-via-macro-2.stderr create mode 100644 tests/ui/rust-2024/reserved-guarded-strings-via-macro.rs create mode 100644 tests/ui/rust-2024/reserved-guarded-strings.rs create mode 100644 tests/ui/rust-2024/reserved-guarded-strings.stderr diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index d173c3ac0327b..eb739a6f457fe 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -4,6 +4,7 @@ use std::str::Chars; /// /// Next characters can be peeked via `first` method, /// and position can be shifted forward via `bump` method. +#[derive(Clone)] pub struct Cursor<'a> { len_remaining: usize, /// Iterator over chars. Slightly faster than a &str. diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 6f8a9792b6ce8..848d8b46daea1 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -29,6 +29,8 @@ pub mod unescape; #[cfg(test)] mod tests; +use std::num::NonZeroU8; + pub use crate::cursor::Cursor; use self::LiteralKind::*; @@ -179,24 +181,27 @@ pub enum DocStyle { /// `rustc_ast::ast::LitKind`). #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum LiteralKind { - /// "12_u8", "0o100", "0b120i99", "1f32". + /// `12_u8`, `0o100`, `0b120i99`, `1f32`. Int { base: Base, empty_int: bool }, - /// "12.34f32", "1e3", but not "1f32". + /// `12.34f32`, `1e3`, but not `1f32`. Float { base: Base, empty_exponent: bool }, - /// "'a'", "'\\'", "'''", "';" + /// `'a'`, `'\\'`, `'''`, `';` Char { terminated: bool }, - /// "b'a'", "b'\\'", "b'''", "b';" + /// `b'a'`, `b'\\'`, `b'''`, `b';` Byte { terminated: bool }, - /// ""abc"", ""abc" + /// `"abc"`, `"abc` Str { terminated: bool }, - /// "b"abc"", "b"abc" + /// `b"abc"`, `b"abc` ByteStr { terminated: bool }, /// `c"abc"`, `c"abc` CStr { terminated: bool }, - /// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a". `None` indicates + /// `#"abc"#`, `#"a`, `##"a"#`. `None` indicates no closing quote. + /// Allows fewer hashes to close the string to support older editions. + GuardedStr { n_start_hashes: Option, n_end_hashes: u8 }, + /// `r"abc"`, `r#"abc"#`, `r####"ab"###"c"####`, `r#"a`. `None` indicates /// an invalid literal. RawStr { n_hashes: Option }, - /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a". `None` + /// `br"abc"`, `br#"abc"#`, `br####"ab"###"c"####`, `br#"a`. `None` /// indicates an invalid literal. RawByteStr { n_hashes: Option }, /// `cr"abc"`, "cr#"abc"#", `cr#"a`. `None` indicates an invalid literal. @@ -365,6 +370,49 @@ impl Cursor<'_> { _ => self.ident_or_unknown_prefix(), }, + // Guarded string literal (reserved syntax). + '#' if matches!(self.first(), '"' | '#') => { + // Create a backup to restore later if this + // turns out to not be a guarded literal. + let backup = self.clone(); + + let mut n_start_hashes: u32 = 1; // Already captured one `#`. + while self.first() == '#' { + n_start_hashes += 1; + self.bump(); + } + + if self.first() == '"' { + self.bump(); + + let res = self.guarded_double_quoted_string(n_start_hashes); + let suffix_start = self.pos_within_token(); + + if let (Ok(n_end_hashes), Ok(n)) = (res, u8::try_from(n_start_hashes)) { + self.eat_literal_suffix(); + + Literal { + kind: GuardedStr { + n_start_hashes: NonZeroU8::new(n), + // Always succeeds because `n_end_hashes <= n` + n_end_hashes: n_end_hashes.try_into().unwrap(), + }, + suffix_start, + } + } else { + Literal { + kind: GuardedStr { n_start_hashes: None, n_end_hashes: 0 }, + suffix_start, + } + } + } else { + // Not a guarded string, so restore old state. + *self = backup; + // Return a pound token. + Pound + } + } + // Byte literal, byte string literal, raw byte string literal or identifier. 'b' => self.c_or_byte_string( |terminated| ByteStr { terminated }, @@ -758,6 +806,34 @@ impl Cursor<'_> { false } + /// Eats the double-quoted string and returns `n_hashes` and an error if encountered. + fn guarded_double_quoted_string(&mut self, n_start_hashes: u32) -> Result { + debug_assert!(self.prev() == '"'); + + // Lex the string itself as a normal string literal + // so we can recover that for older editions later. + if !self.double_quoted_string() { + return Err(RawStrError::NoTerminator { + expected: n_start_hashes, + found: 0, + possible_terminator_offset: None, + }); + } + + // Consume closing '#' symbols. + // Note that this will not consume extra trailing `#` characters: + // `###"abcde"####` is lexed as a `GuardedStr { n_hashes: 3 }` + // followed by a `#` token. + let mut n_end_hashes = 0; + while self.first() == '#' && n_end_hashes < n_start_hashes { + n_end_hashes += 1; + self.bump(); + } + + // Handle `n_end_hashes < n_start_hashes` later. + Ok(n_end_hashes) + } + /// Eats the double-quoted string and returns `n_hashes` and an error if encountered. fn raw_double_quoted_string(&mut self, prefix_len: u32) -> Result { // Wrap the actual function to handle the error with too many hashes. diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 8458b53933537..5b621fb199603 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -157,6 +157,20 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di Applicability::MachineApplicable, ); } + BuiltinLintDiag::ReservedGuardedString(space_span) => { + if let Some(space_span) = space_span { + diag.span_suggestion_verbose( + space_span, + "insert whitespace here to avoid this being parsed as guarded string in Rust 2024", + " ", + Applicability::MachineApplicable, + ); + } else { + diag.help( + "insert whitespace between the `#`s and the opening quote to avoid this being parsed as guarded string in Rust 2024", + ); + } + } BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => { diag.span_note( invoc_span, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 86a0f33a8d158..eca6da697fac7 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -89,6 +89,7 @@ declare_lint_pass! { RUST_2021_INCOMPATIBLE_OR_PATTERNS, RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2021_PRELUDE_COLLISIONS, + RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, SINGLE_USE_LIFETIMES, SOFT_UNSTABLE, @@ -4807,3 +4808,43 @@ declare_lint! { reference: "issue #124559 ", }; } + +declare_lint! { + /// The `rust_2024_guarded_string_incompatible_syntax` lint detects `#` tokens + /// that will be parsed as part of a guarded string literal in Rust 2024. + /// + /// ### Example + /// + /// ```rust,edition2021,compile_fail + /// #![deny(rust_2024_guarded_string_incompatible_syntax)] + /// + /// macro_rules! m { + /// (# $x:expr #) => (); + /// (# $x:expr) => (); + /// } + /// + /// m!(#"hey"#); + /// m!(#"hello"); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Prior to Rust 2024, `#"hey"#` is three tokens: the first `#` + /// followed by the string literal `"hey"` then the final `#`. + /// In Rust 2024, the whole sequence is considered a single token. + /// + /// This lint suggests to add whitespace between the leading `#` + /// and the string to keep them separated in Rust 2024. + // Allow this lint -- rustdoc doesn't yet support threading edition into this lint's parser. + #[allow(rustdoc::invalid_rust_codeblocks)] + pub RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, + Allow, + "will be parsed as a guarded string in Rust 2024", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "issue #123735 ", + }; + crate_level_only +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index ed165188787a5..31c691253755e 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -590,6 +590,7 @@ pub enum BuiltinLintDiag { ProcMacroBackCompat(String), OrPatternsBackCompat(Span, String), ReservedPrefix(Span), + ReservedGuardedString(Option), TrailingMacro(bool, Ident), BreakWithLabelAndLoop(Span), NamedAsmLabel(String), diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 873095dca8722..7503f19890bae 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -672,6 +672,10 @@ parse_require_colon_after_labeled_expression = labeled expression must be follow .label = the label .suggestion = add `:` after the label +parse_reserved_guarded_string = invalid string literal + .note = unprefixed guarded string literals are reserved for future use since Rust 2024 + .suggestion_whitespace = consider inserting whitespace here + parse_return_types_use_thin_arrow = return types are denoted using `->` .suggestion = use `->` instead diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index d06f03a7c1767..6bcd795fe7a60 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2009,6 +2009,24 @@ pub enum UnknownPrefixSugg { }, } +#[derive(Diagnostic)] +#[diag(parse_reserved_guarded_string)] +#[note] +pub struct ReservedGuardedString { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: Option, +} +#[derive(Subdiagnostic)] +#[suggestion( + parse_suggestion_whitespace, + code = " ", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub struct GuardedStringSugg(#[primary_span] pub Span); + #[derive(Diagnostic)] #[diag(parse_too_many_hashes)] pub struct TooManyHashes { diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 1abb1d29562d9..900186367f1ea 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -12,7 +12,8 @@ use rustc_lexer::unescape::{self, EscapeError, Mode}; use rustc_lexer::{Base, DocStyle, RawStrError}; use rustc_lexer::{Cursor, LiteralKind}; use rustc_session::lint::builtin::{ - RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, + RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, + TEXT_DIRECTION_CODEPOINT_IN_COMMENT, }; use rustc_session::lint::BuiltinLintDiag; use rustc_session::parse::ParseSess; @@ -241,6 +242,40 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let prefix_span = self.mk_sp(start, lit_start); return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace); } + rustc_lexer::TokenKind::Literal { + kind: rustc_lexer::LiteralKind::GuardedStr { n_start_hashes, .. }, + suffix_start: _ + } if !self.mk_sp(start, self.pos).edition().at_least_rust_2024() => { + // Check if previous char was `#`, so we don't + // lint for each `#` before the string. + if !( + start > self.start_pos && + self.src.as_bytes()[self.src_index(start) - 1] == b'#' + ) { + let span = self.mk_sp(start, self.pos); + let space_span = n_start_hashes.map(|n_hashes| { + let space_pos = start + BytePos(n_hashes.get().into()); + self.mk_sp(space_pos, space_pos) + }); + + // Before Rust 2021, only emit a lint for migration. + self.psess.buffer_lint_with_diagnostic( + RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, + span, + ast::CRATE_NODE_ID, + "will be parsed as a guarded string in Rust 2024", + BuiltinLintDiag::ReservedGuardedString(space_span), + ); + } + + // reset the state so that only the first `#` was consumed. + let next = start + BytePos(1); + self.pos = next; + self.cursor = Cursor::new(&str_before[1..]); + + let pound_span = self.mk_sp(start, next); + return (Token::new(TokenKind::Pound, pound_span), preceded_by_whitespace); + } rustc_lexer::TokenKind::Literal { kind, suffix_start } => { let suffix_start = start + BytePos(suffix_start); let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind); @@ -490,6 +525,33 @@ impl<'psess, 'src> StringReader<'psess, 'src> { self.report_raw_str_error(start, 1); } } + // RFC 3598 reserved this syntax for future use. As of Rust 2024, + // using this syntax produces an error. In earlier editions, however, it + // only results in an (allowed by default) lint, and is treated as + // separate tokens. + rustc_lexer::LiteralKind::GuardedStr { n_start_hashes, n_end_hashes } => { + let span = self.mk_sp(start, self.pos); + + if let Some(n_start_hashes) = n_start_hashes { + let n = u32::from(n_start_hashes.get()); + let e = u32::from(n_end_hashes); + let expn_data = span.ctxt().outer_expn_data(); + + let space_pos = start + BytePos(n); + let space_span = self.mk_sp(space_pos, space_pos); + + let sugg = if expn_data.is_root() { + Some(errors::GuardedStringSugg(space_span)) + } else { + None + }; + + self.dcx().emit_err(errors::ReservedGuardedString { span, sugg }); + self.cook_unicode(token::Str, Mode::Str, start, end, 1 + n, 1 + e) // ##" "## + } else { + self.dcx().emit_fatal(errors::ReservedGuardedString { span, sugg: None }); + } + } rustc_lexer::LiteralKind::RawByteStr { n_hashes } => { if let Some(n_hashes) = n_hashes { let n = u32::from(n_hashes); diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 336d18a1df1c6..5bb602538efc8 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -850,7 +850,8 @@ impl<'src> Classifier<'src> { | LiteralKind::RawStr { .. } | LiteralKind::RawByteStr { .. } | LiteralKind::CStr { .. } - | LiteralKind::RawCStr { .. } => Class::String, + | LiteralKind::RawCStr { .. } + | LiteralKind::GuardedStr { .. } => Class::String, // Number literals. LiteralKind::Float { .. } | LiteralKind::Int { .. } => Class::Number, }, diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index e5fec67de7060..922bb6ffd1430 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -331,6 +331,10 @@ impl<'a> Converter<'a> { } C_STRING } + rustc_lexer::LiteralKind::GuardedStr { .. } => { + err = "Invalid string literal"; + STRING + } }; let err = if err.is_empty() { None } else { Some(err) }; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/rust_analyzer_span.rs index 0350bde412243..1572585a57fcd 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/rust_analyzer_span.rs @@ -120,6 +120,7 @@ impl server::FreeFunctions for RaSpanServer { 3 + n_hashes.unwrap_or_default() as usize, 1 + n_hashes.unwrap_or_default() as usize, ), + LiteralKind::GuardedStr { .. } => return Err(()), }; let (lit, suffix) = s.split_at(suffix_start as usize); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_id.rs index ad7bd954cf16e..1159e2b1bc908 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_id.rs @@ -113,6 +113,7 @@ impl server::FreeFunctions for TokenIdServer { 3 + n_hashes.unwrap_or_default() as usize, 1 + n_hashes.unwrap_or_default() as usize, ), + LiteralKind::GuardedStr { .. } => return Err(()), }; let (lit, suffix) = s.split_at(suffix_start as usize); diff --git a/tests/ui/rust-2024/auxiliary/reserved-guarded-strings-macro-2021.rs b/tests/ui/rust-2024/auxiliary/reserved-guarded-strings-macro-2021.rs new file mode 100644 index 0000000000000..81080fcdce307 --- /dev/null +++ b/tests/ui/rust-2024/auxiliary/reserved-guarded-strings-macro-2021.rs @@ -0,0 +1,20 @@ +//@ force-host +//@ edition:2021 +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; +use std::str::FromStr; + +#[proc_macro] +pub fn number_of_tokens_in_a_guarded_string_literal(_: TokenStream) -> TokenStream { + TokenStream::from_str("#\"abc\"#").unwrap().into_iter().count().to_string().parse().unwrap() +} + +#[proc_macro] +pub fn number_of_tokens_in_a_guarded_unterminated_string_literal(_: TokenStream) -> TokenStream { + TokenStream::from_str("#\"abc\"").unwrap().into_iter().count().to_string().parse().unwrap() +} diff --git a/tests/ui/rust-2024/auxiliary/reserved-guarded-strings-macro-2024.rs b/tests/ui/rust-2024/auxiliary/reserved-guarded-strings-macro-2024.rs new file mode 100644 index 0000000000000..2c3dc30f0ae9b --- /dev/null +++ b/tests/ui/rust-2024/auxiliary/reserved-guarded-strings-macro-2024.rs @@ -0,0 +1,21 @@ +//@ force-host +//@ compile-flags: -Zunstable-options +//@ edition:2024 +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; +use std::str::FromStr; + +#[proc_macro] +pub fn number_of_tokens_in_a_guarded_string_literal(_: TokenStream) -> TokenStream { + TokenStream::from_str("#\"abc\"#").unwrap().into_iter().count().to_string().parse().unwrap() +} + +#[proc_macro] +pub fn number_of_tokens_in_a_guarded_unterminated_string_literal(_: TokenStream) -> TokenStream { + TokenStream::from_str("#\"abc\"").unwrap().into_iter().count().to_string().parse().unwrap() +} diff --git a/tests/ui/rust-2024/reserved-guarded-strings-lexing.rs b/tests/ui/rust-2024/reserved-guarded-strings-lexing.rs new file mode 100644 index 0000000000000..1784a2946a2bc --- /dev/null +++ b/tests/ui/rust-2024/reserved-guarded-strings-lexing.rs @@ -0,0 +1,22 @@ +//@ edition:2021 + +macro_rules! demo2 { + ( $a:tt $b:tt ) => { println!("two tokens") }; +} + +macro_rules! demo3 { + ( $a:tt $b:tt $c:tt ) => { println!("three tokens") }; +} + +macro_rules! demo4 { + ( $a:tt $b:tt $c:tt $d:tt ) => { println!("four tokens") }; +} + +fn main() { + // Non-ascii identifiers + demo2!(Ñ"foo"); //~ ERROR prefix `Ñ` is unknown + demo4!(Ñ#""#); //~ ERROR prefix `Ñ` is unknown + demo3!(🙃#""); + //~^ ERROR prefix `🙃` is unknown + //~| WARNING identifier contains an uncommon character +} diff --git a/tests/ui/rust-2024/reserved-guarded-strings-lexing.stderr b/tests/ui/rust-2024/reserved-guarded-strings-lexing.stderr new file mode 100644 index 0000000000000..53fa80aec0b4c --- /dev/null +++ b/tests/ui/rust-2024/reserved-guarded-strings-lexing.stderr @@ -0,0 +1,47 @@ +error: prefix `Ñ` is unknown + --> $DIR/reserved-guarded-strings-lexing.rs:17:12 + | +LL | demo2!(Ñ"foo"); + | ^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: consider inserting whitespace here + | +LL | demo2!(Ñ "foo"); + | + + +error: prefix `Ñ` is unknown + --> $DIR/reserved-guarded-strings-lexing.rs:18:12 + | +LL | demo4!(Ñ#""#); + | ^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: consider inserting whitespace here + | +LL | demo4!(Ñ #""#); + | + + +error: prefix `🙃` is unknown + --> $DIR/reserved-guarded-strings-lexing.rs:19:12 + | +LL | demo3!(🙃#""); + | ^^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: consider inserting whitespace here + | +LL | demo3!(🙃 #""); + | + + +warning: identifier contains an uncommon character: '🙃' + --> $DIR/reserved-guarded-strings-lexing.rs:19:12 + | +LL | demo3!(🙃#""); + | ^^ + | + = note: this character is included in the Unicode general security profile + = note: `#[warn(uncommon_codepoints)]` on by default + +error: aborting due to 3 previous errors; 1 warning emitted + diff --git a/tests/ui/rust-2024/reserved-guarded-strings-migration.fixed b/tests/ui/rust-2024/reserved-guarded-strings-migration.fixed new file mode 100644 index 0000000000000..8479ef2442356 --- /dev/null +++ b/tests/ui/rust-2024/reserved-guarded-strings-migration.fixed @@ -0,0 +1,76 @@ +//@ check-pass +//@ run-rustfix +//@ edition:2021 + +#![warn(rust_2024_guarded_string_incompatible_syntax)] + +macro_rules! demo1 { + ( $a:tt ) => { println!("one tokens") }; +} + +macro_rules! demo2 { + ( $a:tt $b:tt ) => { println!("two tokens") }; +} + +macro_rules! demo3 { + ( $a:tt $b:tt $c:tt ) => { println!("three tokens") }; +} + +macro_rules! demo4 { + ( $a:tt $b:tt $c:tt $d:tt ) => { println!("four tokens") }; +} + +macro_rules! demo5 { + ( $a:tt $b:tt $c:tt $d:tt $e:tt ) => { println!("five tokens") }; +} + +macro_rules! demo6 { + ( $a:tt $b:tt $c:tt $d:tt $e:tt $f:tt ) => { println!("six tokens") }; +} + +macro_rules! demo7 { + ( $a:tt $b:tt $c:tt $d:tt $e:tt $f:tt $g:tt ) => { println!("seven tokens") }; +} + +fn main() { + demo1!(""); + demo2!(# ""); + demo3!(# ""#); + demo2!(# "foo"); + demo3!(## "foo"); + demo3!(# "foo"#); + demo4!(### "foo"); + demo4!(## "foo"#); + demo7!(### "foo"###); + + demo2!("foo"#); + demo4!("foo"###); + + demo2!(# ""); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo3!(# ""#); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo5!(#### ""); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo2!(# "foo"); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo4!(### "foo"); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo3!(# "foo"#); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo5!(### "foo"#); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo6!(### "foo"##); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo7!(### "foo"###); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 +} diff --git a/tests/ui/rust-2024/reserved-guarded-strings-migration.rs b/tests/ui/rust-2024/reserved-guarded-strings-migration.rs new file mode 100644 index 0000000000000..b82e9cea61c60 --- /dev/null +++ b/tests/ui/rust-2024/reserved-guarded-strings-migration.rs @@ -0,0 +1,76 @@ +//@ check-pass +//@ run-rustfix +//@ edition:2021 + +#![warn(rust_2024_guarded_string_incompatible_syntax)] + +macro_rules! demo1 { + ( $a:tt ) => { println!("one tokens") }; +} + +macro_rules! demo2 { + ( $a:tt $b:tt ) => { println!("two tokens") }; +} + +macro_rules! demo3 { + ( $a:tt $b:tt $c:tt ) => { println!("three tokens") }; +} + +macro_rules! demo4 { + ( $a:tt $b:tt $c:tt $d:tt ) => { println!("four tokens") }; +} + +macro_rules! demo5 { + ( $a:tt $b:tt $c:tt $d:tt $e:tt ) => { println!("five tokens") }; +} + +macro_rules! demo6 { + ( $a:tt $b:tt $c:tt $d:tt $e:tt $f:tt ) => { println!("six tokens") }; +} + +macro_rules! demo7 { + ( $a:tt $b:tt $c:tt $d:tt $e:tt $f:tt $g:tt ) => { println!("seven tokens") }; +} + +fn main() { + demo1!(""); + demo2!(# ""); + demo3!(# ""#); + demo2!(# "foo"); + demo3!(## "foo"); + demo3!(# "foo"#); + demo4!(### "foo"); + demo4!(## "foo"#); + demo7!(### "foo"###); + + demo2!("foo"#); + demo4!("foo"###); + + demo2!(#""); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo3!(#""#); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo5!(####""); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo2!(#"foo"); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo4!(###"foo"); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo3!(#"foo"#); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo5!(###"foo"#); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo6!(###"foo"##); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 + demo7!(###"foo"###); + //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING hard error in Rust 2024 +} diff --git a/tests/ui/rust-2024/reserved-guarded-strings-migration.stderr b/tests/ui/rust-2024/reserved-guarded-strings-migration.stderr new file mode 100644 index 0000000000000..f45b3cddef8e2 --- /dev/null +++ b/tests/ui/rust-2024/reserved-guarded-strings-migration.stderr @@ -0,0 +1,124 @@ +warning: will be parsed as a guarded string in Rust 2024 + --> $DIR/reserved-guarded-strings-migration.rs:49:12 + | +LL | demo2!(#""); + | ^^^ + | + = warning: this is accepted in the current edition (Rust 2021) but is a hard error in Rust 2024! + = note: for more information, see issue #123735 +note: the lint level is defined here + --> $DIR/reserved-guarded-strings-migration.rs:5:9 + | +LL | #![warn(rust_2024_guarded_string_incompatible_syntax)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: insert whitespace here to avoid this being parsed as guarded string in Rust 2024 + | +LL | demo2!(# ""); + | + + +warning: will be parsed as a guarded string in Rust 2024 + --> $DIR/reserved-guarded-strings-migration.rs:52:12 + | +LL | demo3!(#""#); + | ^^^^ + | + = warning: this is accepted in the current edition (Rust 2021) but is a hard error in Rust 2024! + = note: for more information, see issue #123735 +help: insert whitespace here to avoid this being parsed as guarded string in Rust 2024 + | +LL | demo3!(# ""#); + | + + +warning: will be parsed as a guarded string in Rust 2024 + --> $DIR/reserved-guarded-strings-migration.rs:55:12 + | +LL | demo5!(####""); + | ^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2021) but is a hard error in Rust 2024! + = note: for more information, see issue #123735 +help: insert whitespace here to avoid this being parsed as guarded string in Rust 2024 + | +LL | demo5!(#### ""); + | + + +warning: will be parsed as a guarded string in Rust 2024 + --> $DIR/reserved-guarded-strings-migration.rs:58:12 + | +LL | demo2!(#"foo"); + | ^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2021) but is a hard error in Rust 2024! + = note: for more information, see issue #123735 +help: insert whitespace here to avoid this being parsed as guarded string in Rust 2024 + | +LL | demo2!(# "foo"); + | + + +warning: will be parsed as a guarded string in Rust 2024 + --> $DIR/reserved-guarded-strings-migration.rs:61:12 + | +LL | demo4!(###"foo"); + | ^^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2021) but is a hard error in Rust 2024! + = note: for more information, see issue #123735 +help: insert whitespace here to avoid this being parsed as guarded string in Rust 2024 + | +LL | demo4!(### "foo"); + | + + +warning: will be parsed as a guarded string in Rust 2024 + --> $DIR/reserved-guarded-strings-migration.rs:64:12 + | +LL | demo3!(#"foo"#); + | ^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2021) but is a hard error in Rust 2024! + = note: for more information, see issue #123735 +help: insert whitespace here to avoid this being parsed as guarded string in Rust 2024 + | +LL | demo3!(# "foo"#); + | + + +warning: will be parsed as a guarded string in Rust 2024 + --> $DIR/reserved-guarded-strings-migration.rs:67:12 + | +LL | demo5!(###"foo"#); + | ^^^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2021) but is a hard error in Rust 2024! + = note: for more information, see issue #123735 +help: insert whitespace here to avoid this being parsed as guarded string in Rust 2024 + | +LL | demo5!(### "foo"#); + | + + +warning: will be parsed as a guarded string in Rust 2024 + --> $DIR/reserved-guarded-strings-migration.rs:70:12 + | +LL | demo6!(###"foo"##); + | ^^^^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2021) but is a hard error in Rust 2024! + = note: for more information, see issue #123735 +help: insert whitespace here to avoid this being parsed as guarded string in Rust 2024 + | +LL | demo6!(### "foo"##); + | + + +warning: will be parsed as a guarded string in Rust 2024 + --> $DIR/reserved-guarded-strings-migration.rs:73:12 + | +LL | demo7!(###"foo"###); + | ^^^^^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2021) but is a hard error in Rust 2024! + = note: for more information, see issue #123735 +help: insert whitespace here to avoid this being parsed as guarded string in Rust 2024 + | +LL | demo7!(### "foo"###); + | + + +warning: 9 warnings emitted + diff --git a/tests/ui/rust-2024/reserved-guarded-strings-via-macro-2.rs b/tests/ui/rust-2024/reserved-guarded-strings-via-macro-2.rs new file mode 100644 index 0000000000000..3f9f373ba227b --- /dev/null +++ b/tests/ui/rust-2024/reserved-guarded-strings-via-macro-2.rs @@ -0,0 +1,18 @@ +//@ edition:2021 +//@ aux-build:reserved-guarded-strings-macro-2021.rs +//@ aux-build:reserved-guarded-strings-macro-2024.rs + +extern crate reserved_guarded_strings_macro_2021 as m2021; +extern crate reserved_guarded_strings_macro_2024 as m2024; + +fn main() { + // Ok: + m2021::number_of_tokens_in_a_guarded_string_literal!(); + m2021::number_of_tokens_in_a_guarded_unterminated_string_literal!(); + + // Error, even though *this* crate is 2021: + m2024::number_of_tokens_in_a_guarded_string_literal!(); + //~^ ERROR invalid string literal + m2024::number_of_tokens_in_a_guarded_unterminated_string_literal!(); + //~^ ERROR invalid string literal +} diff --git a/tests/ui/rust-2024/reserved-guarded-strings-via-macro-2.stderr b/tests/ui/rust-2024/reserved-guarded-strings-via-macro-2.stderr new file mode 100644 index 0000000000000..1074c8a682bfb --- /dev/null +++ b/tests/ui/rust-2024/reserved-guarded-strings-via-macro-2.stderr @@ -0,0 +1,20 @@ +error: invalid string literal + --> $DIR/reserved-guarded-strings-via-macro-2.rs:14:5 + | +LL | m2024::number_of_tokens_in_a_guarded_string_literal!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 + = note: this error originates in the macro `m2024::number_of_tokens_in_a_guarded_string_literal` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid string literal + --> $DIR/reserved-guarded-strings-via-macro-2.rs:16:5 + | +LL | m2024::number_of_tokens_in_a_guarded_unterminated_string_literal!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 + = note: this error originates in the macro `m2024::number_of_tokens_in_a_guarded_unterminated_string_literal` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui/rust-2024/reserved-guarded-strings-via-macro.rs b/tests/ui/rust-2024/reserved-guarded-strings-via-macro.rs new file mode 100644 index 0000000000000..f9e3c1e3c51b4 --- /dev/null +++ b/tests/ui/rust-2024/reserved-guarded-strings-via-macro.rs @@ -0,0 +1,12 @@ +//@ run-pass +//@ compile-flags: -Zunstable-options +//@ edition:2024 +//@ aux-build:reserved-guarded-strings-macro-2021.rs + +extern crate reserved_guarded_strings_macro_2021 as m2021; + +fn main() { + // Ok, even though *this* crate is 2024: + assert_eq!(m2021::number_of_tokens_in_a_guarded_string_literal!(), 3); + assert_eq!(m2021::number_of_tokens_in_a_guarded_unterminated_string_literal!(), 2); +} diff --git a/tests/ui/rust-2024/reserved-guarded-strings.rs b/tests/ui/rust-2024/reserved-guarded-strings.rs new file mode 100644 index 0000000000000..5b8442693b6eb --- /dev/null +++ b/tests/ui/rust-2024/reserved-guarded-strings.rs @@ -0,0 +1,60 @@ +//@ compile-flags: -Zunstable-options +//@ edition:2024 + +macro_rules! demo1 { + ( $a:tt ) => { println!("one tokens") }; +} + +macro_rules! demo2 { + ( $a:tt $b:tt ) => { println!("two tokens") }; +} + +macro_rules! demo3 { + ( $a:tt $b:tt $c:tt ) => { println!("three tokens") }; +} + +macro_rules! demo4 { + ( $a:tt $b:tt $c:tt $d:tt ) => { println!("four tokens") }; +} + +macro_rules! demo5 { + ( $a:tt $b:tt $c:tt $d:tt $e:tt ) => { println!("five tokens") }; +} + +macro_rules! demo6 { + ( $a:tt $b:tt $c:tt $d:tt $e:tt $f:tt ) => { println!("six tokens") }; +} + +macro_rules! demo7 { + ( $a:tt $b:tt $c:tt $d:tt $e:tt $f:tt $g:tt ) => { println!("seven tokens") }; +} + +fn main() { + demo1!(""); + demo2!(# ""); + demo3!(# ""#); + demo2!(# "foo"); + demo3!(## "foo"); + demo3!(# "foo"#); + demo4!(### "foo"); + demo4!(## "foo"#); + demo7!(### "foo"###); + + demo2!("foo"#); + demo4!("foo"###); + + demo2!(blah"xx"); //~ ERROR prefix `blah` is unknown + demo2!(blah#"xx"#); + //~^ ERROR prefix `blah` is unknown + //~| ERROR invalid string literal + + demo1!(#""); //~ ERROR invalid string literal + demo1!(#""#); //~ ERROR invalid string literal + demo1!(####""); //~ ERROR invalid string literal + demo1!(#"foo"); //~ ERROR invalid string literal + demo1!(###"foo"); //~ ERROR invalid string literal + demo1!(#"foo"#); //~ ERROR invalid string literal + demo1!(###"foo"#); //~ ERROR invalid string literal + demo1!(###"foo"##); //~ ERROR invalid string literal + demo1!(###"foo"###); //~ ERROR invalid string literal +} diff --git a/tests/ui/rust-2024/reserved-guarded-strings.stderr b/tests/ui/rust-2024/reserved-guarded-strings.stderr new file mode 100644 index 0000000000000..f1a1e217c7fbe --- /dev/null +++ b/tests/ui/rust-2024/reserved-guarded-strings.stderr @@ -0,0 +1,146 @@ +error: prefix `blah` is unknown + --> $DIR/reserved-guarded-strings.rs:46:12 + | +LL | demo2!(blah"xx"); + | ^^^^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: consider inserting whitespace here + | +LL | demo2!(blah "xx"); + | + + +error: prefix `blah` is unknown + --> $DIR/reserved-guarded-strings.rs:47:12 + | +LL | demo2!(blah#"xx"#); + | ^^^^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: consider inserting whitespace here + | +LL | demo2!(blah #"xx"#); + | + + +error: invalid string literal + --> $DIR/reserved-guarded-strings.rs:47:16 + | +LL | demo2!(blah#"xx"#); + | ^^^^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 +help: consider inserting whitespace here + | +LL | demo2!(blah# "xx"#); + | + + +error: invalid string literal + --> $DIR/reserved-guarded-strings.rs:51:12 + | +LL | demo1!(#""); + | ^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 +help: consider inserting whitespace here + | +LL | demo1!(# ""); + | + + +error: invalid string literal + --> $DIR/reserved-guarded-strings.rs:52:12 + | +LL | demo1!(#""#); + | ^^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 +help: consider inserting whitespace here + | +LL | demo1!(# ""#); + | + + +error: invalid string literal + --> $DIR/reserved-guarded-strings.rs:53:12 + | +LL | demo1!(####""); + | ^^^^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 +help: consider inserting whitespace here + | +LL | demo1!(#### ""); + | + + +error: invalid string literal + --> $DIR/reserved-guarded-strings.rs:54:12 + | +LL | demo1!(#"foo"); + | ^^^^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 +help: consider inserting whitespace here + | +LL | demo1!(# "foo"); + | + + +error: invalid string literal + --> $DIR/reserved-guarded-strings.rs:55:12 + | +LL | demo1!(###"foo"); + | ^^^^^^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 +help: consider inserting whitespace here + | +LL | demo1!(### "foo"); + | + + +error: invalid string literal + --> $DIR/reserved-guarded-strings.rs:56:12 + | +LL | demo1!(#"foo"#); + | ^^^^^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 +help: consider inserting whitespace here + | +LL | demo1!(# "foo"#); + | + + +error: invalid string literal + --> $DIR/reserved-guarded-strings.rs:57:12 + | +LL | demo1!(###"foo"#); + | ^^^^^^^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 +help: consider inserting whitespace here + | +LL | demo1!(### "foo"#); + | + + +error: invalid string literal + --> $DIR/reserved-guarded-strings.rs:58:12 + | +LL | demo1!(###"foo"##); + | ^^^^^^^^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 +help: consider inserting whitespace here + | +LL | demo1!(### "foo"##); + | + + +error: invalid string literal + --> $DIR/reserved-guarded-strings.rs:59:12 + | +LL | demo1!(###"foo"###); + | ^^^^^^^^^^^ + | + = note: unprefixed guarded string literals are reserved for future use since Rust 2024 +help: consider inserting whitespace here + | +LL | demo1!(### "foo"###); + | + + +error: aborting due to 12 previous errors +