From 66594ec3e85831fd0f40034c284c5c28add0ffaa Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 23 Jun 2026 08:50:31 -0400 Subject: [PATCH 01/18] Escape grapheme extenders in `str::escape_debug` This matches the behavior of `impl Debug for str`. --- library/alloctests/tests/str.rs | 2 +- library/core/src/char/methods.rs | 23 ++++------------------- library/core/src/fmt/mod.rs | 2 -- library/core/src/str/iter.rs | 13 +++++-------- library/core/src/str/lossy.rs | 1 - library/core/src/str/mod.rs | 21 +++------------------ library/core/src/wtf8.rs | 1 - 7 files changed, 13 insertions(+), 50 deletions(-) diff --git a/library/alloctests/tests/str.rs b/library/alloctests/tests/str.rs index 830f6972f5af5..591b3c95152ef 100644 --- a/library/alloctests/tests/str.rs +++ b/library/alloctests/tests/str.rs @@ -1156,7 +1156,7 @@ fn test_escape_debug() { assert_eq!("\u{10d4ea}\r".escape_debug().to_string(), "\\u{10d4ea}\\r"); assert_eq!( "\u{301}a\u{301}bé\u{e000}".escape_debug().to_string(), - "\\u{301}a\u{301}bé\\u{e000}" + "\\u{301}a\\u{301}bé\\u{e000}" ); } diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index f6930e0a60d42..556cccc6749c2 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -470,9 +470,7 @@ impl char { } /// An extended version of `escape_debug` that optionally permits escaping - /// Extended Grapheme codepoints, single quotes, and double quotes. This - /// allows us to format characters like nonspacing marks better when they're - /// at the start of a string, and allows escaping single quotes in + /// single quotes and double quotes. This allows escaping single quotes in /// characters, and double quotes in strings. #[inline] pub(crate) fn escape_debug_ext(self, args: EscapeDebugExtArgs) -> EscapeDebug { @@ -495,7 +493,7 @@ impl char { _ if self.is_control() || self.is_private_use() || self.is_whitespace() - || args.escape_grapheme_extender && self.is_grapheme_extender() + || self.is_grapheme_extender() || self.is_default_ignorable() || self.is_format_control() || !self.is_assigned() => @@ -2454,16 +2452,6 @@ impl char { } pub(crate) struct EscapeDebugExtArgs { - /// Escape Grapheme Extender codepoints? - /// - /// Note that this excludes - /// U+FF9E HALFWIDTH KATAKANA VOICED SOUND MARK - /// and U+FF9F HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK, - /// which are never escaped, as graphically - /// they are not combining. See - /// for background on these characters. - pub(crate) escape_grapheme_extender: bool, - /// Escape single quotes? pub(crate) escape_single_quote: bool, @@ -2472,11 +2460,8 @@ pub(crate) struct EscapeDebugExtArgs { } impl EscapeDebugExtArgs { - pub(crate) const ESCAPE_ALL: Self = Self { - escape_grapheme_extender: true, - escape_single_quote: true, - escape_double_quote: true, - }; + pub(crate) const ESCAPE_ALL: Self = + Self { escape_single_quote: true, escape_double_quote: true }; } #[inline] diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 6a4c58afc16f3..93ed8c38a569c 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -2956,7 +2956,6 @@ impl Debug for str { let mut chars = rest.chars(); if let Some(c) = chars.next() { let esc = c.escape_debug_ext(EscapeDebugExtArgs { - escape_grapheme_extender: true, escape_single_quote: false, escape_double_quote: true, }); @@ -2988,7 +2987,6 @@ impl Debug for char { fn fmt(&self, f: &mut Formatter<'_>) -> Result { f.write_char('\'')?; let esc = self.escape_debug_ext(EscapeDebugExtArgs { - escape_grapheme_extender: true, escape_single_quote: true, escape_double_quote: false, }); diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 26c48d48d211e..b221b98a7b791 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -3,18 +3,18 @@ use super::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; use super::validations::{next_code_point, next_code_point_reverse}; use super::{ - BytesIsNotEmpty, CharEscapeDebugContinue, CharEscapeDefault, CharEscapeUnicode, - IsAsciiWhitespace, IsNotEmpty, IsWhitespace, LinesMap, UnsafeBytesToStr, from_utf8_unchecked, + BytesIsNotEmpty, CharEscapeDebug, CharEscapeDefault, CharEscapeUnicode, IsAsciiWhitespace, + IsNotEmpty, IsWhitespace, LinesMap, UnsafeBytesToStr, from_utf8_unchecked, }; +use crate::char as char_mod; use crate::fmt::{self, Write}; use crate::iter::{ - Chain, Copied, Filter, FlatMap, Flatten, FusedIterator, Map, TrustedLen, TrustedRandomAccess, + Copied, Filter, FlatMap, FusedIterator, Map, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, }; use crate::num::NonZero; use crate::ops::Try; use crate::slice::{self, Split as SliceSplit}; -use crate::{char as char_mod, option}; /// An iterator over the [`char`]s of a string slice. /// @@ -1579,10 +1579,7 @@ impl FusedIterator for EncodeUtf16<'_> {} #[stable(feature = "str_escape", since = "1.34.0")] #[derive(Clone, Debug)] pub struct EscapeDebug<'a> { - pub(super) inner: Chain< - Flatten>, - FlatMap, char_mod::EscapeDebug, CharEscapeDebugContinue>, - >, + pub(super) inner: FlatMap, char_mod::EscapeDebug, CharEscapeDebug>, } /// The return type of [`str::escape_default`]. diff --git a/library/core/src/str/lossy.rs b/library/core/src/str/lossy.rs index 13e287fade36f..78df1af4643ac 100644 --- a/library/core/src/str/lossy.rs +++ b/library/core/src/str/lossy.rs @@ -123,7 +123,6 @@ impl fmt::Debug for Debug<'_> { let mut from = 0; for (i, c) in valid.char_indices() { let esc = c.escape_debug_ext(EscapeDebugExtArgs { - escape_grapheme_extender: true, escape_single_quote: false, escape_double_quote: true, }); diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index b3b4658645b9f..4bd07bb31e878 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -3120,9 +3120,6 @@ impl str { /// Returns an iterator that escapes each char in `self` with [`char::escape_debug`]. /// - /// Note: only extended grapheme codepoints that begin the string will be - /// escaped. - /// /// # Examples /// /// As an iterator: @@ -3156,15 +3153,7 @@ impl str { without modifying the original"] #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_debug(&self) -> EscapeDebug<'_> { - let mut chars = self.chars(); - EscapeDebug { - inner: chars - .next() - .map(|first| first.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL)) - .into_iter() - .flatten() - .chain(chars.flat_map(CharEscapeDebugContinue)), - } + EscapeDebug { inner: self.chars().flat_map(CharEscapeDebug) } } /// Returns an iterator that escapes each char in `self` with [`char::escape_default`]. @@ -3328,12 +3317,8 @@ impl_fn_for_zst! { }; #[derive(Clone)] - struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { - c.escape_debug_ext(EscapeDebugExtArgs { - escape_grapheme_extender: false, - escape_single_quote: true, - escape_double_quote: true - }) + struct CharEscapeDebug impl Fn = |c: char| -> char::EscapeDebug { + c.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL) }; #[derive(Clone)] diff --git a/library/core/src/wtf8.rs b/library/core/src/wtf8.rs index 56679ea3aa9d9..5d80a35a6c766 100644 --- a/library/core/src/wtf8.rs +++ b/library/core/src/wtf8.rs @@ -147,7 +147,6 @@ impl fmt::Debug for Wtf8 { use crate::fmt::Write as _; for c in s.chars().flat_map(|c| { c.escape_debug_ext(EscapeDebugExtArgs { - escape_grapheme_extender: true, escape_single_quote: false, escape_double_quote: true, }) From 9c8eb44b27ed4d368f55d15eee3546b523812712 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 24 Aug 2026 13:35:15 +1000 Subject: [PATCH 02/18] Flatten and rename `compute_src_directory_via_git` --- src/bootstrap/src/core/config/config.rs | 102 ++++++++++++------------ 1 file changed, 50 insertions(+), 52 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index d96300e0789aa..4997e9649b455 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -443,11 +443,14 @@ impl Config { // Undo `src/bootstrap` manifest_dir.parent().unwrap().parent().unwrap().to_owned() }; - let src = if let Some(s) = compute_src_directory(flags_src, &exec_ctx) { - s - } else { - default_src_dir.clone() - }; + + // Determine the root of the `rust-lang/rust` source directory from one of: + // - An explicit command-line argument `--src=PATH`. + // - Running git to find a checkout directory from the current working directory. + // - The source directory that this bootstrap executable was built from. + let src = flags_src + .or_else(|| compute_src_directory_via_git(&exec_ctx)) + .unwrap_or_else(|| default_src_dir.clone()); #[cfg(test)] { @@ -2063,54 +2066,49 @@ fn reconcile_jemalloc( } } -fn compute_src_directory(src_dir: Option, exec_ctx: &ExecutionContext) -> Option { - if let Some(src) = src_dir { - return Some(src); - } else { - // Infer the source directory. This is non-trivial because we want to support a downloaded bootstrap binary, - // running on a completely different machine from where it was compiled. - let mut cmd = helpers::git(None); - // NOTE: we cannot support running from outside the repository because the only other path we have available - // is set at compile time, which can be wrong if bootstrap was downloaded rather than compiled locally. - // We still support running outside the repository if we find we aren't in a git directory. - - // NOTE: We get a relative path from git to work around an issue on MSYS/mingw. If we used an absolute path, - // and end up using MSYS's git rather than git-for-windows, we would get a unix-y MSYS path. But as bootstrap - // has already been (kinda-cross-)compiled to Windows land, we require a normal Windows path. - cmd.arg("rev-parse").arg("--show-cdup"); - // Discard stderr because we expect this to fail when building from a tarball. - let output = cmd.allow_failure().run_capture_stdout(exec_ctx); - if output.is_success() { - let git_root_relative = output.stdout(); - // We need to canonicalize this path to make sure it uses backslashes instead of forward slashes, - // and to resolve any relative components. - let git_root = env::current_dir() - .unwrap() - .join(PathBuf::from(git_root_relative.trim())) - .canonicalize() - .unwrap(); - let s = git_root.to_str().unwrap(); - - // Bootstrap is quite bad at handling /? in front of paths - let git_root = match s.strip_prefix("\\\\?\\") { - Some(p) => PathBuf::from(p), - None => git_root, - }; - // If this doesn't have at least `stage0`, we guessed wrong. This can happen when, - // for example, the build directory is inside of another unrelated git directory. - // In that case keep the original `CARGO_MANIFEST_DIR` handling. - // - // NOTE: this implies that downloadable bootstrap isn't supported when the build directory is outside - // the source directory. We could fix that by setting a variable from all three of python, ./x, and x.ps1. - if git_root.join("src").join("stage0").exists() { - return Some(git_root); - } - } else { - // We're building from a tarball, not git sources. - // We don't support pre-downloaded bootstrap in this case. - } +fn compute_src_directory_via_git(exec_ctx: &ExecutionContext) -> Option { + // Infer the source directory. This is non-trivial because we want to support a downloaded bootstrap binary, + // running on a completely different machine from where it was compiled. + // NOTE: we cannot support running from outside the repository because the only other path we have available + // is set at compile time, which can be wrong if bootstrap was downloaded rather than compiled locally. + // We still support running outside the repository if we find we aren't in a git directory. + + // NOTE: We get a relative path from git (`--show-cdup`) to work around an issue on MSYS/mingw. + // If we used an absolute path, and end up using MSYS's git rather than git-for-windows, we would + // get a unix-y MSYS path. But as bootstrap has already been (kinda-cross-)compiled to Windows land, + // we require a normal Windows path. + + // Ask git to print the path of the repository root, relative to the working directory. + // If the working directory is the repo root, the output will be empty, which is fine. + let mut cmd = helpers::git(None); + cmd.arg("rev-parse").arg("--show-cdup"); + // Discard stderr because we expect this to fail when building from a tarball. + let output = cmd.allow_failure().run_capture_stdout(exec_ctx); + if output.is_failure() { + // We're building from a tarball, not git sources. + // We don't support pre-downloaded bootstrap in this case. + return None; + } + + // We need to canonicalize this path to make sure it uses backslashes instead of forward slashes, + // and to resolve any relative components. + let stdout = output.stdout(); + let relative_root = stdout.trim(); + let git_root = env::current_dir().unwrap().join(relative_root).canonicalize().unwrap(); + + // Bootstrap is quite bad at handling /? in front of paths + let git_root = match git_root.to_str().unwrap().strip_prefix("\\\\?\\") { + Some(p) => PathBuf::from(p), + None => git_root, }; - None + + // If this doesn't have at least `./src/stage0`, we guessed wrong. This can happen when, + // for example, the build directory is inside of another unrelated git directory. + // In that case keep the original `CARGO_MANIFEST_DIR` handling. + // + // NOTE: this implies that downloadable bootstrap isn't supported when the build directory is outside + // the source directory. We could fix that by setting a variable from all three of python, ./x, and x.ps1. + if git_root.join("src").join("stage0").exists() { Some(git_root) } else { None } } #[derive(Clone)] From 7faee83f1f74c81d369bdce958f1ae06dc7fbe27 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 24 Aug 2026 13:56:59 +1000 Subject: [PATCH 03/18] Replace another `#[cfg(test)]` with `if cfg!(test)` --- src/bootstrap/src/core/config/config.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 4997e9649b455..aadd2ebad0163 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -452,15 +452,13 @@ impl Config { .or_else(|| compute_src_directory_via_git(&exec_ctx)) .unwrap_or_else(|| default_src_dir.clone()); - #[cfg(test)] - { - if let Some(config_path) = flags_config.as_ref() { - assert!( + if cfg!(test) { + match flags_config.as_deref() { + Some(config_path) => assert!( !config_path.starts_with(&src), "Path {config_path:?} should not be inside or equal to src dir {src:?}" - ); - } else { - panic!("During test the config should be explicitly added"); + ), + None => panic!("During test the config should be explicitly added"), } } From de13fc77e5cd3839fb40598b876904ba7fa518c1 Mon Sep 17 00:00:00 2001 From: ltdk Date: Mon, 24 Aug 2026 21:12:51 -0400 Subject: [PATCH 04/18] Add gram config to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 91a2647ca98f4..04e87ca10a9ff 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ Session.vim .vim/ .helix/ .zed/ +.gram/ .favorites.json .settings/ .vs/ From dbea26933ed18a856c612a2157e8cb3babb88c37 Mon Sep 17 00:00:00 2001 From: Jonathan Keller Date: Tue, 25 Aug 2026 11:22:24 -0700 Subject: [PATCH 05/18] Check to ensure we're running against the correct LLVM version --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 2 ++ compiler/rustc_codegen_llvm/src/llvm_util.rs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 8c9bf55b14e45..3c2dc810704d9 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -894,6 +894,8 @@ unsafe extern "C" { SLen: c_uint, ) -> MetadataKindId; + pub(crate) fn LLVMGetVersion(major: &mut c_uint, minor: &mut c_uint, patch: &mut c_uint); + pub(crate) fn LLVMDisposeTargetMachine(T: ptr::NonNull); // Create modules. diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 298b58dd0007f..64b166113e42d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -48,6 +48,24 @@ unsafe fn configure_llvm(sess: &Session) { let mut llvm_c_strs = Vec::with_capacity(n_args + 1); let mut llvm_args = Vec::with_capacity(n_args + 1); + // Check to ensure we're running against the correct LLVM version. + unsafe { + let mut llvm_major = 0; + let mut llvm_minor = 0; + let mut llvm_patch = 0; + llvm::LLVMGetVersion(&mut llvm_major, &mut llvm_minor, &mut llvm_patch); + let expected_version = llvm::LLVMRustVersionMajor(); + if llvm_major != expected_version { + panic!( + concat!( + "LLVM version mismatch: this compiler was built for LLVM {}, ", + "but LLVM {}.{}.{} is loaded" + ), + expected_version, llvm_major, llvm_minor, llvm_patch + ); + } + } + unsafe { llvm::LLVMRustInstallErrorHandlers(); } From 5841e102da7f55ae38a35aaaa8ab3dd4a4b29d0d Mon Sep 17 00:00:00 2001 From: Jonathan Keller Date: Tue, 25 Aug 2026 11:46:16 -0700 Subject: [PATCH 06/18] Look up and print path to wrong LLVM version --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 11 +- compiler/rustc_session/src/filesearch.rs | 153 ++++++++++--------- 2 files changed, 86 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 64b166113e42d..9819699ca5228 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -59,9 +59,16 @@ unsafe fn configure_llvm(sess: &Session) { panic!( concat!( "LLVM version mismatch: this compiler was built for LLVM {}, ", - "but LLVM {}.{}.{} is loaded" + "but LLVM {}.{}.{} was found{}" ), - expected_version, llvm_major, llvm_minor, llvm_patch + expected_version, + llvm_major, + llvm_minor, + llvm_patch, + match rustc_session::filesearch::dll_path(llvm::LLVMGetVersion as *mut _) { + Ok(path) => format!(" at {}", path.display()), + Err(_) => String::new(), + } ); } } diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index d88fed2f84ab8..6ec1466500a86 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -146,86 +146,78 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf { sysroot.join(rustlib_path).join("bin") } +/// Attempts to find the path to the dynamic library containing a function. +/// +/// SAFETY: `function` must be a valid pointer to some function. #[cfg(unix)] -fn current_dll_path() -> Result { - use std::sync::OnceLock; +pub unsafe fn dll_path(function: *mut std::ffi::c_void) -> Result { + use std::ffi::{CStr, OsStr}; + use std::os::unix::prelude::*; - // This is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr` - // needs to iterate over the symbol table of librustc_driver.so until it finds a match. - // As such cache this to avoid recomputing if we try to get the sysroot in multiple places. - static CURRENT_DLL_PATH: OnceLock> = OnceLock::new(); - CURRENT_DLL_PATH - .get_or_init(|| { - use std::ffi::{CStr, OsStr}; - use std::os::unix::prelude::*; - - #[cfg(not(target_os = "aix"))] - unsafe { - let addr = current_dll_path as fn() -> Result as *mut _; - let mut info = std::mem::zeroed(); - if libc::dladdr(addr, &mut info) == 0 { - return Err("dladdr failed".into()); + #[cfg(not(target_os = "aix"))] + unsafe { + let mut info = std::mem::zeroed(); + if libc::dladdr(function, &mut info) == 0 { + return Err("dladdr failed".into()); + } + #[cfg(target_os = "cygwin")] + let fname_ptr = info.dli_fname.as_ptr(); + #[cfg(not(target_os = "cygwin"))] + let fname_ptr = { + assert!(!info.dli_fname.is_null(), "dli_fname cannot be null"); + info.dli_fname + }; + let bytes = CStr::from_ptr(fname_ptr).to_bytes(); + let os = OsStr::from_bytes(bytes); + try_canonicalize(Path::new(os)).map_err(|e| e.to_string()) + } + + #[cfg(target_os = "aix")] + unsafe { + // On AIX, the symbol references a function descriptor. + // A function descriptor is consisted of (See https://reviews.llvm.org/D62532) + // * The address of the entry point of the function. + // * The TOC base address for the function. + // * The environment pointer. + // The function descriptor is in the data section. + let addr = function as u64; + let mut buffer = vec![std::mem::zeroed::(); 64]; + loop { + if libc::loadquery( + libc::L_GETINFO, + buffer.as_mut_ptr() as *mut libc::c_void, + (size_of::() * buffer.len()) as u32, + ) >= 0 + { + break; + } else { + if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM { + return Err("loadquery failed".into()); } - #[cfg(target_os = "cygwin")] - let fname_ptr = info.dli_fname.as_ptr(); - #[cfg(not(target_os = "cygwin"))] - let fname_ptr = { - assert!(!info.dli_fname.is_null(), "dli_fname cannot be null"); - info.dli_fname - }; - let bytes = CStr::from_ptr(fname_ptr).to_bytes(); + buffer.resize(buffer.len() * 2, std::mem::zeroed::()); + } + } + let mut current = buffer.as_mut_ptr() as *mut libc::ld_info; + loop { + let data_base = (*current).ldinfo_dataorg as u64; + let data_end = data_base + (*current).ldinfo_datasize; + if (data_base..data_end).contains(&addr) { + let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes(); let os = OsStr::from_bytes(bytes); - try_canonicalize(Path::new(os)).map_err(|e| e.to_string()) + return try_canonicalize(Path::new(os)).map_err(|e| e.to_string()); } - - #[cfg(target_os = "aix")] - unsafe { - // On AIX, the symbol `current_dll_path` references a function descriptor. - // A function descriptor is consisted of (See https://reviews.llvm.org/D62532) - // * The address of the entry point of the function. - // * The TOC base address for the function. - // * The environment pointer. - // The function descriptor is in the data section. - let addr = current_dll_path as u64; - let mut buffer = vec![std::mem::zeroed::(); 64]; - loop { - if libc::loadquery( - libc::L_GETINFO, - buffer.as_mut_ptr() as *mut libc::c_void, - (size_of::() * buffer.len()) as u32, - ) >= 0 - { - break; - } else { - if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM { - return Err("loadquery failed".into()); - } - buffer.resize(buffer.len() * 2, std::mem::zeroed::()); - } - } - let mut current = buffer.as_mut_ptr() as *mut libc::ld_info; - loop { - let data_base = (*current).ldinfo_dataorg as u64; - let data_end = data_base + (*current).ldinfo_datasize; - if (data_base..data_end).contains(&addr) { - let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes(); - let os = OsStr::from_bytes(bytes); - return try_canonicalize(Path::new(os)).map_err(|e| e.to_string()); - } - if (*current).ldinfo_next == 0 { - break; - } - current = (current as *mut i8).offset((*current).ldinfo_next as isize) - as *mut libc::ld_info; - } - return Err(format!("current dll's address {} is not in the load map", addr)); + if (*current).ldinfo_next == 0 { + break; } - }) - .clone() + current = + (current as *mut i8).offset((*current).ldinfo_next as isize) as *mut libc::ld_info; + } + return Err(format!("current dll's address {} is not in the load map", addr)); + } } #[cfg(windows)] -fn current_dll_path() -> Result { +pub unsafe fn dll_path(function: *mut std::ffi::c_void) -> Result { use std::ffi::OsString; use std::io; use std::os::windows::prelude::*; @@ -240,10 +232,7 @@ fn current_dll_path() -> Result { unsafe { GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - PCWSTR( - current_dll_path as fn() -> Result - as *mut u16, - ), + PCWSTR(function as *mut u16), &mut module, ) } @@ -269,8 +258,20 @@ fn current_dll_path() -> Result { } #[cfg(target_os = "wasi")] +pub unsafe fn dll_path(function: *mut std::ffi::c_void) -> Result { + Err("dll_path is not supported on WASI".to_string()) +} + fn current_dll_path() -> Result { - Err("current_dll_path is not supported on WASI".to_string()) + use std::sync::OnceLock; + + // This is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr` + // needs to iterate over the symbol table of librustc_driver.so until it finds a match. + // As such cache this to avoid recomputing if we try to get the sysroot in multiple places. + static CURRENT_DLL_PATH: OnceLock> = OnceLock::new(); + CURRENT_DLL_PATH + .get_or_init(|| unsafe { dll_path(current_dll_path as fn() -> _ as *mut _) }) + .clone() } /// This function checks if sysroot is found using env::args().next(), and if it From 74ddcf20b975501f7649636fcb76dd45f05e8c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 26 Aug 2026 03:11:20 +0200 Subject: [PATCH 07/18] Add test cases that demonstrate incorrect diagnostics --- tests/ui/lifetimes/raw/three-tokens.rs | 10 ++++- tests/ui/lifetimes/raw/three-tokens.stderr | 20 ++++++++++ .../edition-2015-2018-lexing.rs | 24 ------------ .../pre-2021-lexing.rs | 38 +++++++++++++++++++ .../pre-2021-lexing.stderr | 33 ++++++++++++++++ 5 files changed, 99 insertions(+), 26 deletions(-) create mode 100644 tests/ui/lifetimes/raw/three-tokens.stderr delete mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/edition-2015-2018-lexing.rs create mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.rs create mode 100644 tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.stderr diff --git a/tests/ui/lifetimes/raw/three-tokens.rs b/tests/ui/lifetimes/raw/three-tokens.rs index 2ae54ebbcb537..781f8e3e47913 100644 --- a/tests/ui/lifetimes/raw/three-tokens.rs +++ b/tests/ui/lifetimes/raw/three-tokens.rs @@ -1,6 +1,10 @@ -//@ 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. + +#![warn(rust_2021_prefixes_incompatible_syntax)] macro_rules! ed2015 { ('r # lt) => {}; @@ -8,5 +12,7 @@ macro_rules! ed2015 { } ed2015!('r#lt); +//~^ WARNING prefix `'r` is reserved +//~| WARNING hard error in Rust 2021 fn main() {} diff --git a/tests/ui/lifetimes/raw/three-tokens.stderr b/tests/ui/lifetimes/raw/three-tokens.stderr new file mode 100644 index 0000000000000..413603c2c53d6 --- /dev/null +++ b/tests/ui/lifetimes/raw/three-tokens.stderr @@ -0,0 +1,20 @@ +warning: prefix `'r` is reserved + --> $DIR/three-tokens.rs:14:9 + | +LL | ed2015!('r#lt); + | ^^^ reserved prefix + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see +note: the lint level is defined here + --> $DIR/three-tokens.rs:7:9 + | +LL | #![warn(rust_2021_prefixes_incompatible_syntax)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: insert whitespace here to avoid this being parsed as a prefix in Rust 2021 + | +LL | ed2015!('r# lt); + | + + +warning: 1 warning emitted + diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/edition-2015-2018-lexing.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/edition-2015-2018-lexing.rs deleted file mode 100644 index a503f2bf7a82c..0000000000000 --- a/tests/ui/rfcs/rfc-3348-c-string-literals/edition-2015-2018-lexing.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Regression test for issue #113235. - -//@ check-pass -//@ revisions: edition2015 edition2018 -//@[edition2015] edition: 2015 -//@[edition2018] edition: 2018 - -// Make sure that in pre-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. -// Prefixes including `c` as used by C string literals are only reserved -// in edition 2021 and onward. -// -// Consider checking out rust-2021/reserved-prefixes-migration.rs as well. - -macro_rules! parse { - (c $e:expr) => { - $e - }; -} - -fn main() { - let _: &'static str = parse!(c"hello"); -} diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.rs new file mode 100644 index 0000000000000..3a1c5593ee7aa --- /dev/null +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.rs @@ -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: + + // 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 prefix `c` is unknown + //~| WARNING hard error in Rust 2021 + + macro_rules! indifferent { + ($e:expr) => {}; + (c $e:expr) => {}; + } + + indifferent!(c"..."); + //~^ WARNING prefix `c` is unknown + //~| WARNING hard error in Rust 2021 +} diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.stderr b/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.stderr new file mode 100644 index 0000000000000..530163f72861d --- /dev/null +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.stderr @@ -0,0 +1,33 @@ +warning: prefix `c` is unknown + --> $DIR/pre-2021-lexing.rs:26:34 + | +LL | let _: &'static str = parse!(c"hello"); + | ^ unknown prefix + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see +note: the lint level is defined here + --> $DIR/pre-2021-lexing.rs:7:9 + | +LL | #![warn(rust_2021_prefixes_incompatible_syntax)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: insert whitespace here to avoid this being parsed as a prefix in Rust 2021 + | +LL | let _: &'static str = parse!(c "hello"); + | + + +warning: prefix `c` is unknown + --> $DIR/pre-2021-lexing.rs:35:18 + | +LL | indifferent!(c"..."); + | ^ unknown prefix + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see +help: insert whitespace here to avoid this being parsed as a prefix in Rust 2021 + | +LL | indifferent!(c "..."); + | + + +warning: 2 warnings emitted + From 3ff39765b2649118d8a55d8cee79f0152ded6c53 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 28 Aug 2026 15:58:08 +0200 Subject: [PATCH 08/18] Add regression test for "use of an internal attribute" --- tests/ui/proc-macro/auxiliary/test-re-emit.rs | 8 ++++++++ tests/ui/proc-macro/test-re-emit.rs | 15 +++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 tests/ui/proc-macro/auxiliary/test-re-emit.rs create mode 100644 tests/ui/proc-macro/test-re-emit.rs diff --git a/tests/ui/proc-macro/auxiliary/test-re-emit.rs b/tests/ui/proc-macro/auxiliary/test-re-emit.rs new file mode 100644 index 0000000000000..4b500c4a66166 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/test-re-emit.rs @@ -0,0 +1,8 @@ +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn remove_span(_attr: TokenStream, item: TokenStream) -> TokenStream { + // `.to_string().parse()` will lose the span of the token stream + item.to_string().parse().unwrap() +} diff --git a/tests/ui/proc-macro/test-re-emit.rs b/tests/ui/proc-macro/test-re-emit.rs new file mode 100644 index 0000000000000..c01df98e79a6e --- /dev/null +++ b/tests/ui/proc-macro/test-re-emit.rs @@ -0,0 +1,15 @@ +//@ check-pass +//@ proc-macro: test-re-emit.rs +//@ compile-flags: --test +// Test that we can pass a test through a proc macro that removes the span of the item +// Regression test for https://github.com/rust-lang/rust/issues/161917 + +#[test] +#[test_re_emit::remove_span] +fn meow1() {} + +#[test_re_emit::remove_span] +#[test] +fn meow2() {} + +fn main() {} From ca095daf5fb08010cf044a4253d8d74586f9a354 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 28 Aug 2026 16:11:46 +0200 Subject: [PATCH 09/18] Add regression test for "expected item after attributes" --- .../auxiliary/test-count-attributes.rs | 23 ++++++++++++++ tests/ui/proc-macro/test-count-attributes.rs | 30 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 tests/ui/proc-macro/auxiliary/test-count-attributes.rs create mode 100644 tests/ui/proc-macro/test-count-attributes.rs diff --git a/tests/ui/proc-macro/auxiliary/test-count-attributes.rs b/tests/ui/proc-macro/auxiliary/test-count-attributes.rs new file mode 100644 index 0000000000000..c5658f445df2d --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/test-count-attributes.rs @@ -0,0 +1,23 @@ +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn assert_no_attributes(_attr: TokenStream, item: TokenStream) -> TokenStream { + // This will count the "attributes" (in reality the number of hash symbols) on the item. + assert_eq!(item.to_string().chars().filter(|c| *c == '#').count(), 0); + item +} + +#[proc_macro_attribute] +pub fn assert_one_attribute(_attr: TokenStream, item: TokenStream) -> TokenStream { + // This will count the "attributes" (in reality the number of hash symbols) on the item. + assert_eq!(item.to_string().chars().filter(|c| *c == '#').count(), 1); + item +} + +#[proc_macro_attribute] +pub fn assert_two_attributes(_attr: TokenStream, item: TokenStream) -> TokenStream { + // This will count the "attributes" (in reality the number of hash symbols) on the item. + assert_eq!(item.to_string().chars().filter(|c| *c == '#').count(), 2); + item +} diff --git a/tests/ui/proc-macro/test-count-attributes.rs b/tests/ui/proc-macro/test-count-attributes.rs new file mode 100644 index 0000000000000..930e69cc6381f --- /dev/null +++ b/tests/ui/proc-macro/test-count-attributes.rs @@ -0,0 +1,30 @@ +//@ check-pass +//@ proc-macro: test-count-attributes.rs +//@ compile-flags: --test +// Tests whether attributes on tests can be observed by proc macros +// Regression test for https://github.com/rust-lang/rust/issues/161920 + +#[test] +#[test_count_attributes::assert_no_attributes] +fn meow1() {} + +#[test_count_attributes::assert_one_attribute] +#[test] +fn meow2() {} + +#[test] +#[should_panic] +#[test_count_attributes::assert_one_attribute] +fn meow3() {} + +#[test] +#[test_count_attributes::assert_one_attribute] +#[should_panic] +fn meow4() {} + +#[test_count_attributes::assert_two_attributes] +#[test] +#[should_panic] +fn meow5() {} + +fn main() {} From bf56fa46ed986c79bb0d63e6659941783ab2b803 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 11 Aug 2026 17:20:08 +0100 Subject: [PATCH 10/18] Handle multiple action records in EH personality function --- library/std/src/sys/personality/dwarf/eh.rs | 25 +++++++++++++++++- tests/ui/panics/lsda-multiple-action.rs | 29 +++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/ui/panics/lsda-multiple-action.rs diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs index ef5112ad74f13..c23e0afe6c979 100644 --- a/library/std/src/sys/personality/dwarf/eh.rs +++ b/library/std/src/sys/personality/dwarf/eh.rs @@ -48,9 +48,20 @@ pub struct EHContext<'a> { type LPad = *const u8; pub enum EHAction { None, + /// Destructors should be executed when stack unwinds. Cleanup(LPad), + /// Stack unwind should be stopped as the exception is going to be caught by `catch_unwind`. Catch(LPad), + /// Stack unwind should be stopped for termination (`UnwindAction::Terminate`). + /// + /// Note that due to inlining the landing pad can execute destructors before terminating. So + /// this is different from `Terminate`. + /// + /// Handling of this is mostly identical to `Catch`; except that Rust frames that have no + /// destructors but only `UnwindAction::Terminate` is considered as plain-old-frame (POF) and + /// forced unwind is allowed to unwind past it; so this is treated as `None` during forced unwind. Filter(LPad), + /// Process should be terminated as the call site does not permit unwinding. Terminate, } @@ -160,7 +171,19 @@ unsafe fn interpret_cs_action( let action_record = unsafe { action_table.offset(cs_action_entry as isize - 1) }; let mut action_reader = DwarfReader::new(action_record); let ttype_index = unsafe { action_reader.read_sleb128() }; - if ttype_index == 0 { + let next_action = unsafe { action_reader.read_sleb128() }; + if next_action != 0 { + // We observed multiple actions. Action records contain no duplicates (at least that is + // true for both LLVM/GCC), and as Rust does not have exception specification, this + // indicates that we have at least 2 of "cleanup", "catch" and "filter", so we should + // catch all exceptions. + // + // Note that even for the case of "cleanup" + "filter", decoding them as "catch" is + // fine: "filter" behaves identically to "catch" except for forced unwind; in case of + // forced unwind, hitting a "cleanup" landing pad is UB as it indicates that we're + // unwinding past a non-POF Rust frame. + EHAction::Catch(lpad) + } else if ttype_index == 0 { EHAction::Cleanup(lpad) } else if ttype_index > 0 { // Stop unwinding Rust panics at catch_unwind. diff --git a/tests/ui/panics/lsda-multiple-action.rs b/tests/ui/panics/lsda-multiple-action.rs new file mode 100644 index 0000000000000..236e8b90ca5b1 --- /dev/null +++ b/tests/ui/panics/lsda-multiple-action.rs @@ -0,0 +1,29 @@ +//@ run-pass +//@ needs-unwind +//@ ignore-backends: gcc +//@ compile-flags: -Copt-level=3 + +struct Guard; + +impl Drop for Guard { + fn drop(&mut self) { + core::hint::black_box(()); + } +} + +#[inline(never)] +fn unwind() { + if core::hint::black_box(true) { + std::panic::resume_unwind(Box::new(())); + } +} + +fn main() { + // The `catch_unwind` will generate `landingpad catch` and the destructor will generate + // `landingpad cleanup`; after LLVM inlining it will become `landingpad cleanup catch`, and this + // is translated to action record chains in LSDA. + let _ = std::panic::catch_unwind(|| { + let _guard = Guard; + unwind(); + }); +} From 4f404c1f7e1de1ec058a7729568aaa310a063959 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Mon, 24 Aug 2026 21:08:28 +0300 Subject: [PATCH 11/18] Use `drop_guard` in some places in {core,alloc,std} --- library/alloc/src/boxed/thin.rs | 40 +-- .../alloc/src/collections/binary_heap/mod.rs | 14 +- library/alloc/src/collections/btree/map.rs | 18 +- library/alloc/src/collections/btree/mem.rs | 10 +- library/alloc/src/collections/btree/node.rs | 17 +- library/alloc/src/collections/linked_list.rs | 22 +- .../alloc/src/collections/vec_deque/drain.rs | 247 +++++++++--------- .../src/collections/vec_deque/into_iter.rs | 54 ++-- .../alloc/src/collections/vec_deque/mod.rs | 30 +-- library/alloc/src/rc.rs | 41 +-- library/alloc/src/slice.rs | 35 ++- library/alloc/src/string.rs | 33 +-- library/alloc/src/sync.rs | 56 ++-- library/alloc/src/vec/drain.rs | 43 ++- library/alloc/src/vec/into_iter.rs | 20 +- library/std/src/sys/fs/unix.rs | 36 +-- library/std/src/sys/pal/unix/sync/condvar.rs | 21 +- library/std/src/sys/process/unix/unix.rs | 70 ++--- library/std/src/sys/process/windows/tests.rs | 12 +- 19 files changed, 313 insertions(+), 506 deletions(-) diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 1e60107a1d15c..98904aa500669 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -11,7 +11,7 @@ use core::marker::PhantomData; use core::marker::Unsize; #[cfg(not(no_global_oom_handling))] use core::mem; -use core::mem::SizedTypeProperties; +use core::mem::{DropGuard, SizedTypeProperties}; use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull, Pointee}; @@ -364,38 +364,24 @@ impl WithHeader { // - Assumes that either `value` can be dereferenced, or is the // `NonNull::dangling()` we use when both `T` and `H` are ZSTs. unsafe fn drop(&self, value: *mut T) { - struct DropGuard { - ptr: NonNull, - value_layout: Layout, - _marker: PhantomData, - } - - impl Drop for DropGuard { - fn drop(&mut self) { - // All ZST are allocated statically. - if self.value_layout.size() == 0 { - return; - } + // SAFETY: Caller ensures `value` is valid. + let value_layout = unsafe { Layout::for_value_raw(value) }; - let (layout, value_offset) = - // SAFETY: Layout must have been computable if we're in drop - unsafe { WithHeader::::alloc_layout(self.value_layout).unwrap_unchecked() }; + let _guard; + // All ZST are allocated statically. + if value_layout.size() != 0 { + _guard = DropGuard::new(self.0, |ptr| { + let layout = WithHeader::::alloc_layout(value_layout); + // SAFETY: Layout must have been computable if we're in this callback + let (layout, value_offset) = unsafe { layout.unwrap_unchecked() }; // Since we only allocate for non-ZSTs, the layout size cannot be zero. - debug_assert!(layout.size() != 0); + debug_assert_ne!(layout.size(), 0); // SAFETY: We own the allocation with `layout` at `ptr - value_offset`. - unsafe { alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout) }; - } + unsafe { alloc::dealloc(ptr.as_ptr().sub(value_offset), layout) }; + }); } - // `_guard` will deallocate the memory when dropped, even if `drop_in_place` unwinds. - let _guard = DropGuard { - ptr: self.0, - // SAFETY: Caller ensures `value` is valid. - value_layout: unsafe { Layout::for_value_raw(value) }, - _marker: PhantomData::, - }; - // We only drop the value because the Pointee trait requires that the metadata is copy // aka trivially droppable. // SAFETY: We're the only droppers of `value` and it's not dropped again. diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index cf6018f917a54..0fc83871c815f 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -145,7 +145,7 @@ use core::alloc::Allocator; use core::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedFused, TrustedLen}; -use core::mem::{self, ManuallyDrop, swap}; +use core::mem::{DropGuard, ManuallyDrop, swap}; use core::num::NonZero; use core::ops::{Deref, DerefMut}; use core::{fmt, ptr}; @@ -1914,18 +1914,10 @@ impl<'a, T: Ord, A: Allocator> DrainSorted<'a, T, A> { impl<'a, T: Ord, A: Allocator> Drop for DrainSorted<'a, T, A> { /// Removes heap elements in heap order. fn drop(&mut self) { - struct DropGuard<'r, 'a, T: Ord, A: Allocator>(&'r mut DrainSorted<'a, T, A>); - - impl<'r, 'a, T: Ord, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { - fn drop(&mut self) { - while self.0.inner.pop().is_some() {} - } - } - while let Some(item) = self.inner.pop() { - let guard = DropGuard(self); + let guard = DropGuard::new(&mut *self, |this| while this.inner.pop().is_some() {}); drop(item); - mem::forget(guard); + DropGuard::dismiss(guard); } } } diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index e8832fd6e27ca..da08f9bfa36ed 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -5,7 +5,7 @@ use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; use core::iter::{FusedIterator, TrustedLen}; use core::marker::PhantomData; -use core::mem::{self, ManuallyDrop}; +use core::mem::{self, DropGuard, ManuallyDrop}; use core::ops::{Bound, Index, RangeBounds}; use core::ptr; @@ -1912,24 +1912,18 @@ impl IntoIterator for BTreeMap { #[stable(feature = "btree_drop", since = "1.7.0")] impl Drop for IntoIter { fn drop(&mut self) { - struct DropGuard<'a, K, V, A: AllocatorClone>(&'a mut IntoIter); - - impl<'a, K, V, A: AllocatorClone> Drop for DropGuard<'a, K, V, A> { - fn drop(&mut self) { + while let Some(kv) = self.dying_next() { + let guard = DropGuard::new(&mut *self, |this| { // Continue the same loop we perform below. This only runs when unwinding, so we // don't have to care about panics this time (they'll abort). - while let Some(kv) = self.0.dying_next() { + while let Some(kv) = this.dying_next() { // SAFETY: we consume the dying handle immediately. unsafe { kv.drop_key_val() }; } - } - } - - while let Some(kv) = self.dying_next() { - let guard = DropGuard(self); + }); // SAFETY: we don't touch the tree before consuming the dying handle. unsafe { kv.drop_key_val() }; - mem::forget(guard); + DropGuard::dismiss(guard); } } } diff --git a/library/alloc/src/collections/btree/mem.rs b/library/alloc/src/collections/btree/mem.rs index ad86e9422d974..9734649fd5adc 100644 --- a/library/alloc/src/collections/btree/mem.rs +++ b/library/alloc/src/collections/btree/mem.rs @@ -16,13 +16,7 @@ pub(super) fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { /// If a panic occurs in the `change` closure, the entire process will be aborted. #[inline] pub(super) fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { - struct PanicGuard; - impl Drop for PanicGuard { - fn drop(&mut self) { - intrinsics::abort() - } - } - let guard = PanicGuard; + let guard = mem::DropGuard::new((), |()| intrinsics::abort()); // SAFETY: v is valid for reads and we write a new value before returning. let value = unsafe { ptr::read(v) }; let (new_value, ret) = change(value); @@ -30,6 +24,6 @@ pub(super) fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { unsafe { ptr::write(v, new_value); } - mem::forget(guard); + mem::DropGuard::dismiss(guard); ret } diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index aa38d17bb6dbc..c97f7ac00474a 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -32,7 +32,7 @@ // an edge both identifies a position and contains a pointer to a child node. use core::marker::PhantomData; -use core::mem::{self, MaybeUninit}; +use core::mem::{self, DropGuard, MaybeUninit}; use core::num::NonZero; use core::ptr::{self, NonNull}; use core::slice::SliceIndex; @@ -1237,25 +1237,14 @@ impl Handle, marker::KV> /// The node that the handle refers to must not yet have been deallocated. #[inline] pub(super) unsafe fn drop_key_val(mut self) { - // Run the destructor of the value even if the destructor of the key panics. - struct Dropper<'a, T>(&'a mut MaybeUninit); - impl Drop for Dropper<'_, T> { - #[inline] - fn drop(&mut self) { - // ignore-tidy-undocumented-unsafe - unsafe { - self.0.assume_init_drop(); - } - } - } - debug_assert!(self.idx < self.node.len()); let leaf = self.node.as_leaf_dying(); // ignore-tidy-undocumented-unsafe unsafe { let key = leaf.keys.get_unchecked_mut(self.idx); let val = leaf.vals.get_unchecked_mut(self.idx); - let _guard = Dropper(val); + // Run the destructor of the value even if the destructor of the key panics. + let _guard = DropGuard::new(val, |val| val.assume_init_drop()); key.assume_init_drop(); // dropping the guard will drop the value } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 1417f56e46cf9..953cffc1c396d 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -17,6 +17,7 @@ use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::iter::{FusedIterator, TrustedLen}; use core::marker::PhantomData; +use core::mem::DropGuard; use core::ptr::NonNull; use core::{fmt, mem}; @@ -1192,20 +1193,15 @@ impl LinkedList { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T, A: Allocator> Drop for LinkedList { fn drop(&mut self) { - struct DropGuard<'a, T, A: Allocator>(&'a mut LinkedList); - - impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { - fn drop(&mut self) { - // Continue the same loop we do below. This only runs when a destructor has - // panicked. If another one panics this will abort. - while self.0.pop_front_node().is_some() {} - } - } - // Wrap self so that if a destructor panics, we can try to keep looping - let guard = DropGuard(self); - while guard.0.pop_front_node().is_some() {} - mem::forget(guard); + let mut guard = DropGuard::new(self, |this| { + // Continue the same loop we do below. This only runs when a destructor has + // panicked. If another one panics this will abort. + while this.pop_front_node().is_some() {} + }); + + while guard.pop_front_node().is_some() {} + DropGuard::dismiss(guard); } } diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs index b56af5f0e85b6..48955361e7675 100644 --- a/library/alloc/src/collections/vec_deque/drain.rs +++ b/library/alloc/src/collections/vec_deque/drain.rs @@ -1,6 +1,6 @@ use core::iter::FusedIterator; use core::marker::PhantomData; -use core::mem::{self, SizedTypeProperties}; +use core::mem::{self, DropGuard, SizedTypeProperties}; use core::ptr::NonNull; use core::{fmt, ptr}; @@ -94,144 +94,137 @@ unsafe impl Send for Drain<'_, T, A> {} #[stable(feature = "drain", since = "1.6.0")] impl Drop for Drain<'_, T, A> { fn drop(&mut self) { - struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>); - - let guard = DropGuard(self); - - if mem::needs_drop::() && guard.0.remaining != 0 { - // SAFETY: We just checked that `self.remaining != 0`. - let (front, back) = unsafe { guard.0.as_slices() }; - // since idx is a logical index, we don't need to worry about wrapping. - guard.0.idx += front.len(); - guard.0.remaining -= front.len(); - // SAFETY: This can't have been dropped before since - // `idx` & `remaining` track what's been dropped. - unsafe { ptr::drop_in_place(front) }; - guard.0.remaining = 0; - // SAFETY: Ditto. - unsafe { ptr::drop_in_place(back) }; - } - // Dropping `guard` handles moving the remaining elements into place. - impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { - #[inline] - fn drop(&mut self) { - if mem::needs_drop::() && self.0.remaining != 0 { - // SAFETY: We just checked that `self.remaining != 0`. - unsafe { - let (front, back) = self.0.as_slices(); - ptr::drop_in_place(front); - ptr::drop_in_place(back); - } + let mut guard = DropGuard::new(self, |drain| { + if mem::needs_drop::() && drain.remaining != 0 { + // SAFETY: We just checked that `self.remaining != 0`. + unsafe { + let (front, back) = drain.as_slices(); + ptr::drop_in_place(front); + ptr::drop_in_place(back); } + } - // ignore-tidy-undocumented-unsafe - let source_deque = unsafe { self.0.deque.as_mut() }; + // ignore-tidy-undocumented-unsafe + let source_deque = unsafe { drain.deque.as_mut() }; - let drain_len = self.0.drain_len; - let head_len = source_deque.len; // #elements in front of the drain - let tail_len = self.0.tail_len; // #elements behind the drain - let new_len = head_len + tail_len; + let drain_len = drain.drain_len; + let head_len = source_deque.len; // #elements in front of the drain + let tail_len = drain.tail_len; // #elements behind the drain + let new_len = head_len + tail_len; - if T::IS_ZST { - // no need to copy around any memory if T is a ZST - source_deque.len = new_len; - return; - } + if T::IS_ZST { + // no need to copy around any memory if T is a ZST + source_deque.len = new_len; + return; + } - // Next, we will fill the hole left by the drain with as few writes as possible. - // The code below handles the following control flow and reduces the amount of - // branches under the assumption that `head_len == 0 || tail_len == 0`, i.e. - // draining at the front or at the back of the dequeue is especially common. - // - // H = "head index" = `deque.head` - // h = elements in front of the drain - // d = elements in the drain - // t = elements behind the drain - // - // Note that the buffer may wrap at any point and the wrapping is handled by - // `wrap_copy` and `to_physical_idx`. - // - // Case 1: if `head_len == 0 && tail_len == 0` - // Everything was drained, reset the head index back to 0. - // H - // [ . . . . . d d d d . . . . . ] - // H - // [ . . . . . . . . . . . . . . ] - // - // Case 2: else if `tail_len == 0` - // Don't move data or the head index. - // H - // [ . . . h h h h d d d d . . . ] - // H - // [ . . . h h h h . . . . . . . ] - // - // Case 3: else if `head_len == 0` - // Don't move data, but move the head index. - // H - // [ . . . d d d d t t t t . . . ] - // H - // [ . . . . . . . t t t t . . . ] - // - // Case 4: else if `tail_len <= head_len` - // Move data, but not the head index. - // H - // [ . . h h h h d d d d t t . . ] - // H - // [ . . h h h h t t . . . . . . ] - // - // Case 5: else - // Move data and the head index. - // H - // [ . . h h d d d d t t t t . . ] - // H - // [ . . . . . . h h t t t t . . ] + // Next, we will fill the hole left by the drain with as few writes as possible. + // The code below handles the following control flow and reduces the amount of + // branches under the assumption that `head_len == 0 || tail_len == 0`, i.e. + // draining at the front or at the back of the dequeue is especially common. + // + // H = "head index" = `deque.head` + // h = elements in front of the drain + // d = elements in the drain + // t = elements behind the drain + // + // Note that the buffer may wrap at any point and the wrapping is handled by + // `wrap_copy` and `to_physical_idx`. + // + // Case 1: if `head_len == 0 && tail_len == 0` + // Everything was drained, reset the head index back to 0. + // H + // [ . . . . . d d d d . . . . . ] + // H + // [ . . . . . . . . . . . . . . ] + // + // Case 2: else if `tail_len == 0` + // Don't move data or the head index. + // H + // [ . . . h h h h d d d d . . . ] + // H + // [ . . . h h h h . . . . . . . ] + // + // Case 3: else if `head_len == 0` + // Don't move data, but move the head index. + // H + // [ . . . d d d d t t t t . . . ] + // H + // [ . . . . . . . t t t t . . . ] + // + // Case 4: else if `tail_len <= head_len` + // Move data, but not the head index. + // H + // [ . . h h h h d d d d t t . . ] + // H + // [ . . h h h h t t . . . . . . ] + // + // Case 5: else + // Move data and the head index. + // H + // [ . . h h d d d d t t t t . . ] + // H + // [ . . . . . . h h t t t t . . ] - // When draining at the front (`.drain(..n)`) or at the back (`.drain(n..)`), - // we don't need to copy any data. The number of elements copied would be 0. - if head_len != 0 && tail_len != 0 { - join_head_and_tail_wrapping(source_deque, drain_len, head_len, tail_len); - // Marking this function as cold helps LLVM to eliminate it entirely if - // this branch is never taken. - // We use `#[cold]` instead of `#[inline(never)]`, because inlining this - // function into the general case (`.drain(n..m)`) is fine. - // See `tests/codegen-llvm/vecdeque-drain.rs` for a test. - #[cold] - fn join_head_and_tail_wrapping( - source_deque: &mut VecDeque, - drain_len: usize, - head_len: usize, - tail_len: usize, - ) { - // Pick whether to move the head or the tail here. - let (src, dst, len); - if head_len < tail_len { - src = source_deque.head; - dst = source_deque.to_wrapped_index(drain_len); - len = head_len; - } else { - src = source_deque.to_wrapped_index(head_len + drain_len); - dst = source_deque.to_wrapped_index(head_len); - len = tail_len; - }; + // When draining at the front (`.drain(..n)`) or at the back (`.drain(n..)`), + // we don't need to copy any data. The number of elements copied would be 0. + if head_len != 0 && tail_len != 0 { + join_head_and_tail_wrapping(source_deque, drain_len, head_len, tail_len); + // Marking this function as cold helps LLVM to eliminate it entirely if + // this branch is never taken. + // We use `#[cold]` instead of `#[inline(never)]`, because inlining this + // function into the general case (`.drain(n..m)`) is fine. + // See `tests/codegen-llvm/vecdeque-drain.rs` for a test. + #[cold] + fn join_head_and_tail_wrapping( + source_deque: &mut VecDeque, + drain_len: usize, + head_len: usize, + tail_len: usize, + ) { + // Pick whether to move the head or the tail here. + let (src, dst, len); + if head_len < tail_len { + src = source_deque.head; + dst = source_deque.to_wrapped_index(drain_len); + len = head_len; + } else { + src = source_deque.to_wrapped_index(head_len + drain_len); + dst = source_deque.to_wrapped_index(head_len); + len = tail_len; + }; - // ignore-tidy-undocumented-unsafe - unsafe { - source_deque.wrap_copy(src, dst, len); - } + // ignore-tidy-undocumented-unsafe + unsafe { + source_deque.wrap_copy(src, dst, len); } } + } - if new_len == 0 { - // Special case: If the entire deque was drained, reset the head back to 0, - // like `.clear()` does. - source_deque.head = WrappedIndex::zero(); - } else if head_len < tail_len { - // If we moved the head above, then we need to adjust the head index here. - source_deque.head = source_deque.to_wrapped_index(drain_len); - } - source_deque.len = new_len; + if new_len == 0 { + // Special case: If the entire deque was drained, reset the head back to 0, + // like `.clear()` does. + source_deque.head = WrappedIndex::zero(); + } else if head_len < tail_len { + // If we moved the head above, then we need to adjust the head index here. + source_deque.head = source_deque.to_wrapped_index(drain_len); } + source_deque.len = new_len; + }); + + if mem::needs_drop::() && guard.remaining != 0 { + // SAFETY: We just checked that `self.remaining != 0`. + let (front, back) = unsafe { guard.as_slices() }; + // since idx is a logical index, we don't need to worry about wrapping. + guard.idx += front.len(); + guard.remaining -= front.len(); + // SAFETY: This can't have been dropped before since + // `idx` & `remaining` track what's been dropped. + unsafe { ptr::drop_in_place(front) }; + guard.remaining = 0; + // SAFETY: Ditto. + unsafe { ptr::drop_in_place(back) }; } } } diff --git a/library/alloc/src/collections/vec_deque/into_iter.rs b/library/alloc/src/collections/vec_deque/into_iter.rs index e18b85dd4b694..7c83fff6c4ab1 100644 --- a/library/alloc/src/collections/vec_deque/into_iter.rs +++ b/library/alloc/src/collections/vec_deque/into_iter.rs @@ -1,5 +1,5 @@ use core::iter::{FusedIterator, TrustedLen}; -use core::mem::MaybeUninit; +use core::mem::{DropGuard, MaybeUninit}; use core::num::NonZero; use core::ops::Try; use core::{array, fmt, ptr}; @@ -78,28 +78,20 @@ impl Iterator for IntoIter { F: FnMut(B, Self::Item) -> R, R: Try, { - struct Guard<'a, T, A: Allocator> { - deque: &'a mut VecDeque, - // `consumed <= deque.len` always holds. - consumed: usize, - } - - impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> { - fn drop(&mut self) { - self.deque.len -= self.consumed; - self.deque.head = self.deque.to_wrapped_index(self.consumed); - } - } - - let mut guard = Guard { deque: &mut self.inner, consumed: 0 }; + // `consumed <= deque.len` always holds. + let mut guard = DropGuard::new((&mut self.inner, 0), |(deque, consumed)| { + deque.len -= consumed; + deque.head = deque.to_wrapped_index(consumed); + }); - let (head, tail) = guard.deque.as_slices(); + let (deque, consumed) = &mut *guard; + let (head, tail) = deque.as_slices(); init = head .iter() .map(|elem| { - guard.consumed += 1; - // SAFETY: Because we incremented `guard.consumed`, the + *consumed += 1; + // SAFETY: Because we incremented `consumed`, the // deque effectively forgot the element, so we can take // ownership unsafe { ptr::read(elem) } @@ -108,7 +100,7 @@ impl Iterator for IntoIter { tail.iter() .map(|elem| { - guard.consumed += 1; + *consumed += 1; // SAFETY: Same as above. unsafe { ptr::read(elem) } }) @@ -201,26 +193,18 @@ impl DoubleEndedIterator for IntoIter { F: FnMut(B, Self::Item) -> R, R: Try, { - struct Guard<'a, T, A: Allocator> { - deque: &'a mut VecDeque, - // `consumed <= deque.len` always holds. - consumed: usize, - } - - impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> { - fn drop(&mut self) { - self.deque.len -= self.consumed; - } - } - - let mut guard = Guard { deque: &mut self.inner, consumed: 0 }; + // `consumed <= deque.len` always holds. + let mut guard = DropGuard::new((&mut self.inner, 0), |(deque, consumed)| { + deque.len -= consumed; + }); - let (head, tail) = guard.deque.as_slices(); + let (deque, consumed) = &mut *guard; + let (head, tail) = deque.as_slices(); init = tail .iter() .map(|elem| { - guard.consumed += 1; + *consumed += 1; // SAFETY: See `try_fold`'s safety comment. unsafe { ptr::read(elem) } }) @@ -228,7 +212,7 @@ impl DoubleEndedIterator for IntoIter { head.iter() .map(|elem| { - guard.consumed += 1; + *consumed += 1; // SAFETY: Same as above. unsafe { ptr::read(elem) } }) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 385e172b23207..08abf0e5c5a68 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -17,7 +17,7 @@ use core::iter::{ByRefSized, repeat_n, repeat_with}; // failures in linkchecker even though rustdoc built the docs just fine. #[allow(unused_imports)] use core::mem; -use core::mem::{ManuallyDrop, SizedTypeProperties}; +use core::mem::{DropGuard, ManuallyDrop, SizedTypeProperties}; use core::ops::{Index, IndexMut, Range, RangeBounds}; use core::{fmt, ptr, slice}; @@ -653,37 +653,25 @@ impl VecDeque { mut iter: impl Iterator, len: usize, ) -> usize { - struct Guard<'a, T, A: Allocator> { - deque: &'a mut VecDeque, - written: usize, - } - - impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> { - fn drop(&mut self) { - self.deque.len += self.written; - } - } - let head_room = self.capacity() - dst.as_index(); - let mut guard = Guard { deque: self, written: 0 }; + let mut guard = DropGuard::new((self, 0), |(deque, written)| { + deque.len += written; + }); + let (deque, written) = &mut *guard; if head_room >= len { // ignore-tidy-undocumented-unsafe - unsafe { guard.deque.write_iter(dst, iter, &mut guard.written) }; + unsafe { deque.write_iter(dst, iter, written) }; } else { // ignore-tidy-undocumented-unsafe unsafe { - guard.deque.write_iter( - dst, - ByRefSized(&mut iter).take(head_room), - &mut guard.written, - ); - guard.deque.write_iter(WrappedIndex::zero(), iter, &mut guard.written) + deque.write_iter(dst, ByRefSized(&mut iter).take(head_room), written); + deque.write_iter(WrappedIndex::zero(), iter, written) }; } - guard.written + *written } /// Frobs the head and tail sections around to handle the fact that we diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 09540183c488f..b4822d98bb45a 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2450,47 +2450,32 @@ impl Rc<[T]> { /// Behavior is undefined should the size be wrong. #[cfg(not(no_global_oom_handling))] unsafe fn from_iter_exact(iter: impl Iterator, len: usize) -> Rc<[T]> { - // Panic guard while cloning T elements. - // In the event of a panic, elements that have been written - // into the new RcInner will be dropped, then the memory freed. - struct Guard { - mem: NonNull, - elems: *mut T, - layout: Layout, - n_elems: usize, - } - - impl Drop for Guard { - fn drop(&mut self) { - // ignore-tidy-undocumented-unsafe - unsafe { - let slice = from_raw_parts_mut(self.elems, self.n_elems); - ptr::drop_in_place(slice); - - Global.deallocate(self.mem, self.layout); - } - } - } + use core::mem::DropGuard; // ignore-tidy-undocumented-unsafe unsafe { let ptr = Self::allocate_for_slice(len); - - let mem = ptr as *mut _ as *mut u8; let layout = Layout::for_value_raw(ptr); // Pointer to first element - let elems = (&raw mut (*ptr).value) as *mut T; + let elems = (&raw mut (*ptr).value).as_mut_ptr(); - let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; + // Panic guard while cloning T elements. + // In the event of a panic, elements that have been written + // into the new RcInner will be dropped, then the memory freed. + let mut guard = DropGuard::new(0, |n_elems| { + let slice = from_raw_parts_mut(elems, n_elems); + ptr::drop_in_place(slice); + Global.deallocate(NonNull::new_unchecked(ptr.cast()), layout); + }); for (i, item) in iter.enumerate() { ptr::write(elems.add(i), item); - guard.n_elems += 1; + *guard += 1; } - // All clear. Forget the guard so it doesn't free the new RcInner. - mem::forget(guard); + // All clear. Dismiss the guard so it doesn't free the new RcInner. + DropGuard::dismiss(guard); Self::from_ptr(ptr) } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index c950569e9838b..541b3413f71ba 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -408,35 +408,30 @@ impl [T] { impl ConvertVec for T { #[inline] default fn to_vec(s: &[Self], alloc: A) -> Vec { - struct DropGuard<'a, T, A: Allocator> { - vec: &'a mut Vec, - num_init: usize, - } - impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { - #[inline] - fn drop(&mut self) { + use core::mem::DropGuard; + + let mut guard = DropGuard::new( + (0, Vec::with_capacity_in(s.len(), alloc)), + |(num_init, mut vec)| { // SAFETY: // items were marked initialized in the loop below - unsafe { - self.vec.set_len(self.num_init); - } - } - } - let mut vec = Vec::with_capacity_in(s.len(), alloc); - let mut guard = DropGuard { vec: &mut vec, num_init: 0 }; - let slots = guard.vec.spare_capacity_mut(); + unsafe { vec.set_len(num_init) } + }, + ); + let (num_init, vec) = &mut *guard; + + let slots = vec.spare_capacity_mut(); // .take(slots.len()) is necessary for LLVM to remove bounds checks // and has better codegen than zip. for (i, b) in s.iter().enumerate().take(slots.len()) { - guard.num_init = i; + *num_init = i; slots[i].write(b.clone()); } - core::mem::forget(guard); + + let (_, mut vec) = DropGuard::dismiss(guard); // SAFETY: // the vec was allocated and initialized above to at least this length. - unsafe { - vec.set_len(s.len()); - } + unsafe { vec.set_len(s.len()) }; vec } } diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 38c36fa25e41e..d2b5a5a53a34a 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -46,6 +46,7 @@ use core::error::Error; use core::iter::FusedIterator; #[cfg(not(no_global_oom_handling))] use core::iter::from_fn; +use core::mem::DropGuard; #[cfg(not(no_global_oom_handling))] use core::num::Saturating; #[cfg(not(no_global_oom_handling))] @@ -1689,20 +1690,6 @@ impl String { return; } - struct PanicGuard<'a> { - s: &'a mut String, - write: usize, - } - - impl Drop for PanicGuard<'_> { - fn drop(&mut self) { - debug_assert!(self.write <= self.s.len()); - debug_assert!(str::from_utf8(&self.s.vec[..self.write]).is_ok()); - // SAFETY: Restore the string length to the number of bytes written so far. - unsafe { self.s.vec.set_len(self.write) } - } - } - // Fast path: find the first character that should be removed or return early. let mut chars = self.char_indices(); let (mut read, write) = loop { @@ -1714,26 +1701,32 @@ impl String { drop(chars); // Slow path: at least one character is going to be removed. - let mut g = PanicGuard { s: self, write }; + let mut guard = DropGuard::new((self, write), |(s, write)| { + debug_assert!(write <= s.len()); + debug_assert!(str::from_utf8(&s.vec[..write]).is_ok()); + // SAFETY: Restore the string length to the number of bytes written so far. + unsafe { s.vec.set_len(write) } + }); + let (s, write) = &mut *guard; while read < len { // SAFETY: `read` is within bound because `read` < `len`, so taking // a slice with `len` is safe. - let ch = unsafe { g.s.get_unchecked(read..len).chars().next().unwrap_unchecked() }; + let ch = unsafe { s.get_unchecked(read..len).chars().next().unwrap_unchecked() }; let ch_len = ch.len_utf8(); if f(ch) { // SAFETY: `read` is on a char boundary, as guaranteed above; `g.write` is // within bounds because it is always behind `read`. unsafe { - let ptr = g.s.vec.as_mut_ptr(); - ptr::copy(ptr.add(read), ptr.add(g.write), ch_len); + let ptr = s.vec.as_mut_ptr(); + ptr::copy(ptr.add(read), ptr.add(*write), ch_len); } - g.write += ch_len; + *write += ch_len; } read += ch_len; } // All bytes processed; commit the final length by dropping the guard. - drop(g); + drop(guard); } /// Inserts a character into this `String` at byte position `idx`. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 5754e48a41e0a..27192cf686c12 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -19,6 +19,8 @@ use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; +#[cfg(not(no_global_oom_handling))] +use core::mem::DropGuard; use core::mem::{self, Alignment, ManuallyDrop}; use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; @@ -2417,47 +2419,31 @@ impl Arc<[T]> { /// Behavior is undefined should the size be wrong. #[cfg(not(no_global_oom_handling))] unsafe fn from_iter_exact(iter: impl Iterator, len: usize) -> Arc<[T]> { - // Panic guard while cloning T elements. - // In the event of a panic, elements that have been written - // into the new ArcInner will be dropped, then the memory freed. - struct Guard { - mem: NonNull, - elems: *mut T, - layout: Layout, - n_elems: usize, - } - - impl Drop for Guard { - fn drop(&mut self) { - // ignore-tidy-undocumented-unsafe - unsafe { - let slice = from_raw_parts_mut(self.elems, self.n_elems); - ptr::drop_in_place(slice); - - Global.deallocate(self.mem, self.layout); - } - } - } - // ignore-tidy-undocumented-unsafe unsafe { let ptr = Self::allocate_for_slice(len); - - let mem = ptr as *mut _ as *mut u8; let layout = Layout::for_value_raw(ptr); // Pointer to first element - let elems = (&raw mut (*ptr).data) as *mut T; + let elems = (&raw mut (*ptr).data).as_mut_ptr(); + + // Panic guard while cloning T elements. + // In the event of a panic, elements that have been written + // into the new ArcInner will be dropped, then the memory freed. + let mut guard = DropGuard::new(0, |n_elems| { + let slice = from_raw_parts_mut(elems, n_elems); + ptr::drop_in_place(slice); - let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; + Global.deallocate(NonNull::new_unchecked(ptr.cast()), layout); + }); for (i, item) in iter.enumerate() { ptr::write(elems.add(i), item); - guard.n_elems += 1; + *guard += 1; } - // All clear. Forget the guard so it doesn't free the new ArcInner. - mem::forget(guard); + // All clear. Dismiss the guard so it doesn't free the new ArcInner. + DropGuard::dismiss(guard); Self::from_ptr(ptr) } @@ -2678,15 +2664,7 @@ impl Arc { // If we unwind before the Arc is overwritten, we expose a strong // count of 0, resulting in a UAF (#155746, #157203). // Until the new Arc is written, the old Arc must remain valid - struct Guard<'a, T: ?Sized> { - inner: &'a ArcInner, - } - impl<'a, T: ?Sized> Drop for Guard<'a, T> { - fn drop(&mut self) { - self.inner.strong.store(1, Release); - } - } - let guard = Guard { inner: this.inner() }; + let guard = DropGuard::new(this.inner(), |inner| inner.strong.store(1, Release)); // Can just steal the data, all that's left is Weaks // Note that this can panic in two ways: @@ -2707,7 +2685,7 @@ impl Arc { ); // We are now safe from panics. - mem::forget(guard); + DropGuard::dismiss(guard); // Materialize our own implicit weak pointer, so that it can clean // up the ArcInner as needed. diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs index df3ff0a9b769f..8dae87b480256 100644 --- a/library/alloc/src/vec/drain.rs +++ b/library/alloc/src/vec/drain.rs @@ -1,5 +1,5 @@ use core::iter::{FusedIterator, TrustedLen}; -use core::mem::{self, ManuallyDrop, SizedTypeProperties}; +use core::mem::{self, DropGuard, ManuallyDrop, SizedTypeProperties}; use core::ptr::{self, NonNull}; use core::{fmt, slice}; @@ -176,29 +176,6 @@ impl DoubleEndedIterator for Drain<'_, T, A> { #[stable(feature = "drain", since = "1.6.0")] impl Drop for Drain<'_, T, A> { fn drop(&mut self) { - /// Moves back the un-`Drain`ed elements to restore the original `Vec`. - struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>); - - impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { - fn drop(&mut self) { - if self.0.tail_len > 0 { - // ignore-tidy-undocumented-unsafe - unsafe { - let source_vec = self.0.vec.as_mut(); - // memmove back untouched tail, update to new length - let start = source_vec.len(); - let tail = self.0.tail_start; - if tail != start { - let src = source_vec.as_ptr().add(tail); - let dst = source_vec.as_mut_ptr().add(start); - ptr::copy(src, dst, self.0.tail_len); - } - source_vec.set_len(start + self.0.tail_len); - } - } - } - } - let iter = mem::take(&mut self.iter); let drop_len = iter.len(); @@ -219,7 +196,23 @@ impl Drop for Drain<'_, T, A> { } // ensure elements are moved back into their appropriate places, even when drop_in_place panics - let _guard = DropGuard(self); + let _guard = DropGuard::new(self, |this| { + if this.tail_len > 0 { + // ignore-tidy-undocumented-unsafe + unsafe { + let source_vec = this.vec.as_mut(); + // memmove back untouched tail, update to new length + let start = source_vec.len(); + let tail = this.tail_start; + if tail != start { + let src = source_vec.as_ptr().add(tail); + let dst = source_vec.as_mut_ptr().add(start); + ptr::copy(src, dst, this.tail_len); + } + source_vec.set_len(start + this.tail_len); + } + } + }); if drop_len == 0 { return; diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 46874ff76c093..fd19585a680bb 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -3,7 +3,7 @@ use core::iter::{ TrustedRandomAccessNoCoerce, }; use core::marker::PhantomData; -use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties}; +use core::mem::{DropGuard, ManuallyDrop, MaybeUninit, SizedTypeProperties}; use core::num::NonZero; #[cfg(not(no_global_oom_handling))] use core::ops::Deref; @@ -589,23 +589,11 @@ impl Clone for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter { fn drop(&mut self) { - struct DropGuard<'a, T, A: Allocator>(&'a mut IntoIter); - - impl Drop for DropGuard<'_, T, A> { - fn drop(&mut self) { - // ignore-tidy-undocumented-unsafe - unsafe { - self.0.dealloc_only(); - } - } - } - - let guard = DropGuard(self); + // ignore-tidy-undocumented-unsafe + let mut guard = DropGuard::new(self, |this| unsafe { this.dealloc_only() }); // destroy the remaining elements // ignore-tidy-undocumented-unsafe - unsafe { - ptr::drop_in_place(guard.0.as_raw_mut_slice()); - } + unsafe { ptr::drop_in_place(guard.as_raw_mut_slice()) } // now `guard` will be dropped and do the rest } } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 74b4322d027c5..1045a7b7e2f56 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2299,19 +2299,6 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { #[cfg(target_vendor = "apple")] pub fn copy(from: &Path, to: &Path) -> io::Result { const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA; - - struct FreeOnDrop(libc::copyfile_state_t); - impl Drop for FreeOnDrop { - fn drop(&mut self) { - // The code below ensures that `FreeOnDrop` is never a null pointer - unsafe { - // `copyfile_state_free` returns -1 if the `to` or `from` files - // cannot be closed. However, this is not considered an error. - libc::copyfile_state_free(self.0); - } - } - } - let (reader, reader_metadata) = open_from(from)?; let clonefile_result = run_path_with_cstr(to, &|to| { @@ -2332,24 +2319,29 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?; - // We ensure that `FreeOnDrop` never contains a null pointer so it is + let state = unsafe { libc::copyfile_state_alloc() }; + // We ensure that the guard never contains a null pointer so it is // always safe to call `copyfile_state_free` - let state = unsafe { - let state = libc::copyfile_state_alloc(); - if state.is_null() { - return Err(crate::io::Error::last_os_error()); + if state.is_null() { + return Err(crate::io::Error::last_os_error()); + } + let state = crate::mem::DropGuard::new(state, |state| { + // SAFETY: just checked it's not null + unsafe { + // `copyfile_state_free` returns -1 if the `to` or `from` files + // cannot be closed. However, this is not considered an error. + libc::copyfile_state_free(state); } - FreeOnDrop(state) - }; + }); let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { libc::COPYFILE_DATA }; - cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; + cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), *state, flags) })?; let mut bytes_copied: libc::off_t = 0; cvt(unsafe { libc::copyfile_state_get( - state.0, + *state, libc::COPYFILE_STATE_COPIED as u32, (&raw mut bytes_copied) as *mut libc::c_void, ) diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs index 7c9dcdc8b7375..e3294a0051d34 100644 --- a/library/std/src/sys/pal/unix/sync/condvar.rs +++ b/library/std/src/sys/pal/unix/sync/condvar.rs @@ -151,28 +151,23 @@ impl Condvar { /// # Safety /// May only be called once per instance of `Self`. pub unsafe fn init(self: Pin<&mut Self>) { + use crate::mem::DropGuard; use crate::pin::pin; - struct AttrGuard<'a>(Pin<&'a COpaque>); - impl Drop for AttrGuard<'_> { - fn drop(&mut self) { - unsafe { - let result = libc::pthread_condattr_destroy(self.0.get()); - assert_eq!(result, 0); - } - } - } - unsafe { let attr = pin!(COpaque::::uninit()); + // FIXME(pin-ergonomics): remove the next line. let attr = attr.into_ref(); let r = libc::pthread_condattr_init(attr.get()); assert_eq!(r, 0); - let attr = AttrGuard(attr); - let r = libc::pthread_condattr_setclock(attr.0.get(), Self::CLOCK); + let attr = DropGuard::new(attr, |attr| { + let result = libc::pthread_condattr_destroy(attr.get()); + assert_eq!(result, 0); + }); + let r = libc::pthread_condattr_setclock(attr.get(), Self::CLOCK); assert_eq!(r, 0); - let r = libc::pthread_cond_init(self.as_ref().raw(), attr.0.get()); + let r = libc::pthread_cond_init(self.as_ref().raw(), attr.get()); assert_eq!(r, 0); } } diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index aa47fcb3360c1..ba04471631be9 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -394,19 +394,11 @@ impl Command { // want to be sure to restore the global environment back to what it // once was, ensuring that our temporary override, when free'd, doesn't // corrupt our process's environment. - let mut _reset = None; + let _reset; if let Some(envp) = maybe_envp { - struct Reset(*const *const libc::c_char); - - impl Drop for Reset { - fn drop(&mut self) { - unsafe { - *sys::env::environ() = self.0; - } - } - } - - _reset = Some(Reset(*sys::env::environ())); + _reset = core::mem::DropGuard::new(*sys::env::environ(), |prev| { + *sys::env::environ() = prev; + }); *sys::env::environ() = envp.as_ptr(); } @@ -461,8 +453,8 @@ impl Command { #[cfg(target_os = "linux")] use core::sync::atomic::{Atomic, AtomicU8, Ordering}; - use crate::mem::MaybeUninit; - use crate::pin::{Pin, pin}; + use crate::mem::{DropGuard, MaybeUninit}; + use crate::pin::pin; use crate::sys::helpers::COpaque; use crate::sys::{self, cvt_nz, on_broken_pipe_used}; @@ -679,68 +671,52 @@ impl Command { let pgroup = self.get_pgroup(); - struct PosixSpawnFileActions<'a>(Pin<&'a COpaque>); - - impl Drop for PosixSpawnFileActions<'_> { - fn drop(&mut self) { - unsafe { - libc::posix_spawn_file_actions_destroy(self.0.get()); - } - } - } - - struct PosixSpawnattr<'a>(Pin<&'a COpaque>); - - impl Drop for PosixSpawnattr<'_> { - fn drop(&mut self) { - unsafe { - libc::posix_spawnattr_destroy(self.0.get()); - } - } - } - unsafe { let attrs = pin!(COpaque::uninit()); // FIXME(pin-ergonomics): remove the next line. let attrs = attrs.into_ref(); cvt_nz(libc::posix_spawnattr_init(attrs.get()))?; - let attrs = PosixSpawnattr(attrs); + let attrs = DropGuard::new(attrs, |attrs| { + libc::posix_spawnattr_destroy(attrs.get()); + }); let mut flags = 0; let file_actions = pin!(COpaque::uninit()); let file_actions = file_actions.into_ref(); cvt_nz(libc::posix_spawn_file_actions_init(file_actions.get()))?; - let file_actions = PosixSpawnFileActions(file_actions); + let file_actions = DropGuard::new(file_actions, |file_actions| { + libc::posix_spawn_file_actions_destroy(file_actions.get()); + }); if let Some(fd) = stdio.stdin.fd() { cvt_nz(libc::posix_spawn_file_actions_adddup2( - file_actions.0.get(), + file_actions.get(), fd, libc::STDIN_FILENO, ))?; } if let Some(fd) = stdio.stdout.fd() { cvt_nz(libc::posix_spawn_file_actions_adddup2( - file_actions.0.get(), + file_actions.get(), fd, libc::STDOUT_FILENO, ))?; } if let Some(fd) = stdio.stderr.fd() { cvt_nz(libc::posix_spawn_file_actions_adddup2( - file_actions.0.get(), + file_actions.get(), fd, libc::STDERR_FILENO, ))?; } if let Some((f, cwd)) = addchdir { - cvt_nz(f(file_actions.0.get(), cwd.as_ptr()))?; + cvt_nz(f(file_actions.get(), cwd.as_ptr()))?; } if let Some(pgroup) = pgroup { flags |= libc::POSIX_SPAWN_SETPGROUP; - cvt_nz(libc::posix_spawnattr_setpgroup(attrs.0.get(), pgroup))?; + cvt_nz(libc::posix_spawnattr_setpgroup(attrs.get(), pgroup))?; } // Inherit the signal mask from this process rather than resetting it (i.e. do not call @@ -758,7 +734,7 @@ impl Command { { cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGLOST))?; } - cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.0.get(), default_set.as_ptr()))?; + cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.get(), default_set.as_ptr()))?; flags |= libc::POSIX_SPAWN_SETSIGDEF; } @@ -773,7 +749,7 @@ impl Command { } } - cvt_nz(libc::posix_spawnattr_setflags(attrs.0.get(), flags as _))?; + cvt_nz(libc::posix_spawnattr_setflags(attrs.get(), flags as _))?; // Make sure we synchronize access to the global `environ` resource let _env_lock = sys::env::env_read_lock(); @@ -790,8 +766,8 @@ impl Command { let spawn_res = pidfd_spawnp.get().unwrap()( &mut pidfd, self.get_program_cstr().as_ptr(), - file_actions.0.get(), - attrs.0.get(), + file_actions.get(), + attrs.get(), self.get_argv().as_ptr() as *const _, envp as *const _, ); @@ -832,8 +808,8 @@ impl Command { let spawn_res = spawn_fn( &mut p.pid, self.get_program_cstr().as_ptr(), - file_actions.0.get(), - attrs.0.get(), + file_actions.get(), + attrs.get(), self.get_argv().as_ptr() as *const _, envp as *const _, ); diff --git a/library/std/src/sys/process/windows/tests.rs b/library/std/src/sys/process/windows/tests.rs index bc5e0d5c7fc97..4d13f4d9e3b9f 100644 --- a/library/std/src/sys/process/windows/tests.rs +++ b/library/std/src/sys/process/windows/tests.rs @@ -1,6 +1,7 @@ use super::child_pipe::{Pipes, child_pipe}; use super::{Arg, make_command_line}; use crate::ffi::{OsStr, OsString}; +use crate::mem::DropGuard; use crate::os::windows::io::AsHandle; use crate::process::{Command, Stdio}; use crate::time::Duration; @@ -36,14 +37,9 @@ fn test_thread_handle() { assert!(p.is_ok()); // Ensure the process is killed in the event something goes wrong. - struct DropGuard(crate::process::Child); - impl Drop for DropGuard { - fn drop(&mut self) { - let _ = self.0.kill(); - } - } - let mut p = DropGuard(p.unwrap()); - let p = &mut p.0; + let mut p = DropGuard::new(p.unwrap(), |mut p| { + let _: Result<(), crate::io::Error> = p.kill(); + }); unsafe extern "system" { unsafe fn ResumeThread(hHandle: BorrowedHandle<'_>) -> u32; From 7b159d5ff6bf001b2595993d426f88eb49fe6d29 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 30 Aug 2026 17:18:04 +0200 Subject: [PATCH 12/18] touch up "get attribute" docs. --- Cargo.lock | 1 + compiler/rustc_attr_ir/src/lib.rs | 72 +++++++++++++++++----------- compiler/rustc_middle/Cargo.toml | 1 + compiler/rustc_middle/src/queries.rs | 12 +++-- compiler/rustc_middle/src/ty/mod.rs | 48 +++++++++++-------- 5 files changed, 83 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cb05bce70ec4..2c9dc442663f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4430,6 +4430,7 @@ dependencies = [ "rustc_arena", "rustc_ast", "rustc_ast_ir", + "rustc_attr_ir", "rustc_crate_store", "rustc_data_structures", "rustc_errors", diff --git a/compiler/rustc_attr_ir/src/lib.rs b/compiler/rustc_attr_ir/src/lib.rs index 588bcfafb208d..0b142ca92df6b 100644 --- a/compiler/rustc_attr_ir/src/lib.rs +++ b/compiler/rustc_attr_ir/src/lib.rs @@ -1,7 +1,7 @@ //! Data structures for representing parsed attributes in the Rust compiler. //! //! For detailed documentation about attribute processing, -//! see [rustc_attr_parsing](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html). +//! see [rustc_attr_parsing](../rustc_attr_parsing/index.html). // tidy-alphabetical-start #![feature(const_default)] @@ -20,7 +20,6 @@ pub use lang_items::*; pub use pretty_printing::PrintAttribute; pub use stability::*; -// FIXME remove pub on some of these modules? It's fairly inconsistent. mod attr; mod canonical_symbols; mod data_structures; @@ -35,40 +34,38 @@ pub mod weak_lang_items; /// A trait for types that can provide a list of attributes given a `TyCtxt`. /// -/// It allows `find_attr!` to accept either a `DefId`, `LocalDefId`, `OwnerId`, or `HirId`. -/// It is defined here with a generic `Tcx` because `rustc_hir` can't depend on `rustc_middle`. -/// The concrete implementations are in `rustc_middle`. +/// It is an implementation detail of the [`find_attr!`] macro to be able to accept either a +/// [`DefId`], [`LocalDefId`], [`OwnerId`], or [`HirId`]. It is defined here with a generic `Tcx` +/// because this crate can't depend on `rustc_middle`. The concrete implementations are in +/// `rustc_middle`. +/// +/// Not to be confused with [`rustc_ast::ast_traits::HasAttrs`]. +/// +/// [`DefId`]: rustc_span::def_id::DefId +/// [`LocalDefId`]: rustc_span::def_id::LocalDefId +/// [`OwnerId`]: ../rustc_hir/struct.OwnerId.html +/// [`HirId`]: ../rustc_hir/struct.HirId.html pub trait HasAttrs<'tcx, Tcx> { - fn get_attrs(self, tcx: &Tcx) -> &'tcx [crate::attr::Attribute]; + fn get_attrs(self, tcx: &Tcx) -> &'tcx [crate::Attribute]; } -/// Finds attributes in sequences of attributes by pattern matching. +/// Finds attributes by pattern matching. /// /// A little like `matches` but for attributes. /// -/// ```rust,ignore (illustrative) -/// // finds the repr attribute -/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) { -/// -/// } -/// -/// // checks if one has matched -/// if find_attr!(attrs, AttributeKind::Repr(_)) { -/// -/// } -/// ``` +/// Note that this macro accepts several "id" types: [`DefId`], [`LocalDefId`], [`OwnerId`] and +/// [`HirId`]. /// -/// Often this requires you to first end up with a list of attributes. -/// Often these are available through the `tcx`. +/// # Examples /// -/// As a convenience, this macro can do that for you! +/// It is most commonly used to check whether something has an attribute or to get its contents +/// if it is present: +/// ```rust,ignore (illustrative) +/// let is_naked: bool = find_attr!(tcx, def_id, Naked(..)); /// -/// Instead of providing an attribute list, provide the `tcx` and an id -/// (a `DefId`, `LocalDefId`, `OwnerId` or `HirId`). +/// let is_visible: bool = find_attr!(tcx, def_id, Doc(doc) if doc.hidden.is_none()); /// -/// ```rust,ignore (illustrative) -/// find_attr!(tcx, def_id, ) -/// find_attr!(tcx, hir_id, ) +/// let link_name: Option = find_attr!(tcx, def_id, LinkName { name, .. } => *name); /// ``` /// /// Another common case is finding attributes applied to the root of the current crate. @@ -77,6 +74,27 @@ pub trait HasAttrs<'tcx, Tcx> { /// ```rust, ignore (illustrative) /// find_attr!(tcx, crate, ) /// ``` +/// +/// If you already have a list of attributes in scope, you can also use that: +/// +/// ```rust,ignore (illustrative) +/// let attrs = ; +/// +/// // finds the repr attribute +/// if let Some(r) = find_attr!(attrs, Repr(r) => r) { +/// +/// } +/// +/// // checks if one has matched +/// if find_attr!(attrs, Repr(_)) { +/// +/// } +/// ``` +/// +/// [`DefId`]: rustc_span::def_id::DefId +/// [`LocalDefId`]: rustc_span::def_id::LocalDefId +/// [`OwnerId`]: ../rustc_hir/struct.OwnerId.html +/// [`HirId`]: ../rustc_hir/struct.HirId.html #[macro_export] macro_rules! find_attr { ($tcx: expr, crate, $pattern: pat $(if $guard: expr)?) => { @@ -89,6 +107,7 @@ macro_rules! find_attr { ($tcx: expr, $id: expr, $pattern: pat $(if $guard: expr)?) => { $crate::find_attr!($tcx, $id, $pattern $(if $guard)? => ()).is_some() }; + ($tcx: expr, $id: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{ $crate::find_attr!( $crate::HasAttrs::get_attrs($id, &$tcx), @@ -96,7 +115,6 @@ macro_rules! find_attr { ) }}; - ($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{ $crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some() }}; diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index b26969a830f11..361aa2583fd2b 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -15,6 +15,7 @@ rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } +rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_crate_store = { path = "../rustc_crate_store" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index dcfd7a6e610b8..91f772f15b9ab 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -53,6 +53,8 @@ use rustc_arena::TypedArena; use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::tokenstream::TokenStream; +use rustc_attr_ir::lang_items::{LangItem, LanguageItems}; +use rustc_attr_ir::{CanonicalSymbols, EiiDecl, EiiImpl, StrippedCfgItem}; use rustc_crate_store::{ CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib, }; @@ -63,8 +65,6 @@ use rustc_data_structures::svh::Svh; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{ErrorGuaranteed, catch_fatal_errors}; use rustc_hir as hir; -use rustc_hir::attrs::lang_items::{LangItem, LanguageItems}; -use rustc_hir::attrs::{CanonicalSymbols, EiiDecl, EiiImpl, StrippedCfgItem}; use rustc_hir::def::{DefKind, DocLinkResMap}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdSet, LocalModId}; use rustc_hir::{ItemLocalId, PreciseCapturingArgKind}; @@ -1526,8 +1526,12 @@ rustc_queries! { /// Returns the attributes on the item at `def_id`. /// - /// Do not use this directly, use `tcx.get_attrs` instead. - query attrs_for_def(def_id: DefId) -> &'tcx [hir::Attribute] { + ///
+ /// + /// Do not use this directly, use [`rustc_attr_ir::find_attr`] instead. + /// + ///
+ query attrs_for_def(def_id: DefId) -> &'tcx [rustc_attr_ir::Attribute] { desc { "collecting attributes of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ddaa01640b64b..0ced7d1ea2bc5 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -31,18 +31,18 @@ use rustc_abi::{ use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, NodeId}; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; +use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::{self as attr, StrippedCfgItem, find_attr}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer}; -use rustc_hir::attrs::StrippedCfgItem; -use rustc_hir::attrs::lang_items::LangItem; +use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; use rustc_hir::definitions::PerParentDisambiguatorState; -use rustc_hir::{self as hir, MissingLifetimeKind, attrs as attr, find_attr}; use rustc_index::bit_set::BitMatrix; use rustc_index::{IndexVec, static_assert_size}; pub use rustc_lint_defs::RegisteredTools; @@ -221,7 +221,7 @@ pub struct PerOwnerResolverData<'tcx> { /// Resolution for import nodes, which have multiple resolutions in different namespaces. pub import_res: hir::def::PerNS>> = Default::default(), /// Lifetime parameters that lowering will have to introduce. - pub extra_lifetime_params_map: NodeMap> = Default::default(), + pub extra_lifetime_params_map: NodeMap> = Default::default(), /// The id of the owner pub id: ast::NodeId, @@ -251,7 +251,10 @@ impl<'tcx> PerOwnerResolverData<'tcx> { /// /// The extra lifetimes that appear from the parenthesized `Fn`-trait desugaring /// should appear at the enclosing `PolyTraitRef`. - pub fn extra_lifetime_params(&self, id: NodeId) -> &[(Ident, NodeId, MissingLifetimeKind)] { + pub fn extra_lifetime_params( + &self, + id: NodeId, + ) -> &[(Ident, NodeId, hir::MissingLifetimeKind)] { self.extra_lifetime_params_map.get(&id).map_or(&[], |v| &v[..]) } } @@ -2008,17 +2011,22 @@ impl<'tcx> TyCtxt<'tcx> { self, did: impl Into, attr: Symbol, - ) -> impl Iterator { + ) -> impl Iterator { #[expect(deprecated)] - self.get_all_attrs(did).iter().filter(move |a: &&hir::Attribute| a.has_name(attr)) + self.get_all_attrs(did).iter().filter(move |a: &&rustc_attr_ir::Attribute| a.has_name(attr)) } /// Gets all attributes. /// + ///
+ /// /// To see if an item has a specific attribute, you should use - /// [`rustc_hir::find_attr!`] so you can use matching. + /// [`rustc_attr_ir::find_attr!`] so you can use matching. + /// + ///
+ /// #[deprecated = "Though there are valid usecases for this method, especially when your attribute is not a parsed attribute, usually you want to call rustc_hir::find_attr! instead."] - pub fn get_all_attrs(self, did: impl Into) -> &'tcx [hir::Attribute] { + pub fn get_all_attrs(self, did: impl Into) -> &'tcx [rustc_attr_ir::Attribute] { let did: DefId = did.into(); if let Some(did) = did.as_local() { self.hir_attrs(self.local_def_id_to_hir_id(did)) @@ -2031,8 +2039,8 @@ impl<'tcx> TyCtxt<'tcx> { self, did: DefId, attr: &[Symbol], - ) -> impl Iterator { - let filter_fn = move |a: &&hir::Attribute| a.path_matches(attr); + ) -> impl Iterator { + let filter_fn = move |a: &&rustc_attr_ir::Attribute| a.path_matches(attr); if let Some(did) = did.as_local() { self.hir_attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn) } else { @@ -2474,8 +2482,8 @@ impl<'tcx> TyCtxt<'tcx> { // `HasAttrs` impls: allow `find_attr!(tcx, id, ...)` to work with both DefId-like types and HirId. -impl<'tcx> hir::attrs::HasAttrs<'tcx, TyCtxt<'tcx>> for DefId { - fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [hir::Attribute] { +impl<'tcx> rustc_attr_ir::HasAttrs<'tcx, TyCtxt<'tcx>> for DefId { + fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [rustc_attr_ir::Attribute] { if let Some(did) = self.as_local() { tcx.hir_attrs(tcx.local_def_id_to_hir_id(did)) } else { @@ -2484,20 +2492,20 @@ impl<'tcx> hir::attrs::HasAttrs<'tcx, TyCtxt<'tcx>> for DefId { } } -impl<'tcx> hir::attrs::HasAttrs<'tcx, TyCtxt<'tcx>> for LocalDefId { - fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [hir::Attribute] { +impl<'tcx> rustc_attr_ir::HasAttrs<'tcx, TyCtxt<'tcx>> for LocalDefId { + fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [rustc_attr_ir::Attribute] { tcx.hir_attrs(tcx.local_def_id_to_hir_id(self)) } } -impl<'tcx> hir::attrs::HasAttrs<'tcx, TyCtxt<'tcx>> for hir::OwnerId { - fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [hir::Attribute] { - hir::attrs::HasAttrs::get_attrs(self.def_id, tcx) +impl<'tcx> rustc_attr_ir::HasAttrs<'tcx, TyCtxt<'tcx>> for hir::OwnerId { + fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [rustc_attr_ir::Attribute] { + rustc_attr_ir::HasAttrs::get_attrs(self.def_id, tcx) } } -impl<'tcx> hir::attrs::HasAttrs<'tcx, TyCtxt<'tcx>> for hir::HirId { - fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [hir::Attribute] { +impl<'tcx> rustc_attr_ir::HasAttrs<'tcx, TyCtxt<'tcx>> for hir::HirId { + fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [rustc_attr_ir::Attribute] { tcx.hir_attrs(self) } } From 9b62f138245b4a9370cd37a45dc65c1cf6b3b1f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 30 Aug 2026 18:58:01 +0200 Subject: [PATCH 13/18] remove a couple of redundant clones, thanks clippy --- .../rustc_attr_parsing/src/attributes/diagnostic/mod.rs | 2 +- compiler/rustc_builtin_macros/src/env.rs | 2 +- compiler/rustc_builtin_macros/src/offload.rs | 6 +++--- compiler/rustc_resolve/src/late/diagnostics.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 220d1376ccc6f..53114d0ca9d8a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -361,7 +361,7 @@ fn parse_directive_items<'p>( WrappedParserError { description: e.description, label: e.label, - span: slice_span(input.span, e.span.clone(), is_snippet), + span: slice_span(input.span, e.span, is_snippet), }, input.span, ); diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index 38077109b7811..74653139fec02 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -40,7 +40,7 @@ pub(crate) fn expand_option_env<'cx>( Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)), }; let ExpandResult::Ready(mac) = - expr_to_string(cx, var_expr.clone(), "argument must be a string literal") + expr_to_string(cx, var_expr, "argument must be a string literal") else { return ExpandResult::Retry(()); }; diff --git a/compiler/rustc_builtin_macros/src/offload.rs b/compiler/rustc_builtin_macros/src/offload.rs index d9304a978ccd4..b75131db68207 100644 --- a/compiler/rustc_builtin_macros/src/offload.rs +++ b/compiler/rustc_builtin_macros/src/offload.rs @@ -134,9 +134,9 @@ pub(crate) fn expand_kernel( // host function let mut host_fn = Box::new(ast::Fn { defaultness: ast::Defaultness::Implicit, - sig: sig.clone(), + sig, ident, - generics: generics.clone(), + generics, contract: None, body: Some(body), define_opaque: None, @@ -176,7 +176,7 @@ pub(crate) fn expand_kernel( thin_vec![rustc_offload_kernel, inline_never], ast::ItemKind::Fn(host_fn), ); - item.vis = vis.clone(); + item.vis = vis; Annotatable::Item(item) }; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 0046ccdba6ec4..f5f40a641b66e 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -4273,7 +4273,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // we identified that the return expression references only one argument, we // would suggest borrowing only that argument, and we'd skip the prior // "use `'static`" suggestion entirely. - let mut lifetime_refs = lifetime_refs.clone().into_iter(); + let mut lifetime_refs = lifetime_refs.into_iter(); if let Some(lt) = lifetime_refs.next() && lifetime_refs.next().is_none() && (lt.kind == MissingLifetimeKind::Ampersand From 7ea63ccb7ea003340592addef98148cf15fe30e9 Mon Sep 17 00:00:00 2001 From: Shun Sakai Date: Mon, 31 Aug 2026 02:16:39 +0900 Subject: [PATCH 14/18] Remove redundant braces from `NonZero` doctests --- library/core/src/num/nonzero.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index d2acaf837c761..5d8dee0b9378a 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -73,7 +73,7 @@ impl_zeroable_primitive!( /// For example, `Option>` is the same size as `u32`: /// /// ``` -/// use core::{num::NonZero}; +/// use core::num::NonZero; /// /// assert_eq!(size_of::>>(), size_of::()); /// ``` From 58f825e7e69b482da18a8ea83cb3f8f43415f996 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 30 Aug 2026 19:04:16 +0200 Subject: [PATCH 15/18] Add regression test for "use of an internal attribute" with a `macro_rules!` macro --- tests/ui/macros/parse-test.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/ui/macros/parse-test.rs diff --git a/tests/ui/macros/parse-test.rs b/tests/ui/macros/parse-test.rs new file mode 100644 index 0000000000000..a60d196805cd3 --- /dev/null +++ b/tests/ui/macros/parse-test.rs @@ -0,0 +1,18 @@ +//@ check-pass +//@ compile-flags: --test +// Test that we can pass a test through a macro_rules! macro that removes the span of the item +// Regression test for https://github.com/rust-lang/rust/issues/161917 +#![feature(macro_attr)] + +macro_rules! ohno { + attr() { $(#[$a:meta])* fn $name:ident () $body: block } => { + $(#[$a])* + fn $name () $body + } +} + +#[test] +#[ohno] +fn my_test() {} + +fn main() {} From 9b70ab051c8a6937e6e86b1bdc715d6896c2d18d Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 28 Aug 2026 15:04:51 +0200 Subject: [PATCH 16/18] Revert "Add `rustc_test_entrypoint_marker`" --- compiler/rustc_attr_ir/src/data_structures.rs | 3 - .../rustc_attr_ir/src/encode_cross_crate.rs | 1 - .../src/attributes/test_attrs.rs | 11 --- compiler/rustc_attr_parsing/src/context.rs | 1 - compiler/rustc_builtin_macros/src/test.rs | 9 -- compiler/rustc_feature/src/builtin_attrs.rs | 1 - compiler/rustc_passes/src/check_attr.rs | 1 - compiler/rustc_span/src/symbol.rs | 1 - tests/pretty/tests-are-sorted.pp | 3 - tests/ui-fulldeps/test_entrypoint_attrs.rs | 84 ------------------- 10 files changed, 115 deletions(-) delete mode 100644 tests/ui-fulldeps/test_entrypoint_attrs.rs diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index 3d715c6a8a291..c03b2d0246686 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -1462,9 +1462,6 @@ pub enum AttributeKind { /// Represents `#[rustc_strict_coherence]`. RustcStrictCoherence(Span), - /// Represents `#[rustc_test_entrypoint_marker]` - RustcTestEntrypointMarker, - /// Represents `#[rustc_test_marker]` RustcTestMarker(Symbol), diff --git a/compiler/rustc_attr_ir/src/encode_cross_crate.rs b/compiler/rustc_attr_ir/src/encode_cross_crate.rs index 164ab3c5822d0..205a9603ea28b 100644 --- a/compiler/rustc_attr_ir/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_ir/src/encode_cross_crate.rs @@ -192,7 +192,6 @@ impl AttributeKind { RustcSpecializationTrait => No, RustcStdInternalSymbol => No, RustcStrictCoherence(..) => Yes, - RustcTestEntrypointMarker => No, RustcTestMarker(..) => No, RustcThenThisWouldNeed(..) => No, RustcTrivialFieldReads => Yes, diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index d804c1dd78e32..9f2f7613b1c27 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -214,14 +214,3 @@ impl SingleAttributeParser for RustcTestMarkerParser { Some(AttributeKind::RustcTestMarker(value_str)) } } - -pub(crate) struct RustcTestEntrypointMarkerParser; - -impl NoArgsAttributeParser for RustcTestEntrypointMarkerParser { - const PATH: &[Symbol] = &[sym::rustc_test_entrypoint_marker]; - const ALLOWED_TARGETS: AllowedTargets<'_> = - AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::Closure)]); - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const STABILITY: AttributeStability = unstable!(rustc_attrs); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTestEntrypointMarker; -} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 7fe799a027c54..97e0321fe1def 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -354,7 +354,6 @@ attribute_parsers!( Single>, Single>, Single>, - Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index a00023d8eb884..b27bf8c3f2a20 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -23,9 +23,6 @@ use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute}; /// /// We mark item with an inert attribute "rustc_test_marker" which the test generation /// logic will pick up on. -/// -/// The test function also gains a `#[rustc_test_entrypoint_marker]` attribute for tools to pick up -/// on. This behavior is *unstable*. pub(crate) fn expand_test_case( ecx: &mut ExtCtxt<'_>, attr_sp: Span, @@ -380,12 +377,6 @@ pub(crate) fn expand_test_or_bench( let test_extern = cx.item(sp, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, test_ident)); - let item = { - let mut item = item; - item.attrs.push(cx.attr_word(sym::rustc_test_entrypoint_marker, attr_sp)); - item - }; - debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const)); if is_stmt { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 85a7c5aca0970..61778e9a56b31 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -364,7 +364,6 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ sym::prelude_import, sym::rustc_paren_sugar, sym::rustc_inherit_overflow_checks, - sym::rustc_test_entrypoint_marker, sym::rustc_test_marker, sym::rustc_allow_lifetime_dependent_specialization, sym::rustc_specialization_trait, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2f69823d54afd..f99f921d2ad02 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -400,7 +400,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcSpecializationTrait => (), AttributeKind::RustcStdInternalSymbol => (), AttributeKind::RustcStrictCoherence(..) => (), - AttributeKind::RustcTestEntrypointMarker => (), AttributeKind::RustcTestMarker(..) => (), AttributeKind::RustcThenThisWouldNeed(..) => (), AttributeKind::RustcTrivialFieldReads => (), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6376fe032c64e..6d564c9cf224a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1879,7 +1879,6 @@ symbols! { rustc_splat, rustc_std_internal_symbol, rustc_strict_coherence, - rustc_test_entrypoint_marker, rustc_test_marker, rustc_then_this_would_need, rustc_trivial_field_reads, diff --git a/tests/pretty/tests-are-sorted.pp b/tests/pretty/tests-are-sorted.pp index f49c79f31a5ec..43f9838e68ce9 100644 --- a/tests/pretty/tests-are-sorted.pp +++ b/tests/pretty/tests-are-sorted.pp @@ -30,7 +30,6 @@ testfn: test::StaticTestFn(#[coverage(off)] || test::assert_test_result(m_test())), }; -#[rustc_test_entrypoint_marker] fn m_test() {} extern crate test; @@ -56,7 +55,6 @@ test::assert_test_result(z_test())), }; #[ignore = "not yet implemented"] -#[rustc_test_entrypoint_marker] fn z_test() {} extern crate test; @@ -81,7 +79,6 @@ testfn: test::StaticTestFn(#[coverage(off)] || test::assert_test_result(a_test())), }; -#[rustc_test_entrypoint_marker] fn a_test() {} #[rustc_main] #[coverage(off)] diff --git a/tests/ui-fulldeps/test_entrypoint_attrs.rs b/tests/ui-fulldeps/test_entrypoint_attrs.rs deleted file mode 100644 index dac7406337c57..0000000000000 --- a/tests/ui-fulldeps/test_entrypoint_attrs.rs +++ /dev/null @@ -1,84 +0,0 @@ -//@ run-pass -//@ ignore-cross-compile -//@ ignore-remote -//@ edition: 2024 -//@ ignore-stage1 -//! Uses a rustc driver to check that test entrypoints get a `#[rustc_test_entrypoint_marker]` -//! and can be found using that attribute in rustc drivers (the main use for this attribute). - -#![feature(rustc_private)] - -extern crate rustc_driver; -extern crate rustc_interface; -extern crate rustc_middle; -#[macro_use] -extern crate rustc_hir; - -use interface::Compiler; -use rustc_driver::Compilation; -use rustc_interface::interface; -use rustc_middle::ty::TyCtxt; -use std::io::Write; - -const CRATE_NAME: &str = "input"; - -struct TestAttr { - expected_tests: usize, -} - -impl rustc_driver::Callbacks for TestAttr { - fn after_analysis<'tcx>(&mut self, _compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation { - let mut tests = Vec::new(); - for did in tcx.hir_crate_items(()).definitions() { - if find_attr!(tcx, did, RustcTestEntrypointMarker) { - tests.push(did); - } - } - - // the file contains one test, so we should find one entrypoint marker. - assert_eq!(tests.len(), self.expected_tests); - - Compilation::Stop - } -} - -fn count_tests(src: &str, expected_tests: usize) { - let path = "test_input.rs"; - let mut file = std::fs::File::create(path).unwrap(); - file.write_all(src.as_bytes()).unwrap(); - - let args = [ - "rustc".to_string(), - "--test".to_string(), - "--crate-type=lib".to_string(), - "--crate-name".to_string(), - CRATE_NAME.to_string(), - path.to_string(), - ]; - rustc_driver::catch_fatal_errors(|| -> interface::Result<()> { - rustc_driver::run_compiler(&args, &mut TestAttr { expected_tests }); - Ok(()) - }) - .unwrap() - .unwrap(); -} - -fn main() { - count_tests( - r#" - #[test] - fn meow() {{ }} - "#, - 1, - ); - count_tests( - r#" - #[test] - fn one() {{ }} - - #[test] - fn two() {{ }} - "#, - 2, - ); -} From 08f3b0e88475a3362b2a58489848ead5b1944666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 25 Aug 2026 23:18:41 +0200 Subject: [PATCH 17/18] Unify and improve lint diagnostics for reserved prefixes --- compiler/rustc_lint_defs/src/builtin.rs | 2 +- compiler/rustc_parse/src/diagnostics.rs | 53 ++--------- compiler/rustc_parse/src/lexer/mod.rs | 67 ++++++++------ tests/ui/lifetimes/raw/three-tokens.rs | 8 +- tests/ui/lifetimes/raw/three-tokens.stderr | 16 ++-- .../pre-2021-lexing.rs | 8 +- .../pre-2021-lexing.stderr | 16 ++-- .../reserved-prefixes-migration.fixed | 20 ++--- .../rust-2021/reserved-prefixes-migration.rs | 20 ++--- .../reserved-prefixes-migration.stderr | 40 ++++----- .../reserved-guarded-strings-lexing.rs | 38 ++++---- .../reserved-guarded-strings-lexing.stderr | 82 ++++++++--------- .../reserved-guarded-strings-migration.fixed | 44 +++++----- .../reserved-guarded-strings-migration.rs | 44 +++++----- .../reserved-guarded-strings-migration.stderr | 88 +++++++++---------- 15 files changed, 261 insertions(+), 285 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 5ce5a33b4352e..655e993ba8210 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -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"), }; crate_level_only } diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index 2be549703e4e6..84786cdbf5c1e 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -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( @@ -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)] diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 47366c210243d..5bcd2a60ddc9d 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -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; @@ -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) @@ -386,10 +396,13 @@ impl<'psess, 'src> Lexer<'psess, 'src> { 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, + // FIXME(fmease): Wrong! + sugg: prefix_span.shrink_to_hi(), + } ); // Reset the state so we just lex the `'r`. @@ -1089,10 +1102,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(), }, ); } @@ -1167,18 +1181,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, }, ); diff --git a/tests/ui/lifetimes/raw/three-tokens.rs b/tests/ui/lifetimes/raw/three-tokens.rs index 781f8e3e47913..80a3451536be4 100644 --- a/tests/ui/lifetimes/raw/three-tokens.rs +++ b/tests/ui/lifetimes/raw/three-tokens.rs @@ -6,13 +6,13 @@ #![warn(rust_2021_prefixes_incompatible_syntax)] -macro_rules! ed2015 { +macro_rules! check { ('r # lt) => {}; ($lt:lifetime) => { compile_error!() }; } -ed2015!('r#lt); -//~^ WARNING prefix `'r` is reserved -//~| WARNING hard error in Rust 2021 +check!('r#lt); +//~^ WARNING parsed as a prefix in Rust 2021 and onward +//~| WARNING this changes meaning in Rust 2021 fn main() {} diff --git a/tests/ui/lifetimes/raw/three-tokens.stderr b/tests/ui/lifetimes/raw/three-tokens.stderr index 413603c2c53d6..e0c39d917a874 100644 --- a/tests/ui/lifetimes/raw/three-tokens.stderr +++ b/tests/ui/lifetimes/raw/three-tokens.stderr @@ -1,20 +1,20 @@ -warning: prefix `'r` is reserved - --> $DIR/three-tokens.rs:14:9 +warning: `r` is parsed as a prefix in Rust 2021 and onward + --> $DIR/three-tokens.rs:14:8 | -LL | ed2015!('r#lt); - | ^^^ reserved prefix +LL | check!('r#lt); + | ^^^ | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = warning: this changes meaning in Rust 2021 = note: for more information, see note: the lint level is defined here --> $DIR/three-tokens.rs:7:9 | LL | #![warn(rust_2021_prefixes_incompatible_syntax)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: insert whitespace here to avoid this being parsed as a prefix in Rust 2021 +help: consider inserting whitespace here to avoid this | -LL | ed2015!('r# lt); - | + +LL | check!('r# lt); + | + warning: 1 warning emitted diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.rs b/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.rs index 3a1c5593ee7aa..c0df662c19ea9 100644 --- a/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.rs +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.rs @@ -24,8 +24,8 @@ fn main() { } let _: &'static str = parse!(c"hello"); - //~^ WARNING prefix `c` is unknown - //~| WARNING hard error in Rust 2021 + //~^ WARNING parsed as a C string literal in Rust 2021 and onward + //~| WARNING this changes meaning in Rust 2021 macro_rules! indifferent { ($e:expr) => {}; @@ -33,6 +33,6 @@ fn main() { } indifferent!(c"..."); - //~^ WARNING prefix `c` is unknown - //~| WARNING hard error in Rust 2021 + //~^ WARNING parsed as a C string literal in Rust 2021 and onward + //~| WARNING this changes meaning in Rust 2021 } diff --git a/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.stderr b/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.stderr index 530163f72861d..decf7888151b8 100644 --- a/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.stderr +++ b/tests/ui/rfcs/rfc-3348-c-string-literals/pre-2021-lexing.stderr @@ -1,30 +1,30 @@ -warning: prefix `c` is unknown +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"); - | ^ unknown prefix + | ^^^^^^^^ | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = warning: this changes meaning in Rust 2021 = note: for more information, see note: the lint level is defined here --> $DIR/pre-2021-lexing.rs:7:9 | LL | #![warn(rust_2021_prefixes_incompatible_syntax)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: insert whitespace here to avoid this being parsed as a prefix in Rust 2021 +help: consider inserting whitespace here to avoid this | LL | let _: &'static str = parse!(c "hello"); | + -warning: prefix `c` is unknown +warning: this is parsed as a C string literal in Rust 2021 and onward --> $DIR/pre-2021-lexing.rs:35:18 | LL | indifferent!(c"..."); - | ^ unknown prefix + | ^^^^^^ | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = warning: this changes meaning in Rust 2021 = note: for more information, see -help: insert whitespace here to avoid this being parsed as a prefix in Rust 2021 +help: consider inserting whitespace here to avoid this | LL | indifferent!(c "..."); | + diff --git a/tests/ui/rust-2021/reserved-prefixes-migration.fixed b/tests/ui/rust-2021/reserved-prefixes-migration.fixed index 399ff1c75ba8d..04b76a599568f 100644 --- a/tests/ui/rust-2021/reserved-prefixes-migration.fixed +++ b/tests/ui/rust-2021/reserved-prefixes-migration.fixed @@ -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 { @@ -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 } diff --git a/tests/ui/rust-2021/reserved-prefixes-migration.rs b/tests/ui/rust-2021/reserved-prefixes-migration.rs index 5adb9a00e3a21..0cee52a9bff95 100644 --- a/tests/ui/rust-2021/reserved-prefixes-migration.rs +++ b/tests/ui/rust-2021/reserved-prefixes-migration.rs @@ -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 { @@ -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 } diff --git a/tests/ui/rust-2021/reserved-prefixes-migration.stderr b/tests/ui/rust-2021/reserved-prefixes-migration.stderr index 8092c63687784..9612ea7958224 100644 --- a/tests/ui/rust-2021/reserved-prefixes-migration.stderr +++ b/tests/ui/rust-2021/reserved-prefixes-migration.stderr @@ -1,69 +1,69 @@ -warning: prefix `z` is unknown +warning: `z` is parsed as a prefix in Rust 2021 and onward --> $DIR/reserved-prefixes-migration.rs:16:9 | LL | m2!(z"hey"); - | ^ unknown prefix + | ^ | - = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2021! + = warning: this changes meaning in Rust 2021 = note: for more information, see note: the lint level is defined here --> $DIR/reserved-prefixes-migration.rs:5:9 | LL | #![warn(rust_2021_prefixes_incompatible_syntax)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: insert whitespace here to avoid this being parsed as a prefix in Rust 2021 +help: consider inserting whitespace here to avoid this | LL | m2!(z "hey"); | + -warning: prefix `prefix` is unknown +warning: `prefix` is parsed as a prefix in Rust 2021 and onward --> $DIR/reserved-prefixes-migration.rs:19:9 | LL | m2!(prefix"hey"); - | ^^^^^^ unknown prefix + | ^^^^^^ | - = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2021! + = warning: this changes meaning in Rust 2021 = note: for more information, see -help: insert whitespace here to avoid this being parsed as a prefix in Rust 2021 +help: consider inserting whitespace here to avoid this | LL | m2!(prefix "hey"); | + -warning: prefix `hey` is unknown +warning: `hey` is parsed as a prefix in Rust 2021 and onward --> $DIR/reserved-prefixes-migration.rs:22:9 | LL | m3!(hey#123); - | ^^^ unknown prefix + | ^^^ | - = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2021! + = warning: this changes meaning in Rust 2021 = note: for more information, see -help: insert whitespace here to avoid this being parsed as a prefix in Rust 2021 +help: consider inserting whitespace here to avoid this | LL | m3!(hey #123); | + -warning: prefix `hey` is unknown +warning: `hey` is parsed as a prefix in Rust 2021 and onward --> $DIR/reserved-prefixes-migration.rs:25:9 | LL | m3!(hey#hey); - | ^^^ unknown prefix + | ^^^ | - = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2021! + = warning: this changes meaning in Rust 2021 = note: for more information, see -help: insert whitespace here to avoid this being parsed as a prefix in Rust 2021 +help: consider inserting whitespace here to avoid this | LL | m3!(hey #hey); | + -warning: prefix `kind` is unknown +warning: `kind` is parsed as a prefix in Rust 2021 and onward --> $DIR/reserved-prefixes-migration.rs:35:14 | LL | #name = #kind#value - | ^^^^ unknown prefix + | ^^^^ | - = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2021! + = warning: this changes meaning in Rust 2021 = note: for more information, see -help: insert whitespace here to avoid this being parsed as a prefix in Rust 2021 +help: consider inserting whitespace here to avoid this | LL | #name = #kind #value | + diff --git a/tests/ui/rust-2024/reserved-guarded-strings-lexing.rs b/tests/ui/rust-2024/reserved-guarded-strings-lexing.rs index 43413f7470e89..ffac319703e5c 100644 --- a/tests/ui/rust-2024/reserved-guarded-strings-lexing.rs +++ b/tests/ui/rust-2024/reserved-guarded-strings-lexing.rs @@ -26,44 +26,44 @@ macro_rules! demo7 { fn main() { demo3!(## "foo"); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo4!(### "foo"); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - demo4!(## "foo"#); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + demo4!(## "foo"#); + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo7!(### "foo"###); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo4!("foo"###); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 // Non-ascii identifiers @@ -71,10 +71,10 @@ fn main() { //~^ ERROR prefix `Ñ` is unknown demo4!(Ñ#""#); //~^ ERROR prefix `Ñ` is unknown - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo3!(🙃#""); //~^ ERROR identifiers cannot contain emoji - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 } diff --git a/tests/ui/rust-2024/reserved-guarded-strings-lexing.stderr b/tests/ui/rust-2024/reserved-guarded-strings-lexing.stderr index 488f66bb01d37..3aa105f5617e1 100644 --- a/tests/ui/rust-2024/reserved-guarded-strings-lexing.stderr +++ b/tests/ui/rust-2024/reserved-guarded-strings-lexing.stderr @@ -28,7 +28,7 @@ error: identifiers cannot contain emoji: `🙃` LL | demo3!(🙃#""); | ^^ -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:28:12 | LL | demo3!(## "foo"); @@ -41,12 +41,12 @@ note: the lint level is defined here | LL | #![warn(rust_2024_guarded_string_incompatible_syntax)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo3!(# # "foo"); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:31:12 | LL | demo4!(### "foo"); @@ -54,12 +54,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!(# ## "foo"); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:31:13 | LL | demo4!(### "foo"); @@ -67,25 +67,25 @@ 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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!(## # "foo"); | + -warning: reserved token in Rust 2024 - --> $DIR/reserved-guarded-strings-lexing.rs:36:12 +warning: this is parsed as a reserved token in Rust 2024 and onward + --> $DIR/reserved-guarded-strings-lexing.rs:36:11 | -LL | demo4!(## "foo"#); - | ^^ +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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | -LL | demo4!(# # "foo"#); - | + +LL | demo4!(# # "foo"#); + | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:39:12 | LL | demo7!(### "foo"###); @@ -93,12 +93,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo7!(# ## "foo"###); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:39:13 | LL | demo7!(### "foo"###); @@ -106,12 +106,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo7!(## # "foo"###); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:39:21 | LL | demo7!(### "foo"###); @@ -119,12 +119,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo7!(### "foo"# ##); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:39:22 | LL | demo7!(### "foo"###); @@ -132,12 +132,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo7!(### "foo"## #); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:49:12 | LL | demo5!(###"foo"#); @@ -145,12 +145,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo5!(# ##"foo"#); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:49:13 | LL | demo5!(###"foo"#); @@ -158,12 +158,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo5!(## #"foo"#); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:49:14 | LL | demo5!(###"foo"#); @@ -171,12 +171,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo5!(### "foo"#); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:56:12 | LL | demo5!(#"foo"###); @@ -184,12 +184,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo5!(# "foo"###); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:56:18 | LL | demo5!(#"foo"###); @@ -197,12 +197,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo5!(#"foo"# ##); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:56:19 | LL | demo5!(#"foo"###); @@ -210,12 +210,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo5!(#"foo"## #); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:63:17 | LL | demo4!("foo"###); @@ -223,12 +223,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!("foo"# ##); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:63:18 | LL | demo4!("foo"###); @@ -236,12 +236,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!("foo"## #); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:72:13 | LL | demo4!(Ñ#""#); @@ -249,12 +249,12 @@ LL | demo4!(Ñ#""#); | = warning: this is accepted in the current edition (Rust 2021) but is a hard error in Rust 2024! = note: for more information, see -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!(Ñ# ""#); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-lexing.rs:76:13 | LL | demo3!(🙃#""); @@ -262,7 +262,7 @@ 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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo3!(🙃# ""); | + diff --git a/tests/ui/rust-2024/reserved-guarded-strings-migration.fixed b/tests/ui/rust-2024/reserved-guarded-strings-migration.fixed index ef00ed3f61070..93804182eea34 100644 --- a/tests/ui/rust-2024/reserved-guarded-strings-migration.fixed +++ b/tests/ui/rust-2024/reserved-guarded-strings-migration.fixed @@ -38,62 +38,62 @@ fn main() { demo2!("foo"#); demo3!(# # "foo"); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo4!(# # # "foo"); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo4!(# # "foo"#); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo6!(# # # "foo"# #); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo4!("foo"# # #); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo2!(# ""); - //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [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 index cf2d8716ad2e5..391d353cb1ceb 100644 --- a/tests/ui/rust-2024/reserved-guarded-strings-migration.rs +++ b/tests/ui/rust-2024/reserved-guarded-strings-migration.rs @@ -38,62 +38,62 @@ fn main() { demo2!("foo"#); demo3!(## "foo"); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo4!(### "foo"); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo4!(## "foo"#); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo6!(### "foo"##); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo4!("foo"###); - //~^ WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 demo2!(#""); - //~^ WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~^ WARNING parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [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 parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING parsed as a guarded string in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a guarded string literal in Rust 2024 and onward [rust_2024_guarded_string_incompatible_syntax] //~| WARNING hard error in Rust 2024 - //~| WARNING reserved token in Rust 2024 [rust_2024_guarded_string_incompatible_syntax] + //~| WARNING parsed as a reserved token in Rust 2024 and onward [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 index 9e6c4554281b7..cc3cd85c889e6 100644 --- a/tests/ui/rust-2024/reserved-guarded-strings-migration.stderr +++ b/tests/ui/rust-2024/reserved-guarded-strings-migration.stderr @@ -1,4 +1,4 @@ -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:40:12 | LL | demo3!(## "foo"); @@ -11,12 +11,12 @@ note: the lint level is defined here | LL | #![warn(rust_2024_guarded_string_incompatible_syntax)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo3!(# # "foo"); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:43:12 | LL | demo4!(### "foo"); @@ -24,12 +24,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!(# ## "foo"); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:43:13 | LL | demo4!(### "foo"); @@ -37,12 +37,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!(## # "foo"); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:48:12 | LL | demo4!(## "foo"#); @@ -50,12 +50,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!(# # "foo"#); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:51:12 | LL | demo6!(### "foo"##); @@ -63,12 +63,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo6!(# ## "foo"##); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:51:13 | LL | demo6!(### "foo"##); @@ -76,12 +76,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo6!(## # "foo"##); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:51:21 | LL | demo6!(### "foo"##); @@ -89,12 +89,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo6!(### "foo"# #); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:59:17 | LL | demo4!("foo"###); @@ -102,12 +102,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!("foo"# ##); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:59:18 | LL | demo4!("foo"###); @@ -115,12 +115,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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!("foo"## #); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:65:12 | LL | demo2!(#""); @@ -128,12 +128,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo2!(# ""); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:68:12 | LL | demo3!(#""#); @@ -141,12 +141,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo3!(# ""#); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:71:12 | LL | demo3!(##""); @@ -154,12 +154,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo3!(# #""); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:71:13 | LL | demo3!(##""); @@ -167,12 +167,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo3!(## ""); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:76:12 | LL | demo2!(#"foo"); @@ -180,12 +180,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo2!(# "foo"); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:79:12 | LL | demo3!(##"foo"); @@ -193,12 +193,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo3!(# #"foo"); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:79:13 | LL | demo3!(##"foo"); @@ -206,12 +206,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo3!(## "foo"); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:84:12 | LL | demo3!(#"foo"#); @@ -219,12 +219,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo3!(# "foo"#); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:87:12 | LL | demo4!(##"foo"#); @@ -232,12 +232,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!(# #"foo"#); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:87:13 | LL | demo4!(##"foo"#); @@ -245,12 +245,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo4!(## "foo"#); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:92:12 | LL | demo5!(##"foo"##); @@ -258,12 +258,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo5!(# #"foo"##); | + -warning: will be parsed as a guarded string in Rust 2024 +warning: this is parsed as a guarded string literal in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:92:13 | LL | demo5!(##"foo"##); @@ -271,12 +271,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 -help: insert whitespace here to avoid this being parsed as a guarded string in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo5!(## "foo"##); | + -warning: reserved token in Rust 2024 +warning: this is parsed as a reserved token in Rust 2024 and onward --> $DIR/reserved-guarded-strings-migration.rs:92:19 | LL | demo5!(##"foo"##); @@ -284,7 +284,7 @@ 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 -help: insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024 +help: consider inserting whitespace here to avoid this | LL | demo5!(##"foo"# #); | + From 3fca755fdfd5d3b4bc7d5e6789a3348e86be1f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 26 Aug 2026 01:30:37 +0200 Subject: [PATCH 18/18] Fix the suggestion span used for splitting raw lifetimes apart --- compiler/rustc_parse/src/lexer/mod.rs | 16 ++++++---------- tests/ui/lifetimes/raw/three-tokens.stderr | 6 +++--- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 5bcd2a60ddc9d..6ed61a9f4e01d 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -343,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 @@ -391,7 +389,11 @@ 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, @@ -400,16 +402,10 @@ impl<'psess, 'src> Lexer<'psess, 'src> { subject: "`r`".into(), kind: "prefix", edition: Edition::Edition2021, - // FIXME(fmease): Wrong! 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) } diff --git a/tests/ui/lifetimes/raw/three-tokens.stderr b/tests/ui/lifetimes/raw/three-tokens.stderr index e0c39d917a874..db2cd8644f3e8 100644 --- a/tests/ui/lifetimes/raw/three-tokens.stderr +++ b/tests/ui/lifetimes/raw/three-tokens.stderr @@ -2,7 +2,7 @@ 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 @@ -13,8 +13,8 @@ LL | #![warn(rust_2021_prefixes_incompatible_syntax)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider inserting whitespace here to avoid this | -LL | check!('r# lt); - | + +LL | check!('r #lt); + | + warning: 1 warning emitted