From 4983fdaada0c78d48e57f177453e5b954d5caad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sun, 23 Feb 2025 19:40:57 +0100 Subject: [PATCH 0001/2093] libstd: init(): dup() subsequent /dev/nulls instead of opening them again This will be faster, and also it deduplicates the code so win/win The dup() is actually infallible here. But whatever. Before: poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 1 ([{fd=2, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 2 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f5749313050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 2 ([{fd=0, revents=POLLNVAL}, {fd=2, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 0 openat(AT_FDCWD, "/dev/null", O_RDWR) = 2 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7efe12006050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 3 ([{fd=0, revents=POLLNVAL}, {fd=1, revents=POLLNVAL}, {fd=2, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 0 openat(AT_FDCWD, "/dev/null", O_RDWR) = 1 openat(AT_FDCWD, "/dev/null", O_RDWR) = 2 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fc2dc7ca050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 After: poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 1 ([{fd=1, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 1 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f488a3fb050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 2 ([{fd=1, revents=POLLNVAL}, {fd=2, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 1 dup(1) = 2 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a8943c050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 3 ([{fd=0, revents=POLLNVAL}, {fd=1, revents=POLLNVAL}, {fd=2, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 0 dup(0) = 1 dup(0) = 2 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f4e3a4c7050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 --- library/std/src/sys/pal/unix/mod.rs | 48 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index c0b56d8d2b28..ec61fcad7e88 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -61,6 +61,28 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn sanitize_standard_fds() { + let mut opened_devnull = -1; + let mut open_devnull = || { + #[cfg(not(all(target_os = "linux", target_env = "gnu")))] + use libc::open as open64; + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use libc::open64; + + if opened_devnull != -1 { + if libc::dup(opened_devnull) != -1 { + return; + } + } + opened_devnull = open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0); + if opened_devnull == -1 { + // If the stream is closed but we failed to reopen it, abort the + // process. Otherwise we wouldn't preserve the safety of + // operations on the corresponding Rust object Stdin, Stdout, or + // Stderr. + libc::abort(); + } + }; + // fast path with a single syscall for systems with poll() #[cfg(not(any( miri, @@ -76,11 +98,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_vendor = "apple", )))] 'poll: { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::open as open64; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::open64; - use crate::sys::os::errno; let pfds: &mut [_] = &mut [ libc::pollfd { fd: 0, events: 0, revents: 0 }, @@ -108,13 +125,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { if pfd.revents & libc::POLLNVAL == 0 { continue; } - if open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0) == -1 { - // If the stream is closed but we failed to reopen it, abort the - // process. Otherwise we wouldn't preserve the safety of - // operations on the corresponding Rust object Stdin, Stdout, or - // Stderr. - libc::abort(); - } + open_devnull(); } return; } @@ -131,21 +142,10 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "vita", )))] { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::open as open64; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::open64; - use crate::sys::os::errno; for fd in 0..3 { if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF { - if open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0) == -1 { - // If the stream is closed but we failed to reopen it, abort the - // process. Otherwise we wouldn't preserve the safety of - // operations on the corresponding Rust object Stdin, Stdout, or - // Stderr. - libc::abort(); - } + open_devnull(); } } } From c73cfb83b7b7855f19603420990775ebb3d32d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Wed, 26 Mar 2025 02:08:15 +0100 Subject: [PATCH 0002/2093] #[allow(dead_code)] --- library/std/src/sys/pal/unix/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index ec61fcad7e88..20b670fdd19b 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -61,19 +61,21 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn sanitize_standard_fds() { + #[allow(dead_code)] let mut opened_devnull = -1; + #[allow(dead_code)] let mut open_devnull = || { #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::open as open64; + use libc::open; #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::open64; + use libc::open64 as open; if opened_devnull != -1 { if libc::dup(opened_devnull) != -1 { return; } } - opened_devnull = open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0); + opened_devnull = open(c"/dev/null".as_ptr(), libc::O_RDWR, 0); if opened_devnull == -1 { // If the stream is closed but we failed to reopen it, abort the // process. Otherwise we wouldn't preserve the safety of From bebaa8a29da776739f7d507d4e6a679656e687f2 Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 9 Apr 2025 12:43:06 -0500 Subject: [PATCH 0003/2093] add sitemap to rust docs --- src/bootstrap/src/core/build_steps/dist.rs | 1 + src/doc/robots.txt | 2 ++ src/doc/sitemap.txt | 4 ++++ 3 files changed, 7 insertions(+) create mode 100644 src/doc/sitemap.txt diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 83f71aeed720..6ec39ffc4366 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -82,6 +82,7 @@ impl Step for Docs { tarball.set_product_name("Rust Documentation"); tarball.add_bulk_dir(builder.doc_out(host), dest); tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, FileType::Regular); + tarball.add_file(builder.src.join("src/doc/sitemap.txt"), dest, FileType::Regular); Some(tarball.generate()) } } diff --git a/src/doc/robots.txt b/src/doc/robots.txt index 3a2552e9a159..71a60f89c14c 100644 --- a/src/doc/robots.txt +++ b/src/doc/robots.txt @@ -9,3 +9,5 @@ Disallow: /beta/book/first-edition/ Disallow: /beta/book/second-edition/ Disallow: /nightly/book/first-edition/ Disallow: /nightly/book/second-edition/ + +Sitemap: https://doc.rust-lang.org/sitemap.txt diff --git a/src/doc/sitemap.txt b/src/doc/sitemap.txt new file mode 100644 index 000000000000..6e5f0fbad37f --- /dev/null +++ b/src/doc/sitemap.txt @@ -0,0 +1,4 @@ +https://doc.rust-lang.org/stable/ +https://doc.rust-lang.org/beta/ +https://doc.rust-lang.org/nightly/ + From ac761f28d15764887a0a1df4fa9067dcc7e0e94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 12 Apr 2025 23:20:51 +0200 Subject: [PATCH 0004/2093] =?UTF-8?q?https://github.com/rust-lang/rust/pul?= =?UTF-8?q?l/139717#issuecomment-2799036117=20=F0=9F=A5=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/std/src/sys/pal/unix/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 20b670fdd19b..ebfc92ebfeee 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -61,9 +61,9 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn sanitize_standard_fds() { - #[allow(dead_code)] + #[allow(dead_code, unused_variables, unused_mut)] let mut opened_devnull = -1; - #[allow(dead_code)] + #[allow(dead_code, unused_variables, unused_mut)] let mut open_devnull = || { #[cfg(not(all(target_os = "linux", target_env = "gnu")))] use libc::open; From a22c8ed6311979d292e835e5a5498d0e3cc95dc0 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Sat, 12 Apr 2025 16:33:38 -0700 Subject: [PATCH 0005/2093] zkVM: Fix env::ArgsOs The zkVM implementation of `env::ArgsOs` incorrectly reports the full length even after having iterated. Instead, use a range approach which works out to be simpler. Also, implement more iterator methods like the other platforms in #139847. --- library/std/src/sys/args/zkvm.rs | 71 ++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/library/std/src/sys/args/zkvm.rs b/library/std/src/sys/args/zkvm.rs index 194ba7159d45..e4b1ad540ac0 100644 --- a/library/std/src/sys/args/zkvm.rs +++ b/library/std/src/sys/args/zkvm.rs @@ -1,18 +1,19 @@ use crate::ffi::OsString; use crate::fmt; +use crate::num::NonZero; use crate::sys::os_str; use crate::sys::pal::{WORD_SIZE, abi}; use crate::sys_common::FromInner; +#[derive(Clone)] pub struct Args { - i_forward: usize, - i_back: usize, - count: usize, + front: usize, + back: usize, } pub fn args() -> Args { let count = unsafe { abi::sys_argc() }; - Args { i_forward: 0, i_back: 0, count } + Args { front: 0, back: count } } impl Args { @@ -38,44 +39,80 @@ impl Args { } } +impl !Send for Args {} +impl !Sync for Args {} + impl fmt::Debug for Args { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().finish() + f.debug_list().entries(self.clone()).finish() } } impl Iterator for Args { type Item = OsString; + #[inline] fn next(&mut self) -> Option { - if self.i_forward >= self.count - self.i_back { + if self.front == self.back { None } else { - let arg = Self::argv(self.i_forward); - self.i_forward += 1; + let arg = Self::argv(self.front); + self.front += 1; Some(arg) } } + #[inline] fn size_hint(&self) -> (usize, Option) { - (self.count, Some(self.count)) + let len = self.len(); + (len, Some(len)) } -} -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.count + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + let step_size = self.len().min(n); + self.front += step_size; + NonZero::new(n - step_size).map_or(Ok(()), Err) } } impl DoubleEndedIterator for Args { + #[inline] fn next_back(&mut self) -> Option { - if self.i_back >= self.count - self.i_forward { + if self.back == self.front { None } else { - let arg = Self::argv(self.count - 1 - self.i_back); - self.i_back += 1; - Some(arg) + self.back -= 1; + Some(Self::argv(self.back)) } } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + let step_size = self.len().min(n); + self.back -= step_size; + NonZero::new(n - step_size).map_or(Ok(()), Err) + } +} + +impl ExactSizeIterator for Args { + #[inline] + fn len(&self) -> usize { + self.back - self.front + } + + #[inline] + fn is_empty(&self) -> bool { + self.front == self.back + } } From 00095982a0d2f6c8bfb5cb2706338ad2b14304f5 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Tue, 15 Apr 2025 02:41:51 -0700 Subject: [PATCH 0006/2093] zkVM: Cache args from host Retrieve argc/argv from the host once, on demand when the first `env::ArgsOs` is constructed, and globally cache it. Copy each argument to an `OsString` while iterating. --- library/std/src/sys/args/zkvm.rs | 86 ++++++++++++-------------------- 1 file changed, 31 insertions(+), 55 deletions(-) diff --git a/library/std/src/sys/args/zkvm.rs b/library/std/src/sys/args/zkvm.rs index e4b1ad540ac0..d26bf1eaff91 100644 --- a/library/std/src/sys/args/zkvm.rs +++ b/library/std/src/sys/args/zkvm.rs @@ -1,26 +1,20 @@ -use crate::ffi::OsString; -use crate::fmt; +use crate::ffi::{OsStr, OsString}; use crate::num::NonZero; -use crate::sys::os_str; +use crate::sync::OnceLock; use crate::sys::pal::{WORD_SIZE, abi}; -use crate::sys_common::FromInner; - -#[derive(Clone)] -pub struct Args { - front: usize, - back: usize, -} +use crate::{fmt, ptr, slice}; pub fn args() -> Args { - let count = unsafe { abi::sys_argc() }; - Args { front: 0, back: count } + Args { iter: ARGS.get_or_init(|| get_args()).iter() } } -impl Args { - /// Use sys_argv to get the arg at the requested index. Does not check that i is less than argc - /// and will not return if the index is out of bounds. - fn argv(i: usize) -> OsString { - let arg_len = unsafe { abi::sys_argv(crate::ptr::null_mut(), 0, i) }; +fn get_args() -> Vec<&'static OsStr> { + let argc = unsafe { abi::sys_argc() }; + let mut args = Vec::with_capacity(argc); + + for i in 0..argc { + // Get the size of the argument then the data. + let arg_len = unsafe { abi::sys_argv(ptr::null_mut(), 0, i) }; let arg_len_words = (arg_len + WORD_SIZE - 1) / WORD_SIZE; let words = unsafe { abi::sys_alloc_words(arg_len_words) }; @@ -28,15 +22,16 @@ impl Args { let arg_len2 = unsafe { abi::sys_argv(words, arg_len_words, i) }; debug_assert_eq!(arg_len, arg_len2); - // Convert to OsString. - // - // FIXME: We can probably get rid of the extra copy here if we - // reimplement "os_str" instead of just using the generic unix - // "os_str". - let arg_bytes: &[u8] = - unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, arg_len) }; - OsString::from_inner(os_str::Buf { inner: arg_bytes.to_vec() }) + let arg_bytes = unsafe { slice::from_raw_parts(words.cast(), arg_len) }; + args.push(unsafe { OsStr::from_encoded_bytes_unchecked(arg_bytes) }); } + args +} + +static ARGS: OnceLock> = OnceLock::new(); + +pub struct Args { + iter: slice::Iter<'static, &'static OsStr>, } impl !Send for Args {} @@ -44,75 +39,56 @@ impl !Sync for Args {} impl fmt::Debug for Args { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() + self.iter.as_slice().fmt(f) } } impl Iterator for Args { type Item = OsString; - #[inline] fn next(&mut self) -> Option { - if self.front == self.back { - None - } else { - let arg = Self::argv(self.front); - self.front += 1; - Some(arg) - } + self.iter.next().map(|arg| arg.to_os_string()) } #[inline] fn size_hint(&self) -> (usize, Option) { - let len = self.len(); - (len, Some(len)) + self.iter.size_hint() } #[inline] fn count(self) -> usize { - self.len() + self.iter.len() } - #[inline] - fn last(mut self) -> Option { - self.next_back() + fn last(self) -> Option { + self.iter.last().map(|arg| arg.to_os_string()) } #[inline] fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { - let step_size = self.len().min(n); - self.front += step_size; - NonZero::new(n - step_size).map_or(Ok(()), Err) + self.iter.advance_by(n) } } impl DoubleEndedIterator for Args { - #[inline] fn next_back(&mut self) -> Option { - if self.back == self.front { - None - } else { - self.back -= 1; - Some(Self::argv(self.back)) - } + self.iter.next_back().map(|arg| arg.to_os_string()) } #[inline] fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { - let step_size = self.len().min(n); - self.back -= step_size; - NonZero::new(n - step_size).map_or(Ok(()), Err) + self.iter.advance_back_by(n) } } impl ExactSizeIterator for Args { #[inline] fn len(&self) -> usize { - self.back - self.front + self.iter.len() } #[inline] fn is_empty(&self) -> bool { - self.front == self.back + self.iter.is_empty() } } From 1ddb4d0062fbe55b88e55cffc00fe8b898512536 Mon Sep 17 00:00:00 2001 From: Michael Rieder Date: Fri, 4 Apr 2025 10:01:37 +0200 Subject: [PATCH 0007/2093] Fix parameter order for `_by()` variants of `min` / `max`/ `minmax` in `std::cmp` --- library/core/src/cmp.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index c315131f4136..b407dc5f6ef1 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -1552,6 +1552,9 @@ pub fn min(v1: T, v2: T) -> T { /// /// Returns the first argument if the comparison determines them to be equal. /// +/// The parameter order is preserved when calling the `compare` function, i.e. `v1` is +/// always passed as the first argument and `v2` as the second. +/// /// # Examples /// /// ``` @@ -1567,12 +1570,17 @@ pub fn min(v1: T, v2: T) -> T { /// /// let result = cmp::min_by(1, -1, abs_cmp); /// assert_eq!(result, 1); +/// +/// let rhs_abs_cmp = |x: &i32, y: &i32| x.cmp(&y.abs()); +/// +/// let result = cmp::min_by(-2, 1, rhs_abs_cmp); +/// assert_eq!(result, -2); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { - if compare(&v2, &v1).is_lt() { v2 } else { v1 } + if compare(&v1, &v2).is_le() { v1 } else { v2 } } /// Returns the element that gives the minimum value from the specified function. @@ -1644,6 +1652,9 @@ pub fn max(v1: T, v2: T) -> T { /// /// Returns the second argument if the comparison determines them to be equal. /// +/// The parameter order is preserved when calling the `compare` function, i.e. `v1` is +/// always passed as the first argument and `v2` as the second. +/// /// # Examples /// /// ``` @@ -1659,12 +1670,17 @@ pub fn max(v1: T, v2: T) -> T { /// /// let result = cmp::max_by(1, -1, abs_cmp); /// assert_eq!(result, -1); +/// +/// let rhs_abs_cmp = |x: &i32, y: &i32| x.cmp(&y.abs()); +/// +/// let result = cmp::max_by(-2, 1, rhs_abs_cmp); +/// assert_eq!(result, 1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { - if compare(&v2, &v1).is_lt() { v1 } else { v2 } + if compare(&v1, &v2).is_gt() { v1 } else { v2 } } /// Returns the element that gives the maximum value from the specified function. @@ -1743,6 +1759,9 @@ where /// /// Returns `[v1, v2]` if the comparison determines them to be equal. /// +/// The parameter order is preserved when calling the `compare` function, i.e. `v1` is +/// always passed as the first argument and `v2` as the second. +/// /// # Examples /// /// ``` @@ -1755,6 +1774,10 @@ where /// assert_eq!(cmp::minmax_by(-1, 2, abs_cmp), [-1, 2]); /// assert_eq!(cmp::minmax_by(-2, 2, abs_cmp), [-2, 2]); /// +/// let rhs_abs_cmp = |x: &i32, y: &i32| x.cmp(&y.abs()); +/// +/// assert_eq!(cmp::minmax_by(-2, 1, rhs_abs_cmp), [-2, 1]); +/// /// // You can destructure the result using array patterns /// let [min, max] = cmp::minmax_by(-42, 17, abs_cmp); /// assert_eq!(min, 17); @@ -1767,7 +1790,7 @@ pub fn minmax_by(v1: T, v2: T, compare: F) -> [T; 2] where F: FnOnce(&T, &T) -> Ordering, { - if compare(&v2, &v1).is_lt() { [v2, v1] } else { [v1, v2] } + if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] } } /// Returns minimum and maximum values with respect to the specified key function. From aab1563d42ed7cf54b017ad414f95fa45526f566 Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Sun, 18 Dec 2022 19:36:39 +0100 Subject: [PATCH 0008/2093] `impl PartialEq<{str,String}> for {Path,PathBuf}` Comparison of paths and strings is expected to be possible and needed e.g. in tests. This change adds the impls os `PartialEq` between strings and paths, both owned and unsized, in both directions. ACP: https://github.com/rust-lang/libs-team/issues/151 --- library/std/src/path.rs | 65 +++++++++++++++++++ tests/ui/inference/issue-72616.stderr | 12 ++-- .../partialeq_suggest_swap_on_e0277.stderr | 2 + 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 1a4a7aa7448c..30b12a0e7967 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2068,6 +2068,38 @@ impl PartialEq for PathBuf { } } +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for PathBuf { + #[inline] + fn eq(&self, other: &str) -> bool { + &*self == other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for str { + #[inline] + fn eq(&self, other: &PathBuf) -> bool { + other == self + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for PathBuf { + #[inline] + fn eq(&self, other: &String) -> bool { + **self == **other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for String { + #[inline] + fn eq(&self, other: &PathBuf) -> bool { + other == self + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for PathBuf { fn hash(&self, h: &mut H) { @@ -3242,6 +3274,39 @@ impl PartialEq for Path { } } +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for Path { + #[inline] + fn eq(&self, other: &str) -> bool { + let other: &OsStr = other.as_ref(); + self == other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for str { + #[inline] + fn eq(&self, other: &Path) -> bool { + other == self + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for Path { + #[inline] + fn eq(&self, other: &String) -> bool { + self == &*other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for String { + #[inline] + fn eq(&self, other: &Path) -> bool { + other == self + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for Path { fn hash(&self, h: &mut H) { diff --git a/tests/ui/inference/issue-72616.stderr b/tests/ui/inference/issue-72616.stderr index 31a0586301df..a271639996fd 100644 --- a/tests/ui/inference/issue-72616.stderr +++ b/tests/ui/inference/issue-72616.stderr @@ -6,14 +6,10 @@ LL | if String::from("a") == "a".try_into().unwrap() {} | | | type must be known at this point | - = note: cannot satisfy `String: PartialEq<_>` - = help: the following types implement trait `PartialEq`: - `String` implements `PartialEq<&str>` - `String` implements `PartialEq` - `String` implements `PartialEq` - `String` implements `PartialEq>` - `String` implements `PartialEq` - `String` implements `PartialEq` + = note: multiple `impl`s satisfying `String: PartialEq<_>` found in the following crates: `alloc`, `std`: + - impl PartialEq for String; + - impl PartialEq for String; + - impl PartialEq for String; help: try using a fully qualified path to specify the expected types | LL - if String::from("a") == "a".try_into().unwrap() {} diff --git a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr index ebe103ef19a1..c5984f53f68b 100644 --- a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr +++ b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr @@ -10,6 +10,8 @@ LL | String::from("Girls Band Cry") == T(String::from("Girls Band Cry")); `String` implements `PartialEq` `String` implements `PartialEq` `String` implements `PartialEq>` + `String` implements `PartialEq` + `String` implements `PartialEq` `String` implements `PartialEq` `String` implements `PartialEq` = note: `T` implements `PartialEq` From 3688b33cb34f7fbee69a285cf4d76f45a227ef40 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 11 Mar 2025 17:11:58 +0100 Subject: [PATCH 0009/2093] Move `adjust_derefs_manually_drop` into `clippy_utils` --- clippy_lints/src/dereference.rs | 11 +---------- clippy_utils/src/ty/mod.rs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index a22a2ee66d25..30f9a6374109 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{implements_trait, is_manually_drop}; +use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop}; use clippy_utils::{ DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, }; -use core::mem; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; @@ -707,14 +706,6 @@ fn try_parse_ref_op<'tcx>( )) } -// Checks if the adjustments contains a deref of `ManuallyDrop<_>` -fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool { - adjustments.iter().any(|a| { - let ty = mem::replace(&mut ty, a.target); - matches!(a.kind, Adjust::Deref(Some(ref op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty) - }) -} - // Checks whether the type for a deref call actually changed the type, not just the mutability of // the reference. fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index c50ad17bfad1..f0402e2aff20 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -17,6 +17,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, @@ -30,7 +31,7 @@ use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Obligation, ObligationCause}; use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; -use std::iter; +use std::{iter, mem}; use crate::path_res; use crate::paths::{PathNS, lookup_path_str}; @@ -1362,7 +1363,6 @@ pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())) } -/// Gets the index of a field by name. pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { match *ty.kind() { ty::Adt(def, _) if def.is_union() || def.is_struct() => { @@ -1372,3 +1372,11 @@ pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { _ => None, } } + +/// Checks if the adjustments contain a mutable dereference of a `ManuallyDrop<_>`. +pub fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool { + adjustments.iter().any(|a| { + let ty = mem::replace(&mut ty, a.target); + matches!(a.kind, Adjust::Deref(Some(op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty) + }) +} From 2cfbb05d6a9293aa2ef93bca98c2ec3d373cea6d Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 10 Mar 2025 18:30:12 +0100 Subject: [PATCH 0010/2093] Convert `deref_addrof` lint to late lint This is necessary in order to use type analysis in later commits. --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/reference.rs | 21 +++++++-------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 006145cc623c..ceb423b02ee2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -717,7 +717,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf))); store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain)); store.register_late_pass(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf))); - store.register_early_pass(|| Box::new(reference::DerefAddrOf)); + store.register_late_pass(|_| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone()))); diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 4bff37216eda..cb414d791191 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; -use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_hir::{Expr, ExprKind, Mutability, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::{BytePos, Span}; @@ -37,17 +37,10 @@ declare_clippy_lint! { declare_lint_pass!(DerefAddrOf => [DEREF_ADDROF]); -fn without_parens(mut e: &Expr) -> &Expr { - while let ExprKind::Paren(ref child_e) = e.kind { - e = child_e; - } - e -} - -impl EarlyLintPass for DerefAddrOf { - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { - if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind - && let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind +impl LateLintPass<'_> for DerefAddrOf { + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { + if let ExprKind::Unary(UnOp::Deref, deref_target) = e.kind + && let ExprKind::AddrOf(_, mutability, addrof_target) = deref_target.kind // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section. // See #12854 for details. && !matches!(addrof_target.kind, ExprKind::Array(_)) @@ -79,7 +72,7 @@ impl EarlyLintPass for DerefAddrOf { }) }; - if *mutability == Mutability::Mut { + if mutability == Mutability::Mut { generate_snippet("mut") } else { generate_snippet("&") From d78e7628d1376d1fe1feaff4079f590ba305aac9 Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 25 Apr 2025 15:26:52 -0500 Subject: [PATCH 0011/2093] scrape-examples.js: give each function a signature --- .../html/static/js/scrape-examples.js | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index d641405c8753..7ffa5d5b42a7 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -1,7 +1,4 @@ -/* global addClass, hasClass, removeClass, onEachLazy */ - -// Eventually fix this. -// @ts-nocheck +/* global addClass, hasClass, removeClass, onEachLazy, nonnull */ "use strict"; @@ -14,7 +11,12 @@ const DEFAULT_MAX_LINES = 5; const HIDDEN_MAX_LINES = 10; - // Scroll code block to the given code location + /** + * Scroll code block to the given code location + * @param {HTMLElement} elt + * @param {[number, number]} loc + * @param {boolean} isHidden + */ function scrollToLoc(elt, loc, isHidden) { const lines = elt.querySelectorAll("[data-nosnippet]"); let scrollOffset; @@ -35,10 +37,15 @@ scrollOffset = offsetMid - halfHeight; } - lines[0].parentElement.scrollTo(0, scrollOffset); - elt.querySelector(".rust").scrollTo(0, scrollOffset); + nonnull(lines[0].parentElement).scrollTo(0, scrollOffset); + nonnull(elt.querySelector(".rust")).scrollTo(0, scrollOffset); } + /** + * @param {HTMLElement} parent + * @param {string} className + * @param {string} content + */ function createScrapeButton(parent, className, content) { const button = document.createElement("button"); button.className = className; @@ -50,14 +57,15 @@ window.updateScrapedExample = (example, buttonHolder) => { let locIndex = 0; const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight")); - const link = example.querySelector(".scraped-example-title a"); + const link = nonnull(example.querySelector(".scraped-example-title a")); let expandButton = null; if (!example.classList.contains("expanded")) { expandButton = createScrapeButton(buttonHolder, "expand", "Show all"); } - const isHidden = example.parentElement.classList.contains("more-scraped-examples"); + const isHidden = nonnull(example.parentElement).classList.contains("more-scraped-examples"); + // @ts-expect-error const locs = example.locs; if (locs.length > 1) { const next = createScrapeButton(buttonHolder, "next", "Next usage"); @@ -106,7 +114,14 @@ } }; + /** + * Intitialize the `locs` field + * + * @param {HTMLElement} example + * @param {boolean} isHidden + */ function setupLoc(example, isHidden) { + // @ts-expect-error example.locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); // Start with the first example in view scrollToLoc(example, example.locs[0][0], isHidden); From b4c77e12408a3ae05ef6779eff0d8af3613716e4 Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 25 Apr 2025 16:16:18 -0500 Subject: [PATCH 0012/2093] rustdoc js: add ScrapedLoc type --- src/librustdoc/html/static/js/rustdoc.d.ts | 7 +++++++ src/librustdoc/html/static/js/scrape-examples.js | 13 +++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 0d2e19e019f3..a10f72414920 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -491,4 +491,11 @@ declare namespace rustdoc { options?: string[], default: string | boolean, } + + /** + * Single element in the data-locs field of a scraped example. + * First field is the start and end char index, + * other fields seem to be unused. + */ + type ScrapedLoc = [[number, number], string, string] } diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 7ffa5d5b42a7..32642799d887 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -18,6 +18,9 @@ * @param {boolean} isHidden */ function scrollToLoc(elt, loc, isHidden) { + /** @type {HTMLElement[]} */ + // blocked on https://github.com/microsoft/TypeScript/issues/29037 + // @ts-expect-error const lines = elt.querySelectorAll("[data-nosnippet]"); let scrollOffset; @@ -57,6 +60,8 @@ window.updateScrapedExample = (example, buttonHolder) => { let locIndex = 0; const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight")); + + /** @type {HTMLAnchorElement} */ const link = nonnull(example.querySelector(".scraped-example-title a")); let expandButton = null; @@ -72,6 +77,7 @@ const prev = createScrapeButton(buttonHolder, "prev", "Previous usage"); // Toggle through list of examples in a given file + /** @type {function(function(): void): void} */ const onChangeLoc = changeIndex => { removeClass(highlights[locIndex], "focus"); changeIndex(); @@ -117,14 +123,13 @@ /** * Intitialize the `locs` field * - * @param {HTMLElement} example + * @param {HTMLElement & {locs?: rustdoc.ScrapedLoc[]}} example * @param {boolean} isHidden */ function setupLoc(example, isHidden) { - // @ts-expect-error - example.locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); + const locs = example.locs = JSON.parse(nonnull(nonnull(example.attributes.getNamedItem("data-locs")).textContent)); // Start with the first example in view - scrollToLoc(example, example.locs[0][0], isHidden); + scrollToLoc(example, locs[0][0], isHidden); } const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example"); From 35ba7dd217f3a1af420189927205d89cc2c02718 Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 25 Apr 2025 16:28:50 -0500 Subject: [PATCH 0013/2093] rustdoc js: add rustdoc.ScrapedLoc type --- src/librustdoc/html/static/js/rustdoc.d.ts | 2 ++ src/librustdoc/html/static/js/scrape-examples.js | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index a10f72414920..2c0d47447c0b 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -496,6 +496,8 @@ declare namespace rustdoc { * Single element in the data-locs field of a scraped example. * First field is the start and end char index, * other fields seem to be unused. + * + * generated by `render_call_locations` in `render/mod.rs`. */ type ScrapedLoc = [[number, number], string, string] } diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 32642799d887..5dab4cc01561 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -127,7 +127,9 @@ * @param {boolean} isHidden */ function setupLoc(example, isHidden) { - const locs = example.locs = JSON.parse(nonnull(nonnull(example.attributes.getNamedItem("data-locs")).textContent)); + const locs_str = example.attributes.getNamedItem("data-locs")).textContent; + const locs = example.locs = + JSON.parse(nonnull(nonnull(locs_str)); // Start with the first example in view scrollToLoc(example, locs[0][0], isHidden); } From 8799d3dd97e38af3e31a2e76369d647e01b8a423 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 28 Apr 2025 18:44:46 -0500 Subject: [PATCH 0014/2093] fix typo --- src/librustdoc/html/static/js/scrape-examples.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 5dab4cc01561..7a231dc3b774 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -1,4 +1,4 @@ -/* global addClass, hasClass, removeClass, onEachLazy, nonnull */ + /* global addClass, hasClass, removeClass, onEachLazy, nonnull */ "use strict"; @@ -127,9 +127,10 @@ * @param {boolean} isHidden */ function setupLoc(example, isHidden) { - const locs_str = example.attributes.getNamedItem("data-locs")).textContent; - const locs = example.locs = - JSON.parse(nonnull(nonnull(locs_str)); + const locs_str = example.attributes.getNamedItem("data-locs").textContent; + const locs = + JSON.parse(nonnull(nonnull(locs_str))); + example.locs = locs; // Start with the first example in view scrollToLoc(example, locs[0][0], isHidden); } From b06113dc5cd6fbb2cc63918f9e861929537f41fd Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 22 May 2025 16:14:15 -0500 Subject: [PATCH 0015/2093] scrape-examples.js: add another nonnull() invokation --- src/librustdoc/html/static/js/scrape-examples.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 7a231dc3b774..ec472666ce16 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -127,7 +127,7 @@ * @param {boolean} isHidden */ function setupLoc(example, isHidden) { - const locs_str = example.attributes.getNamedItem("data-locs").textContent; + const locs_str = nonnull(example.attributes.getNamedItem("data-locs")).textContent; const locs = JSON.parse(nonnull(nonnull(locs_str))); example.locs = locs; From c021e7a9d2818e7d9f71bd38124d4e2c212007d6 Mon Sep 17 00:00:00 2001 From: lolbinarycat Date: Tue, 27 May 2025 12:50:14 -0500 Subject: [PATCH 0016/2093] scrape-examples.js: fix typos Co-authored-by: Guillaume Gomez --- src/librustdoc/html/static/js/rustdoc.d.ts | 2 +- src/librustdoc/html/static/js/scrape-examples.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 2c0d47447c0b..686d77abde51 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -497,7 +497,7 @@ declare namespace rustdoc { * First field is the start and end char index, * other fields seem to be unused. * - * generated by `render_call_locations` in `render/mod.rs`. + * Generated by `render_call_locations` in `render/mod.rs`. */ type ScrapedLoc = [[number, number], string, string] } diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index ec472666ce16..eeab591bcd80 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -121,7 +121,7 @@ }; /** - * Intitialize the `locs` field + * Initialize the `locs` field * * @param {HTMLElement & {locs?: rustdoc.ScrapedLoc[]}} example * @param {boolean} isHidden From 964eb82b63f7c99f951e5b5f262dc8800f7bd6ec Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Thu, 29 May 2025 22:10:40 +0300 Subject: [PATCH 0017/2093] Stabilize `ip_from` --- library/core/src/net/ip_addr.rs | 12 ++++++------ library/coretests/tests/lib.rs | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index aaa68e8d7d1a..1d1d184bb21a 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -627,13 +627,13 @@ impl Ipv4Addr { /// # Examples /// /// ``` - /// #![feature(ip_from)] /// use std::net::Ipv4Addr; /// /// let addr = Ipv4Addr::from_octets([13u8, 12u8, 11u8, 10u8]); /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); /// ``` - #[unstable(feature = "ip_from", issue = "131360")] + #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_octets(octets: [u8; 4]) -> Ipv4Addr { @@ -1460,7 +1460,6 @@ impl Ipv6Addr { /// # Examples /// /// ``` - /// #![feature(ip_from)] /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::from_segments([ @@ -1475,7 +1474,8 @@ impl Ipv6Addr { /// addr /// ); /// ``` - #[unstable(feature = "ip_from", issue = "131360")] + #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_segments(segments: [u16; 8]) -> Ipv6Addr { @@ -2025,7 +2025,6 @@ impl Ipv6Addr { /// # Examples /// /// ``` - /// #![feature(ip_from)] /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::from_octets([ @@ -2040,7 +2039,8 @@ impl Ipv6Addr { /// addr /// ); /// ``` - #[unstable(feature = "ip_from", issue = "131360")] + #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_octets(octets: [u8; 16]) -> Ipv6Addr { diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 693b14ef7620..b9c642461ead 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -47,7 +47,6 @@ #![feature(hashmap_internals)] #![feature(int_roundings)] #![feature(ip)] -#![feature(ip_from)] #![feature(is_ascii_octdigit)] #![feature(isolate_most_least_significant_one)] #![feature(iter_advance_by)] From ed81a111d1999a6b62f2cfea21c241e557b800da Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Thu, 19 Jun 2025 15:56:32 +0000 Subject: [PATCH 0018/2093] wasm: rm static mut --- library/std/src/sys/alloc/wasm.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/library/std/src/sys/alloc/wasm.rs b/library/std/src/sys/alloc/wasm.rs index c8fab992a88a..48e2fdd4ecce 100644 --- a/library/std/src/sys/alloc/wasm.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -16,12 +16,15 @@ //! The crate itself provides a global allocator which on wasm has no //! synchronization as there are no threads! -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#![allow(static_mut_refs)] +use core::cell::SyncUnsafeCell; use crate::alloc::{GlobalAlloc, Layout, System}; -static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new(); +struct SyncDlmalloc(dlmalloc::Dlmalloc); +unsafe impl Sync for SyncDlmalloc {} + +static DLMALLOC: SyncUnsafeCell = + SyncUnsafeCell::new(SyncDlmalloc(dlmalloc::Dlmalloc::new())); #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { @@ -30,7 +33,7 @@ unsafe impl GlobalAlloc for System { // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling malloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } + unsafe { (*DLMALLOC.get()).0.malloc(layout.size(), layout.align()) } } #[inline] @@ -38,7 +41,7 @@ unsafe impl GlobalAlloc for System { // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling calloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } + unsafe { (*DLMALLOC.get()).0.calloc(layout.size(), layout.align()) } } #[inline] @@ -46,7 +49,7 @@ unsafe impl GlobalAlloc for System { // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling free() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } + unsafe { (*DLMALLOC.get()).0.free(ptr, layout.size(), layout.align()) } } #[inline] @@ -54,7 +57,7 @@ unsafe impl GlobalAlloc for System { // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling realloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } + unsafe { (*DLMALLOC.get()).0.realloc(ptr, layout.size(), layout.align(), new_size) } } } From eb2913b01b3e423b2bcd73cc62406ffecbe59ea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Thu, 19 Jun 2025 21:55:55 +0200 Subject: [PATCH 0019/2093] Fix unsoundness in some tests --- library/coretests/tests/io/borrowed_buf.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/coretests/tests/io/borrowed_buf.rs b/library/coretests/tests/io/borrowed_buf.rs index fbd3864dcac1..8fbf9c3e8a2a 100644 --- a/library/coretests/tests/io/borrowed_buf.rs +++ b/library/coretests/tests/io/borrowed_buf.rs @@ -66,7 +66,7 @@ fn clear() { #[test] fn set_init() { - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16]; + let buf: &mut [_] = &mut [MaybeUninit::zeroed(); 16]; let mut rbuf: BorrowedBuf<'_> = buf.into(); unsafe { @@ -134,7 +134,7 @@ fn reborrow_written() { #[test] fn cursor_set_init() { - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16]; + let buf: &mut [_] = &mut [MaybeUninit::zeroed(); 16]; let mut rbuf: BorrowedBuf<'_> = buf.into(); unsafe { From 4acf3baecda9c5080bcfdc1ebb4560fbb2a4567e Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Fri, 20 Jun 2025 13:18:05 -0700 Subject: [PATCH 0020/2093] libtest: expose --fail-fast --- library/test/src/cli.rs | 4 +++- src/doc/rustc/src/tests/index.md | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 8840714a6623..d19141b8c2c7 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -57,6 +57,7 @@ fn optgroups() -> getopts::Options { .optflag("", "test", "Run tests and not benchmarks") .optflag("", "bench", "Run benchmarks instead of tests") .optflag("", "list", "List all tests and benchmarks") + .optflag("", "fail-fast", "Don't start new tests after the first failure") .optflag("h", "help", "Display this message") .optopt("", "logfile", "Write logs to the specified file (deprecated)", "PATH") .optflag( @@ -270,6 +271,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { let exact = matches.opt_present("exact"); let list = matches.opt_present("list"); let skip = matches.opt_strs("skip"); + let fail_fast = matches.opt_present("fail-fast"); let bench_benchmarks = matches.opt_present("bench"); let run_tests = !bench_benchmarks || matches.opt_present("test"); @@ -307,7 +309,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { skip, time_options, options, - fail_fast: false, + fail_fast, }; Ok(test_opts) diff --git a/src/doc/rustc/src/tests/index.md b/src/doc/rustc/src/tests/index.md index 12de69a4c9ee..7033184e52aa 100644 --- a/src/doc/rustc/src/tests/index.md +++ b/src/doc/rustc/src/tests/index.md @@ -158,6 +158,16 @@ unstable-options` flag. See [tracking issue The following options affect how tests are executed. +#### `--fail-fast` + +Stops tests after the first failure. + +If running tests in parallel (which is the default), then tests that have already been started on +other threads will be allowed to run to completion before the process exits. + +Note that when running tests in parallel, the test execution order is non-deterministic: +if multiple tests would fail, the first failure encountered will be reported. + #### `--test-threads` _NUM_THREADS_ Sets the number of threads to use for running tests in parallel. By default, From 547c72901ad145b40d7271e3ddef2b4ac0c53619 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Fri, 20 Jun 2025 13:27:53 -0700 Subject: [PATCH 0021/2093] Make fail-fast unstable --- library/test/src/cli.rs | 2 +- src/doc/rustc/src/tests/index.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index d19141b8c2c7..ea91ebc4e987 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -262,6 +262,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { // Unstable flags let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process"); let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic"); + let fail_fast = unstable_optflag!(matches, allow_unstable, "fail-fast"); let time_options = get_time_options(&matches, allow_unstable)?; let shuffle = get_shuffle(&matches, allow_unstable)?; let shuffle_seed = get_shuffle_seed(&matches, allow_unstable)?; @@ -271,7 +272,6 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { let exact = matches.opt_present("exact"); let list = matches.opt_present("list"); let skip = matches.opt_strs("skip"); - let fail_fast = matches.opt_present("fail-fast"); let bench_benchmarks = matches.opt_present("bench"); let run_tests = !bench_benchmarks || matches.opt_present("test"); diff --git a/src/doc/rustc/src/tests/index.md b/src/doc/rustc/src/tests/index.md index 7033184e52aa..ab96fbcd3178 100644 --- a/src/doc/rustc/src/tests/index.md +++ b/src/doc/rustc/src/tests/index.md @@ -168,6 +168,8 @@ other threads will be allowed to run to completion before the process exits. Note that when running tests in parallel, the test execution order is non-deterministic: if multiple tests would fail, the first failure encountered will be reported. +⚠️ 🚧 This requires the `-Z unstable-options` flag. + #### `--test-threads` _NUM_THREADS_ Sets the number of threads to use for running tests in parallel. By default, From a8d417d1a53dc22c42054327aca52a3b6a034150 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:46:36 +0100 Subject: [PATCH 0022/2093] rework `useless_vec` lint --- clippy_lints/src/vec.rs | 226 ++++++++++++++++++++++++---------------- 1 file changed, 136 insertions(+), 90 deletions(-) diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 7b6a25123e85..1646f2220212 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,4 +1,6 @@ use std::collections::BTreeMap; +use std::collections::btree_map::Entry; +use std::mem; use std::ops::ControlFlow; use clippy_config::Conf; @@ -20,15 +22,36 @@ use rustc_span::{DesugaringKind, Span}; pub struct UselessVec { too_large_for_stack: u64, msrv: Msrv, - span_to_lint_map: BTreeMap>, + /// Maps from a `vec![]` source callsite invocation span to the "state" (i.e., whether we can + /// emit a warning there or not). + /// + /// The purpose of this is to buffer lints up until `check_expr_post` so that we can cancel a + /// lint while visiting, because a `vec![]` invocation span can appear multiple times when + /// it is passed as a macro argument, once in a context that doesn't require a `Vec<_>` and + /// another time that does. Consider: + /// ``` + /// macro_rules! m { + /// ($v:expr) => { + /// let a = $v; + /// $v.push(3); + /// } + /// } + /// m!(vec![1, 2]); + /// ``` + /// The macro invocation expands to two `vec![1, 2]` invocations. If we eagerly suggest changing + /// the first `vec![1, 2]` (which is shared with the other expn) to an array which indeed would + /// work, we get a false positive warning on the `$v.push(3)` which really requires `$v` to + /// be a vector. + span_to_state: BTreeMap, allow_in_test: bool, } + impl UselessVec { pub fn new(conf: &'static Conf) -> Self { Self { too_large_for_stack: conf.too_large_for_stack, msrv: conf.msrv, - span_to_lint_map: BTreeMap::new(), + span_to_state: BTreeMap::new(), allow_in_test: conf.allow_useless_vec_in_tests, } } @@ -62,17 +85,28 @@ declare_clippy_lint! { impl_lint_pass!(UselessVec => [USELESS_VEC]); -impl<'tcx> LateLintPass<'tcx> for UselessVec { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) else { - return; - }; - if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { - return; - } - // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!` - let callsite = expr.span.parent_callsite().unwrap_or(expr.span); +/// The "state" of a `vec![]` invocation, indicating whether it can or cannot be changed. +enum VecState { + Change { + suggest_ty: SuggestedType, + vec_snippet: String, + expr_hir_id: HirId, + }, + NoChange, +} +enum VecToArray { + /// Expression does not need to be a `Vec<_>` and its type can be changed to an array (or + /// slice). + Possible, + /// Expression must be a `Vec<_>`. Type cannot change. + Impossible, +} + +impl UselessVec { + /// Checks if the surrounding environment requires this expression to actually be of type + /// `Vec<_>`, or if it can be changed to `&[]`/`[]` without causing type errors. + fn expr_usage_requires_vec(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) -> VecToArray { match cx.tcx.parent_hir_node(expr.hir_id) { // search for `let foo = vec![_]` expressions where all uses of `foo` // adjust to slices or call a method that exist on slices (e.g. len) @@ -100,107 +134,119 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .is_continue(); if only_slice_uses { - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, SuggestedType::Array); + VecToArray::Possible } else { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible } }, // if the local pattern has a specified type, do not lint. Node::LetStmt(LetStmt { ty: Some(_), .. }) if higher::VecArgs::hir(cx, expr).is_some() => { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible }, // search for `for _ in vec![...]` Node::Expr(Expr { span, .. }) if span.is_desugaring(DesugaringKind::ForLoop) && self.msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) => { - let suggest_slice = suggest_type(expr); - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice); + VecToArray::Possible }, // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]` _ => { - let suggest_slice = suggest_type(expr); - if adjusts_to_slice(cx, expr) { - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice); + VecToArray::Possible } else { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible } }, } } - - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - for (span, lint_opt) in &self.span_to_lint_map { - if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt { - let help_msg = format!("you can use {} directly", suggest_slice.desc()); - span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| { - // If the `vec!` macro contains comment, better not make the suggestion machine - // applicable as it would remove them. - let applicability = if *applicability != Applicability::Unspecified - && let source_map = cx.tcx.sess.source_map() - && span_contains_comment(source_map, *span) - { - Applicability::Unspecified - } else { - *applicability - }; - diag.span_suggestion(*span, help_msg, snippet, applicability); - }); - } - } - } } -impl UselessVec { - fn check_vec_macro<'tcx>( - &mut self, - cx: &LateContext<'tcx>, - vec_args: &higher::VecArgs<'tcx>, - span: Span, - hir_id: HirId, - suggest_slice: SuggestedType, - ) { - if span.from_expansion() { - return; +impl<'tcx> LateLintPass<'tcx> for UselessVec { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) + // The `vec![]` or `&vec![]` invocation span. + && let vec_span = expr.span.parent_callsite().unwrap_or(expr.span) + && !vec_span.from_expansion() + { + if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { + return; + } + + match self.expr_usage_requires_vec(cx, expr) { + VecToArray::Possible => { + let suggest_ty = suggest_type(expr); + + // Size and `Copy` checks don't depend on the enclosing usage of the expression + // and don't need to be inserted into the state map. + let vec_snippet = match vec_args { + higher::VecArgs::Repeat(expr, len) => { + if is_copy(cx, cx.typeck_results().expr_ty(expr)) + && let Some(Constant::Int(length)) = ConstEvalCtxt::new(cx).eval(len) + && let Ok(length) = u64::try_from(length) + && size_of(cx, expr) + .checked_mul(length) + .is_some_and(|size| size <= self.too_large_for_stack) + { + suggest_ty.snippet(cx, Some(expr.span), Some(len.span)) + } else { + return; + } + }, + higher::VecArgs::Vec(args) => { + if let Ok(length) = u64::try_from(args.len()) + && size_of(cx, expr) + .checked_mul(length) + .is_some_and(|size| size <= self.too_large_for_stack) + { + suggest_ty.snippet( + cx, + args.first().zip(args.last()).map(|(first, last)| { + first.span.source_callsite().to(last.span.source_callsite()) + }), + None, + ) + } else { + return; + } + }, + }; + + if let Entry::Vacant(entry) = self.span_to_state.entry(vec_span) { + entry.insert(VecState::Change { + suggest_ty, + vec_snippet, + expr_hir_id: expr.hir_id, + }); + } + }, + VecToArray::Impossible => { + self.span_to_state.insert(vec_span, VecState::NoChange); + }, + } } + } - let snippet = match *vec_args { - higher::VecArgs::Repeat(elem, len) => { - if let Some(Constant::Int(len_constant)) = ConstEvalCtxt::new(cx).eval(len) { - // vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also - if !is_copy(cx, cx.typeck_results().expr_ty(elem)) { - return; - } - - #[expect(clippy::cast_possible_truncation)] - if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { - return; - } - - suggest_slice.snippet(cx, Some(elem.span), Some(len.span)) - } else { - return; - } - }, - higher::VecArgs::Vec(args) => { - let args_span = if let Some(last) = args.iter().last() { - if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { - return; - } - Some(args[0].span.source_callsite().to(last.span.source_callsite())) - } else { - None - }; - suggest_slice.snippet(cx, args_span, None) - }, - }; - - self.span_to_lint_map.entry(span).or_insert(Some(( - hir_id, - suggest_slice, - snippet, - Applicability::MachineApplicable, - ))); + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for (span, state) in mem::take(&mut self.span_to_state) { + if let VecState::Change { + suggest_ty, + vec_snippet, + expr_hir_id, + } = state + { + span_lint_hir_and_then(cx, USELESS_VEC, expr_hir_id, span, "useless use of `vec!`", |diag| { + let help_msg = format!("you can use {} directly", suggest_ty.desc()); + // If the `vec!` macro contains comment, better not make the suggestion machine applicable as it + // would remove them. + let applicability = if span_contains_comment(cx.tcx.sess.source_map(), span) { + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; + diag.span_suggestion(span, help_msg, vec_snippet, applicability); + }); + } + } } } From bfd73ccae051b32686486d1849ea92750d259ad6 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:26:22 +0200 Subject: [PATCH 0023/2093] Add test case for array false positive --- tests/ui/vec.fixed | 9 +++++++++ tests/ui/vec.rs | 9 +++++++++ tests/ui/vec.stderr | 8 +++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index f360a8afadf6..55742459c92c 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -242,3 +242,12 @@ fn issue_12101() { for a in &[1, 2] {} //~^ useless_vec } + +fn issue_14531() { + // The lint used to suggest using an array rather than a reference to a slice. + + fn requires_ref_slice(v: &[()]) {} + let v = &[]; + //~^ useless_vec + requires_ref_slice(v); +} diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index a779d33557cb..fbf7131323c3 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -242,3 +242,12 @@ fn issue_12101() { for a in &(vec![1, 2]) {} //~^ useless_vec } + +fn issue_14531() { + // The lint used to suggest using an array rather than a reference to a slice. + + fn requires_ref_slice(v: &[()]) {} + let v = &vec![]; + //~^ useless_vec + requires_ref_slice(v); +} diff --git a/tests/ui/vec.stderr b/tests/ui/vec.stderr index 806d6617200f..d16c8a8944a2 100644 --- a/tests/ui/vec.stderr +++ b/tests/ui/vec.stderr @@ -127,5 +127,11 @@ error: useless use of `vec!` LL | for a in &(vec![1, 2]) {} | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` -error: aborting due to 21 previous errors +error: useless use of `vec!` + --> tests/ui/vec.rs:250:13 + | +LL | let v = &vec![]; + | ^^^^^^^ help: you can use a slice directly: `&[]` + +error: aborting due to 22 previous errors From 47e8c315f3e600357f0147b867de798b1b96cd03 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 21 Jun 2025 15:34:31 +0200 Subject: [PATCH 0024/2093] Make sure spans are from the root --- clippy_lints/src/vec.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 1646f2220212..52b30ddce12b 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -25,7 +25,7 @@ pub struct UselessVec { /// Maps from a `vec![]` source callsite invocation span to the "state" (i.e., whether we can /// emit a warning there or not). /// - /// The purpose of this is to buffer lints up until `check_expr_post` so that we can cancel a + /// The purpose of this is to buffer lints up until `check_crate_post` so that we can cancel a /// lint while visiting, because a `vec![]` invocation span can appear multiple times when /// it is passed as a macro argument, once in a context that doesn't require a `Vec<_>` and /// another time that does. Consider: @@ -187,7 +187,11 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .checked_mul(length) .is_some_and(|size| size <= self.too_large_for_stack) { - suggest_ty.snippet(cx, Some(expr.span), Some(len.span)) + suggest_ty.snippet( + cx, + Some(expr.span.source_callsite()), + Some(len.span.source_callsite()), + ) } else { return; } @@ -267,11 +271,17 @@ impl SuggestedType { } fn snippet(self, cx: &LateContext<'_>, args_span: Option, len_span: Option) -> String { + // Invariant of the lint as implemented: all spans are from the root context (and as a result, + // always trivially crate-local). + assert!(args_span.is_none_or(|s| !s.from_expansion())); + assert!(len_span.is_none_or(|s| !s.from_expansion())); + let maybe_args = args_span - .and_then(|sp| sp.get_source_text(cx)) + .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) .map_or(String::new(), |x| x.to_owned()); let maybe_len = len_span - .and_then(|sp| sp.get_source_text(cx).map(|s| format!("; {s}"))) + .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) + .map(|st| format!("; {st}")) .unwrap_or_default(); match self { From 06ae1bee205c9f85cc89047568737a87c791a032 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 22 Jun 2025 20:43:31 +0800 Subject: [PATCH 0025/2093] Make doc for transpose api better --- library/core/src/option.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index f2a1e901188f..2132376b3c57 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2037,9 +2037,9 @@ impl Option<&mut T> { impl Option> { /// Transposes an `Option` of a [`Result`] into a [`Result`] of an `Option`. /// - /// [`None`] will be mapped to [Ok]\([None]). - /// [Some]\([Ok]\(\_)) and [Some]\([Err]\(\_)) will be mapped to - /// [Ok]\([Some]\(\_)) and [Err]\(\_). + /// [Some]\([Ok]\(\_)) is mapped to [Ok]\([Some]\(\_)), + /// [Some]\([Err]\(\_)) is mapped to [Err]\(\_), + /// and [`None`] will be mapped to [Ok]\([None]). /// /// # Examples /// @@ -2047,9 +2047,9 @@ impl Option> { /// #[derive(Debug, Eq, PartialEq)] /// struct SomeErr; /// - /// let x: Result, SomeErr> = Ok(Some(5)); - /// let y: Option> = Some(Ok(5)); - /// assert_eq!(x, y.transpose()); + /// let x: Option> = Some(Ok(5)); + /// let y: Result, SomeErr> = Ok(Some(5)); + /// assert_eq!(x.transpose(), y); /// ``` #[inline] #[stable(feature = "transpose_result", since = "1.33.0")] From 1045b70304ab6a875fc9fbf10cb7e7c010edd3ab Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 26 Jun 2025 18:03:00 -0700 Subject: [PATCH 0026/2093] tests: add test for invalid interrupt signatures --- .../interrupt-invalid-signature.avr.stderr | 28 +++++ .../interrupt-invalid-signature.i686.stderr | 15 +++ .../interrupt-invalid-signature.msp430.stderr | 28 +++++ ...interrupt-invalid-signature.riscv32.stderr | 54 +++++++++ ...interrupt-invalid-signature.riscv64.stderr | 54 +++++++++ tests/ui/abi/interrupt-invalid-signature.rs | 110 ++++++++++++++++++ .../interrupt-invalid-signature.x64.stderr | 15 +++ 7 files changed, 304 insertions(+) create mode 100644 tests/ui/abi/interrupt-invalid-signature.avr.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.i686.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.msp430.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.riscv32.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.riscv64.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.rs create mode 100644 tests/ui/abi/interrupt-invalid-signature.x64.stderr diff --git a/tests/ui/abi/interrupt-invalid-signature.avr.stderr b/tests/ui/abi/interrupt-invalid-signature.avr.stderr new file mode 100644 index 000000000000..91f5ca2022e3 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.avr.stderr @@ -0,0 +1,28 @@ +error: invalid signature for `extern "avr-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:44:35 + | +LL | extern "avr-interrupt" fn avr_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "avr-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "avr-interrupt" fn avr_arg(_byte: u8) {} +LL + extern "avr-interrupt" fn avr_arg() {} + | + +error: invalid signature for `extern "avr-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:59:40 + | +LL | extern "avr-interrupt" fn avr_ret() -> u8 { + | ^^ + | + = note: functions with the "avr-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "avr-interrupt" fn avr_ret() -> u8 { +LL + extern "avr-interrupt" fn avr_ret() { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.i686.stderr b/tests/ui/abi/interrupt-invalid-signature.i686.stderr new file mode 100644 index 000000000000..06fd513df19e --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.i686.stderr @@ -0,0 +1,15 @@ +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + | + = note: functions with the "custom" ABI cannot have a return type +help: remove the return type + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/interrupt-invalid-signature.msp430.stderr b/tests/ui/abi/interrupt-invalid-signature.msp430.stderr new file mode 100644 index 000000000000..38479f93de58 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.msp430.stderr @@ -0,0 +1,28 @@ +error: invalid signature for `extern "msp430-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:40:41 + | +LL | extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "msp430-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} +LL + extern "msp430-interrupt" fn msp430_arg() {} + | + +error: invalid signature for `extern "msp430-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:65:46 + | +LL | extern "msp430-interrupt" fn msp430_ret() -> u8 { + | ^^ + | + = note: functions with the "msp430-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "msp430-interrupt" fn msp430_ret() -> u8 { +LL + extern "msp430-interrupt" fn msp430_ret() { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr b/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr new file mode 100644 index 000000000000..d632b17ab83f --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr @@ -0,0 +1,54 @@ +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:48:43 + | +LL | extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +LL + extern "riscv-interrupt-m" fn riscv_m_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:52:43 + | +LL | extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +LL + extern "riscv-interrupt-s" fn riscv_s_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:71:48 + | +LL | extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { +LL + extern "riscv-interrupt-m" fn riscv_m_ret() { + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:77:48 + | +LL | extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { +LL + extern "riscv-interrupt-s" fn riscv_s_ret() { + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr b/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr new file mode 100644 index 000000000000..d632b17ab83f --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr @@ -0,0 +1,54 @@ +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:48:43 + | +LL | extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +LL + extern "riscv-interrupt-m" fn riscv_m_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:52:43 + | +LL | extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +LL + extern "riscv-interrupt-s" fn riscv_s_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:71:48 + | +LL | extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { +LL + extern "riscv-interrupt-m" fn riscv_m_ret() { + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:77:48 + | +LL | extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { +LL + extern "riscv-interrupt-s" fn riscv_s_ret() { + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.rs b/tests/ui/abi/interrupt-invalid-signature.rs new file mode 100644 index 000000000000..e389285b0691 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.rs @@ -0,0 +1,110 @@ +/*! Tests interrupt ABIs have a constricted signature + +Most interrupt ABIs share a similar restriction in terms of not allowing most signatures. +Specifically, they generally cannot have arguments or return types. +So we test that they error in essentially all of the same places. +A notable and interesting exception is x86. + +This test uses `cfg` because it is not testing whether these ABIs work on the platform. +*/ +//@ add-core-stubs +//@ revisions: x64 i686 riscv32 riscv64 avr msp430 +// +//@ [x64] needs-llvm-components: x86 +//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib +//@ [i686] needs-llvm-components: x86 +//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib +//@ [riscv32] needs-llvm-components: riscv +//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib +//@ [riscv64] needs-llvm-components: riscv +//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib +//@ [avr] needs-llvm-components: avr +//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib +//@ [msp430] needs-llvm-components: msp430 +//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib +#![no_core] +#![feature( + no_core, + abi_msp430_interrupt, + abi_avr_interrupt, + abi_x86_interrupt, + abi_riscv_interrupt +)] + +extern crate minicore; +use minicore::*; + +/* most extern "interrupt" definitions should not accept args */ + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} +//[msp430]~^ ERROR invalid signature + +#[cfg(avr)] +extern "avr-interrupt" fn avr_arg(_byte: u8) {} +//[avr]~^ ERROR invalid signature + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +//[riscv32,riscv64]~^ ERROR invalid signature + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +//[riscv32,riscv64]~^ ERROR invalid signature + + +/* all extern "interrupt" definitions should not return non-1ZST values */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret() -> u8 { + //[avr]~^ ERROR invalid signature + 1 +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret() -> u8 { + //[msp430]~^ ERROR invalid signature + 1 +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + //[riscv32,riscv64]~^ ERROR invalid signature + 1 +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + //[riscv32,riscv64]~^ ERROR invalid signature + 1 +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret() -> u8 { + //[x64,i686]~^ ERROR invalid signature + 1 +} + + + +/* extern "interrupt" fnptrs with invalid signatures */ + +#[cfg(avr)] +fn avr_ptr(_f: extern "avr-interrupt" fn(u8) -> u8) { +} + +#[cfg(msp430)] +fn msp430_ptr(_f: extern "msp430-interrupt" fn(u8) -> u8) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_m_ptr(_f: extern "riscv-interrupt-m" fn(u8) -> u8) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_s_ptr(_f: extern "riscv-interrupt-s" fn(u8) -> u8) { +} + +#[cfg(any(x64,i686))] +fn x86_ptr(_f: extern "x86-interrupt" fn() -> u8) { +} diff --git a/tests/ui/abi/interrupt-invalid-signature.x64.stderr b/tests/ui/abi/interrupt-invalid-signature.x64.stderr new file mode 100644 index 000000000000..06fd513df19e --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.x64.stderr @@ -0,0 +1,15 @@ +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + | + = note: functions with the "custom" ABI cannot have a return type +help: remove the return type + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + +error: aborting due to 1 previous error + From db33e98540fca8f3268cafc6276dbefd55681d55 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 26 Jun 2025 18:02:54 -0700 Subject: [PATCH 0027/2093] compiler: fixup error message for x86-interrupt invalid returns --- compiler/rustc_ast_passes/messages.ftl | 2 +- tests/ui/abi/interrupt-invalid-signature.i686.stderr | 2 +- tests/ui/abi/interrupt-invalid-signature.x64.stderr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 6eddce7b590a..869c3a58ae3d 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -17,7 +17,7 @@ ast_passes_abi_must_not_have_parameters_or_return_type= ast_passes_abi_must_not_have_return_type= invalid signature for `extern {$abi}` function - .note = functions with the "custom" ABI cannot have a return type + .note = functions with the {$abi} ABI cannot have a return type .help = remove the return type ast_passes_assoc_const_without_body = diff --git a/tests/ui/abi/interrupt-invalid-signature.i686.stderr b/tests/ui/abi/interrupt-invalid-signature.i686.stderr index 06fd513df19e..86f2e097c37e 100644 --- a/tests/ui/abi/interrupt-invalid-signature.i686.stderr +++ b/tests/ui/abi/interrupt-invalid-signature.i686.stderr @@ -4,7 +4,7 @@ error: invalid signature for `extern "x86-interrupt"` function LL | extern "x86-interrupt" fn x86_ret() -> u8 { | ^^ | - = note: functions with the "custom" ABI cannot have a return type + = note: functions with the "x86-interrupt" ABI cannot have a return type help: remove the return type --> $DIR/interrupt-invalid-signature.rs:83:40 | diff --git a/tests/ui/abi/interrupt-invalid-signature.x64.stderr b/tests/ui/abi/interrupt-invalid-signature.x64.stderr index 06fd513df19e..86f2e097c37e 100644 --- a/tests/ui/abi/interrupt-invalid-signature.x64.stderr +++ b/tests/ui/abi/interrupt-invalid-signature.x64.stderr @@ -4,7 +4,7 @@ error: invalid signature for `extern "x86-interrupt"` function LL | extern "x86-interrupt" fn x86_ret() -> u8 { | ^^ | - = note: functions with the "custom" ABI cannot have a return type + = note: functions with the "x86-interrupt" ABI cannot have a return type help: remove the return type --> $DIR/interrupt-invalid-signature.rs:83:40 | From d6b3314fb29cc688a968811b6a78dc8a5309cb7d Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 26 Jun 2025 19:35:28 -0700 Subject: [PATCH 0028/2093] compiler: allow interrupts to return () or ! --- .../rustc_ast_passes/src/ast_validation.rs | 16 ++- .../ui/abi/interrupt-returns-never-or-unit.rs | 110 ++++++++++++++++++ 2 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 tests/ui/abi/interrupt-returns-never-or-unit.rs diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index da2482512037..478cec23fd57 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -396,7 +396,13 @@ impl<'a> AstValidator<'a> { if let InterruptKind::X86 = interrupt_kind { // "x86-interrupt" is special because it does have arguments. // FIXME(workingjubilee): properly lint on acceptable input types. - if let FnRetTy::Ty(ref ret_ty) = sig.decl.output { + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output + && match &ret_ty.kind { + TyKind::Never => false, + TyKind::Tup(tup) if tup.is_empty() => false, + _ => true, + } + { self.dcx().emit_err(errors::AbiMustNotHaveReturnType { span: ret_ty.span, abi, @@ -455,7 +461,13 @@ impl<'a> AstValidator<'a> { fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) { let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect(); - if let FnRetTy::Ty(ref ret_ty) = sig.decl.output { + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output + && match &ret_ty.kind { + TyKind::Never => false, + TyKind::Tup(tup) if tup.is_empty() => false, + _ => true, + } + { spans.push(ret_ty.span); } diff --git a/tests/ui/abi/interrupt-returns-never-or-unit.rs b/tests/ui/abi/interrupt-returns-never-or-unit.rs new file mode 100644 index 000000000000..34d509c83287 --- /dev/null +++ b/tests/ui/abi/interrupt-returns-never-or-unit.rs @@ -0,0 +1,110 @@ +/*! Tests interrupt ABIs can return ! + +Most interrupt ABIs share a similar restriction in terms of not allowing most signatures, +but it makes sense to allow them to return ! because they could indeed be divergent. + +This test uses `cfg` because it is not testing whether these ABIs work on the platform. +*/ +//@ add-core-stubs +//@ revisions: x64 i686 riscv32 riscv64 avr msp430 +//@ build-pass +// +//@ [x64] needs-llvm-components: x86 +//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib +//@ [i686] needs-llvm-components: x86 +//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib +//@ [riscv32] needs-llvm-components: riscv +//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib +//@ [riscv64] needs-llvm-components: riscv +//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib +//@ [avr] needs-llvm-components: avr +//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib +//@ [msp430] needs-llvm-components: msp430 +//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib +#![no_core] +#![feature( + no_core, + abi_msp430_interrupt, + abi_avr_interrupt, + abi_x86_interrupt, + abi_riscv_interrupt +)] + +extern crate minicore; +use minicore::*; + +/* interrupts can return never */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +/* interrupts can return explicit () */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +/* extern "interrupt" fnptrs can return ! too */ + +#[cfg(avr)] +fn avr_ptr(_f: extern "avr-interrupt" fn() -> !) { +} + +#[cfg(msp430)] +fn msp430_ptr(_f: extern "msp430-interrupt" fn() -> !) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_m_ptr(_f: extern "riscv-interrupt-m" fn() -> !) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_s_ptr(_f: extern "riscv-interrupt-s" fn() -> !) { +} + +#[cfg(any(x64,i686))] +fn x86_ptr(_f: extern "x86-interrupt" fn() -> !) { +} From b5ab966626e006172027e658fb1e43cf79f1c21a Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Fri, 27 Jun 2025 11:03:23 -0700 Subject: [PATCH 0029/2093] remember how to write never returns --- .../ui/abi/interrupt-returns-never-or-unit.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/ui/abi/interrupt-returns-never-or-unit.rs b/tests/ui/abi/interrupt-returns-never-or-unit.rs index 34d509c83287..8e224229a0b1 100644 --- a/tests/ui/abi/interrupt-returns-never-or-unit.rs +++ b/tests/ui/abi/interrupt-returns-never-or-unit.rs @@ -37,54 +37,54 @@ use minicore::*; #[cfg(avr)] extern "avr-interrupt" fn avr_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(msp430)] extern "msp430-interrupt" fn msp430_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-m" fn riscv_m_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-s" fn riscv_s_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(any(x64,i686))] extern "x86-interrupt" fn x86_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } /* interrupts can return explicit () */ #[cfg(avr)] extern "avr-interrupt" fn avr_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(msp430)] extern "msp430-interrupt" fn msp430_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-m" fn riscv_m_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-s" fn riscv_s_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(any(x64,i686))] extern "x86-interrupt" fn x86_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } /* extern "interrupt" fnptrs can return ! too */ From 7804d949dfcc32003dfd5412b44066d3546e4299 Mon Sep 17 00:00:00 2001 From: Jayden Qi Date: Sat, 22 Feb 2025 13:03:50 -0800 Subject: [PATCH 0030/2093] fix: wasm-bare targets compiling x86 builtins Added sanity check to bootstrap to hard error on wasm builds without clang, and changed distribution image `dist-various-2` to use clang to build for official targets. --- src/bootstrap/src/core/sanity.rs | 11 +++++++++++ src/ci/docker/host-x86_64/dist-various-2/Dockerfile | 4 ++++ src/ci/docker/host-x86_64/test-various/Dockerfile | 7 ++++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 958058d982ba..fa6987d4bf57 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -328,6 +328,17 @@ than building it. .entry(*target) .or_insert_with(|| Target::from_triple(&target.triple)); + // compiler-rt c fallbacks for wasm cannot be built with gcc + if target.contains("wasm") // bare metal targets without wasi sdk + && (build.config.optimized_compiler_builtins(*target) + || build.config.rust_std_features.contains("compiler-builtins-c")) + { + let is_clang = build.cc_tool(*target).is_like_clang(); + if !is_clang { + panic!("only clang supports building c code for wasm targets"); + } + } + if (target.contains("-none-") || target.contains("nvptx")) && build.no_std(*target) == Some(false) { diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index e1d83d360872..931cc688402f 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -59,6 +59,10 @@ ENV \ CXX_i686_unknown_uefi=clang++-11 \ CC_x86_64_unknown_uefi=clang-11 \ CXX_x86_64_unknown_uefi=clang++-11 \ + CC_wasm32_unknown_unknown=clang-11 \ + CXX_wasm32_unknown_unknown=clang++-11 \ + CC_wasm32v1_none=clang-11 \ + CXX_wasm32v1_none=clang++-11 \ CC=gcc-9 \ CXX=g++-9 diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 8d2e45ae497e..6c3e62267e1c 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -4,6 +4,7 @@ ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ clang-11 \ llvm-11 \ + gcc-multilib \ g++ \ make \ ninja-build \ @@ -59,8 +60,8 @@ RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v19.0 tar -xJ ENV PATH "$PATH:/wasmtime-v19.0.0-x86_64-linux" -ENV WASM_TARGETS=wasm32-wasip1 -ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_TARGETS \ +ENV WASM_WASIP_TARGET=wasm32-wasip1 +ENV WASM_WASIP_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_WASIP_TARGET \ tests/run-make \ tests/ui \ tests/mir-opt \ @@ -90,4 +91,4 @@ ENV UEFI_TARGETS=aarch64-unknown-uefi,i686-unknown-uefi,x86_64-unknown-uefi \ ENV UEFI_SCRIPT python3 /checkout/x.py --stage 2 build --host='' --target $UEFI_TARGETS && \ python3 -u /uefi_qemu_test/run.py -ENV SCRIPT $WASM_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT && $UEFI_SCRIPT +ENV SCRIPT $WASM_WASIP_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT && $UEFI_SCRIPT From e2d2926e6840294fe2e0b1a72d162e8d9facf8ea Mon Sep 17 00:00:00 2001 From: Jayden Qi Date: Fri, 14 Mar 2025 18:40:11 -0700 Subject: [PATCH 0031/2093] fix: install correct cc for wasm32-unknown-emscripten Also fixed a typo in the sanity check for bootstrap, as we are checking for clang-likeness in every wasm target. --- src/bootstrap/src/core/sanity.rs | 2 +- src/ci/docker/host-x86_64/dist-various-1/Dockerfile | 9 +++++++++ .../host-x86_64/dist-various-1/install-emscripten.sh | 12 ++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100755 src/ci/docker/host-x86_64/dist-various-1/install-emscripten.sh diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index fa6987d4bf57..37b2d782931a 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -329,7 +329,7 @@ than building it. .or_insert_with(|| Target::from_triple(&target.triple)); // compiler-rt c fallbacks for wasm cannot be built with gcc - if target.contains("wasm") // bare metal targets without wasi sdk + if target.contains("wasm") && (build.config.optimized_compiler_builtins(*target) || build.config.rust_std_features.contains("compiler-builtins-c")) { diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile index 5c459e5cd180..1e45ef45c9be 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -55,6 +55,15 @@ RUN ./install-riscv64-none-elf.sh COPY host-x86_64/dist-various-1/install-riscv32-none-elf.sh /build RUN ./install-riscv32-none-elf.sh +COPY host-x86_64/dist-various-1/install-llvm-mingw.sh /build +RUN ./install-llvm-mingw.sh + +COPY host-x86_64/dist-various-1/install-emscripten.sh /build +RUN ./install-emscripten.sh + +# Add Emscripten to PATH +ENV PATH="/build/emsdk:/build/emsdk/upstream/emscripten:/build/emsdk/node/current/bin:${PATH}" + # Suppress some warnings in the openwrt toolchains we downloaded ENV STAGING_DIR=/tmp diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-emscripten.sh b/src/ci/docker/host-x86_64/dist-various-1/install-emscripten.sh new file mode 100755 index 000000000000..eeb54ca67f7b --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-various-1/install-emscripten.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -ex + +apt-get update +apt-get install -y --no-install-recommends \ + nodejs \ + default-jre + +git clone https://github.com/emscripten-core/emsdk.git +cd emsdk +./emsdk install latest +./emsdk activate latest From 2a0fdef9260a72ded605ae1d980cad70644f25cb Mon Sep 17 00:00:00 2001 From: Jayden Qi Date: Tue, 1 Jul 2025 14:12:01 -0700 Subject: [PATCH 0032/2093] fix: error message --- src/bootstrap/src/core/sanity.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 37b2d782931a..295faa25e1c3 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -333,9 +333,14 @@ than building it. && (build.config.optimized_compiler_builtins(*target) || build.config.rust_std_features.contains("compiler-builtins-c")) { - let is_clang = build.cc_tool(*target).is_like_clang(); - if !is_clang { - panic!("only clang supports building c code for wasm targets"); + let cc_tool = build.cc_tool(*target); + if !cc_tool.is_like_clang() { + panic!( + "Clang is required to build C code for Wasm targets, got `{}` instead\n\ + this is because compiler-builtins is configured to build C source. Either \ + ensure Clang is used, or adjust this configuration.", + cc_tool.path().display() + ); } } From 8b1dbac4e508c4b27f0461ad2631b6df07f1af73 Mon Sep 17 00:00:00 2001 From: Jayden Qi Date: Tue, 1 Jul 2025 14:12:21 -0700 Subject: [PATCH 0033/2093] fix: remove unneeded(?) install script --- src/ci/docker/host-x86_64/dist-various-1/Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile index 1e45ef45c9be..4d5980027cac 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -55,9 +55,6 @@ RUN ./install-riscv64-none-elf.sh COPY host-x86_64/dist-various-1/install-riscv32-none-elf.sh /build RUN ./install-riscv32-none-elf.sh -COPY host-x86_64/dist-various-1/install-llvm-mingw.sh /build -RUN ./install-llvm-mingw.sh - COPY host-x86_64/dist-various-1/install-emscripten.sh /build RUN ./install-emscripten.sh From 3ddd2ef8c518efe10c811a229f056e96864d83c1 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 2 Jul 2025 13:15:07 -0700 Subject: [PATCH 0034/2093] Mention that casting to *const () is a way to roundtrip with from_raw_parts --- library/core/src/ptr/metadata.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 0deac3621e84..dc3ec3fd1994 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -106,6 +106,9 @@ pub const fn metadata(ptr: *const T) -> ::Metadat /// For slices, see the documentation of [`slice::from_raw_parts`] for safety requirements. /// For trait objects, the metadata must come from a pointer to the same underlying erased type. /// +/// If you are attempting to deconstruct a DST in a generic context to be reconstructed later, +/// a thin pointer can always be obtained by casting `*const T` to `*const ()`. +/// /// [`slice::from_raw_parts`]: crate::slice::from_raw_parts #[unstable(feature = "ptr_metadata", issue = "81513")] #[inline] From 937044097b8fc23b387ab58fc233109551e8536f Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 3 Jul 2025 23:43:02 +0800 Subject: [PATCH 0035/2093] stabilize `const_array_each_ref` --- library/core/src/array/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 5a46c04527b9..1739da105d1d 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -618,11 +618,11 @@ impl [T; N] { /// assert_eq!(strings.len(), 3); /// ``` #[stable(feature = "array_methods", since = "1.77.0")] - #[rustc_const_unstable(feature = "const_array_each_ref", issue = "133289")] + #[rustc_const_stable(feature = "const_array_each_ref", since = "CURRENT_RUSTC_VERSION")] pub const fn each_ref(&self) -> [&T; N] { let mut buf = [null::(); N]; - // FIXME(const-hack): We would like to simply use iterators for this (as in the original implementation), but this is not allowed in constant expressions. + // FIXME(const_trait_impl): We would like to simply use iterators for this (as in the original implementation), but this is not allowed in constant expressions. let mut i = 0; while i < N { buf[i] = &raw const self[i]; @@ -649,11 +649,11 @@ impl [T; N] { /// assert_eq!(floats, [0.0, 2.7, -1.0]); /// ``` #[stable(feature = "array_methods", since = "1.77.0")] - #[rustc_const_unstable(feature = "const_array_each_ref", issue = "133289")] + #[rustc_const_stable(feature = "const_array_each_ref", since = "CURRENT_RUSTC_VERSION")] pub const fn each_mut(&mut self) -> [&mut T; N] { let mut buf = [null_mut::(); N]; - // FIXME(const-hack): We would like to simply use iterators for this (as in the original implementation), but this is not allowed in constant expressions. + // FIXME(const_trait_impl): We would like to simply use iterators for this (as in the original implementation), but this is not allowed in constant expressions. let mut i = 0; while i < N { buf[i] = &raw mut self[i]; From c40e004a6bf4750c4bd03945852f2d5388d30d3b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 2 Jul 2025 07:25:46 +0200 Subject: [PATCH 0036/2093] gai: remove typo from test file, and add a reference id --- .../generic_arg_infer/{parend_infer.rs => paren_infer.rs} | 1 + 1 file changed, 1 insertion(+) rename tests/ui/const-generics/generic_arg_infer/{parend_infer.rs => paren_infer.rs} (94%) diff --git a/tests/ui/const-generics/generic_arg_infer/parend_infer.rs b/tests/ui/const-generics/generic_arg_infer/paren_infer.rs similarity index 94% rename from tests/ui/const-generics/generic_arg_infer/parend_infer.rs rename to tests/ui/const-generics/generic_arg_infer/paren_infer.rs index 9d7df8016cb1..869683b70566 100644 --- a/tests/ui/const-generics/generic_arg_infer/parend_infer.rs +++ b/tests/ui/const-generics/generic_arg_infer/paren_infer.rs @@ -1,4 +1,5 @@ //@ check-pass +//@ reference: items.generics.const.inferred struct Foo; From dee1b192cb4343cbe228dcf1e553b6f380595b53 Mon Sep 17 00:00:00 2001 From: Jayden Qi Date: Thu, 3 Jul 2025 15:17:36 -0700 Subject: [PATCH 0037/2093] fix: allow emcc --- src/bootstrap/src/core/sanity.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 295faa25e1c3..d26d4ce1ba8d 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -334,7 +334,8 @@ than building it. || build.config.rust_std_features.contains("compiler-builtins-c")) { let cc_tool = build.cc_tool(*target); - if !cc_tool.is_like_clang() { + if !cc_tool.is_like_clang() && !cc_tool.path().ends_with("emcc") { + // emcc works as well panic!( "Clang is required to build C code for Wasm targets, got `{}` instead\n\ this is because compiler-builtins is configured to build C source. Either \ From a3208108b0b2b98004dac023aaeb794c1ec9149a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 4 Jul 2025 19:10:39 -0400 Subject: [PATCH 0038/2093] fix(lib-std-fs): handle `usize` overflow in `read` & `read_to_string` --- library/std/src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 72ad7c244eeb..9fbfd92eaf88 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -304,7 +304,7 @@ pub struct DirBuilder { pub fn read>(path: P) -> io::Result> { fn inner(path: &Path) -> io::Result> { let mut file = File::open(path)?; - let size = file.metadata().map(|m| m.len() as usize).ok(); + let size = file.metadata().map(|m| usize::try_from(m.len()).unwrap_or(usize::MAX)).ok(); let mut bytes = Vec::try_with_capacity(size.unwrap_or(0))?; io::default_read_to_end(&mut file, &mut bytes, size)?; Ok(bytes) @@ -346,7 +346,7 @@ pub fn read>(path: P) -> io::Result> { pub fn read_to_string>(path: P) -> io::Result { fn inner(path: &Path) -> io::Result { let mut file = File::open(path)?; - let size = file.metadata().map(|m| m.len() as usize).ok(); + let size = file.metadata().map(|m| usize::try_from(m.len()).unwrap_or(usize::MAX)).ok(); let mut string = String::new(); string.try_reserve_exact(size.unwrap_or(0))?; io::default_read_to_string(&mut file, &mut string, size)?; From 6030997800b681cee91acf6ef8aba199d03041a6 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 23 May 2025 08:52:52 -0400 Subject: [PATCH 0039/2093] Remove `verify_inside_clippy_dir` since we always set the current directory. --- clippy_dev/src/setup/git_hook.rs | 6 ------ clippy_dev/src/setup/mod.rs | 20 -------------------- clippy_dev/src/setup/toolchain.rs | 6 ------ clippy_dev/src/setup/vscode.rs | 6 ------ 4 files changed, 38 deletions(-) diff --git a/clippy_dev/src/setup/git_hook.rs b/clippy_dev/src/setup/git_hook.rs index c7c53bc69d0b..c5a1e8264c7f 100644 --- a/clippy_dev/src/setup/git_hook.rs +++ b/clippy_dev/src/setup/git_hook.rs @@ -1,8 +1,6 @@ use std::fs; use std::path::Path; -use super::verify_inside_clippy_dir; - /// Rusts setup uses `git rev-parse --git-common-dir` to get the root directory of the repo. /// I've decided against this for the sake of simplicity and to make sure that it doesn't install /// the hook if `clippy_dev` would be used in the rust tree. The hook also references this tool @@ -35,10 +33,6 @@ pub fn install_hook(force_override: bool) { } fn check_precondition(force_override: bool) -> bool { - if !verify_inside_clippy_dir() { - return false; - } - // Make sure that we can find the git repository let git_path = Path::new(REPO_GIT_DIR); if !git_path.exists() || !git_path.is_dir() { diff --git a/clippy_dev/src/setup/mod.rs b/clippy_dev/src/setup/mod.rs index b0d318146391..5e938fff126d 100644 --- a/clippy_dev/src/setup/mod.rs +++ b/clippy_dev/src/setup/mod.rs @@ -2,23 +2,3 @@ pub mod git_hook; pub mod intellij; pub mod toolchain; pub mod vscode; - -use std::path::Path; - -const CLIPPY_DEV_DIR: &str = "clippy_dev"; - -/// This function verifies that the tool is being executed in the clippy directory. -/// This is useful to ensure that setups only modify Clippy's resources. The verification -/// is done by checking that `clippy_dev` is a sub directory of the current directory. -/// -/// It will print an error message and return `false` if the directory could not be -/// verified. -fn verify_inside_clippy_dir() -> bool { - let path = Path::new(CLIPPY_DEV_DIR); - if path.exists() && path.is_dir() { - true - } else { - eprintln!("error: unable to verify that the working directory is clippy's directory"); - false - } -} diff --git a/clippy_dev/src/setup/toolchain.rs b/clippy_dev/src/setup/toolchain.rs index ecd80215f7e8..c70b56461a7f 100644 --- a/clippy_dev/src/setup/toolchain.rs +++ b/clippy_dev/src/setup/toolchain.rs @@ -8,13 +8,7 @@ use walkdir::WalkDir; use crate::utils::exit_if_err; -use super::verify_inside_clippy_dir; - pub fn create(standalone: bool, force: bool, release: bool, name: &str) { - if !verify_inside_clippy_dir() { - return; - } - let rustup_home = std::env::var("RUSTUP_HOME").unwrap(); let toolchain = std::env::var("RUSTUP_TOOLCHAIN").unwrap(); diff --git a/clippy_dev/src/setup/vscode.rs b/clippy_dev/src/setup/vscode.rs index a37c873eed4f..a24aef65991f 100644 --- a/clippy_dev/src/setup/vscode.rs +++ b/clippy_dev/src/setup/vscode.rs @@ -1,8 +1,6 @@ use std::fs; use std::path::Path; -use super::verify_inside_clippy_dir; - const VSCODE_DIR: &str = ".vscode"; const TASK_SOURCE_FILE: &str = "util/etc/vscode-tasks.json"; const TASK_TARGET_FILE: &str = ".vscode/tasks.json"; @@ -22,10 +20,6 @@ pub fn install_tasks(force_override: bool) { } fn check_install_precondition(force_override: bool) -> bool { - if !verify_inside_clippy_dir() { - return false; - } - let vs_dir_path = Path::new(VSCODE_DIR); if vs_dir_path.exists() { // verify the target will be valid From 905342525f88b757a4f7026b2b51a21621999fd1 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 9 Jul 2025 21:27:27 -0400 Subject: [PATCH 0040/2093] Add a mailmap entry for gnzlbg The submodule->subtree changes add a lot of commits with the GitHub email. --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 90533e81b39f..68abb9003a05 100644 --- a/.mailmap +++ b/.mailmap @@ -255,6 +255,7 @@ Guillaume Gomez Guillaume Gomez ggomez Guillaume Gomez Guillaume Gomez Guillaume Gomez Guillaume Gomez +gnzlbg hamidreza kalbasi Hanna Kruppe Heather From 2261154549996eb7737a895e5df5fa894dc03e42 Mon Sep 17 00:00:00 2001 From: kennytm Date: Thu, 10 Jul 2025 18:22:18 +0800 Subject: [PATCH 0041/2093] core: add Peekable::next_if_map --- library/core/src/iter/adapters/peekable.rs | 102 ++++++++++++++++++ .../coretests/tests/iter/adapters/peekable.rs | 86 +++++++++++++++ library/coretests/tests/lib.rs | 1 + 3 files changed, 189 insertions(+) diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index a6522659620a..a55de75d56c6 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -317,6 +317,108 @@ impl Peekable { { self.next_if(|next| next == expected) } + + /// Consumes the next value of this iterator and applies a function `f` on it, + /// returning the result if the closure returns `Ok`. + /// + /// Otherwise if the closure returns `Err` the value is put back for the next iteration. + /// + /// The content of the `Err` variant is typically the original value of the closure, + /// but this is not required. If a different value is returned, + /// the next `peek()` or `next()` call will result in this new value. + /// This is similar to modifying the output of `peek_mut()`. + /// + /// If the closure panics, the next value will always be consumed and dropped + /// even if the panic is caught, because the closure never returned an `Err` value to put back. + /// + /// # Examples + /// + /// Parse the leading decimal number from an iterator of characters. + /// ``` + /// #![feature(peekable_next_if_map)] + /// let mut iter = "125 GOTO 10".chars().peekable(); + /// let mut line_num = 0_u32; + /// while let Some(digit) = iter.next_if_map(|c| c.to_digit(10).ok_or(c)) { + /// line_num = line_num * 10 + digit; + /// } + /// assert_eq!(line_num, 125); + /// assert_eq!(iter.collect::(), " GOTO 10"); + /// ``` + /// + /// Matching custom types. + /// ``` + /// #![feature(peekable_next_if_map)] + /// + /// #[derive(Debug, PartialEq, Eq)] + /// enum Node { + /// Comment(String), + /// Red(String), + /// Green(String), + /// Blue(String), + /// } + /// + /// /// Combines all consecutive `Comment` nodes into a single one. + /// fn combine_comments(nodes: Vec) -> Vec { + /// let mut result = Vec::with_capacity(nodes.len()); + /// let mut iter = nodes.into_iter().peekable(); + /// let mut comment_text = None::; + /// loop { + /// // Typically the closure in .next_if_map() matches on the input, + /// // extracts the desired pattern into an `Ok`, + /// // and puts the rest into an `Err`. + /// while let Some(text) = iter.next_if_map(|node| match node { + /// Node::Comment(text) => Ok(text), + /// other => Err(other), + /// }) { + /// comment_text.get_or_insert_default().push_str(&text); + /// } + /// + /// if let Some(text) = comment_text.take() { + /// result.push(Node::Comment(text)); + /// } + /// if let Some(node) = iter.next() { + /// result.push(node); + /// } else { + /// break; + /// } + /// } + /// result + /// } + ///# assert_eq!( // hiding the test to avoid cluttering the documentation. + ///# combine_comments(vec![ + ///# Node::Comment("The".to_owned()), + ///# Node::Comment("Quick".to_owned()), + ///# Node::Comment("Brown".to_owned()), + ///# Node::Red("Fox".to_owned()), + ///# Node::Green("Jumped".to_owned()), + ///# Node::Comment("Over".to_owned()), + ///# Node::Blue("The".to_owned()), + ///# Node::Comment("Lazy".to_owned()), + ///# Node::Comment("Dog".to_owned()), + ///# ]), + ///# vec![ + ///# Node::Comment("TheQuickBrown".to_owned()), + ///# Node::Red("Fox".to_owned()), + ///# Node::Green("Jumped".to_owned()), + ///# Node::Comment("Over".to_owned()), + ///# Node::Blue("The".to_owned()), + ///# Node::Comment("LazyDog".to_owned()), + ///# ], + ///# ) + /// ``` + #[unstable(feature = "peekable_next_if_map", issue = "143702")] + pub fn next_if_map(&mut self, f: impl FnOnce(I::Item) -> Result) -> Option { + let unpeek = if let Some(item) = self.next() { + match f(item) { + Ok(result) => return Some(result), + Err(item) => Some(item), + } + } else { + None + }; + self.peeked = Some(unpeek); + None + } } #[unstable(feature = "trusted_len", issue = "37572")] diff --git a/library/coretests/tests/iter/adapters/peekable.rs b/library/coretests/tests/iter/adapters/peekable.rs index 7f4341b8902c..f0549e8d6c2c 100644 --- a/library/coretests/tests/iter/adapters/peekable.rs +++ b/library/coretests/tests/iter/adapters/peekable.rs @@ -271,3 +271,89 @@ fn test_peekable_non_fused() { assert_eq!(iter.peek(), None); assert_eq!(iter.next_back(), None); } + +#[test] +fn test_peekable_next_if_map_mutation() { + fn collatz((mut num, mut len): (u64, u32)) -> Result { + let jump = num.trailing_zeros(); + num >>= jump; + len += jump; + if num == 1 { Ok(len) } else { Err((3 * num + 1, len + 1)) } + } + + let mut iter = once((3, 0)).peekable(); + assert_eq!(iter.peek(), Some(&(3, 0))); + assert_eq!(iter.next_if_map(collatz), None); + assert_eq!(iter.peek(), Some(&(10, 1))); + assert_eq!(iter.next_if_map(collatz), None); + assert_eq!(iter.peek(), Some(&(16, 3))); + assert_eq!(iter.next_if_map(collatz), Some(7)); + assert_eq!(iter.peek(), None); + assert_eq!(iter.next_if_map(collatz), None); +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_peekable_next_if_map_panic() { + use core::cell::Cell; + use std::panic::{AssertUnwindSafe, catch_unwind}; + + struct BitsetOnDrop<'a> { + value: u32, + cell: &'a Cell, + } + impl<'a> Drop for BitsetOnDrop<'a> { + fn drop(&mut self) { + self.cell.update(|v| v | self.value); + } + } + + let cell = &Cell::new(0); + let mut it = [ + BitsetOnDrop { value: 1, cell }, + BitsetOnDrop { value: 2, cell }, + BitsetOnDrop { value: 4, cell }, + BitsetOnDrop { value: 8, cell }, + ] + .into_iter() + .peekable(); + + // sanity check, .peek() won't consume the value, .next() will transfer ownership. + let item = it.peek().unwrap(); + assert_eq!(item.value, 1); + assert_eq!(cell.get(), 0); + let item = it.next().unwrap(); + assert_eq!(item.value, 1); + assert_eq!(cell.get(), 0); + drop(item); + assert_eq!(cell.get(), 1); + + // next_if_map returning Ok should transfer the value out. + let item = it.next_if_map(Ok).unwrap(); + assert_eq!(item.value, 2); + assert_eq!(cell.get(), 1); + drop(item); + assert_eq!(cell.get(), 3); + + // next_if_map returning Err should not drop anything. + assert_eq!(it.next_if_map::<()>(Err), None); + assert_eq!(cell.get(), 3); + assert_eq!(it.peek().unwrap().value, 4); + assert_eq!(cell.get(), 3); + + // next_if_map panicking should consume and drop the item. + let result = catch_unwind({ + let mut it = AssertUnwindSafe(&mut it); + move || it.next_if_map::<()>(|_| panic!()) + }); + assert!(result.is_err()); + assert_eq!(cell.get(), 7); + assert_eq!(it.next().unwrap().value, 8); + assert_eq!(cell.get(), 15); + assert!(it.peek().is_none()); + + // next_if_map should *not* execute the closure if the iterator is exhausted. + assert!(it.next_if_map::<()>(|_| panic!()).is_none()); + assert!(it.peek().is_none()); + assert_eq!(cell.get(), 15); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index fdef736c0c0f..18e6e96c235f 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -78,6 +78,7 @@ #![feature(next_index)] #![feature(numfmt)] #![feature(pattern)] +#![feature(peekable_next_if_map)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] From fc8bf97095d6578be5464c4f10e1f11971738d80 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 23 May 2025 13:19:51 -0400 Subject: [PATCH 0042/2093] Rename `exit_if_err` to `run_exit_on_err` --- clippy_dev/src/dogfood.rs | 48 ++++++++++++++----------------- clippy_dev/src/lint.rs | 36 +++++++++++------------ clippy_dev/src/setup/toolchain.rs | 12 ++++---- clippy_dev/src/utils.rs | 26 ++++++++--------- 4 files changed, 55 insertions(+), 67 deletions(-) diff --git a/clippy_dev/src/dogfood.rs b/clippy_dev/src/dogfood.rs index 7e9d92458d05..c1ee3633d3ed 100644 --- a/clippy_dev/src/dogfood.rs +++ b/clippy_dev/src/dogfood.rs @@ -1,4 +1,5 @@ -use crate::utils::exit_if_err; +use crate::utils::run_exit_on_err; +use itertools::Itertools; use std::process::Command; /// # Panics @@ -6,30 +7,23 @@ use std::process::Command; /// Panics if unable to run the dogfood test #[allow(clippy::fn_params_excessive_bools)] pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { - let mut cmd = Command::new("cargo"); - - cmd.args(["test", "--test", "dogfood"]) - .args(["--features", "internal"]) - .args(["--", "dogfood_clippy", "--nocapture"]); - - let mut dogfood_args = Vec::new(); - if fix { - dogfood_args.push("--fix"); - } - - if allow_dirty { - dogfood_args.push("--allow-dirty"); - } - - if allow_staged { - dogfood_args.push("--allow-staged"); - } - - if allow_no_vcs { - dogfood_args.push("--allow-no-vcs"); - } - - cmd.env("__CLIPPY_DOGFOOD_ARGS", dogfood_args.join(" ")); - - exit_if_err(cmd.status()); + run_exit_on_err( + "cargo test", + Command::new("cargo") + .args(["test", "--test", "dogfood"]) + .args(["--features", "internal"]) + .args(["--", "dogfood_clippy", "--nocapture"]) + .env( + "__CLIPPY_DOGFOOD_ARGS", + [ + if fix { "--fix" } else { "" }, + if allow_dirty { "--allow-dirty" } else { "" }, + if allow_staged { "--allow-staged" } else { "" }, + if allow_no_vcs { "--allow-no-vcs" } else { "" }, + ] + .iter() + .filter(|x| !x.is_empty()) + .join(" "), + ), + ); } diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index 0d66f167a386..f450bf5d8a2f 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -1,4 +1,4 @@ -use crate::utils::{cargo_clippy_path, exit_if_err}; +use crate::utils::{cargo_clippy_path, run_exit_on_err}; use std::process::{self, Command}; use std::{env, fs}; @@ -12,8 +12,9 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator }; if is_file { - exit_if_err( - Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) + run_exit_on_err( + "cargo run", + Command::new(env::var("CARGO").unwrap_or("cargo".into())) .args(["run", "--bin", "clippy-driver", "--"]) .args(["-L", "./target/debug"]) .args(["-Z", "no-codegen"]) @@ -21,24 +22,21 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator .arg(path) .args(args) // Prevent rustc from creating `rustc-ice-*` files the console output is enough. - .env("RUSTC_ICE", "0") - .status(), + .env("RUSTC_ICE", "0"), ); } else { - exit_if_err( - Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) - .arg("build") - .status(), + run_exit_on_err( + "cargo build", + Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())).arg("build"), + ); + run_exit_on_err( + "cargo clippy", + Command::new(cargo_clippy_path()) + .arg("clippy") + .args(args) + // Prevent rustc from creating `rustc-ice-*` files the console output is enough. + .env("RUSTC_ICE", "0") + .current_dir(path), ); - - let status = Command::new(cargo_clippy_path()) - .arg("clippy") - .args(args) - // Prevent rustc from creating `rustc-ice-*` files the console output is enough. - .env("RUSTC_ICE", "0") - .current_dir(path) - .status(); - - exit_if_err(status); } } diff --git a/clippy_dev/src/setup/toolchain.rs b/clippy_dev/src/setup/toolchain.rs index c70b56461a7f..4c1db4e8a67d 100644 --- a/clippy_dev/src/setup/toolchain.rs +++ b/clippy_dev/src/setup/toolchain.rs @@ -1,3 +1,4 @@ +use crate::utils::run_exit_on_err; use std::env::consts::EXE_SUFFIX; use std::env::current_dir; use std::ffi::OsStr; @@ -6,8 +7,6 @@ use std::path::{Path, PathBuf}; use std::process::Command; use walkdir::WalkDir; -use crate::utils::exit_if_err; - pub fn create(standalone: bool, force: bool, release: bool, name: &str) { let rustup_home = std::env::var("RUSTUP_HOME").unwrap(); let toolchain = std::env::var("RUSTUP_TOOLCHAIN").unwrap(); @@ -45,11 +44,10 @@ pub fn create(standalone: bool, force: bool, release: bool, name: &str) { } } - let status = Command::new("cargo") - .arg("build") - .args(release.then_some("--release")) - .status(); - exit_if_err(status); + run_exit_on_err( + "cargo build", + Command::new("cargo").arg("build").args(release.then_some("--release")), + ); install_bin("cargo-clippy", &dest, standalone, release); install_bin("clippy-driver", &dest, standalone, release); diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 89962a110341..f518460f5829 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -8,7 +8,7 @@ use std::ffi::OsStr; use std::fs::{self, OpenOptions}; use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; use std::path::{Path, PathBuf}; -use std::process::{self, Command, ExitStatus, Stdio}; +use std::process::{self, Command, Stdio}; use std::{env, thread}; use walkdir::WalkDir; @@ -288,19 +288,6 @@ impl ClippyInfo { } } -/// # Panics -/// Panics if given command result was failed. -pub fn exit_if_err(status: io::Result) { - match status.expect("failed to run command").code() { - Some(0) => {}, - Some(n) => process::exit(n), - None => { - eprintln!("Killed by signal"); - process::exit(1); - }, - } -} - #[derive(Clone, Copy)] pub enum UpdateStatus { Unchanged, @@ -653,6 +640,17 @@ pub fn write_file(path: &Path, contents: &str) { expect_action(fs::write(path, contents), ErrAction::Write, path); } +pub fn run_exit_on_err(path: &(impl AsRef + ?Sized), cmd: &mut Command) { + match expect_action(cmd.status(), ErrAction::Run, path.as_ref()).code() { + Some(0) => {}, + Some(n) => process::exit(n), + None => { + eprintln!("{} killed by signal", path.as_ref().display()); + process::exit(1); + }, + } +} + #[must_use] pub fn run_with_output(path: &(impl AsRef + ?Sized), cmd: &mut Command) -> Vec { fn f(path: &Path, cmd: &mut Command) -> Vec { From ed50c8a0e987511d10ec11936f9e88fb297b18b4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 23 May 2025 14:22:45 -0400 Subject: [PATCH 0043/2093] Make `dev serve` actually detect all updates --- clippy_dev/src/serve.rs | 109 ++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 38 deletions(-) diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 498ffeba9d67..73d9a1e82373 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -1,7 +1,11 @@ +use crate::utils::{ErrAction, expect_action}; +use core::fmt::Display; +use core::mem; use std::path::Path; use std::process::Command; use std::time::{Duration, SystemTime}; -use std::{env, thread}; +use std::{env, fs, thread}; +use walkdir::WalkDir; #[cfg(windows)] const PYTHON: &str = "python"; @@ -18,56 +22,85 @@ pub fn run(port: u16, lint: Option) -> ! { Some(lint) => format!("http://localhost:{port}/#{lint}"), }); + let mut last_update = mtime("util/gh-pages/index.html"); loop { - let index_time = mtime("util/gh-pages/index.html"); - let times = [ - "clippy_lints/src", - "util/gh-pages/index_template.html", - "tests/compile-test.rs", - ] - .map(mtime); - - if times.iter().any(|&time| index_time < time) { - Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) - .arg("collect-metadata") - .spawn() - .unwrap() - .wait() - .unwrap(); + if is_metadata_outdated(mem::replace(&mut last_update, SystemTime::now())) { + // Ignore the command result; we'll fall back to displaying the old metadata. + let _ = expect_action( + Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) + .arg("collect-metadata") + .status(), + ErrAction::Run, + "cargo collect-metadata", + ); + last_update = SystemTime::now(); } + + // Only start the web server the first time around. if let Some(url) = url.take() { thread::spawn(move || { - let mut child = Command::new(PYTHON) - .arg("-m") - .arg("http.server") - .arg(port.to_string()) - .current_dir("util/gh-pages") - .spawn() - .unwrap(); + let mut child = expect_action( + Command::new(PYTHON) + .args(["-m", "http.server", port.to_string().as_str()]) + .current_dir("util/gh-pages") + .spawn(), + ErrAction::Run, + "python -m http.server", + ); // Give some time for python to start thread::sleep(Duration::from_millis(500)); // Launch browser after first export.py has completed and http.server is up let _result = opener::open(url); - child.wait().unwrap(); + expect_action(child.wait(), ErrAction::Run, "python -m http.server"); }); } + + // Delay to avoid updating the metadata too aggressively. thread::sleep(Duration::from_millis(1000)); } } -fn mtime(path: impl AsRef) -> SystemTime { - let path = path.as_ref(); - if path.is_dir() { - path.read_dir() - .into_iter() - .flatten() - .flatten() - .map(|entry| mtime(entry.path())) - .max() - .unwrap_or(SystemTime::UNIX_EPOCH) - } else { - path.metadata() - .and_then(|metadata| metadata.modified()) - .unwrap_or(SystemTime::UNIX_EPOCH) +fn log_err_and_continue(res: Result, path: &Path) -> Option { + match res { + Ok(x) => Some(x), + Err(ref e) => { + eprintln!("error reading `{}`: {e}", path.display()); + None + }, } } + +fn mtime(path: &str) -> SystemTime { + log_err_and_continue(fs::metadata(path), path.as_ref()) + .and_then(|metadata| log_err_and_continue(metadata.modified(), path.as_ref())) + .unwrap_or(SystemTime::UNIX_EPOCH) +} + +fn is_metadata_outdated(time: SystemTime) -> bool { + // Ignore all IO errors here. We don't want to stop them from hosting the server. + if time < mtime("util/gh-pages/index_template.html") || time < mtime("tests/compile-test.rs") { + return true; + } + let Some(dir) = log_err_and_continue(fs::read_dir("."), ".".as_ref()) else { + return false; + }; + dir.map_while(|e| log_err_and_continue(e, ".".as_ref())).any(|e| { + let name = e.file_name(); + let name_bytes = name.as_encoded_bytes(); + if (name_bytes.starts_with(b"clippy_lints") && name_bytes != b"clippy_lints_internal") + || name_bytes == b"clippy_config" + { + WalkDir::new(&name) + .into_iter() + .map_while(|e| log_err_and_continue(e, name.as_ref())) + .filter(|e| e.file_type().is_file()) + .filter_map(|e| { + log_err_and_continue(e.metadata(), e.path()) + .and_then(|m| log_err_and_continue(m.modified(), e.path())) + }) + .any(|ftime| time < ftime) + } else { + false + } + }) +} From 46ff0a85657fcc08f36fec0a06455536c3296121 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 23 May 2025 14:32:10 -0400 Subject: [PATCH 0044/2093] Add helper function for creating cargo commands. --- clippy_dev/src/dogfood.rs | 5 ++--- clippy_dev/src/lint.rs | 11 ++++------- clippy_dev/src/serve.rs | 8 +++----- clippy_dev/src/setup/toolchain.rs | 5 ++--- clippy_dev/src/utils.rs | 10 ++++++++++ 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/clippy_dev/src/dogfood.rs b/clippy_dev/src/dogfood.rs index c1ee3633d3ed..d0fca952b932 100644 --- a/clippy_dev/src/dogfood.rs +++ b/clippy_dev/src/dogfood.rs @@ -1,6 +1,5 @@ -use crate::utils::run_exit_on_err; +use crate::utils::{cargo_cmd, run_exit_on_err}; use itertools::Itertools; -use std::process::Command; /// # Panics /// @@ -9,7 +8,7 @@ use std::process::Command; pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { run_exit_on_err( "cargo test", - Command::new("cargo") + cargo_cmd() .args(["test", "--test", "dogfood"]) .args(["--features", "internal"]) .args(["--", "dogfood_clippy", "--nocapture"]) diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index f450bf5d8a2f..d8d3e39116b9 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -1,6 +1,6 @@ -use crate::utils::{cargo_clippy_path, run_exit_on_err}; +use crate::utils::{cargo_clippy_path, cargo_cmd, run_exit_on_err}; +use std::fs; use std::process::{self, Command}; -use std::{env, fs}; pub fn run<'a>(path: &str, edition: &str, args: impl Iterator) { let is_file = match fs::metadata(path) { @@ -14,7 +14,7 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator if is_file { run_exit_on_err( "cargo run", - Command::new(env::var("CARGO").unwrap_or("cargo".into())) + cargo_cmd() .args(["run", "--bin", "clippy-driver", "--"]) .args(["-L", "./target/debug"]) .args(["-Z", "no-codegen"]) @@ -25,10 +25,7 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator .env("RUSTC_ICE", "0"), ); } else { - run_exit_on_err( - "cargo build", - Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())).arg("build"), - ); + run_exit_on_err("cargo build", cargo_cmd().arg("build")); run_exit_on_err( "cargo clippy", Command::new(cargo_clippy_path()) diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 73d9a1e82373..d9e018133813 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -1,10 +1,10 @@ -use crate::utils::{ErrAction, expect_action}; +use crate::utils::{ErrAction, cargo_cmd, expect_action}; use core::fmt::Display; use core::mem; use std::path::Path; use std::process::Command; use std::time::{Duration, SystemTime}; -use std::{env, fs, thread}; +use std::{fs, thread}; use walkdir::WalkDir; #[cfg(windows)] @@ -27,9 +27,7 @@ pub fn run(port: u16, lint: Option) -> ! { if is_metadata_outdated(mem::replace(&mut last_update, SystemTime::now())) { // Ignore the command result; we'll fall back to displaying the old metadata. let _ = expect_action( - Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) - .arg("collect-metadata") - .status(), + cargo_cmd().arg("collect-metadata").status(), ErrAction::Run, "cargo collect-metadata", ); diff --git a/clippy_dev/src/setup/toolchain.rs b/clippy_dev/src/setup/toolchain.rs index 4c1db4e8a67d..c64ae4ef3c36 100644 --- a/clippy_dev/src/setup/toolchain.rs +++ b/clippy_dev/src/setup/toolchain.rs @@ -1,10 +1,9 @@ -use crate::utils::run_exit_on_err; +use crate::utils::{cargo_cmd, run_exit_on_err}; use std::env::consts::EXE_SUFFIX; use std::env::current_dir; use std::ffi::OsStr; use std::fs; use std::path::{Path, PathBuf}; -use std::process::Command; use walkdir::WalkDir; pub fn create(standalone: bool, force: bool, release: bool, name: &str) { @@ -46,7 +45,7 @@ pub fn create(standalone: bool, force: bool, release: bool, name: &str) { run_exit_on_err( "cargo build", - Command::new("cargo").arg("build").args(release.then_some("--release")), + cargo_cmd().arg("build").args(release.then_some("--release")), ); install_bin("cargo-clippy", &dest, standalone, release); diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index f518460f5829..4b77a49a9f6e 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -130,6 +130,16 @@ pub fn cargo_clippy_path() -> PathBuf { path } +/// Creates a `Command` for running cargo. +#[must_use] +pub fn cargo_cmd() -> Command { + if let Some(path) = env::var_os("CARGO") { + Command::new(path) + } else { + Command::new("cargo") + } +} + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Version { pub major: u16, From ed1088eec4235e7bb86ff9ea4c231c5b5472f7b7 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 23 May 2025 14:37:52 -0400 Subject: [PATCH 0045/2093] Use `track_caller` more for better error spans in `clippy_dev` --- clippy_dev/src/utils.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 4b77a49a9f6e..0c5020c6982b 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -338,6 +338,7 @@ pub struct FileUpdater { dst_buf: String, } impl FileUpdater { + #[track_caller] fn update_file_checked_inner( &mut self, tool: &str, @@ -361,6 +362,7 @@ impl FileUpdater { } } + #[track_caller] fn update_file_inner(&mut self, path: &Path, update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus) { let mut file = File::open(path, OpenOptions::new().read(true).write(true)); file.read_to_cleared_string(&mut self.src_buf); @@ -370,6 +372,7 @@ impl FileUpdater { } } + #[track_caller] pub fn update_file_checked( &mut self, tool: &str, @@ -380,6 +383,7 @@ impl FileUpdater { self.update_file_checked_inner(tool, mode, path.as_ref(), update); } + #[track_caller] pub fn update_file( &mut self, path: impl AsRef, @@ -598,6 +602,7 @@ impl<'txt> RustSearcher<'txt> { } } +#[track_caller] #[expect(clippy::must_use_candidate)] pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { match OpenOptions::new().create_new(true).write(true).open(new_name) { @@ -620,6 +625,7 @@ pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { } } +#[track_caller] #[expect(clippy::must_use_candidate)] pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { match fs::create_dir(new_name) { @@ -646,10 +652,12 @@ pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { } } +#[track_caller] pub fn write_file(path: &Path, contents: &str) { expect_action(fs::write(path, contents), ErrAction::Write, path); } +#[track_caller] pub fn run_exit_on_err(path: &(impl AsRef + ?Sized), cmd: &mut Command) { match expect_action(cmd.status(), ErrAction::Run, path.as_ref()).code() { Some(0) => {}, @@ -661,6 +669,7 @@ pub fn run_exit_on_err(path: &(impl AsRef + ?Sized), cmd: &mut Command) { } } +#[track_caller] #[must_use] pub fn run_with_output(path: &(impl AsRef + ?Sized), cmd: &mut Command) -> Vec { fn f(path: &Path, cmd: &mut Command) -> Vec { @@ -746,6 +755,7 @@ pub fn split_args_for_threads( } } +#[track_caller] #[expect(clippy::must_use_candidate)] pub fn delete_file_if_exists(path: &Path) -> bool { match fs::remove_file(path) { @@ -755,6 +765,7 @@ pub fn delete_file_if_exists(path: &Path) -> bool { } } +#[track_caller] pub fn delete_dir_if_exists(path: &Path) { match fs::remove_dir_all(path) { Ok(()) => {}, From e624b7759282d5a76e585ff198e997e7b4ccd072 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 24 May 2025 14:17:33 -0400 Subject: [PATCH 0046/2093] Inline `cargo_clippy_path` and add a note about removing it when possible --- clippy_dev/src/lint.rs | 29 +++++++++++++++++------------ clippy_dev/src/utils.rs | 17 ----------------- 2 files changed, 17 insertions(+), 29 deletions(-) diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index d8d3e39116b9..2d9f563cdae2 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -1,16 +1,14 @@ -use crate::utils::{cargo_clippy_path, cargo_cmd, run_exit_on_err}; -use std::fs; -use std::process::{self, Command}; +use crate::utils::{ErrAction, cargo_cmd, expect_action, run_exit_on_err}; +use std::process::Command; +use std::{env, fs}; + +#[cfg(not(windows))] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; +#[cfg(windows)] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; pub fn run<'a>(path: &str, edition: &str, args: impl Iterator) { - let is_file = match fs::metadata(path) { - Ok(metadata) => metadata.is_file(), - Err(e) => { - eprintln!("Failed to read {path}: {e:?}"); - process::exit(1); - }, - }; - + let is_file = expect_action(fs::metadata(path), ErrAction::Read, path).is_file(); if is_file { run_exit_on_err( "cargo run", @@ -25,10 +23,17 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator .env("RUSTC_ICE", "0"), ); } else { + // Ideally this would just be `cargo run`, but the working directory needs to be + // set to clippy's directory when building, and the target project's directory + // when running clippy. `cargo` can only set a single working directory for both + // when using `run`. run_exit_on_err("cargo build", cargo_cmd().arg("build")); + + let mut exe = env::current_exe().expect("failed to get current executable name"); + exe.set_file_name(CARGO_CLIPPY_EXE); run_exit_on_err( "cargo clippy", - Command::new(cargo_clippy_path()) + Command::new(exe) .arg("clippy") .args(args) // Prevent rustc from creating `rustc-ice-*` files the console output is enough. diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 0c5020c6982b..dfa2d186097b 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -12,11 +12,6 @@ use std::process::{self, Command, Stdio}; use std::{env, thread}; use walkdir::WalkDir; -#[cfg(not(windows))] -static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; -#[cfg(windows)] -static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; - #[derive(Clone, Copy)] pub enum ErrAction { Open, @@ -118,18 +113,6 @@ impl<'a> File<'a> { } } -/// Returns the path to the `cargo-clippy` binary -/// -/// # Panics -/// -/// Panics if the path of current executable could not be retrieved. -#[must_use] -pub fn cargo_clippy_path() -> PathBuf { - let mut path = env::current_exe().expect("failed to get current executable name"); - path.set_file_name(CARGO_CLIPPY_EXE); - path -} - /// Creates a `Command` for running cargo. #[must_use] pub fn cargo_cmd() -> Command { From 913681464e21e8c7d27ea25b8504710d3fd847d9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 24 May 2025 14:18:06 -0400 Subject: [PATCH 0047/2093] Make `utils` module private. --- clippy_dev/src/lib.rs | 7 ++++--- clippy_dev/src/main.rs | 11 ++++++----- clippy_dev/src/utils.rs | 10 ---------- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 3361443196ab..eaf9589c8fe2 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -16,8 +16,7 @@ )] #![allow(clippy::missing_panics_doc)] -// The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate. -#[allow(unused_extern_crates)] +#[expect(unused_extern_crates, reason = "required to link to rustc crates")] extern crate rustc_driver; extern crate rustc_lexer; extern crate rustc_literal_escaper; @@ -33,4 +32,6 @@ pub mod serve; pub mod setup; pub mod sync; pub mod update_lints; -pub mod utils; + +mod utils; +pub use utils::{ClippyInfo, UpdateMode}; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 26aa269fb638..5fef231f6ca1 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -4,14 +4,15 @@ use clap::{Args, Parser, Subcommand}; use clippy_dev::{ - deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, update_lints, utils, + ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, + update_lints, }; use std::convert::Infallible; use std::env; fn main() { let dev = Dev::parse(); - let clippy = utils::ClippyInfo::search_for_manifest(); + let clippy = ClippyInfo::search_for_manifest(); if let Err(e) = env::set_current_dir(&clippy.path) { panic!("error setting current directory to `{}`: {e}", clippy.path.display()); } @@ -26,8 +27,8 @@ fn main() { allow_staged, allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), - DevCommand::Fmt { check } => fmt::run(utils::UpdateMode::from_check(check)), - DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)), + DevCommand::Fmt { check } => fmt::run(UpdateMode::from_check(check)), + DevCommand::UpdateLints { check } => update_lints::update(UpdateMode::from_check(check)), DevCommand::NewLint { pass, name, @@ -35,7 +36,7 @@ fn main() { r#type, msrv, } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) { - Ok(()) => update_lints::update(utils::UpdateMode::Change), + Ok(()) => update_lints::update(UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {e}"), }, DevCommand::Setup(SetupCommand { subcommand }) => match subcommand { diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index dfa2d186097b..057951d0e33b 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -434,7 +434,6 @@ pub enum Token<'a> { OpenParen, Pound, Semi, - Slash, } pub struct RustSearcher<'txt> { @@ -512,7 +511,6 @@ impl<'txt> RustSearcher<'txt> { | (Token::OpenParen, lexer::TokenKind::OpenParen) | (Token::Pound, lexer::TokenKind::Pound) | (Token::Semi, lexer::TokenKind::Semi) - | (Token::Slash, lexer::TokenKind::Slash) | ( Token::LitStr, lexer::TokenKind::Literal { @@ -586,7 +584,6 @@ impl<'txt> RustSearcher<'txt> { } #[track_caller] -#[expect(clippy::must_use_candidate)] pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { match OpenOptions::new().create_new(true).write(true).open(new_name) { Ok(file) => drop(file), @@ -609,7 +606,6 @@ pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { } #[track_caller] -#[expect(clippy::must_use_candidate)] pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { match fs::create_dir(new_name) { Ok(()) => {}, @@ -635,11 +631,6 @@ pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { } } -#[track_caller] -pub fn write_file(path: &Path, contents: &str) { - expect_action(fs::write(path, contents), ErrAction::Write, path); -} - #[track_caller] pub fn run_exit_on_err(path: &(impl AsRef + ?Sized), cmd: &mut Command) { match expect_action(cmd.status(), ErrAction::Run, path.as_ref()).code() { @@ -739,7 +730,6 @@ pub fn split_args_for_threads( } #[track_caller] -#[expect(clippy::must_use_candidate)] pub fn delete_file_if_exists(path: &Path) -> bool { match fs::remove_file(path) { Ok(()) => true, From f6bdffdcba7498fd3fe7b18bca966b23da6c463f Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Mon, 20 Nov 2023 02:18:52 +0300 Subject: [PATCH 0048/2093] Add Ref/RefMut try_map method --- library/core/src/cell.rs | 93 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c53a2b2beb4c..2f1a74fa2416 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1573,6 +1573,47 @@ impl<'b, T: ?Sized> Ref<'b, T> { } } + /// Tries to makes a new `Ref` for a component of the borrowed data. + /// On failure, the original guard is returned alongside with the error + /// returned by the closure. + /// + /// The `RefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `Ref::try_map(...)`. A method would interfere with methods of the same + /// name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(refcell_try_map)] + /// use std::cell::{RefCell, Ref}; + /// use std::str::{from_utf8, Utf8Error}; + /// + /// let c = RefCell::new(vec![0xF0, 0x9F, 0xA6 ,0x80]); + /// let b1: Ref<'_, Vec> = c.borrow(); + /// let b2: Result, _> = Ref::try_map(b1, |v| from_utf8(v)); + /// assert_eq!(&*b2.unwrap(), "🦀"); + /// + /// let c = RefCell::new(vec![0xF0, 0x9F, 0xA6]); + /// let b1: Ref<'_, Vec> = c.borrow(); + /// let b2: Result<_, (Ref<'_, Vec>, Utf8Error)> = Ref::try_map(b1, |v| from_utf8(v)); + /// let (b3, e) = b2.unwrap_err(); + /// assert_eq!(*b3, vec![0xF0, 0x9F, 0xA6]); + /// assert_eq!(e.valid_up_to(), 0); + /// ``` + #[unstable(feature = "refcell_try_map", issue = "143801")] + #[inline] + pub fn try_map( + orig: Ref<'b, T>, + f: impl FnOnce(&T) -> Result<&U, E>, + ) -> Result, (Self, E)> { + match f(&*orig) { + Ok(value) => Ok(Ref { value: NonNull::from(value), borrow: orig.borrow }), + Err(e) => Err((orig, e)), + } + } + /// Splits a `Ref` into multiple `Ref`s for different components of the /// borrowed data. /// @@ -1734,6 +1775,58 @@ impl<'b, T: ?Sized> RefMut<'b, T> { } } + /// Tries to makes a new `RefMut` for a component of the borrowed data. + /// On failure, the original guard is returned alongside with the error + /// returned by the closure. + /// + /// The `RefCell` is already mutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `RefMut::try_map(...)`. A method would interfere with methods of the same + /// name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(refcell_try_map)] + /// use std::cell::{RefCell, RefMut}; + /// use std::str::{from_utf8_mut, Utf8Error}; + /// + /// let c = RefCell::new(vec![0x68, 0x65, 0x6C, 0x6C, 0x6F]); + /// { + /// let b1: RefMut<'_, Vec> = c.borrow_mut(); + /// let b2: Result, _> = RefMut::try_map(b1, |v| from_utf8_mut(v)); + /// let mut b2 = b2.unwrap(); + /// assert_eq!(&*b2, "hello"); + /// b2.make_ascii_uppercase(); + /// } + /// assert_eq!(*c.borrow(), "HELLO".as_bytes()); + /// + /// let c = RefCell::new(vec![0xFF]); + /// let b1: RefMut<'_, Vec> = c.borrow_mut(); + /// let b2: Result<_, (RefMut<'_, Vec>, Utf8Error)> = RefMut::try_map(b1, |v| from_utf8_mut(v)); + /// let (b3, e) = b2.unwrap_err(); + /// assert_eq!(*b3, vec![0xFF]); + /// assert_eq!(e.valid_up_to(), 0); + /// ``` + #[unstable(feature = "refcell_try_map", issue = "143801")] + #[inline] + pub fn try_map( + mut orig: RefMut<'b, T>, + f: impl FnOnce(&mut T) -> Result<&mut U, E>, + ) -> Result, (Self, E)> { + // SAFETY: function holds onto an exclusive reference for the duration + // of its call through `orig`, and the pointer is only de-referenced + // inside of the function call never allowing the exclusive reference to + // escape. + match f(&mut *orig) { + Ok(value) => { + Ok(RefMut { value: NonNull::from(value), borrow: orig.borrow, marker: PhantomData }) + } + Err(e) => Err((orig, e)), + } + } + /// Splits a `RefMut` into multiple `RefMut`s for different components of the /// borrowed data. /// From 81e8c6797cc3c94134d6fce11ddb38e03be8c85a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 15 Jul 2025 07:18:56 -0400 Subject: [PATCH 0049/2093] `redundant_clone`: split iterator checks into `redundant_iter_cloned` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + .../src/methods/iter_overeager_cloned.rs | 5 ++-- clippy_lints/src/methods/mod.rs | 26 +++++++++++++++++++ tests/ui/iter_overeager_cloned.fixed | 12 ++++----- tests/ui/iter_overeager_cloned.rs | 12 ++++----- tests/ui/iter_overeager_cloned.stderr | 4 +-- 7 files changed, 44 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a92fbdc767bd..8d770ad2a338 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6304,6 +6304,7 @@ Released 2018-09-13 [`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_guards`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_guards +[`redundant_iter_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_iter_cloned [`redundant_locals`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index c3f8e02b4c06..233a1c42841b 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -449,6 +449,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, crate::methods::REDUNDANT_AS_STR_INFO, + crate::methods::REDUNDANT_ITER_CLONED_INFO, crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_FILTER_MAP_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index f5fe4316eb0d..d43b22c2bd51 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -10,8 +10,7 @@ use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty::{self, BorrowKind}; use rustc_span::{Symbol, sym}; -use super::ITER_OVEREAGER_CLONED; -use crate::redundant_clone::REDUNDANT_CLONE; +use super::{ITER_OVEREAGER_CLONED, REDUNDANT_ITER_CLONED}; #[derive(Clone, Copy)] pub(super) enum Op<'a> { @@ -96,7 +95,7 @@ pub(super) fn check<'tcx>( } let (lint, msg, trailing_clone) = match op { - Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_CLONE, "unneeded cloning of iterator items", ""), + Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_ITER_CLONED, "unneeded cloning of iterator items", ""), Op::LaterCloned | Op::FixClosure(_, _) => ( ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f2dabdd34387..c68caea89bbd 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4565,6 +4565,31 @@ declare_clippy_lint! { "hardcoded localhost IP address" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `Iterator::cloned` where the original value could be used + /// instead. + /// + /// ### Why is this bad? + /// It is not always possible for the compiler to eliminate useless allocations and + /// deallocations generated by redundant `clone()`s. + /// + /// ### Example + /// ```no_run + /// let x = vec![String::new()]; + /// let _ = x.iter().cloned().map(|x| x.len()); + /// ``` + /// Use instead: + /// ```no_run + /// let x = vec![String::new()]; + /// let _ = x.iter().map(|x| x.len()); + /// ``` + #[clippy::version = "1.90.0"] + pub REDUNDANT_ITER_CLONED, + perf, + "detects redundant calls to `Iterator::cloned`" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4744,6 +4769,7 @@ impl_lint_pass!(Methods => [ IO_OTHER_ERROR, SWAP_WITH_TEMPORARY, IP_CONSTANT, + REDUNDANT_ITER_CLONED, ]); /// Extracts a method call name, args, and `Span` of the method name. diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index b0e548f17909..4171f19469a4 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)] #![allow( dead_code, clippy::let_unit_value, @@ -16,7 +16,7 @@ fn main() { //~^ iter_overeager_cloned let _: usize = vec.iter().filter(|x| x == &"2").count(); - //~^ redundant_clone + //~^ redundant_iter_cloned let _: Vec<_> = vec.iter().take(2).cloned().collect(); //~^ iter_overeager_cloned @@ -77,19 +77,19 @@ fn main() { } let _ = vec.iter().map(|x| x.len()); - //~^ redundant_clone + //~^ redundant_iter_cloned // This would fail if changed. let _ = vec.iter().cloned().map(|x| x + "2"); let _ = vec.iter().for_each(|x| assert!(!x.is_empty())); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().all(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().any(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index cedf62a6b473..fe6aba24dd3e 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -1,4 +1,4 @@ -#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)] #![allow( dead_code, clippy::let_unit_value, @@ -16,7 +16,7 @@ fn main() { //~^ iter_overeager_cloned let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); - //~^ redundant_clone + //~^ redundant_iter_cloned let _: Vec<_> = vec.iter().cloned().take(2).collect(); //~^ iter_overeager_cloned @@ -78,19 +78,19 @@ fn main() { } let _ = vec.iter().cloned().map(|x| x.len()); - //~^ redundant_clone + //~^ redundant_iter_cloned // This would fail if changed. let _ = vec.iter().cloned().map(|x| x + "2"); let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().cloned().all(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().cloned().any(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index 1616dec95b79..f234d19e4aaa 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -25,8 +25,8 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | | | help: try: `.count()` | - = note: `-D clippy::redundant-clone` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` + = note: `-D clippy::redundant-iter-cloned` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_iter_cloned)]` error: unnecessarily eager cloning of iterator items --> tests/ui/iter_overeager_cloned.rs:21:21 From 87dd4695fe5fe2e76999d5040deb20a018e21942 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 15 Jul 2025 17:41:31 -0400 Subject: [PATCH 0050/2093] Add `Default` impls for `Pin`ned `Box`, `Rc`, `Arc` --- library/alloc/src/boxed.rs | 16 +++++++++++++++- library/alloc/src/rc.rs | 19 ++++++++++++++++--- library/alloc/src/sync.rs | 13 +++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 3db37f1d16f3..fa12d379c8ca 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1672,7 +1672,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for Box { - /// Creates a `Box`, with the `Default` value for T. + /// Creates a `Box`, with the `Default` value for `T`. #[inline] fn default() -> Self { let mut x: Box> = Box::new_uninit(); @@ -1695,6 +1695,7 @@ impl Default for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for Box<[T]> { + /// Creates an empty `[T]` inside a `Box`. #[inline] fn default() -> Self { let ptr: Unique<[T]> = Unique::<[T; 0]>::dangling(); @@ -1716,6 +1717,19 @@ impl Default for Box { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Pin> +where + T: ?Sized, + Box: Default, +{ + #[inline] + fn default() -> Self { + Box::into_pin(Box::::default()) + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Box { diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 5018ff4ad71f..529b583cdd2b 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2357,7 +2357,7 @@ impl Default for Rc { /// assert_eq!(*x, 0); /// ``` #[inline] - fn default() -> Rc { + fn default() -> Self { unsafe { Self::from_inner( Box::leak(Box::write( @@ -2373,7 +2373,7 @@ impl Default for Rc { #[cfg(not(no_global_oom_handling))] #[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Rc { - /// Creates an empty str inside an Rc + /// Creates an empty `str` inside an `Rc`. /// /// This may or may not share an allocation with other Rcs on the same thread. #[inline] @@ -2387,7 +2387,7 @@ impl Default for Rc { #[cfg(not(no_global_oom_handling))] #[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Rc<[T]> { - /// Creates an empty `[T]` inside an Rc + /// Creates an empty `[T]` inside an `Rc`. /// /// This may or may not share an allocation with other Rcs on the same thread. #[inline] @@ -2397,6 +2397,19 @@ impl Default for Rc<[T]> { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Pin> +where + T: ?Sized, + Rc: Default, +{ + #[inline] + fn default() -> Self { + unsafe { Pin::new_unchecked(Rc::::default()) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] trait RcEqIdent { fn eq(&self, other: &Rc) -> bool; diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index b8925f4544f4..29caa7bc5393 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -3654,6 +3654,19 @@ impl Default for Arc<[T]> { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Pin> +where + T: ?Sized, + Arc: Default, +{ + #[inline] + fn default() -> Self { + unsafe { Pin::new_unchecked(Arc::::default()) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for Arc { fn hash(&self, state: &mut H) { From 14c6e50d4740e13bf36b6c272485804ba9ad9117 Mon Sep 17 00:00:00 2001 From: Josh Simmons Date: Thu, 17 Jul 2025 09:11:44 +0200 Subject: [PATCH 0051/2093] Stabilize as_array_of_cells --- library/core/src/cell.rs | 4 ++-- tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index d6d1bf2effae..583a3340c262 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -698,14 +698,14 @@ impl Cell<[T; N]> { /// # Examples /// /// ``` - /// #![feature(as_array_of_cells)] /// use std::cell::Cell; /// /// let mut array: [i32; 3] = [1, 2, 3]; /// let cell_array: &Cell<[i32; 3]> = Cell::from_mut(&mut array); /// let array_cell: &[Cell; 3] = cell_array.as_array_of_cells(); /// ``` - #[unstable(feature = "as_array_of_cells", issue = "88248")] + #[stable(feature = "as_array_of_cells", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "as_array_of_cells", since = "CURRENT_RUSTC_VERSION")] pub const fn as_array_of_cells(&self) -> &[Cell; N] { // SAFETY: `Cell` has the same memory layout as `T`. unsafe { &*(self as *const Cell<[T; N]> as *const [Cell; N]) } diff --git a/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs b/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs index d3b441fbe88f..700f875a4f63 100644 --- a/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs +++ b/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs @@ -1,7 +1,5 @@ //@ run-pass -#![feature(as_array_of_cells)] - use std::cell::Cell; fn main() { From 6e002317cec5fa0c55a88307f839f2b5fb31922c Mon Sep 17 00:00:00 2001 From: Maksim Bondarenkov Date: Sat, 19 Jul 2025 14:19:54 +0300 Subject: [PATCH 0052/2093] opt-dist: rebuild rustc when doing static LLVM builds when building LLVM it's obvious that in case of shared build rustc doesn't need to be recompiled, but with static builds it would be better to compile rustc again to ensure we linked proper library. maybe I didn't understand the pipeline correctly, but it was strange for me to see that in Stage 5 LLVM is built while rustc is not --- src/tools/opt-dist/src/exec.rs | 6 ++++++ src/tools/opt-dist/src/main.rs | 10 ++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tools/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs index 56eff2ca2a7d..e5a037dc4e9b 100644 --- a/src/tools/opt-dist/src/exec.rs +++ b/src/tools/opt-dist/src/exec.rs @@ -189,6 +189,12 @@ impl Bootstrap { self } + /// Rebuild rustc in case of statically linked LLVM + pub fn rustc_rebuild(mut self) -> Self { + self.cmd = self.cmd.arg("--keep-stage").arg("0"); + self + } + pub fn run(self, timer: &mut TimerSection) -> anyhow::Result<()> { self.cmd.run()?; let metrics = load_metrics(&self.metrics_path)?; diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index 7857f196626b..8dc778836129 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -363,8 +363,14 @@ fn execute_pipeline( let mut dist = Bootstrap::dist(env, &dist_args) .llvm_pgo_optimize(llvm_pgo_profile.as_ref()) - .rustc_pgo_optimize(&rustc_pgo_profile) - .avoid_rustc_rebuild(); + .rustc_pgo_optimize(&rustc_pgo_profile); + + // if LLVM is not built we'll have PGO optimized rustc + dist = if env.supports_shared_llvm() || !env.build_llvm() { + dist.avoid_rustc_rebuild() + } else { + dist.rustc_rebuild() + }; for bolt_profile in bolt_profiles { dist = dist.with_bolt_profile(bolt_profile); From 175afd76180d47b655377768a87a55f8581686f0 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Mon, 21 Apr 2025 23:27:01 -0700 Subject: [PATCH 0053/2093] Stabilize `new_zeroed_alloc` --- compiler/rustc_index/src/bit_set.rs | 4 ++-- compiler/rustc_index/src/lib.rs | 2 +- library/alloc/src/boxed.rs | 16 ++-------------- library/alloc/src/rc.rs | 16 ++-------------- library/alloc/src/sync.rs | 14 ++------------ library/std/src/lib.rs | 1 - 6 files changed, 9 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 645d95b1dba9..0b3bc8963a3b 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -638,7 +638,7 @@ impl ChunkedBitSet { }; #[cfg(not(feature = "nightly"))] let mut words = { - // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291). + // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#129396). let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); // SAFETY: `words` can safely be all zeroes. let words = unsafe { words.assume_init() }; @@ -704,7 +704,7 @@ impl ChunkedBitSet { }; #[cfg(not(feature = "nightly"))] let mut words = { - // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291). + // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#129396). let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); // SAFETY: `words` can safely be all zeroes. let words = unsafe { words.assume_init() }; diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index cc680838e7e7..9d647209c6f8 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -1,9 +1,9 @@ // tidy-alphabetical-start #![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))] +#![cfg_attr(bootstrap, feature(new_zeroed_alloc))] #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", feature(extend_one, step_trait, test))] #![cfg_attr(feature = "nightly", feature(new_range_api))] -#![cfg_attr(feature = "nightly", feature(new_zeroed_alloc))] // tidy-alphabetical-end pub mod bit_set; diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 3db37f1d16f3..173d0103c119 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -290,8 +290,6 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// let zero = Box::::new_zeroed(); /// let zero = unsafe { zero.assume_init() }; /// @@ -301,7 +299,7 @@ impl Box { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed() -> Box> { Self::new_zeroed_in(Global) @@ -358,7 +356,6 @@ impl Box { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_uninit() -> Result>, AllocError> { Box::try_new_uninit_in(Global) @@ -384,7 +381,6 @@ impl Box { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_zeroed() -> Result>, AllocError> { Box::try_new_zeroed_in(Global) @@ -463,7 +459,6 @@ impl Box { #[unstable(feature = "allocator_api", issue = "32838")] #[cfg(not(no_global_oom_handling))] #[must_use] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_uninit_in(alloc: A) -> Box, A> where A: Allocator, @@ -496,7 +491,6 @@ impl Box { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> where A: Allocator, @@ -532,7 +526,6 @@ impl Box { /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] #[cfg(not(no_global_oom_handling))] - // #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_zeroed_in(alloc: A) -> Box, A> where @@ -570,7 +563,6 @@ impl Box { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> where A: Allocator, @@ -660,8 +652,6 @@ impl Box<[T]> { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// let values = Box::<[u32]>::new_zeroed_slice(3); /// let values = unsafe { values.assume_init() }; /// @@ -670,7 +660,7 @@ impl Box<[T]> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Box<[mem::MaybeUninit]> { unsafe { RawVec::with_capacity_zeroed(len).into_box(len) } @@ -785,7 +775,6 @@ impl Box<[T], A> { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_uninit_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { unsafe { RawVec::with_capacity_in(len, alloc).into_box(len) } @@ -813,7 +802,6 @@ impl Box<[T], A> { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { unsafe { RawVec::with_capacity_zeroed_in(len, alloc).into_box(len) } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 5018ff4ad71f..0b5c9240db0f 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -515,8 +515,6 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::rc::Rc; /// /// let zero = Rc::::new_zeroed(); @@ -527,7 +525,7 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed() -> Rc> { unsafe { @@ -589,7 +587,6 @@ impl Rc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_uninit() -> Result>, AllocError> { unsafe { Ok(Rc::from_ptr(Rc::try_allocate_for_layout( @@ -622,7 +619,6 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - //#[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_zeroed() -> Result>, AllocError> { unsafe { Ok(Rc::from_ptr(Rc::try_allocate_for_layout( @@ -690,7 +686,6 @@ impl Rc { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_uninit_in(alloc: A) -> Rc, A> { unsafe { @@ -728,7 +723,6 @@ impl Rc { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_zeroed_in(alloc: A) -> Rc, A> { unsafe { @@ -873,7 +867,6 @@ impl Rc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -912,7 +905,6 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - //#[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -1054,8 +1046,6 @@ impl Rc<[T]> { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::rc::Rc; /// /// let values = Rc::<[u32]>::new_zeroed_slice(3); @@ -1066,7 +1056,7 @@ impl Rc<[T]> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Rc<[mem::MaybeUninit]> { unsafe { @@ -1129,7 +1119,6 @@ impl Rc<[T], A> { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_uninit_slice_in(len: usize, alloc: A) -> Rc<[mem::MaybeUninit], A> { unsafe { Rc::from_ptr_in(Rc::allocate_for_slice_in(len, &alloc), alloc) } @@ -1158,7 +1147,6 @@ impl Rc<[T], A> { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Rc<[mem::MaybeUninit], A> { unsafe { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index b8925f4544f4..4090a04f21a9 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -516,8 +516,6 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::sync::Arc; /// /// let zero = Arc::::new_zeroed(); @@ -529,7 +527,7 @@ impl Arc { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed() -> Arc> { unsafe { @@ -603,7 +601,6 @@ impl Arc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_uninit() -> Result>, AllocError> { unsafe { Ok(Arc::from_ptr(Arc::try_allocate_for_layout( @@ -636,7 +633,6 @@ impl Arc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_zeroed() -> Result>, AllocError> { unsafe { Ok(Arc::from_ptr(Arc::try_allocate_for_layout( @@ -703,7 +699,6 @@ impl Arc { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_uninit_in(alloc: A) -> Arc, A> { unsafe { @@ -741,7 +736,6 @@ impl Arc { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_zeroed_in(alloc: A) -> Arc, A> { unsafe { @@ -927,7 +921,6 @@ impl Arc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -966,7 +959,6 @@ impl Arc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -1197,8 +1189,6 @@ impl Arc<[T]> { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::sync::Arc; /// /// let values = Arc::<[u32]>::new_zeroed_slice(3); @@ -1210,7 +1200,7 @@ impl Arc<[T]> { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Arc<[mem::MaybeUninit]> { unsafe { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 311b2cb93239..2c0061dc4032 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -376,7 +376,6 @@ #![feature(allocator_api)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] -#![feature(new_zeroed_alloc)] #![feature(slice_concat_trait)] #![feature(thin_box)] #![feature(try_reserve_kind)] From 788fb08bdb375eb811d682b4f24566d0aad1baca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 18 Jul 2025 12:51:44 +0200 Subject: [PATCH 0054/2093] Reject relaxed bounds inside associated type bounds --- compiler/rustc_ast_lowering/src/lib.rs | 12 ++++++++---- .../src/hir_ty_lowering/bounds.rs | 8 +++----- tests/ui/trait-bounds/more_maybe_bounds.rs | 8 ++++++-- tests/ui/trait-bounds/more_maybe_bounds.stderr | 6 +++--- .../unsized/relaxed-bounds-invalid-places.rs | 4 ++++ .../relaxed-bounds-invalid-places.stderr | 18 +++++++++++++----- 6 files changed, 37 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 533ee9bff54f..d3e8f62618f8 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -296,6 +296,7 @@ enum RelaxedBoundPolicy<'a> { enum RelaxedBoundForbiddenReason { TraitObjectTy, SuperTrait, + AssocTyBounds, LateBoundVarsInScope, } @@ -1102,9 +1103,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &*self.arena.alloc(self.ty(constraint.span, hir::TyKind::Err(guar))); hir::AssocItemConstraintKind::Equality { term: err_ty.into() } } else { - // FIXME(#135229): These should be forbidden! - let bounds = - self.lower_param_bounds(bounds, RelaxedBoundPolicy::Allowed, itctx); + let bounds = self.lower_param_bounds( + bounds, + RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::AssocTyBounds), + itctx, + ); hir::AssocItemConstraintKind::Bound { bounds } } } @@ -2117,7 +2120,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { diag.emit(); return; } - RelaxedBoundForbiddenReason::LateBoundVarsInScope => {} + RelaxedBoundForbiddenReason::AssocTyBounds + | RelaxedBoundForbiddenReason::LateBoundVarsInScope => {} }; } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index d7a827c649dd..bd0d0d7c1062 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -199,12 +199,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // However, this can easily get out of sync! Ideally, we would perform this step // where we are guaranteed to catch *all* bounds like in // `Self::lower_poly_trait_ref`. List of concrete issues: - // FIXME(more_maybe_bounds): We don't call this for e.g., trait object tys or - // supertrait bounds! + // FIXME(more_maybe_bounds): We don't call this for trait object tys, supertrait + // bounds or associated type bounds (ATB)! // FIXME(trait_alias, #143122): We don't call it for the RHS. Arguably however, - // AST lowering should reject them outright. - // FIXME(associated_type_bounds): We don't call this for them. However, AST - // lowering should reject them outright (#135229). + // AST lowering should reject them outright. let bounds = collect_relaxed_bounds(hir_bounds, self_ty_where_predicates); self.check_and_report_invalid_relaxed_bounds(bounds); } diff --git a/tests/ui/trait-bounds/more_maybe_bounds.rs b/tests/ui/trait-bounds/more_maybe_bounds.rs index 47348b0a0dd7..d367dd5b299d 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.rs +++ b/tests/ui/trait-bounds/more_maybe_bounds.rs @@ -1,7 +1,7 @@ // FIXME(more_maybe_bounds): Even under `more_maybe_bounds` / `-Zexperimental-default-bounds`, // trying to relax non-default bounds should still be an error in all contexts! As you can see -// there are places like supertrait bounds and trait object types where we currently don't perform -// this check. +// there are places like supertrait bounds, trait object types or associated type bounds (ATB) +// where we currently don't perform this check. #![feature(auto_traits, more_maybe_bounds, negative_impls)] trait Trait1 {} @@ -13,11 +13,15 @@ trait Trait4 where Self: Trait1 {} // FIXME: `?Trait2` should be rejected, `Trait2` isn't marked `#[lang = "default_traitN"]`. fn foo(_: Box<(dyn Trait3 + ?Trait2)>) {} + fn bar(_: &T) {} //~^ ERROR bound modifier `?` can only be applied to default traits like `Sized` //~| ERROR bound modifier `?` can only be applied to default traits like `Sized` //~| ERROR bound modifier `?` can only be applied to default traits like `Sized` +// FIXME: `?Trait1` should be rejected, `Trait1` isn't marked `#[lang = "default_traitN"]`. +fn baz() where T: Iterator {} + struct S; impl !Trait2 for S {} impl Trait1 for S {} diff --git a/tests/ui/trait-bounds/more_maybe_bounds.stderr b/tests/ui/trait-bounds/more_maybe_bounds.stderr index 09c9fc311657..8dd83fc7728a 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.stderr +++ b/tests/ui/trait-bounds/more_maybe_bounds.stderr @@ -1,17 +1,17 @@ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:20 + --> $DIR/more_maybe_bounds.rs:17:20 | LL | fn bar(_: &T) {} | ^^^^^^^ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:30 + --> $DIR/more_maybe_bounds.rs:17:30 | LL | fn bar(_: &T) {} | ^^^^^^^ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:40 + --> $DIR/more_maybe_bounds.rs:17:40 | LL | fn bar(_: &T) {} | ^^^^^^^ diff --git a/tests/ui/unsized/relaxed-bounds-invalid-places.rs b/tests/ui/unsized/relaxed-bounds-invalid-places.rs index b8eda1e7786b..4c1f242a01ce 100644 --- a/tests/ui/unsized/relaxed-bounds-invalid-places.rs +++ b/tests/ui/unsized/relaxed-bounds-invalid-places.rs @@ -22,6 +22,10 @@ impl S1 { fn f() where T: ?Sized {} //~ ERROR this relaxed bound is not permitted here } +// Test associated type bounds (ATB). +// issue: +struct S6(T) where T: Iterator; //~ ERROR this relaxed bound is not permitted here + trait Tr: ?Sized {} //~ ERROR relaxed bounds are not permitted in supertrait bounds // Test that relaxed `Sized` bounds are rejected in trait object types: diff --git a/tests/ui/unsized/relaxed-bounds-invalid-places.stderr b/tests/ui/unsized/relaxed-bounds-invalid-places.stderr index 30285d626936..d3f0535e2f0c 100644 --- a/tests/ui/unsized/relaxed-bounds-invalid-places.stderr +++ b/tests/ui/unsized/relaxed-bounds-invalid-places.stderr @@ -38,8 +38,16 @@ LL | fn f() where T: ?Sized {} | = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item +error: this relaxed bound is not permitted here + --> $DIR/relaxed-bounds-invalid-places.rs:27:41 + | +LL | struct S6(T) where T: Iterator; + | ^^^^^^ + | + = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item + error: relaxed bounds are not permitted in supertrait bounds - --> $DIR/relaxed-bounds-invalid-places.rs:25:11 + --> $DIR/relaxed-bounds-invalid-places.rs:29:11 | LL | trait Tr: ?Sized {} | ^^^^^^ @@ -47,19 +55,19 @@ LL | trait Tr: ?Sized {} = note: traits are `?Sized` by default error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:29:20 + --> $DIR/relaxed-bounds-invalid-places.rs:33:20 | LL | type O1 = dyn Tr + ?Sized; | ^^^^^^ error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:30:15 + --> $DIR/relaxed-bounds-invalid-places.rs:34:15 | LL | type O2 = dyn ?Sized + ?Sized + Tr; | ^^^^^^ error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:30:24 + --> $DIR/relaxed-bounds-invalid-places.rs:34:24 | LL | type O2 = dyn ?Sized + ?Sized + Tr; | ^^^^^^ @@ -76,5 +84,5 @@ error: bound modifier `?` can only be applied to `Sized` LL | struct S5(*const T) where T: ?Trait<'static> + ?Sized; | ^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors From 30033b45bb2fcbc0f267acaf9fab22411422e166 Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Tue, 22 Jul 2025 23:16:49 +0200 Subject: [PATCH 0055/2093] document assumptions about `Clone` and `Eq` traits --- library/core/src/clone.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index a34d1b4a0649..7afdf50ce51d 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -139,6 +139,30 @@ mod uninit; /// // Note: With the manual implementations the above line will compile. /// ``` /// +/// ## `Clone` and `PartialEq`/`Eq` +/// `Clone` is intended for the duplication of objects. Consequently, when implementing +/// both `Clone` and [`PartialEq`], the following property is expected to hold: +/// ```text +/// x == x -> x.clone() == x +/// ``` +/// In other words, if an object compares equal to itself, +/// its clone must also compare equal to the original. +/// +/// For types that also implement [`Eq`] – for which `x == x` always holds – +/// this implies that `x.clone() == x` must always be true. +/// Standard library collections such as +/// [`HashMap`], [`HashSet`], [`BTreeMap`], [`BTreeSet`] and [`BinaryHeap`] +/// rely on their keys respecting this property for correct behavior. +/// +/// This property is automatically satisfied when deriving both `Clone` and [`PartialEq`] +/// using `#[derive(Clone, PartialEq)]` or when additionally deriving [`Eq`] +/// using `#[derive(Clone, PartialEq, Eq)]`. +/// +/// Violating this property is a logic error. The behavior resulting from a logic error is not +/// specified, but users of the trait must ensure that such logic errors do *not* result in +/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these +/// methods. +/// /// ## Additional implementors /// /// In addition to the [implementors listed below][impls], From cdbcd72f341c949e4fa11f58d76a094bd435d18f Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Wed, 23 Jul 2025 00:41:48 +0200 Subject: [PATCH 0056/2093] remove trailing whitespace --- library/core/src/clone.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 7afdf50ce51d..64916f49f3a5 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -155,7 +155,7 @@ mod uninit; /// rely on their keys respecting this property for correct behavior. /// /// This property is automatically satisfied when deriving both `Clone` and [`PartialEq`] -/// using `#[derive(Clone, PartialEq)]` or when additionally deriving [`Eq`] +/// using `#[derive(Clone, PartialEq)]` or when additionally deriving [`Eq`] /// using `#[derive(Clone, PartialEq, Eq)]`. /// /// Violating this property is a logic error. The behavior resulting from a logic error is not From 623317eb5e2bf73b53a5cd52d693ac032cf5aa32 Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Wed, 23 Jul 2025 01:44:18 +0200 Subject: [PATCH 0057/2093] add links to collections --- library/core/src/clone.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 64916f49f3a5..c4778edf0fcc 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -176,6 +176,11 @@ mod uninit; /// (even if the referent doesn't), /// while variables captured by mutable reference never implement `Clone`. /// +/// [`HashMap`]: ../../std/collections/struct.HashMap.html +/// [`HashSet`]: ../../std/collections/struct.HashSet.html +/// [`BTreeMap`]: ../../std/collections/struct.BTreeMap.html +/// [`BTreeSet`]: ../../std/collections/struct.BTreeSet.html +/// [`BinaryHeap`]: ../../std/collections/struct.BinaryHeap.html /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "clone"] From 84f834b87f1f0b2eb56e94652eb3a4b440402a93 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Jul 2025 23:23:40 +0200 Subject: [PATCH 0058/2093] atomicrmw on pointers: move integer-pointer cast hacks into backend --- src/builder.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index a4ec4bf8deac..032f5d0a622d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1656,6 +1656,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, + ret_ptr: bool, ) -> RValue<'gcc> { let size = get_maybe_pointer_size(src); let name = match op { @@ -1683,6 +1684,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let atomic_function = self.context.get_builtin_function(name); let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc()); + // FIXME: If `ret_ptr` is true and `src` is an integer, we should really tell GCC + // that this is a pointer operation that needs to preserve provenance -- but like LLVM, + // GCC does not currently seems to support that. let void_ptr_type = self.context.new_type::<*mut ()>(); let volatile_void_ptr_type = void_ptr_type.make_volatile(); let dst = self.context.new_cast(self.location, dst, volatile_void_ptr_type); @@ -1690,7 +1694,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let new_src_type = atomic_function.get_param(1).to_rvalue().get_type(); let src = self.context.new_bitcast(self.location, src, new_src_type); let res = self.context.new_call(self.location, atomic_function, &[dst, src, order]); - self.context.new_cast(self.location, res, src.get_type()) + let res_type = if ret_ptr { void_ptr_type } else { src.get_type() }; + self.context.new_cast(self.location, res, res_type) } fn atomic_fence(&mut self, order: AtomicOrdering, scope: SynchronizationScope) { From c6ac515a2bcf6dbb4e27b11111da80f5ff36a88d Mon Sep 17 00:00:00 2001 From: "Pascal S. de Kloe" Date: Thu, 26 Jun 2025 14:52:39 +0200 Subject: [PATCH 0059/2093] fmt of non-decimals is always non-negative due to the two's-complement output --- library/core/src/fmt/num.rs | 86 ++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 7d41ae45093e..a52749d43259 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -54,44 +54,31 @@ unsafe trait GenericRadix: Sized { /// Converts an integer to corresponding radix digit. fn digit(x: u8) -> u8; - /// Format an integer using the radix using a formatter. + /// Format an unsigned integer using the radix using a formatter. fn fmt_int(&self, mut x: T, f: &mut fmt::Formatter<'_>) -> fmt::Result { // The radix can be as low as 2, so we need a buffer of at least 128 // characters for a base 2 number. let zero = T::zero(); - let is_nonnegative = x >= zero; let mut buf = [MaybeUninit::::uninit(); 128]; let mut offset = buf.len(); let base = T::from_u8(Self::BASE); - if is_nonnegative { - // Accumulate each digit of the number from the least significant - // to the most significant figure. - loop { - let n = x % base; // Get the current place value. - x = x / base; // Deaccumulate the number. - offset -= 1; - buf[offset].write(Self::digit(n.to_u8())); // Store the digit in the buffer. - if x == zero { - // No more digits left to accumulate. - break; - }; - } - } else { - // Do the same as above, but accounting for two's complement. - loop { - let n = zero - (x % base); // Get the current place value. - x = x / base; // Deaccumulate the number. - offset -= 1; - buf[offset].write(Self::digit(n.to_u8())); // Store the digit in the buffer. - if x == zero { - // No more digits left to accumulate. - break; - }; - } + + // Accumulate each digit of the number from the least significant + // to the most significant figure. + loop { + let n = x % base; // Get the current place value. + x = x / base; // Deaccumulate the number. + offset -= 1; + buf[offset].write(Self::digit(n.to_u8())); // Store the digit in the buffer. + if x == zero { + // No more digits left to accumulate. + break; + }; } + // SAFETY: Starting from `offset`, all elements of the slice have been set. - let buf_slice = unsafe { slice_buffer_to_str(&buf, offset) }; - f.pad_integral(is_nonnegative, Self::PREFIX, buf_slice) + let digits = unsafe { slice_buffer_to_str(&buf, offset) }; + f.pad_integral(true, Self::PREFIX, digits) } } @@ -132,11 +119,11 @@ radix! { LowerHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'a' + radix! { UpperHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'A' + (x - 10) } macro_rules! int_base { - (fmt::$Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { + (fmt::$Trait:ident for $T:ident -> $Radix:ident) => { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::$Trait for $T { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - $Radix.fmt_int(*self as $U, f) + $Radix.fmt_int(*self, f) } } }; @@ -144,15 +131,36 @@ macro_rules! int_base { macro_rules! integer { ($Int:ident, $Uint:ident) => { - int_base! { fmt::Binary for $Int as $Uint -> Binary } - int_base! { fmt::Octal for $Int as $Uint -> Octal } - int_base! { fmt::LowerHex for $Int as $Uint -> LowerHex } - int_base! { fmt::UpperHex for $Int as $Uint -> UpperHex } + int_base! { fmt::Binary for $Uint -> Binary } + int_base! { fmt::Octal for $Uint -> Octal } + int_base! { fmt::LowerHex for $Uint -> LowerHex } + int_base! { fmt::UpperHex for $Uint -> UpperHex } - int_base! { fmt::Binary for $Uint as $Uint -> Binary } - int_base! { fmt::Octal for $Uint as $Uint -> Octal } - int_base! { fmt::LowerHex for $Uint as $Uint -> LowerHex } - int_base! { fmt::UpperHex for $Uint as $Uint -> UpperHex } + // Format signed integers as unsigned (two’s complement representation). + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::Binary for $Int { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Binary::fmt(&self.cast_unsigned(), f) + } + } + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::Octal for $Int { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Octal::fmt(&self.cast_unsigned(), f) + } + } + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::LowerHex for $Int { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerHex::fmt(&self.cast_unsigned(), f) + } + } + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::UpperHex for $Int { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.cast_unsigned(), f) + } + } }; } integer! { isize, usize } From 7704a3968523e872dbda05e449cd6375e4520bad Mon Sep 17 00:00:00 2001 From: "Pascal S. de Kloe" Date: Sun, 29 Jun 2025 17:08:40 +0200 Subject: [PATCH 0060/2093] fmt benchmarks for binary, octal and hex --- library/coretests/benches/fmt.rs | 180 +++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/library/coretests/benches/fmt.rs b/library/coretests/benches/fmt.rs index ee8e981b46b9..f45b921b9393 100644 --- a/library/coretests/benches/fmt.rs +++ b/library/coretests/benches/fmt.rs @@ -162,3 +162,183 @@ fn write_u8_min(bh: &mut Bencher) { black_box(format!("{}", black_box(u8::MIN))); }); } + +#[bench] +fn write_i8_bin(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:b}", black_box(0_i8)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(100_i8)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(-100_i8)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(1_i8 << 4)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i16_bin(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:b}", black_box(0_i16)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(100_i16)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(-100_i16)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(1_i16 << 8)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i32_bin(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:b}", black_box(0_i32)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(100_i32)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(-100_i32)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(1_i32 << 16)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i64_bin(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:b}", black_box(0_i64)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(100_i64)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(-100_i64)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(1_i64 << 32)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i128_bin(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:b}", black_box(0_i128)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(100_i128)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(-100_i128)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(1_i128 << 64)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i8_oct(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:o}", black_box(0_i8)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(100_i8)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(-100_i8)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(1_i8 << 4)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i16_oct(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:o}", black_box(0_i16)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(100_i16)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(-100_i16)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(1_i16 << 8)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i32_oct(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:o}", black_box(0_i32)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(100_i32)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(-100_i32)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(1_i32 << 16)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i64_oct(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:o}", black_box(0_i64)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(100_i64)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(-100_i64)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(1_i64 << 32)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i128_oct(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:o}", black_box(0_i128)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(100_i128)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(-100_i128)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(1_i128 << 64)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i8_hex(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:x}", black_box(0_i8)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(100_i8)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(-100_i8)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(1_i8 << 4)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i16_hex(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:x}", black_box(0_i16)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(100_i16)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(-100_i16)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(1_i16 << 8)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i32_hex(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:x}", black_box(0_i32)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(100_i32)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(-100_i32)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(1_i32 << 16)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i64_hex(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:x}", black_box(0_i64)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(100_i64)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(-100_i64)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(1_i64 << 32)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i128_hex(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:x}", black_box(0_i128)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(100_i128)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(-100_i128)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(1_i128 << 64)).unwrap(); + black_box(&mut buf).clear(); + }); +} From a6f266612a42737ee46b69645244975e75406b60 Mon Sep 17 00:00:00 2001 From: morinmorin Date: Fri, 25 Jul 2025 23:22:36 +0900 Subject: [PATCH 0061/2093] std/sys/fd: remove `- 1` from `READ_LIMIT` on Darwin Darwin's `read`/`write` syscalls emit `EINVAL` only when `nbyte > INT_MAX`. The case `nbyte == INT_MAX` is valid, so the subtraction can be removed. --- library/std/src/sys/fd/unix.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index cdca73cdca11..a12f692e7543 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -37,10 +37,10 @@ pub struct FileDesc(OwnedFd); // // On Apple targets however, apparently the 64-bit libc is either buggy or // intentionally showing odd behavior by rejecting any read with a size -// larger than or equal to INT_MAX. To handle both of these the read -// size is capped on both platforms. +// larger than INT_MAX. To handle both of these the read size is capped on +// both platforms. const READ_LIMIT: usize = if cfg!(target_vendor = "apple") { - libc::c_int::MAX as usize - 1 + libc::c_int::MAX as usize } else { libc::ssize_t::MAX as usize }; From bd33a02ea6c01a7053451b0205edb01e4172a601 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 7 Jun 2025 08:25:20 +0200 Subject: [PATCH 0062/2093] Get the block content from the proper context The first statement of the block might have been in a different context from the expression. Walk up to the right context to get bounds properly. Also, switch to `snippet_with_applicability()` since we know that we are in the right context already. --- clippy_lints/src/if_then_some_else_none.rs | 32 +++++++------- tests/ui/if_then_some_else_none.fixed | 41 +++++++++++++++++ tests/ui/if_then_some_else_none.rs | 51 ++++++++++++++++++++++ tests/ui/if_then_some_else_none.stderr | 25 ++++++++++- 4 files changed, 133 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 7158f9419c1c..b50d91f10146 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -2,16 +2,16 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, - path_res, peel_blocks, + path_res, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; declare_clippy_lint! { @@ -71,21 +71,21 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind - && let ctxt = expr.span.ctxt() - && then_expr.span.ctxt() == ctxt + && !expr.span.from_expansion() + && !then_expr.span.from_expansion() && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && !is_else_clause(cx.tcx, expr) && !is_in_const_context(cx) - && !expr.span.in_external_macro(cx.sess().source_map()) && self.msrv.meets(cx, msrvs::BOOL_THEN) && !contains_return(then_block.stmts) { let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { - "then_some" + sym::then_some } else { - "then" + sym::then }; + let ctxt = expr.span.ctxt(); span_lint_and_then( cx, @@ -98,16 +98,18 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { } let mut app = Applicability::MachineApplicable; - let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) + let cond_snip = Sugg::hir_with_context(cx, cond, ctxt, "[condition]", &mut app) .maybe_paren() .to_string(); let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; - let method_body = if let Some(first_stmt) = then_block.stmts.first() { - let (block_snippet, _) = - snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app); - let closure = if method_name == "then" { "|| " } else { "" }; - format!("{closure} {{ {block_snippet}; {arg_snip} }}") - } else if method_name == "then" { + let method_body = if let Some(first_stmt) = then_block.stmts.first() + && let Some(first_stmt_span) = walk_span_to_context(first_stmt.span, ctxt) + { + let block_snippet = + snippet_with_applicability(cx, first_stmt_span.until(then_expr.span), "..", &mut app); + let closure = if method_name == sym::then { "|| " } else { "" }; + format!("{closure} {{ {} {arg_snip} }}", block_snippet.trim_end()) + } else if method_name == sym::then { (std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned() } else { arg_snip.into_owned() diff --git a/tests/ui/if_then_some_else_none.fixed b/tests/ui/if_then_some_else_none.fixed index d14a805b6667..0fd130609aee 100644 --- a/tests/ui/if_then_some_else_none.fixed +++ b/tests/ui/if_then_some_else_none.fixed @@ -165,3 +165,44 @@ mod issue15257 { do_something((i % 2 == 0).then_some(closure_fn)); } } + +fn issue15005() { + struct Counter { + count: u32, + } + + impl Counter { + fn new() -> Counter { + Counter { count: 0 } + } + } + + impl Iterator for Counter { + type Item = u32; + + fn next(&mut self) -> Option { + //~v if_then_some_else_none + (self.count < 5).then(|| { self.count += 1; self.count }) + } + } +} + +fn statements_from_macro() { + macro_rules! mac { + () => { + println!("foo"); + println!("bar"); + }; + } + //~v if_then_some_else_none + let _ = true.then(|| { mac!(); 42 }); +} + +fn dont_lint_inside_macros() { + macro_rules! mac { + ($cond:expr, $res:expr) => { + if $cond { Some($res) } else { None } + }; + } + let _: Option = mac!(true, 42); +} diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index bb0072f31573..640828aa9bf6 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -211,3 +211,54 @@ mod issue15257 { }); } } + +fn issue15005() { + struct Counter { + count: u32, + } + + impl Counter { + fn new() -> Counter { + Counter { count: 0 } + } + } + + impl Iterator for Counter { + type Item = u32; + + fn next(&mut self) -> Option { + //~v if_then_some_else_none + if self.count < 5 { + self.count += 1; + Some(self.count) + } else { + None + } + } + } +} + +fn statements_from_macro() { + macro_rules! mac { + () => { + println!("foo"); + println!("bar"); + }; + } + //~v if_then_some_else_none + let _ = if true { + mac!(); + Some(42) + } else { + None + }; +} + +fn dont_lint_inside_macros() { + macro_rules! mac { + ($cond:expr, $res:expr) => { + if $cond { Some($res) } else { None } + }; + } + let _: Option = mac!(true, 42); +} diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index c2e624a0a73b..58651a055942 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -116,5 +116,28 @@ LL | | None LL | | }); | |_________^ help: try: `(i % 2 == 0).then_some(closure_fn)` -error: aborting due to 11 previous errors +error: this could be simplified with `bool::then` + --> tests/ui/if_then_some_else_none.rs:231:13 + | +LL | / if self.count < 5 { +LL | | self.count += 1; +LL | | Some(self.count) +LL | | } else { +LL | | None +LL | | } + | |_____________^ help: try: `(self.count < 5).then(|| { self.count += 1; self.count })` + +error: this could be simplified with `bool::then` + --> tests/ui/if_then_some_else_none.rs:249:13 + | +LL | let _ = if true { + | _____________^ +LL | | mac!(); +LL | | Some(42) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try: `true.then(|| { mac!(); 42 })` + +error: aborting due to 13 previous errors From 7ed763b2f9e7fd3cba54ab935906afce8b1b7404 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 26 Jul 2025 19:36:01 +0200 Subject: [PATCH 0063/2093] Do not replace `match` by `if` if any arm contains a binding --- clippy_lints/src/matches/match_bool.rs | 6 +++++- tests/ui/match_bool.fixed | 13 +++++++++++++ tests/ui/match_bool.rs | 13 +++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/match_bool.rs b/clippy_lints/src/matches/match_bool.rs index b90cf6357c5c..a2c8741f4f74 100644 --- a/clippy_lints/src/matches/match_bool.rs +++ b/clippy_lints/src/matches/match_bool.rs @@ -12,7 +12,11 @@ use super::MATCH_BOOL; pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { // Type of expression is `bool`. - if *cx.typeck_results().expr_ty(scrutinee).kind() == ty::Bool { + if *cx.typeck_results().expr_ty(scrutinee).kind() == ty::Bool + && arms + .iter() + .all(|arm| arm.pat.walk_short(|p| !matches!(p.kind, PatKind::Binding(..)))) + { span_lint_and_then( cx, MATCH_BOOL, diff --git a/tests/ui/match_bool.fixed b/tests/ui/match_bool.fixed index 1dfb82db1206..876ae935afde 100644 --- a/tests/ui/match_bool.fixed +++ b/tests/ui/match_bool.fixed @@ -61,4 +61,17 @@ fn issue14099() { } } } +fn issue15351() { + let mut d = false; + match d { + false => println!("foo"), + ref mut d => *d = false, + } + + match d { + false => println!("foo"), + e => println!("{e}"), + } +} + fn main() {} diff --git a/tests/ui/match_bool.rs b/tests/ui/match_bool.rs index 719b4e51eb6d..a134ad8346e2 100644 --- a/tests/ui/match_bool.rs +++ b/tests/ui/match_bool.rs @@ -113,4 +113,17 @@ fn issue14099() { } } +fn issue15351() { + let mut d = false; + match d { + false => println!("foo"), + ref mut d => *d = false, + } + + match d { + false => println!("foo"), + e => println!("{e}"), + } +} + fn main() {} From 7ead5764a88d91803db898e679dd49f2520cdae6 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 26 Jul 2025 13:22:24 -0700 Subject: [PATCH 0064/2093] Implement `ptr_cast_array` --- library/core/src/ptr/const_ptr.rs | 9 +++++++++ library/core/src/ptr/mut_ptr.rs | 9 +++++++++ library/core/src/ptr/non_null.rs | 7 +++++++ 3 files changed, 25 insertions(+) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 2ad520b7ead7..90d08f60a045 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1547,6 +1547,15 @@ impl *const [T] { } } +impl *const T { + /// Casts from a pointer-to-`T` to a pointer-to-`[T; N]`. + #[inline] + #[unstable(feature = "ptr_cast_array", issue = "144514")] + pub const fn cast_array(self) -> *const [T; N] { + self.cast() + } +} + impl *const [T; N] { /// Returns a raw pointer to the array's buffer. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 579e2461103d..dec212d04c96 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1965,6 +1965,15 @@ impl *mut [T] { } } +impl *mut T { + /// Casts from a pointer-to-`T` to a pointer-to-`[T; N]`. + #[inline] + #[unstable(feature = "ptr_cast_array", issue = "144514")] + pub const fn cast_array(self) -> *mut [T; N] { + self.cast() + } +} + impl *mut [T; N] { /// Returns a raw pointer to the array's buffer. /// diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 62da6567cca7..245a0a08321c 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -193,6 +193,13 @@ impl NonNull { // requirements for a reference. unsafe { &mut *self.cast().as_ptr() } } + + /// Casts from a pointer-to-`T` to a pointer-to-`[T; N]`. + #[inline] + #[unstable(feature = "ptr_cast_array", issue = "144514")] + pub const fn cast_array(self) -> NonNull<[T; N]> { + self.cast() + } } impl NonNull { From 51b0416c19f436393b3c070cc037a8f062bcbd83 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 26 Jul 2025 13:26:53 -0700 Subject: [PATCH 0065/2093] Use `cast_array` in core --- library/core/src/slice/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 6fe5affc48be..13e182b5f87a 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -330,7 +330,7 @@ impl [T] { } else { // SAFETY: We explicitly check for the correct number of elements, // and do not let the reference outlive the slice. - Some(unsafe { &*(self.as_ptr().cast::<[T; N]>()) }) + Some(unsafe { &*(self.as_ptr().cast_array()) }) } } @@ -361,7 +361,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // do not let the reference outlive the slice, // and require exclusive access to the entire slice to mutate the chunk. - Some(unsafe { &mut *(self.as_mut_ptr().cast::<[T; N]>()) }) + Some(unsafe { &mut *(self.as_mut_ptr().cast_array()) }) } } @@ -389,7 +389,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // and do not let the references outlive the slice. - Some((unsafe { &*(first.as_ptr().cast::<[T; N]>()) }, tail)) + Some((unsafe { &*(first.as_ptr().cast_array()) }, tail)) } /// Returns a mutable array reference to the first `N` items in the slice and the remaining @@ -422,7 +422,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // do not let the reference outlive the slice, // and enforce exclusive mutability of the chunk by the split. - Some((unsafe { &mut *(first.as_mut_ptr().cast::<[T; N]>()) }, tail)) + Some((unsafe { &mut *(first.as_mut_ptr().cast_array()) }, tail)) } /// Returns an array reference to the last `N` items in the slice and the remaining slice. @@ -450,7 +450,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // and do not let the references outlive the slice. - Some((init, unsafe { &*(last.as_ptr().cast::<[T; N]>()) })) + Some((init, unsafe { &*(last.as_ptr().cast_array()) })) } /// Returns a mutable array reference to the last `N` items in the slice and the remaining @@ -484,7 +484,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // do not let the reference outlive the slice, // and enforce exclusive mutability of the chunk by the split. - Some((init, unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) })) + Some((init, unsafe { &mut *(last.as_mut_ptr().cast_array()) })) } /// Returns an array reference to the last `N` items in the slice. @@ -513,7 +513,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // and do not let the references outlive the slice. - Some(unsafe { &*(last.as_ptr().cast::<[T; N]>()) }) + Some(unsafe { &*(last.as_ptr().cast_array()) }) } /// Returns a mutable array reference to the last `N` items in the slice. @@ -544,7 +544,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // do not let the reference outlive the slice, // and require exclusive access to the entire slice to mutate the chunk. - Some(unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) }) + Some(unsafe { &mut *(last.as_mut_ptr().cast_array()) }) } /// Returns a reference to an element or subslice depending on the type of @@ -848,7 +848,7 @@ impl [T] { #[must_use] pub const fn as_array(&self) -> Option<&[T; N]> { if self.len() == N { - let ptr = self.as_ptr() as *const [T; N]; + let ptr = self.as_ptr().cast_array(); // SAFETY: The underlying array of a slice can be reinterpreted as an actual array `[T; N]` if `N` is not greater than the slice's length. let me = unsafe { &*ptr }; @@ -866,7 +866,7 @@ impl [T] { #[must_use] pub const fn as_mut_array(&mut self) -> Option<&mut [T; N]> { if self.len() == N { - let ptr = self.as_mut_ptr() as *mut [T; N]; + let ptr = self.as_mut_ptr().cast_array(); // SAFETY: The underlying array of a slice can be reinterpreted as an actual array `[T; N]` if `N` is not greater than the slice's length. let me = unsafe { &mut *ptr }; From ca58013caab2ce7855468177df579bb99ac49fdb Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Tue, 29 Jul 2025 09:48:59 +0930 Subject: [PATCH 0066/2093] Enable features that are always available in a live system. While some neon and crypto features may not be supported on the switch at boot (e.g. on the a53 cores), the features will _always_ be available if running as a sysmodule or homebrew application under Horizon/Atmosphere. --- .../src/spec/targets/aarch64_nintendo_switch_freestanding.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs index 9b81362b27db..8d5390f57718 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: "aarch64".into(), options: TargetOptions { - features: "+v8a".into(), + features: "+v8a,+neon,+crypto".into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), link_script: Some(LINKER_SCRIPT.into()), From 05cd6e854b09ce803b72443f94f3e58e71c24db6 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Tue, 29 Jul 2025 11:01:22 +0930 Subject: [PATCH 0067/2093] Add the CRC instructions. --- .../src/spec/targets/aarch64_nintendo_switch_freestanding.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs index 8d5390f57718..61e4cad3fa20 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: "aarch64".into(), options: TargetOptions { - features: "+v8a,+neon,+crypto".into(), + features: "+v8a,+neon,+crypto,+crc".into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), link_script: Some(LINKER_SCRIPT.into()), From c72bb70ffd284538519242663fa9443037f4d41e Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Tue, 29 Jul 2025 08:39:28 +0200 Subject: [PATCH 0068/2093] clearer wording for `unsafe` code --- library/core/src/clone.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index c4778edf0fcc..6fcba298a432 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -160,8 +160,8 @@ mod uninit; /// /// Violating this property is a logic error. The behavior resulting from a logic error is not /// specified, but users of the trait must ensure that such logic errors do *not* result in -/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these -/// methods. +/// undefined behavior. This means that `unsafe` code **must not** rely on this property +/// being satisfied. /// /// ## Additional implementors /// From 0ed3ef4ea48c7587a164572a7bda3290beb3a81f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 29 Jul 2025 10:16:28 +0200 Subject: [PATCH 0069/2093] Use GH app for authenticating pull PRs --- src/tools/miri/.github/workflows/ci.yml | 7 ++++++- src/tools/miri/triagebot.toml | 3 --- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index c47f9695624b..668caef8f7f5 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -169,6 +169,11 @@ jobs: run: rustup toolchain install nightly --profile minimal - name: Install rustup-toolchain-install-master run: cargo install -f rustup-toolchain-install-master + - uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ vars.APP_CLIENT_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} - name: Push changes to a branch and create PR run: | # Make it easier to see what happens. @@ -200,7 +205,7 @@ jobs: git push -u origin $BRANCH gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.' env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} cron-fail-notify: name: cronjob failure notification diff --git a/src/tools/miri/triagebot.toml b/src/tools/miri/triagebot.toml index a0ce9f800242..5d6b596ea54c 100644 --- a/src/tools/miri/triagebot.toml +++ b/src/tools/miri/triagebot.toml @@ -50,9 +50,6 @@ new_pr = true [autolabel."S-waiting-on-author"] new_draft = true -# Automatically close and reopen PRs made by bots to run CI on them -[bot-pull-requests] - # Canonicalize issue numbers to avoid closing the wrong issue when upstreaming this subtree [canonicalize-issue-links] From 1fd183ea920bbe20718a4c45315fe2b30bbf4b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 29 Jul 2025 12:28:46 +0200 Subject: [PATCH 0070/2093] Apply suggestions from code review Co-authored-by: Ralf Jung --- src/tools/miri/.github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 668caef8f7f5..ba496d767129 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -169,12 +169,13 @@ jobs: run: rustup toolchain install nightly --profile minimal - name: Install rustup-toolchain-install-master run: cargo install -f rustup-toolchain-install-master + # Create a token for the next step so it can create a PR that actually runs CI. - uses: actions/create-github-app-token@v2 id: app-token with: app-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} - - name: Push changes to a branch and create PR + - name: pull changes from rustc and create PR run: | # Make it easier to see what happens. set -x From ba5b6b9ec472dc32bdaa8b18c22d30bd6abf7ebc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 17 Jul 2025 20:00:19 +0200 Subject: [PATCH 0071/2093] const-eval: full support for pointer fragments --- compiler/rustc_const_eval/messages.ftl | 11 +- .../src/const_eval/eval_queries.rs | 7 + compiler/rustc_const_eval/src/errors.rs | 16 +- .../rustc_const_eval/src/interpret/intern.rs | 99 +++++++----- .../rustc_const_eval/src/interpret/memory.rs | 44 ++---- .../rustc_const_eval/src/interpret/place.rs | 8 +- .../src/interpret/validity.rs | 4 +- .../src/mir/interpret/allocation.rs | 84 ++++++----- .../interpret/allocation/provenance_map.rs | 142 +++++++++--------- .../rustc_middle/src/mir/interpret/error.rs | 3 - .../rustc_middle/src/mir/interpret/pointer.rs | 12 +- compiler/rustc_middle/src/mir/pretty.rs | 4 +- library/core/src/ptr/mod.rs | 38 +---- library/coretests/tests/ptr.rs | 12 +- src/tools/miri/src/machine.rs | 10 +- src/tools/miri/src/shims/native_lib/mod.rs | 2 +- .../miri/tests/fail/provenance/mix-ptrs1.rs | 21 +++ .../tests/fail/provenance/mix-ptrs1.stderr | 16 ++ .../miri/tests/fail/provenance/mix-ptrs2.rs | 37 +++++ .../tests/fail/provenance/mix-ptrs2.stderr | 16 ++ ...uninit_alloc_diagnostic_with_provenance.rs | 11 +- ...it_alloc_diagnostic_with_provenance.stderr | 2 +- .../fail/tracing/partial_init.stderr | 2 +- src/tools/miri/tests/pass/provenance.rs | 35 ----- src/tools/miri/tests/pass/transmute_ptr.rs | 21 +-- .../heap/ptr_not_made_global.stderr | 4 +- .../heap/ptr_not_made_global_mut.stderr | 2 +- .../const-eval/partial_ptr_overwrite.rs | 12 -- .../const-eval/partial_ptr_overwrite.stderr | 12 -- tests/ui/consts/const-eval/ptr_fragments.rs | 63 ++++++++ .../const-eval/ptr_fragments_in_final.rs | 25 +++ .../const-eval/ptr_fragments_in_final.stderr | 10 ++ .../ui/consts/const-eval/read_partial_ptr.rs | 49 ++++++ .../consts/const-eval/read_partial_ptr.stderr | 39 +++++ tests/ui/consts/missing_span_in_backtrace.rs | 13 +- .../consts/missing_span_in_backtrace.stderr | 16 +- 36 files changed, 543 insertions(+), 359 deletions(-) create mode 100644 src/tools/miri/tests/fail/provenance/mix-ptrs1.rs create mode 100644 src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr create mode 100644 src/tools/miri/tests/fail/provenance/mix-ptrs2.rs create mode 100644 src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr delete mode 100644 tests/ui/consts/const-eval/partial_ptr_overwrite.rs delete mode 100644 tests/ui/consts/const-eval/partial_ptr_overwrite.stderr create mode 100644 tests/ui/consts/const-eval/ptr_fragments.rs create mode 100644 tests/ui/consts/const-eval/ptr_fragments_in_final.rs create mode 100644 tests/ui/consts/const-eval/ptr_fragments_in_final.stderr create mode 100644 tests/ui/consts/const-eval/read_partial_ptr.rs create mode 100644 tests/ui/consts/const-eval/read_partial_ptr.stderr diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index aa0bc42d4485..10cdc372ddd4 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -57,7 +57,7 @@ const_eval_const_context = {$kind -> } const_eval_const_heap_ptr_in_final = encountered `const_allocate` pointer in final value that was not made global - .note = use `const_make_global` to make allocated pointers immutable before returning + .note = use `const_make_global` to turn allocated pointers into immutable globals before returning const_eval_const_make_global_ptr_already_made_global = attempting to call `const_make_global` twice on the same allocation {$alloc} @@ -231,6 +231,9 @@ const_eval_mutable_borrow_escaping = const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind} +const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind} + .note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value + const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead const_eval_non_const_await = @@ -299,10 +302,8 @@ const_eval_panic = evaluation panicked: {$msg} const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str` -const_eval_partial_pointer_copy = - unable to copy parts of a pointer from memory at {$ptr} -const_eval_partial_pointer_overwrite = - unable to overwrite parts of a pointer in memory at {$ptr} +const_eval_partial_pointer_read = + unable to read parts of a pointer from memory at {$ptr} const_eval_pointer_arithmetic_overflow = overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize` diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 5835660e1c38..4da2663319c3 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -117,6 +117,13 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( ecx.tcx.dcx().emit_err(errors::ConstHeapPtrInFinal { span: ecx.tcx.span }), ))); } + Err(InternError::PartialPointer) => { + throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error( + ecx.tcx + .dcx() + .emit_err(errors::PartialPtrInFinal { span: ecx.tcx.span, kind: intern_kind }), + ))); + } } interp_ok(R::make_result(ret, ecx)) diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index b6a64035261e..322fd8e9363a 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -51,6 +51,15 @@ pub(crate) struct ConstHeapPtrInFinal { pub span: Span, } +#[derive(Diagnostic)] +#[diag(const_eval_partial_pointer_in_final)] +#[note] +pub(crate) struct PartialPtrInFinal { + #[primary_span] + pub span: Span, + pub kind: InternKind, +} + #[derive(Diagnostic)] #[diag(const_eval_unstable_in_stable_exposed)] pub(crate) struct UnstableInStableExposed { @@ -832,8 +841,7 @@ impl ReportErrorExt for UnsupportedOpInfo { UnsupportedOpInfo::Unsupported(s) => s.clone().into(), UnsupportedOpInfo::ExternTypeField => const_eval_extern_type_field, UnsupportedOpInfo::UnsizedLocal => const_eval_unsized_local, - UnsupportedOpInfo::OverwritePartialPointer(_) => const_eval_partial_pointer_overwrite, - UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy, + UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_read, UnsupportedOpInfo::ReadPointerAsInt(_) => const_eval_read_pointer_as_int, UnsupportedOpInfo::ThreadLocalStatic(_) => const_eval_thread_local_static, UnsupportedOpInfo::ExternStatic(_) => const_eval_extern_static, @@ -844,7 +852,7 @@ impl ReportErrorExt for UnsupportedOpInfo { use UnsupportedOpInfo::*; use crate::fluent_generated::*; - if let ReadPointerAsInt(_) | OverwritePartialPointer(_) | ReadPartialPointer(_) = self { + if let ReadPointerAsInt(_) | ReadPartialPointer(_) = self { diag.help(const_eval_ptr_as_bytes_1); diag.help(const_eval_ptr_as_bytes_2); } @@ -856,7 +864,7 @@ impl ReportErrorExt for UnsupportedOpInfo { | UnsupportedOpInfo::ExternTypeField | Unsupported(_) | ReadPointerAsInt(_) => {} - OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => { + ReadPartialPointer(ptr) => { diag.arg("ptr", ptr); } ThreadLocalStatic(did) | ExternStatic(did) => rustc_middle::ty::tls::with(|tcx| { diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index bb59b9f54186..5d510a983526 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -19,9 +19,12 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir as hir; use rustc_hir::definitions::{DefPathData, DisambiguatorState}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; -use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult}; +use rustc_middle::mir::interpret::{ + AllocBytes, ConstAllocation, CtfeProvenance, InterpResult, Provenance, +}; use rustc_middle::query::TyCtxtAt; use rustc_middle::span_bug; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::layout::TyAndLayout; use rustc_span::def_id::LocalDefId; use tracing::{instrument, trace}; @@ -52,6 +55,45 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> { } } +fn prepare_alloc<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( + tcx: TyCtxt<'tcx>, + kind: MemoryKind, + alloc: &mut Allocation, + mutability: Mutability, +) -> Result<(), InternError> { + match kind { + MemoryKind::Machine(const_eval::MemoryKind::Heap { was_made_global }) => { + if !was_made_global { + // Attempting to intern a `const_allocate`d pointer that was not made global via + // `const_make_global`. + tcx.dcx().delayed_bug("non-global heap allocation in const value"); + return Err(InternError::ConstAllocNotGlobal); + } + } + MemoryKind::Stack | MemoryKind::CallerLocation => {} + } + + if !alloc.provenance_merge_bytes(&tcx) { + // Per-byte provenance is not supported by backends, so we cannot accept it here. + tcx.dcx().delayed_bug("partial pointer in const value"); + return Err(InternError::PartialPointer); + } + + // Set allocation mutability as appropriate. This is used by LLVM to put things into + // read-only memory, and also by Miri when evaluating other globals that + // access this one. + match mutability { + Mutability::Not => { + alloc.mutability = Mutability::Not; + } + Mutability::Mut => { + // This must be already mutable, we won't "un-freeze" allocations ever. + assert_eq!(alloc.mutability, Mutability::Mut); + } + } + Ok(()) +} + /// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory. /// /// `mutability` can be used to force immutable interning: if it is `Mutability::Not`, the @@ -72,31 +114,13 @@ fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>( return Err(InternError::DanglingPointer); }; - match kind { - MemoryKind::Machine(const_eval::MemoryKind::Heap { was_made_global }) => { - if !was_made_global { - // Attempting to intern a `const_allocate`d pointer that was not made global via - // `const_make_global`. We want to error here, but we have to first put the - // allocation back into the `alloc_map` to keep things in a consistent state. - ecx.memory.alloc_map.insert(alloc_id, (kind, alloc)); - return Err(InternError::ConstAllocNotGlobal); - } - } - MemoryKind::Stack | MemoryKind::CallerLocation => {} + if let Err(err) = prepare_alloc(*ecx.tcx, kind, &mut alloc, mutability) { + // We want to error here, but we have to first put the + // allocation back into the `alloc_map` to keep things in a consistent state. + ecx.memory.alloc_map.insert(alloc_id, (kind, alloc)); + return Err(err); } - // Set allocation mutability as appropriate. This is used by LLVM to put things into - // read-only memory, and also by Miri when evaluating other globals that - // access this one. - match mutability { - Mutability::Not => { - alloc.mutability = Mutability::Not; - } - Mutability::Mut => { - // This must be already mutable, we won't "un-freeze" allocations ever. - assert_eq!(alloc.mutability, Mutability::Mut); - } - } // link the alloc id to the actual allocation let alloc = ecx.tcx.mk_const_alloc(alloc); if let Some(static_id) = ecx.machine.static_def_id() { @@ -166,6 +190,7 @@ pub enum InternError { BadMutablePointer, DanglingPointer, ConstAllocNotGlobal, + PartialPointer, } /// Intern `ret` and everything it references. @@ -221,13 +246,11 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( let mut todo: Vec<_> = if is_static { // Do not steal the root allocation, we need it later to create the return value of `eval_static_initializer`. // But still change its mutability to match the requested one. - let alloc = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap(); - alloc.1.mutability = base_mutability; - alloc.1.provenance().ptrs().iter().map(|&(_, prov)| prov).collect() + let (kind, alloc) = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap(); + prepare_alloc(*ecx.tcx, *kind, alloc, base_mutability)?; + alloc.provenance().ptrs().iter().map(|&(_, prov)| prov).collect() } else { - intern_shallow(ecx, base_alloc_id, base_mutability, Some(&mut disambiguator)) - .unwrap() - .collect() + intern_shallow(ecx, base_alloc_id, base_mutability, Some(&mut disambiguator))?.collect() }; // We need to distinguish "has just been interned" from "was already in `tcx`", // so we track this in a separate set. @@ -235,7 +258,6 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( // Whether we encountered a bad mutable pointer. // We want to first report "dangling" and then "mutable", so we need to delay reporting these // errors. - let mut result = Ok(()); let mut found_bad_mutable_ptr = false; // Keep interning as long as there are things to intern. @@ -310,20 +332,15 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( // okay with losing some potential for immutability here. This can anyway only affect // `static mut`. just_interned.insert(alloc_id); - match intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator)) { - Ok(nested) => todo.extend(nested), - Err(err) => { - ecx.tcx.dcx().delayed_bug("error during const interning"); - result = Err(err); - } - } + let next = intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator))?; + todo.extend(next); } - if found_bad_mutable_ptr && result.is_ok() { + if found_bad_mutable_ptr { // We found a mutable pointer inside a const where inner allocations should be immutable, // and there was no other error. This should usually never happen! However, this can happen // in unleash-miri mode, so report it as a normal error then. if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you { - result = Err(InternError::BadMutablePointer); + return Err(InternError::BadMutablePointer); } else { span_bug!( ecx.tcx.span, @@ -331,7 +348,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( ); } } - result + Ok(()) } /// Intern `ret`. This function assumes that `ret` references no other allocation. diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 47bebf5371a9..b5703e789ff4 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1310,29 +1310,20 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> } /// Mark the given sub-range (relative to this allocation reference) as uninitialized. - pub fn write_uninit(&mut self, range: AllocRange) -> InterpResult<'tcx> { + pub fn write_uninit(&mut self, range: AllocRange) { let range = self.range.subrange(range); - self.alloc - .write_uninit(&self.tcx, range) - .map_err(|e| e.to_interp_error(self.alloc_id)) - .into() + self.alloc.write_uninit(&self.tcx, range); } /// Mark the entire referenced range as uninitialized - pub fn write_uninit_full(&mut self) -> InterpResult<'tcx> { - self.alloc - .write_uninit(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id)) - .into() + pub fn write_uninit_full(&mut self) { + self.alloc.write_uninit(&self.tcx, self.range); } /// Remove all provenance in the reference range. - pub fn clear_provenance(&mut self) -> InterpResult<'tcx> { - self.alloc - .clear_provenance(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id)) - .into() + pub fn clear_provenance(&mut self) { + self.alloc.clear_provenance(&self.tcx, self.range); } } @@ -1423,11 +1414,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Side-step AllocRef and directly access the underlying bytes more efficiently. // (We are staying inside the bounds here and all bytes do get overwritten so all is good.) - let alloc_id = alloc_ref.alloc_id; - let bytes = alloc_ref - .alloc - .get_bytes_unchecked_for_overwrite(&alloc_ref.tcx, alloc_ref.range) - .map_err(move |e| e.to_interp_error(alloc_id))?; + let bytes = + alloc_ref.alloc.get_bytes_unchecked_for_overwrite(&alloc_ref.tcx, alloc_ref.range); // `zip` would stop when the first iterator ends; we want to definitely // cover all of `bytes`. for dest in bytes { @@ -1509,10 +1497,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `get_bytes_mut` will clear the provenance, which is correct, // since we don't want to keep any provenance at the target. // This will also error if copying partial provenance is not supported. - let provenance = src_alloc - .provenance() - .prepare_copy(src_range, dest_offset, num_copies, self) - .map_err(|e| e.to_interp_error(src_alloc_id))?; + let provenance = + src_alloc.provenance().prepare_copy(src_range, dest_offset, num_copies, self); // Prepare a copy of the initialization mask. let init = src_alloc.init_mask().prepare_copy(src_range); @@ -1530,10 +1516,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { dest_range, )?; // Yes we do overwrite all bytes in `dest_bytes`. - let dest_bytes = dest_alloc - .get_bytes_unchecked_for_overwrite_ptr(&tcx, dest_range) - .map_err(|e| e.to_interp_error(dest_alloc_id))? - .as_mut_ptr(); + let dest_bytes = + dest_alloc.get_bytes_unchecked_for_overwrite_ptr(&tcx, dest_range).as_mut_ptr(); if init.no_bytes_init() { // Fast path: If all bytes are `uninit` then there is nothing to copy. The target range @@ -1542,9 +1526,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // This also avoids writing to the target bytes so that the backing allocation is never // touched if the bytes stay uninitialized for the whole interpreter execution. On contemporary // operating system this can avoid physically allocating the page. - dest_alloc - .write_uninit(&tcx, dest_range) - .map_err(|e| e.to_interp_error(dest_alloc_id))?; + dest_alloc.write_uninit(&tcx, dest_range); // `write_uninit` also resets the provenance, so we are done. return interp_ok(()); } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index e2284729efdc..afd96a7c0c9e 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -700,7 +700,7 @@ where match value { Immediate::Scalar(scalar) => { - alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar) + alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar)?; } Immediate::ScalarPair(a_val, b_val) => { let BackendRepr::ScalarPair(a, b) = layout.backend_repr else { @@ -720,10 +720,10 @@ where alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?; alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?; // We don't have to reset padding here, `write_immediate` will anyway do a validation run. - interp_ok(()) } Immediate::Uninit => alloc.write_uninit_full(), } + interp_ok(()) } pub fn write_uninit( @@ -743,7 +743,7 @@ where // Zero-sized access return interp_ok(()); }; - alloc.write_uninit_full()?; + alloc.write_uninit_full(); } } interp_ok(()) @@ -767,7 +767,7 @@ where // Zero-sized access return interp_ok(()); }; - alloc.clear_provenance()?; + alloc.clear_provenance(); } } interp_ok(()) diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 693b37829600..f8d9ebcafa76 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -949,7 +949,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { let padding_size = offset - padding_cleared_until; let range = alloc_range(padding_start, padding_size); trace!("reset_padding on {}: resetting padding range {range:?}", mplace.layout.ty); - alloc.write_uninit(range)?; + alloc.write_uninit(range); } padding_cleared_until = offset + size; } @@ -1239,7 +1239,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, if self.reset_provenance_and_padding { // We can't share this with above as above, we might be looking at read-only memory. let mut alloc = self.ecx.get_ptr_alloc_mut(mplace.ptr(), size)?.expect("we already excluded size 0"); - alloc.clear_provenance()?; + alloc.clear_provenance(); // Also, mark this as containing data, not padding. self.add_data_range(mplace.ptr(), size); } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 27ead5145319..2ea92a39d482 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -306,8 +306,6 @@ pub enum AllocError { ScalarSizeMismatch(ScalarSizeMismatch), /// Encountered a pointer where we needed raw bytes. ReadPointerAsInt(Option), - /// Partially overwriting a pointer. - OverwritePartialPointer(Size), /// Partially copying a pointer. ReadPartialPointer(Size), /// Using uninitialized data where it is not allowed. @@ -331,9 +329,6 @@ impl AllocError { ReadPointerAsInt(info) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))), ), - OverwritePartialPointer(offset) => InterpErrorKind::Unsupported( - UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)), - ), ReadPartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)), ), @@ -633,11 +628,11 @@ impl Allocation &mut self, cx: &impl HasDataLayout, range: AllocRange, - ) -> AllocResult<&mut [u8]> { + ) -> &mut [u8] { self.mark_init(range, true); - self.provenance.clear(range, cx)?; + self.provenance.clear(range, cx); - Ok(&mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()]) + &mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()] } /// A raw pointer variant of `get_bytes_unchecked_for_overwrite` that avoids invalidating existing immutable aliases @@ -646,15 +641,15 @@ impl Allocation &mut self, cx: &impl HasDataLayout, range: AllocRange, - ) -> AllocResult<*mut [u8]> { + ) -> *mut [u8] { self.mark_init(range, true); - self.provenance.clear(range, cx)?; + self.provenance.clear(range, cx); assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check // Crucially, we go via `AllocBytes::as_mut_ptr`, not `AllocBytes::deref_mut`. let begin_ptr = self.bytes.as_mut_ptr().wrapping_add(range.start.bytes_usize()); let len = range.end().bytes_usize() - range.start.bytes_usize(); - Ok(ptr::slice_from_raw_parts_mut(begin_ptr, len)) + ptr::slice_from_raw_parts_mut(begin_ptr, len) } /// This gives direct mutable access to the entire buffer, just exposing their internal state @@ -723,26 +718,45 @@ impl Allocation let ptr = Pointer::new(prov, Size::from_bytes(bits)); return Ok(Scalar::from_pointer(ptr, cx)); } - - // If we can work on pointers byte-wise, join the byte-wise provenances. - if Prov::OFFSET_IS_ADDR { - let mut prov = self.provenance.get(range.start, cx); + // The other easy case is total absence of provenance. + if self.provenance.range_empty(range, cx) { + return Ok(Scalar::from_uint(bits, range.size)); + } + // If we get here, we have to check per-byte provenance, and join them together. + let prov = 'prov: { + // Initialize with first fragment. Must have index 0. + let Some((mut joint_prov, 0)) = self.provenance.get_byte(range.start, cx) else { + break 'prov None; + }; + // Update with the remaining fragments. for offset in Size::from_bytes(1)..range.size { - let this_prov = self.provenance.get(range.start + offset, cx); - prov = Prov::join(prov, this_prov); + // Ensure there is provenance here and it has the right index. + let Some((frag_prov, frag_idx)) = + self.provenance.get_byte(range.start + offset, cx) + else { + break 'prov None; + }; + // Wildcard provenance is allowed to come with any index (this is needed + // for Miri's native-lib mode to work). + if u64::from(frag_idx) != offset.bytes() && Some(frag_prov) != Prov::WILDCARD { + break 'prov None; + } + // Merge this byte's provenance with the previous ones. + joint_prov = match Prov::join(joint_prov, frag_prov) { + Some(prov) => prov, + None => break 'prov None, + }; } - // Now use this provenance. - let ptr = Pointer::new(prov, Size::from_bytes(bits)); - return Ok(Scalar::from_maybe_pointer(ptr, cx)); - } else { - // Without OFFSET_IS_ADDR, the only remaining case we can handle is total absence of - // provenance. - if self.provenance.range_empty(range, cx) { - return Ok(Scalar::from_uint(bits, range.size)); - } - // Else we have mixed provenance, that doesn't work. + break 'prov Some(joint_prov); + }; + if prov.is_none() && !Prov::OFFSET_IS_ADDR { + // There are some bytes with provenance here but overall the provenance does not add up. + // We need `OFFSET_IS_ADDR` to fall back to no-provenance here; without that option, we must error. return Err(AllocError::ReadPartialPointer(range.start)); } + // We can use this provenance. + let ptr = Pointer::new(prov, Size::from_bytes(bits)); + return Ok(Scalar::from_maybe_pointer(ptr, cx)); } else { // We are *not* reading a pointer. // If we can just ignore provenance or there is none, that's easy. @@ -782,7 +796,7 @@ impl Allocation let endian = cx.data_layout().endian; // Yes we do overwrite all the bytes in `dst`. - let dst = self.get_bytes_unchecked_for_overwrite(cx, range)?; + let dst = self.get_bytes_unchecked_for_overwrite(cx, range); write_target_uint(endian, dst, bytes).unwrap(); // See if we have to also store some provenance. @@ -795,10 +809,9 @@ impl Allocation } /// Write "uninit" to the given memory range. - pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult { + pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) { self.mark_init(range, false); - self.provenance.clear(range, cx)?; - Ok(()) + self.provenance.clear(range, cx); } /// Mark all bytes in the given range as initialised and reset the provenance @@ -817,9 +830,12 @@ impl Allocation } /// Remove all provenance in the given memory range. - pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult { - self.provenance.clear(range, cx)?; - return Ok(()); + pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) { + self.provenance.clear(range, cx); + } + + pub fn provenance_merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool { + self.provenance.merge_bytes(cx) } /// Applies a previously prepared provenance copy. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 119d4be64e6a..ea8596ea2865 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -10,7 +10,7 @@ use rustc_macros::HashStable; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use tracing::trace; -use super::{AllocError, AllocRange, AllocResult, CtfeProvenance, Provenance, alloc_range}; +use super::{AllocRange, CtfeProvenance, Provenance, alloc_range}; /// Stores the provenance information of pointers stored in memory. #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -19,25 +19,25 @@ pub struct ProvenanceMap { /// `Provenance` in this map applies from the given offset for an entire pointer-size worth of /// bytes. Two entries in this map are always at least a pointer size apart. ptrs: SortedMap, - /// Provenance in this map only applies to the given single byte. - /// This map is disjoint from the previous. It will always be empty when - /// `Prov::OFFSET_IS_ADDR` is false. - bytes: Option>>, + /// This stores byte-sized provenance fragments. + /// The `u8` indicates the position of this byte inside its original pointer. + /// If the bytes are re-assembled in their original order, the pointer can be used again. + /// Wildcard provenance is allowed to have index 0 everywhere. + bytes: Option>>, } // These impls are generic over `Prov` since `CtfeProvenance` is only decodable/encodable // for some particular `D`/`S`. impl> Decodable for ProvenanceMap { fn decode(d: &mut D) -> Self { - assert!(!Prov::OFFSET_IS_ADDR); // only `CtfeProvenance` is ever serialized + // `bytes` is not in the serialized format Self { ptrs: Decodable::decode(d), bytes: None } } } impl> Encodable for ProvenanceMap { fn encode(&self, s: &mut S) { let Self { ptrs, bytes } = self; - assert!(!Prov::OFFSET_IS_ADDR); // only `CtfeProvenance` is ever serialized - debug_assert!(bytes.is_none()); // without `OFFSET_IS_ADDR`, this is always empty + assert!(bytes.is_none()); // interning refuses allocations with pointer fragments ptrs.encode(s) } } @@ -58,10 +58,10 @@ impl ProvenanceMap { /// Give access to the ptr-sized provenances (which can also be thought of as relocations, and /// indeed that is how codegen treats them). /// - /// Only exposed with `CtfeProvenance` provenance, since it panics if there is bytewise provenance. + /// Only use on interned allocations, as other allocations may have per-byte provenance! #[inline] pub fn ptrs(&self) -> &SortedMap { - debug_assert!(self.bytes.is_none()); // `CtfeProvenance::OFFSET_IS_ADDR` is false so this cannot fail + assert!(self.bytes.is_none(), "`ptrs()` called on non-interned allocation"); &self.ptrs } } @@ -88,12 +88,12 @@ impl ProvenanceMap { } /// `pm.range_ptrs_is_empty(r, cx)` == `pm.range_ptrs_get(r, cx).is_empty()`, but is faster. - pub(super) fn range_ptrs_is_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool { + fn range_ptrs_is_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool { self.ptrs.range_is_empty(Self::adjusted_range_ptrs(range, cx)) } /// Returns all byte-wise provenance in the given range. - fn range_bytes_get(&self, range: AllocRange) -> &[(Size, Prov)] { + fn range_bytes_get(&self, range: AllocRange) -> &[(Size, (Prov, u8))] { if let Some(bytes) = self.bytes.as_ref() { bytes.range(range.start..range.end()) } else { @@ -107,19 +107,47 @@ impl ProvenanceMap { } /// Get the provenance of a single byte. - pub fn get(&self, offset: Size, cx: &impl HasDataLayout) -> Option { + pub fn get_byte(&self, offset: Size, cx: &impl HasDataLayout) -> Option<(Prov, u8)> { let prov = self.range_ptrs_get(alloc_range(offset, Size::from_bytes(1)), cx); debug_assert!(prov.len() <= 1); if let Some(entry) = prov.first() { // If it overlaps with this byte, it is on this byte. debug_assert!(self.bytes.as_ref().is_none_or(|b| !b.contains_key(&offset))); - Some(entry.1) + Some((entry.1, (offset - entry.0).bytes() as u8)) } else { // Look up per-byte provenance. self.bytes.as_ref().and_then(|b| b.get(&offset).copied()) } } + /// Attempt to merge per-byte provenance back into ptr chunks, if the right fragments + /// sit next to each other. Return `false` is that is not possible due to partial pointers. + pub fn merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool { + let Some(bytes) = self.bytes.as_deref_mut() else { + return true; + }; + let ptr_size = cx.data_layout().pointer_size(); + while let Some((offset, (prov, _))) = bytes.iter().next().copied() { + // Check if this fragment starts a pointer. + let range = offset..offset + ptr_size; + let frags = bytes.range(range.clone()); + if frags.len() != ptr_size.bytes_usize() { + return false; + } + for (idx, (_offset, (frag_prov, frag_idx))) in frags.iter().copied().enumerate() { + if frag_prov != prov || frag_idx != idx as u8 { + return false; + } + } + // Looks like a pointer! Move it over to the ptr provenance map. + bytes.remove_range(range); + self.ptrs.insert(offset, prov); + } + // We managed to convert everything into whole pointers. + self.bytes = None; + true + } + /// Check if there is ptr-sized provenance at the given index. /// Does not mean anything for bytewise provenance! But can be useful as an optimization. pub fn get_ptr(&self, offset: Size) -> Option { @@ -137,7 +165,7 @@ impl ProvenanceMap { /// Yields all the provenances stored in this map. pub fn provenances(&self) -> impl Iterator { - let bytes = self.bytes.iter().flat_map(|b| b.values()); + let bytes = self.bytes.iter().flat_map(|b| b.values().map(|(p, _i)| p)); self.ptrs.values().chain(bytes).copied() } @@ -148,16 +176,12 @@ impl ProvenanceMap { /// Removes all provenance inside the given range. /// If there is provenance overlapping with the edges, might result in an error. - pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) -> AllocResult { + pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) { let start = range.start; let end = range.end(); // Clear the bytewise part -- this is easy. - if Prov::OFFSET_IS_ADDR { - if let Some(bytes) = self.bytes.as_mut() { - bytes.remove_range(start..end); - } - } else { - debug_assert!(self.bytes.is_none()); + if let Some(bytes) = self.bytes.as_mut() { + bytes.remove_range(start..end); } let pointer_size = cx.data_layout().pointer_size(); @@ -168,7 +192,7 @@ impl ProvenanceMap { // Find all provenance overlapping the given range. if self.range_ptrs_is_empty(range, cx) { // No provenance in this range, we are done. This is the common case. - return Ok(()); + return; } // This redoes some of the work of `range_get_ptrs_is_empty`, but this path is much @@ -179,28 +203,20 @@ impl ProvenanceMap { // We need to handle clearing the provenance from parts of a pointer. if first < start { - if !Prov::OFFSET_IS_ADDR { - // We can't split up the provenance into less than a pointer. - return Err(AllocError::OverwritePartialPointer(first)); - } // Insert the remaining part in the bytewise provenance. let prov = self.ptrs[&first]; let bytes = self.bytes.get_or_insert_with(Box::default); for offset in first..start { - bytes.insert(offset, prov); + bytes.insert(offset, (prov, (offset - first).bytes() as u8)); } } if last > end { let begin_of_last = last - pointer_size; - if !Prov::OFFSET_IS_ADDR { - // We can't split up the provenance into less than a pointer. - return Err(AllocError::OverwritePartialPointer(begin_of_last)); - } // Insert the remaining part in the bytewise provenance. let prov = self.ptrs[&begin_of_last]; let bytes = self.bytes.get_or_insert_with(Box::default); for offset in end..last { - bytes.insert(offset, prov); + bytes.insert(offset, (prov, (offset - begin_of_last).bytes() as u8)); } } @@ -208,8 +224,6 @@ impl ProvenanceMap { // Since provenance do not overlap, we know that removing until `last` (exclusive) is fine, // i.e., this will not remove any other provenance just after the ones we care about. self.ptrs.remove_range(first..last); - - Ok(()) } /// Overwrites all provenance in the given range with wildcard provenance. @@ -218,10 +232,6 @@ impl ProvenanceMap { /// /// Provided for usage in Miri and panics otherwise. pub fn write_wildcards(&mut self, cx: &impl HasDataLayout, range: AllocRange) { - assert!( - Prov::OFFSET_IS_ADDR, - "writing wildcard provenance is not supported when `OFFSET_IS_ADDR` is false" - ); let wildcard = Prov::WILDCARD.unwrap(); let bytes = self.bytes.get_or_insert_with(Box::default); @@ -229,21 +239,22 @@ impl ProvenanceMap { // Remove pointer provenances that overlap with the range, then readd the edge ones bytewise. let ptr_range = Self::adjusted_range_ptrs(range, cx); let ptrs = self.ptrs.range(ptr_range.clone()); - if let Some((offset, prov)) = ptrs.first() { - for byte_ofs in *offset..range.start { - bytes.insert(byte_ofs, *prov); + if let Some((offset, prov)) = ptrs.first().copied() { + for byte_ofs in offset..range.start { + bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8)); } } - if let Some((offset, prov)) = ptrs.last() { - for byte_ofs in range.end()..*offset + cx.data_layout().pointer_size() { - bytes.insert(byte_ofs, *prov); + if let Some((offset, prov)) = ptrs.last().copied() { + for byte_ofs in range.end()..offset + cx.data_layout().pointer_size() { + bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8)); } } self.ptrs.remove_range(ptr_range); // Overwrite bytewise provenance. for offset in range.start..range.end() { - bytes.insert(offset, wildcard); + // The fragment index does not matter for wildcard provenance. + bytes.insert(offset, (wildcard, 0)); } } } @@ -253,7 +264,7 @@ impl ProvenanceMap { /// Offsets are already adjusted to the destination allocation. pub struct ProvenanceCopy { dest_ptrs: Option>, - dest_bytes: Option>, + dest_bytes: Option>, } impl ProvenanceMap { @@ -263,7 +274,7 @@ impl ProvenanceMap { dest: Size, count: u64, cx: &impl HasDataLayout, - ) -> AllocResult> { + ) -> ProvenanceCopy { let shift_offset = move |idx, offset| { // compute offset for current repetition let dest_offset = dest + src.size * idx; // `Size` operations @@ -301,24 +312,16 @@ impl ProvenanceMap { let mut dest_bytes_box = None; let begin_overlap = self.range_ptrs_get(alloc_range(src.start, Size::ZERO), cx).first(); let end_overlap = self.range_ptrs_get(alloc_range(src.end(), Size::ZERO), cx).first(); - if !Prov::OFFSET_IS_ADDR { - // There can't be any bytewise provenance, and we cannot split up the begin/end overlap. - if let Some(entry) = begin_overlap { - return Err(AllocError::ReadPartialPointer(entry.0)); - } - if let Some(entry) = end_overlap { - return Err(AllocError::ReadPartialPointer(entry.0)); - } - debug_assert!(self.bytes.is_none()); - } else { - let mut bytes = Vec::new(); + // We only need to go here if there is some overlap or some bytewise provenance. + if begin_overlap.is_some() || end_overlap.is_some() || self.bytes.is_some() { + let mut bytes: Vec<(Size, (Prov, u8))> = Vec::new(); // First, if there is a part of a pointer at the start, add that. if let Some(entry) = begin_overlap { trace!("start overlapping entry: {entry:?}"); // For really small copies, make sure we don't run off the end of the `src` range. let entry_end = cmp::min(entry.0 + ptr_size, src.end()); for offset in src.start..entry_end { - bytes.push((offset, entry.1)); + bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); } } else { trace!("no start overlapping entry"); @@ -334,8 +337,9 @@ impl ProvenanceMap { let entry_start = cmp::max(entry.0, src.start); for offset in entry_start..src.end() { if bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < offset) { - // The last entry, if it exists, has a lower offset than us. - bytes.push((offset, entry.1)); + // The last entry, if it exists, has a lower offset than us, so we + // can add it at the end and remain sorted. + bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); } else { // There already is an entry for this offset in there! This can happen when the // start and end range checks actually end up hitting the same pointer, so we @@ -358,7 +362,7 @@ impl ProvenanceMap { dest_bytes_box = Some(dest_bytes.into_boxed_slice()); } - Ok(ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box }) + ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box } } /// Applies a provenance copy. @@ -368,14 +372,10 @@ impl ProvenanceMap { if let Some(dest_ptrs) = copy.dest_ptrs { self.ptrs.insert_presorted(dest_ptrs.into()); } - if Prov::OFFSET_IS_ADDR { - if let Some(dest_bytes) = copy.dest_bytes - && !dest_bytes.is_empty() - { - self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into()); - } - } else { - debug_assert!(copy.dest_bytes.is_none()); + if let Some(dest_bytes) = copy.dest_bytes + && !dest_bytes.is_empty() + { + self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into()); } } } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 2b0cfb865645..d8168697b4ae 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -577,9 +577,6 @@ pub enum UnsupportedOpInfo { // // The variants below are only reachable from CTFE/const prop, miri will never emit them. // - /// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state - /// cannot be represented by the CTFE interpreter. - OverwritePartialPointer(Pointer), /// Attempting to read or copy parts of a pointer to somewhere else; without knowing absolute /// addresses, the resulting state cannot be represented by the CTFE interpreter. ReadPartialPointer(Pointer), diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index e25ff7651f65..c581b2ebf092 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -56,7 +56,7 @@ impl PointerArithmetic for T {} /// mostly opaque; the `Machine` trait extends it with some more operations that also have access to /// some global state. /// The `Debug` rendering is used to display bare provenance, and for the default impl of `fmt`. -pub trait Provenance: Copy + fmt::Debug + 'static { +pub trait Provenance: Copy + PartialEq + fmt::Debug + 'static { /// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address. /// - If `false`, the offset *must* be relative. This means the bytes representing a pointer are /// different from what the Abstract Machine prescribes, so the interpreter must prevent any @@ -79,7 +79,7 @@ pub trait Provenance: Copy + fmt::Debug + 'static { fn get_alloc_id(self) -> Option; /// Defines the 'join' of provenance: what happens when doing a pointer load and different bytes have different provenance. - fn join(left: Option, right: Option) -> Option; + fn join(left: Self, right: Self) -> Option; } /// The type of provenance in the compile-time interpreter. @@ -192,8 +192,8 @@ impl Provenance for CtfeProvenance { Some(self.alloc_id()) } - fn join(_left: Option, _right: Option) -> Option { - panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false") + fn join(left: Self, right: Self) -> Option { + if left == right { Some(left) } else { None } } } @@ -224,8 +224,8 @@ impl Provenance for AllocId { Some(self) } - fn join(_left: Option, _right: Option) -> Option { - panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false") + fn join(_left: Self, _right: Self) -> Option { + unreachable!() } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 809cdb329f79..c0dd9a58f85a 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1826,7 +1826,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( ascii.push('╼'); i += ptr_size; } - } else if let Some(prov) = alloc.provenance().get(i, &tcx) { + } else if let Some((prov, idx)) = alloc.provenance().get_byte(i, &tcx) { // Memory with provenance must be defined assert!( alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok() @@ -1836,7 +1836,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( // Format is similar to "oversized" above. let j = i.bytes_usize(); let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0]; - write!(w, "╾{c:02x}{prov:#?} (1 ptr byte)╼")?; + write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼")?; i += Size::from_bytes(1); } else if alloc .init_mask() diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index dbe3999b4a43..7339ccd40488 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1345,40 +1345,6 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// assert_eq!(x, [7, 8, 3, 4]); /// assert_eq!(y, [1, 2, 9]); /// ``` -/// -/// # Const evaluation limitations -/// -/// If this function is invoked during const-evaluation, the current implementation has a small (and -/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y` -/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function may -/// fail to evaluate (similar to a panic during const-evaluation). This behavior may change in the -/// future. -/// -/// The limitation is illustrated by the following example: -/// -/// ``` -/// use std::mem::size_of; -/// use std::ptr; -/// -/// const { unsafe { -/// const PTR_SIZE: usize = size_of::<*const i32>(); -/// let mut data1 = [0u8; PTR_SIZE]; -/// let mut data2 = [0u8; PTR_SIZE]; -/// // Store a pointer in `data1`. -/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42); -/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks. -/// // This call will fail, because the pointer in `data1` crosses the boundary -/// // between several of the 1-byte chunks that are being swapped here. -/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE); -/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size -/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between -/// // two chunks. -/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1); -/// // Read the pointer from `data2` and dereference it. -/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned(); -/// assert!(*ptr == 42); -/// } } -/// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] #[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "1.88.0")] @@ -1407,9 +1373,7 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { const_eval_select!( @capture[T] { x: *mut T, y: *mut T, count: usize }: if const { - // At compile-time we want to always copy this in chunks of `T`, to ensure that if there - // are pointers inside `T` we will copy them in one go rather than trying to copy a part - // of a pointer (which would not work). + // At compile-time we don't need all the special code below. // SAFETY: Same preconditions as this function unsafe { swap_nonoverlapping_const(x, y, count) } } else { diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index 197a14423b59..c13fb96a67f9 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -936,22 +936,18 @@ fn test_const_swap_ptr() { assert!(*s1.0.ptr == 666); assert!(*s2.0.ptr == 1); - // Swap them back, again as an array. + // Swap them back, byte-for-byte unsafe { ptr::swap_nonoverlapping( - ptr::from_mut(&mut s1).cast::(), - ptr::from_mut(&mut s2).cast::(), - 1, + ptr::from_mut(&mut s1).cast::(), + ptr::from_mut(&mut s2).cast::(), + size_of::(), ); } // Make sure they still work. assert!(*s1.0.ptr == 1); assert!(*s2.0.ptr == 666); - - // This is where we'd swap again using a `u8` type and a `count` of `size_of::()` if it - // were not for the limitation of `swap_nonoverlapping` around pointers crossing multiple - // elements. }; } diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 7271d3f619c7..b8a187fed7c0 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -285,16 +285,16 @@ impl interpret::Provenance for Provenance { Ok(()) } - fn join(left: Option, right: Option) -> Option { + fn join(left: Self, right: Self) -> Option { match (left, right) { // If both are the *same* concrete tag, that is the result. ( - Some(Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }), - Some(Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }), - ) if left_alloc == right_alloc && left_tag == right_tag => left, + Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }, + Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }, + ) if left_alloc == right_alloc && left_tag == right_tag => Some(left), // If one side is a wildcard, the best possible outcome is that it is equal to the other // one, and we use that. - (Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o, + (Provenance::Wildcard, o) | (o, Provenance::Wildcard) => Some(o), // Otherwise, fall back to `None`. _ => None, } diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 2827ed997a7c..36c18379cb86 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -246,7 +246,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let p_map = alloc.provenance(); for idx in overlap { // If a provenance was read by the foreign code, expose it. - if let Some(prov) = p_map.get(Size::from_bytes(idx), this) { + if let Some((prov, _idx)) = p_map.get_byte(Size::from_bytes(idx), this) { this.expose_provenance(prov)?; } } diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs b/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs new file mode 100644 index 000000000000..0b7266a4da6d --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs @@ -0,0 +1,21 @@ +use std::{mem, ptr}; + +const PTR_SIZE: usize = mem::size_of::<&i32>(); + +fn main() { + unsafe { + let ptr = &0 as *const i32; + let arr = [ptr; 2]; + // We want to do a scalar read of a pointer at offset PTR_SIZE/2 into this array. But we + // cannot use a packed struct or `read_unaligned`, as those use the memcpy code path in + // Miri. So instead we shift the entire array by a bit and then the actual read we want to + // do is perfectly aligned. + let mut target_arr = [ptr::null::(); 3]; + let target = target_arr.as_mut_ptr().cast::(); + target.add(PTR_SIZE / 2).cast::<[*const i32; 2]>().write_unaligned(arr); + // Now target_arr[1] is a mix of the two `ptr` we had stored in `arr`. + // They all have the same provenance, but not in the right order, so we reject this. + let strange_ptr = target_arr[1]; + assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0); //~ERROR: no provenance + } +} diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr new file mode 100644 index 000000000000..88d75135a763 --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr @@ -0,0 +1,16 @@ +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + --> tests/fail/provenance/mix-ptrs1.rs:LL:CC + | +LL | assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at RUSTLIB/core/src/macros/mod.rs:LL:CC + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs2.rs b/src/tools/miri/tests/fail/provenance/mix-ptrs2.rs new file mode 100644 index 000000000000..9ef40c194abd --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs2.rs @@ -0,0 +1,37 @@ +use std::mem; + +const PTR_SIZE: usize = mem::size_of::<&i32>(); + +/// Overwrite one byte of a pointer, then restore it. +fn main() { + unsafe fn ptr_bytes<'x>(ptr: &'x mut *const i32) -> &'x mut [mem::MaybeUninit; PTR_SIZE] { + mem::transmute(ptr) + } + + // Returns a value with the same provenance as `x` but 0 for the integer value. + // `x` must be initialized. + unsafe fn zero_with_provenance(x: mem::MaybeUninit) -> mem::MaybeUninit { + let ptr = [x; PTR_SIZE]; + let ptr: *const i32 = mem::transmute(ptr); + let mut ptr = ptr.with_addr(0); + ptr_bytes(&mut ptr)[0] + } + + unsafe { + let ptr = &42; + let mut ptr = ptr as *const i32; + // Get a bytewise view of the pointer. + let ptr_bytes = ptr_bytes(&mut ptr); + + // The highest bytes must be 0 for this to work. + let hi = if cfg!(target_endian = "little") { ptr_bytes.len() - 1 } else { 0 }; + assert_eq!(*ptr_bytes[hi].as_ptr().cast::(), 0); + // Overwrite provenance on the last byte. + ptr_bytes[hi] = mem::MaybeUninit::new(0); + // Restore it from the another byte. + ptr_bytes[hi] = zero_with_provenance(ptr_bytes[1]); + + // Now ptr is almost good, except the provenance fragment indices do not work out... + assert_eq!(*ptr, 42); //~ERROR: no provenance + } +} diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr new file mode 100644 index 000000000000..636378952ba5 --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr @@ -0,0 +1,16 @@ +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + --> tests/fail/provenance/mix-ptrs2.rs:LL:CC + | +LL | assert_eq!(*ptr, 42); + | ^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at RUSTLIB/core/src/macros/mod.rs:LL:CC + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs index 1cf70d9d9a34..78a5098d21a1 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs @@ -9,11 +9,10 @@ use std::alloc::{Layout, alloc, dealloc}; use std::mem::{self, MaybeUninit}; use std::slice::from_raw_parts; -fn byte_with_provenance(val: u8, prov: *const T) -> MaybeUninit { - let ptr = prov.with_addr(val as usize); +fn byte_with_provenance(val: u8, prov: *const T, frag_idx: usize) -> MaybeUninit { + let ptr = prov.with_addr(usize::from_ne_bytes([val; _])); let bytes: [MaybeUninit; mem::size_of::<*const ()>()] = unsafe { mem::transmute(ptr) }; - let lsb = if cfg!(target_endian = "little") { 0 } else { bytes.len() - 1 }; - bytes[lsb] + bytes[frag_idx] } fn main() { @@ -21,10 +20,10 @@ fn main() { unsafe { let ptr = alloc(layout); let ptr_raw = ptr.cast::>(); - *ptr_raw.add(0) = byte_with_provenance(0x42, &42u8); + *ptr_raw.add(0) = byte_with_provenance(0x42, &42u8, 0); *ptr.add(1) = 0x12; *ptr.add(2) = 0x13; - *ptr_raw.add(3) = byte_with_provenance(0x43, &0u8); + *ptr_raw.add(3) = byte_with_provenance(0x43, &0u8, 1); let slice1 = from_raw_parts(ptr, 8); let slice2 = from_raw_parts(ptr.add(8), 8); drop(slice1.cmp(slice2)); diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr index d3253317faee..305d8d4fbede 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr @@ -17,7 +17,7 @@ LL | drop(slice1.cmp(slice2)); Uninitialized memory occurred at ALLOC[0x4..0x8], in this allocation: ALLOC (Rust heap, size: 16, align: 8) { - ╾42[ALLOC] (1 ptr byte)╼ 12 13 ╾43[ALLOC] (1 ptr byte)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░ + ╾42[ALLOC] (ptr fragment 0)╼ 12 13 ╾43[ALLOC] (ptr fragment 1)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░ } ALLOC (global (static or const), size: 1, align: 1) { 2a │ * diff --git a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr index 74a599ede5c3..1c0ad2f0dc91 100644 --- a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr +++ b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr @@ -35,7 +35,7 @@ LL | partial_init(); Uninitialized memory occurred at ALLOC[0x2..0x3], in this allocation: ALLOC (stack variable, size: 3, align: 1) { - ╾00[wildcard] (1 ptr byte)╼ ╾00[wildcard] (1 ptr byte)╼ __ │ ━━░ + ╾00[wildcard] (ptr fragment 0)╼ ╾00[wildcard] (ptr fragment 0)╼ __ │ ━━░ } note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/pass/provenance.rs b/src/tools/miri/tests/pass/provenance.rs index 8c52b703190d..46f5bd73d55b 100644 --- a/src/tools/miri/tests/pass/provenance.rs +++ b/src/tools/miri/tests/pass/provenance.rs @@ -6,7 +6,6 @@ const PTR_SIZE: usize = mem::size_of::<&i32>(); fn main() { basic(); - partial_overwrite_then_restore(); bytewise_ptr_methods(); bytewise_custom_memcpy(); bytewise_custom_memcpy_chunked(); @@ -29,40 +28,6 @@ fn basic() { assert_eq!(unsafe { *ptr_back }, 42); } -/// Overwrite one byte of a pointer, then restore it. -fn partial_overwrite_then_restore() { - unsafe fn ptr_bytes<'x>(ptr: &'x mut *const i32) -> &'x mut [mem::MaybeUninit; PTR_SIZE] { - mem::transmute(ptr) - } - - // Returns a value with the same provenance as `x` but 0 for the integer value. - // `x` must be initialized. - unsafe fn zero_with_provenance(x: mem::MaybeUninit) -> mem::MaybeUninit { - let ptr = [x; PTR_SIZE]; - let ptr: *const i32 = mem::transmute(ptr); - let mut ptr = ptr.with_addr(0); - ptr_bytes(&mut ptr)[0] - } - - unsafe { - let ptr = &42; - let mut ptr = ptr as *const i32; - // Get a bytewise view of the pointer. - let ptr_bytes = ptr_bytes(&mut ptr); - - // The highest bytes must be 0 for this to work. - let hi = if cfg!(target_endian = "little") { ptr_bytes.len() - 1 } else { 0 }; - assert_eq!(*ptr_bytes[hi].as_ptr().cast::(), 0); - // Overwrite provenance on the last byte. - ptr_bytes[hi] = mem::MaybeUninit::new(0); - // Restore it from the another byte. - ptr_bytes[hi] = zero_with_provenance(ptr_bytes[1]); - - // Now ptr should be good again. - assert_eq!(*ptr, 42); - } -} - fn bytewise_ptr_methods() { let mut ptr1 = &1; let mut ptr2 = &2; diff --git a/src/tools/miri/tests/pass/transmute_ptr.rs b/src/tools/miri/tests/pass/transmute_ptr.rs index 0944781c6def..0a53b7782947 100644 --- a/src/tools/miri/tests/pass/transmute_ptr.rs +++ b/src/tools/miri/tests/pass/transmute_ptr.rs @@ -1,6 +1,6 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows -use std::{mem, ptr}; +use std::mem; fn t1() { // If we are careful, we can exploit data layout... @@ -27,27 +27,8 @@ fn ptr_integer_array() { let _x: [u8; PTR_SIZE] = unsafe { mem::transmute(&0) }; } -fn ptr_in_two_halves() { - unsafe { - let ptr = &0 as *const i32; - let arr = [ptr; 2]; - // We want to do a scalar read of a pointer at offset PTR_SIZE/2 into this array. But we - // cannot use a packed struct or `read_unaligned`, as those use the memcpy code path in - // Miri. So instead we shift the entire array by a bit and then the actual read we want to - // do is perfectly aligned. - let mut target_arr = [ptr::null::(); 3]; - let target = target_arr.as_mut_ptr().cast::(); - target.add(PTR_SIZE / 2).cast::<[*const i32; 2]>().write_unaligned(arr); - // Now target_arr[1] is a mix of the two `ptr` we had stored in `arr`. - let strange_ptr = target_arr[1]; - // Check that the provenance works out. - assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0); - } -} - fn main() { t1(); t2(); ptr_integer_array(); - ptr_in_two_halves(); } diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr b/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr index cb2bb1e8cd85..e5b108df2982 100644 --- a/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr @@ -4,7 +4,7 @@ error: encountered `const_allocate` pointer in final value that was not made glo LL | const FOO: &i32 = foo(); | ^^^^^^^^^^^^^^^ | - = note: use `const_make_global` to make allocated pointers immutable before returning + = note: use `const_make_global` to turn allocated pointers into immutable globals before returning error: encountered `const_allocate` pointer in final value that was not made global --> $DIR/ptr_not_made_global.rs:12:1 @@ -12,7 +12,7 @@ error: encountered `const_allocate` pointer in final value that was not made glo LL | const FOO_RAW: *const i32 = foo(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: use `const_make_global` to make allocated pointers immutable before returning + = note: use `const_make_global` to turn allocated pointers into immutable globals before returning error: aborting due to 2 previous errors diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr index 2445ce633d6f..2d1993f96d37 100644 --- a/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr @@ -4,7 +4,7 @@ error: encountered `const_allocate` pointer in final value that was not made glo LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; | ^^^^^^^^^^^^^^^^^^^ | - = note: use `const_make_global` to make allocated pointers immutable before returning + = note: use `const_make_global` to turn allocated pointers into immutable globals before returning error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/partial_ptr_overwrite.rs b/tests/ui/consts/const-eval/partial_ptr_overwrite.rs deleted file mode 100644 index bd97bec0f71a..000000000000 --- a/tests/ui/consts/const-eval/partial_ptr_overwrite.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Test for the behavior described in . - -const PARTIAL_OVERWRITE: () = { - let mut p = &42; - unsafe { - let ptr: *mut _ = &mut p; - *(ptr as *mut u8) = 123; //~ ERROR unable to overwrite parts of a pointer - } - let x = *p; -}; - -fn main() {} diff --git a/tests/ui/consts/const-eval/partial_ptr_overwrite.stderr b/tests/ui/consts/const-eval/partial_ptr_overwrite.stderr deleted file mode 100644 index 6ef1cfd35c8c..000000000000 --- a/tests/ui/consts/const-eval/partial_ptr_overwrite.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0080]: unable to overwrite parts of a pointer in memory at ALLOC0 - --> $DIR/partial_ptr_overwrite.rs:7:9 - | -LL | *(ptr as *mut u8) = 123; - | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `PARTIAL_OVERWRITE` failed here - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/ptr_fragments.rs b/tests/ui/consts/const-eval/ptr_fragments.rs new file mode 100644 index 000000000000..04dcbe55590e --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments.rs @@ -0,0 +1,63 @@ +//! Test that various operations involving pointer fragments work as expected. +//@ run-pass + +use std::mem::{self, MaybeUninit, transmute}; +use std::ptr; + +type Byte = MaybeUninit; + +const unsafe fn memcpy(dst: *mut Byte, src: *const Byte, n: usize) { + let mut i = 0; + while i < n { + *dst.add(i) = *src.add(i); + i += 1; + } +} + +const _MEMCPY: () = unsafe { + let ptr = &42; + let mut ptr2 = ptr::null::(); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>()); + assert!(*ptr2 == 42); +}; +const _MEMCPY_OFFSET: () = unsafe { + // Same as above, but the pointer has a non-zero offset so not all the data bytes are the same. + let ptr = &(42, 18); + let ptr = &ptr.1; + let mut ptr2 = ptr::null::(); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>()); + assert!(*ptr2 == 18); +}; + +const MEMCPY_RET: MaybeUninit<*const i32> = unsafe { + let ptr = &42; + let mut ptr2 = MaybeUninit::new(ptr::null::()); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>()); + // Return in a MaybeUninit so it does not get treated as a scalar. + ptr2 +}; + +#[allow(dead_code)] +fn reassemble_ptr_fragments_in_static() { + static DATA: i32 = 1i32; + + #[cfg(target_pointer_width = "64")] + struct Thing { + x: MaybeUninit, + y: MaybeUninit, + } + #[cfg(target_pointer_width = "32")] + struct Thing { + x: MaybeUninit, + y: MaybeUninit, + } + + static X: Thing = unsafe { + let Thing { x, y } = transmute(&raw const DATA); + Thing { x, y } + }; +} + +fn main() { + assert_eq!(unsafe { MEMCPY_RET.assume_init().read() }, 42); +} diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs new file mode 100644 index 000000000000..e2f3f51b0863 --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs @@ -0,0 +1,25 @@ +//! Test that we properly error when there is a pointer fragment in the final value. + +use std::{mem::{self, MaybeUninit}, ptr}; + +type Byte = MaybeUninit; + +const unsafe fn memcpy(dst: *mut Byte, src: *const Byte, n: usize) { + let mut i = 0; + while i < n { + dst.add(i).write(src.add(i).read()); + i += 1; + } +} + +const MEMCPY_RET: MaybeUninit<*const i32> = unsafe { //~ERROR: partial pointer in final value + let ptr = &42; + let mut ptr2 = MaybeUninit::new(ptr::null::()); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>() / 2); + // Return in a MaybeUninit so it does not get treated as a scalar. + ptr2 +}; + +fn main() { + assert_eq!(unsafe { MEMCPY_RET.assume_init().read() }, 42); +} diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr new file mode 100644 index 000000000000..628bf2566e59 --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr @@ -0,0 +1,10 @@ +error: encountered partial pointer in final value of constant + --> $DIR/ptr_fragments_in_final.rs:15:1 + | +LL | const MEMCPY_RET: MaybeUninit<*const i32> = unsafe { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value + +error: aborting due to 1 previous error + diff --git a/tests/ui/consts/const-eval/read_partial_ptr.rs b/tests/ui/consts/const-eval/read_partial_ptr.rs new file mode 100644 index 000000000000..bccef9c0bc6c --- /dev/null +++ b/tests/ui/consts/const-eval/read_partial_ptr.rs @@ -0,0 +1,49 @@ +//! Ensure we error when trying to load from a pointer whose provenance has been messed with. + +const PARTIAL_OVERWRITE: () = { + let mut p = &42; + // Overwrite one byte with a no-provenance value. + unsafe { + let ptr: *mut _ = &mut p; + *(ptr as *mut u8) = 123; + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +const PTR_BYTES_SWAP: () = { + let mut p = &42; + // Swap the first two bytes. + unsafe { + let ptr = &mut p as *mut _ as *mut std::mem::MaybeUninit; + let byte0 = ptr.read(); + let byte1 = ptr.add(1).read(); + ptr.write(byte1); + ptr.add(1).write(byte0); + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +const PTR_BYTES_REPEAT: () = { + let mut p = &42; + // Duplicate the first byte over the second. + unsafe { + let ptr = &mut p as *mut _ as *mut std::mem::MaybeUninit; + let byte0 = ptr.read(); + ptr.add(1).write(byte0); + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +const PTR_BYTES_MIX: () = { + let mut p = &42; + let q = &43; + // Overwrite the first byte of p with the first byte of q. + unsafe { + let ptr = &mut p as *mut _ as *mut std::mem::MaybeUninit; + let qtr = &q as *const _ as *const std::mem::MaybeUninit; + ptr.write(qtr.read()); + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +fn main() {} diff --git a/tests/ui/consts/const-eval/read_partial_ptr.stderr b/tests/ui/consts/const-eval/read_partial_ptr.stderr new file mode 100644 index 000000000000..196606c77a37 --- /dev/null +++ b/tests/ui/consts/const-eval/read_partial_ptr.stderr @@ -0,0 +1,39 @@ +error[E0080]: unable to read parts of a pointer from memory at ALLOC0 + --> $DIR/read_partial_ptr.rs:10:13 + | +LL | let x = *p; + | ^^ evaluation of `PARTIAL_OVERWRITE` failed here + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error[E0080]: unable to read parts of a pointer from memory at ALLOC1 + --> $DIR/read_partial_ptr.rs:23:13 + | +LL | let x = *p; + | ^^ evaluation of `PTR_BYTES_SWAP` failed here + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error[E0080]: unable to read parts of a pointer from memory at ALLOC2 + --> $DIR/read_partial_ptr.rs:34:13 + | +LL | let x = *p; + | ^^ evaluation of `PTR_BYTES_REPEAT` failed here + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error[E0080]: unable to read parts of a pointer from memory at ALLOC3 + --> $DIR/read_partial_ptr.rs:46:13 + | +LL | let x = *p; + | ^^ evaluation of `PTR_BYTES_MIX` failed here + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/missing_span_in_backtrace.rs b/tests/ui/consts/missing_span_in_backtrace.rs index 4f3f9aa6adac..09a11c75fd7b 100644 --- a/tests/ui/consts/missing_span_in_backtrace.rs +++ b/tests/ui/consts/missing_span_in_backtrace.rs @@ -1,3 +1,4 @@ +//! Check what happens when the error occurs inside a std function that we can't print the span of. //@ ignore-backends: gcc //@ compile-flags: -Z ui-testing=no @@ -7,15 +8,15 @@ use std::{ }; const X: () = { - let mut ptr1 = &1; - let mut ptr2 = &2; + let mut x1 = 1; + let mut x2 = 2; // Swap them, bytewise. unsafe { - ptr::swap_nonoverlapping( //~ ERROR unable to copy parts of a pointer - &mut ptr1 as *mut _ as *mut MaybeUninit, - &mut ptr2 as *mut _ as *mut MaybeUninit, - mem::size_of::<&i32>(), + ptr::swap_nonoverlapping( //~ ERROR beyond the end of the allocation + &mut x1 as *mut _ as *mut MaybeUninit, + &mut x2 as *mut _ as *mut MaybeUninit, + 10, ); } }; diff --git a/tests/ui/consts/missing_span_in_backtrace.stderr b/tests/ui/consts/missing_span_in_backtrace.stderr index 0ac1e281107d..55dde6358286 100644 --- a/tests/ui/consts/missing_span_in_backtrace.stderr +++ b/tests/ui/consts/missing_span_in_backtrace.stderr @@ -1,15 +1,13 @@ -error[E0080]: unable to copy parts of a pointer from memory at ALLOC0 - --> $DIR/missing_span_in_backtrace.rs:15:9 +error[E0080]: memory access failed: attempting to access 1 byte, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes + --> $DIR/missing_span_in_backtrace.rs:16:9 | -15 | / ptr::swap_nonoverlapping( -16 | | &mut ptr1 as *mut _ as *mut MaybeUninit, -17 | | &mut ptr2 as *mut _ as *mut MaybeUninit, -18 | | mem::size_of::<&i32>(), -19 | | ); +16 | / ptr::swap_nonoverlapping( +17 | | &mut x1 as *mut _ as *mut MaybeUninit, +18 | | &mut x2 as *mut _ as *mut MaybeUninit, +19 | | 10, +20 | | ); | |_________^ evaluation of `X` failed inside this call | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported note: inside `swap_nonoverlapping::compiletime::>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL note: inside `std::ptr::swap_nonoverlapping_const::>` From 02ac116e53f525a929d251bf9dc4f2ea1cacd3c8 Mon Sep 17 00:00:00 2001 From: Eduard Stefes Date: Tue, 29 Jul 2025 11:27:07 +0200 Subject: [PATCH 0072/2093] Fix tests for big-endian The tests fail on s390x and presumably other big-endian systems, due to print of raw values and padding bytes. To fix the tests remove the raw output values in the error note with `normalize-stderr`. --- tests/ui/consts/const-eval/union-const-eval-field.rs | 1 + tests/ui/consts/const-eval/union-const-eval-field.stderr | 8 ++++---- tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs | 1 + tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr | 8 ++++---- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/ui/consts/const-eval/union-const-eval-field.rs b/tests/ui/consts/const-eval/union-const-eval-field.rs index 719e59b007c0..2c9061a7a50f 100644 --- a/tests/ui/consts/const-eval/union-const-eval-field.rs +++ b/tests/ui/consts/const-eval/union-const-eval-field.rs @@ -1,5 +1,6 @@ //@ dont-require-annotations: NOTE //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" +//@ normalize-stderr: "([[:xdigit:]]{2}\s){4}(__\s){4}\s+│\s+([?|\.]){4}\W{4}" -> "HEX_DUMP" type Field1 = i32; type Field2 = f32; diff --git a/tests/ui/consts/const-eval/union-const-eval-field.stderr b/tests/ui/consts/const-eval/union-const-eval-field.stderr index 1843ce273ac0..3b7e5508d56c 100644 --- a/tests/ui/consts/const-eval/union-const-eval-field.stderr +++ b/tests/ui/consts/const-eval/union-const-eval-field.stderr @@ -1,21 +1,21 @@ error[E0080]: reading memory at ALLOC0[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory - --> $DIR/union-const-eval-field.rs:29:37 + --> $DIR/union-const-eval-field.rs:30:37 | LL | const FIELD3: Field3 = unsafe { UNION.field3 }; | ^^^^^^^^^^^^ evaluation of `read_field3::FIELD3` failed here | = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - 00 00 80 3f __ __ __ __ │ ...?░░░░ + HEX_DUMP } note: erroneous constant encountered - --> $DIR/union-const-eval-field.rs:31:5 + --> $DIR/union-const-eval-field.rs:32:5 | LL | FIELD3 | ^^^^^^ note: erroneous constant encountered - --> $DIR/union-const-eval-field.rs:31:5 + --> $DIR/union-const-eval-field.rs:32:5 | LL | FIELD3 | ^^^^^^ diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs index 15f4a9a778ed..ed15f5bba962 100644 --- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs +++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs @@ -1,3 +1,4 @@ +//@ normalize-stderr: "[[:xdigit:]]{2} __ ([[:xdigit:]]{2}\s){2}" -> "HEX_DUMP" #![feature(core_intrinsics)] const RAW_EQ_PADDING: bool = unsafe { diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr index 5f4ef14d5869..329da35297e1 100644 --- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr +++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr @@ -1,15 +1,15 @@ error[E0080]: reading memory at ALLOC0[0x0..0x4], but memory is uninitialized at [0x1..0x2], and this operation requires initialized memory - --> $DIR/intrinsic-raw_eq-const-bad.rs:4:5 + --> $DIR/intrinsic-raw_eq-const-bad.rs:5:5 | LL | std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_EQ_PADDING` failed here | = note: the raw bytes of the constant (size: 4, align: 2) { - 01 __ 02 00 │ .░.. + HEX_DUMP │ .░.. } error[E0080]: unable to turn pointer into integer - --> $DIR/intrinsic-raw_eq-const-bad.rs:9:5 + --> $DIR/intrinsic-raw_eq-const-bad.rs:10:5 | LL | std::intrinsics::raw_eq(&(&0), &(&1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_EQ_PTR` failed here @@ -18,7 +18,7 @@ LL | std::intrinsics::raw_eq(&(&0), &(&1)) = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: accessing memory with alignment 1, but alignment 4 is required - --> $DIR/intrinsic-raw_eq-const-bad.rs:16:5 + --> $DIR/intrinsic-raw_eq-const-bad.rs:17:5 | LL | std::intrinsics::raw_eq(aref, aref) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_EQ_NOT_ALIGNED` failed here From 64ed2b5f3d0d13cbb3206c02cd28a7a527657071 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 31 Jul 2025 07:58:56 +0200 Subject: [PATCH 0073/2093] rustup --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 2178caf63968..1ced6098acf4 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -733dab558992d902d6d17576de1da768094e2cf3 +32e7a4b92b109c24e9822c862a7c74436b50e564 From 76a213bb18497b17e2e753c6bfb4e08c9fc36d9a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 31 Jul 2025 08:12:43 +0200 Subject: [PATCH 0074/2093] rely on preinstalled rustup on windows-arm --- src/tools/miri/.github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index ba496d767129..4e55200cddf0 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -69,12 +69,6 @@ jobs: sudo apt update # Install needed packages sudo apt install $(echo "libatomic1: zlib1g-dev:" | sed 's/:/:${{ matrix.multiarch }}/g') - - name: Install rustup on Windows ARM - if: ${{ matrix.os == 'windows-11-arm' }} - run: | - curl -LOs https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe - ./rustup-init.exe -y --no-modify-path - echo "$USERPROFILE/.cargo/bin" >> "$GITHUB_PATH" - uses: ./.github/workflows/setup with: toolchain_flags: "--host ${{ matrix.host_target }}" From 431fc2a7ea59ec16c5ffc6464b31b33745b4c32e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 31 Jul 2025 08:06:10 +0200 Subject: [PATCH 0075/2093] bless cargo miri doctest execution --- src/tools/miri/test-cargo-miri/run-test.py | 2 +- src/tools/miri/test-cargo-miri/test.default.stdout.ref | 1 + src/tools/miri/test-cargo-miri/test.filter.stdout.ref | 1 + src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref | 2 ++ 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 40bfe7f845fe..6b3b6343b825 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -37,7 +37,7 @@ def cargo_miri(cmd, quiet = True, targets = None): def normalize_stdout(str): str = str.replace("src\\", "src/") # normalize paths across platforms - str = re.sub("finished in \\d+\\.\\d\\ds", "finished in $TIME", str) # the time keeps changing, obviously + str = re.sub("\\b\\d+\\.\\d+s\\b", "$TIME", str) # the time keeps changing, obviously return str def check_output(actual, path, name): diff --git a/src/tools/miri/test-cargo-miri/test.default.stdout.ref b/src/tools/miri/test-cargo-miri/test.default.stdout.ref index 2d74d82f769b..ef092ef703bb 100644 --- a/src/tools/miri/test-cargo-miri/test.default.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.default.stdout.ref @@ -14,3 +14,4 @@ running 5 tests ..... test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref index b68bc983276f..071aa5691c1a 100644 --- a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref @@ -15,3 +15,4 @@ running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref index a376530a8cfb..20139e9ffe62 100644 --- a/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref @@ -25,8 +25,10 @@ running 5 tests ..... test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME running 5 tests ..... test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME From 27478bef40046250b85d2796eb61ab185cd0d7ce Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 10 Mar 2025 18:39:52 +0100 Subject: [PATCH 0076/2093] Do not use `DerefMut` on `ManuallyDrop<_>` reached through unions --- clippy_lints/src/reference.rs | 32 ++++++++++++++++++++- tests/ui/deref_addrof.fixed | 54 ++++++++++++++++++++++++++++++++++- tests/ui/deref_addrof.rs | 54 ++++++++++++++++++++++++++++++++++- tests/ui/deref_addrof.stderr | 48 +++++++++++++++++++++++-------- 4 files changed, 173 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index cb414d791191..42d1dcaee36b 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::ty::adjust_derefs_manually_drop; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, UnOp}; +use rustc_hir::{Expr, ExprKind, Mutability, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::{BytePos, Span}; @@ -44,6 +45,7 @@ impl LateLintPass<'_> for DerefAddrOf { // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section. // See #12854 for details. && !matches!(addrof_target.kind, ExprKind::Array(_)) + && !is_manually_drop_through_union(cx, addrof_target) && deref_target.span.eq_ctxt(e.span) && !addrof_target.span.from_expansion() { @@ -102,3 +104,31 @@ impl LateLintPass<'_> for DerefAddrOf { } } } + +/// Check if `expr` is part of an access to a `ManuallyDrop` entity reached through a union +fn is_manually_drop_through_union(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if is_reached_through_union(cx, expr) { + let typeck = cx.typeck_results(); + for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) { + if let Node::Expr(expr) = node { + if adjust_derefs_manually_drop(typeck.expr_adjustments(expr), typeck.expr_ty(expr)) { + return true; + } + } else { + break; + } + } + } + false +} + +/// Checks whether `expr` denotes an object reached through a union +fn is_reached_through_union(cx: &LateContext<'_>, mut expr: &Expr<'_>) -> bool { + while let ExprKind::Field(parent, _) | ExprKind::Index(parent, _, _) = expr.kind { + if cx.typeck_results().expr_ty_adjusted(parent).is_union() { + return true; + } + expr = parent; + } + false +} diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 35dbd790e890..6b200edc38ba 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -1,6 +1,11 @@ //@aux-build:proc_macros.rs -#![allow(clippy::return_self_not_must_use, clippy::useless_vec)] +#![allow( + dangerous_implicit_autorefs, + clippy::explicit_auto_deref, + clippy::return_self_not_must_use, + clippy::useless_vec +)] #![warn(clippy::deref_addrof)] extern crate proc_macros; @@ -72,3 +77,50 @@ impl S { //~^ deref_addrof } } + +fn issue14386() { + use std::mem::ManuallyDrop; + + #[derive(Copy, Clone)] + struct Data { + num: u64, + } + + #[derive(Clone, Copy)] + struct M { + md: ManuallyDrop<[u8; 4]>, + } + + union DataWithPadding<'lt> { + data: ManuallyDrop, + prim: ManuallyDrop, + padding: [u8; size_of::()], + tup: (ManuallyDrop, ()), + indirect: M, + indirect_arr: [M; 2], + indirect_ref: &'lt mut M, + } + + let mut a = DataWithPadding { + padding: [0; size_of::()], + }; + unsafe { + a.padding = [1; size_of::()]; + //~^ deref_addrof + a.tup.1 = (); + //~^ deref_addrof + *a.prim = 0; + //~^ deref_addrof + + (*&mut a.data).num = 42; + (*&mut a.tup).0.num = 42; + (*&mut a.indirect.md)[3] = 1; + (*&mut a.indirect_arr[1].md)[3] = 1; + (*&mut a.indirect_ref.md)[3] = 1; + + // Check that raw pointers are properly considered as well + *a.prim = 0; + //~^ deref_addrof + (*&raw mut a.data).num = 42; + } +} diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 96d1b92ef7be..abc9e819adef 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -1,6 +1,11 @@ //@aux-build:proc_macros.rs -#![allow(clippy::return_self_not_must_use, clippy::useless_vec)] +#![allow( + dangerous_implicit_autorefs, + clippy::explicit_auto_deref, + clippy::return_self_not_must_use, + clippy::useless_vec +)] #![warn(clippy::deref_addrof)] extern crate proc_macros; @@ -72,3 +77,50 @@ impl S { //~^ deref_addrof } } + +fn issue14386() { + use std::mem::ManuallyDrop; + + #[derive(Copy, Clone)] + struct Data { + num: u64, + } + + #[derive(Clone, Copy)] + struct M { + md: ManuallyDrop<[u8; 4]>, + } + + union DataWithPadding<'lt> { + data: ManuallyDrop, + prim: ManuallyDrop, + padding: [u8; size_of::()], + tup: (ManuallyDrop, ()), + indirect: M, + indirect_arr: [M; 2], + indirect_ref: &'lt mut M, + } + + let mut a = DataWithPadding { + padding: [0; size_of::()], + }; + unsafe { + (*&mut a.padding) = [1; size_of::()]; + //~^ deref_addrof + (*&mut a.tup).1 = (); + //~^ deref_addrof + **&mut a.prim = 0; + //~^ deref_addrof + + (*&mut a.data).num = 42; + (*&mut a.tup).0.num = 42; + (*&mut a.indirect.md)[3] = 1; + (*&mut a.indirect_arr[1].md)[3] = 1; + (*&mut a.indirect_ref.md)[3] = 1; + + // Check that raw pointers are properly considered as well + **&raw mut a.prim = 0; + //~^ deref_addrof + (*&raw mut a.data).num = 42; + } +} diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 81414b625b2f..3c422a4040bb 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -1,5 +1,5 @@ error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:23:13 + --> tests/ui/deref_addrof.rs:28:13 | LL | let b = *&a; | ^^^ help: try: `a` @@ -8,55 +8,55 @@ LL | let b = *&a; = help: to override `-D warnings` add `#[allow(clippy::deref_addrof)]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:26:13 + --> tests/ui/deref_addrof.rs:31:13 | LL | let b = *&get_number(); | ^^^^^^^^^^^^^^ help: try: `get_number()` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:32:13 + --> tests/ui/deref_addrof.rs:37:13 | LL | let b = *&bytes[1..2][0]; | ^^^^^^^^^^^^^^^^ help: try: `bytes[1..2][0]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:37:13 + --> tests/ui/deref_addrof.rs:42:13 | LL | let b = *&(a); | ^^^^^ help: try: `(a)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:40:13 + --> tests/ui/deref_addrof.rs:45:13 | LL | let b = *(&a); | ^^^^^ help: try: `a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:44:13 + --> tests/ui/deref_addrof.rs:49:13 | LL | let b = *((&a)); | ^^^^^^^ help: try: `a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:47:13 + --> tests/ui/deref_addrof.rs:52:13 | LL | let b = *&&a; | ^^^^ help: try: `&a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:50:14 + --> tests/ui/deref_addrof.rs:55:14 | LL | let b = **&aref; | ^^^^^^ help: try: `aref` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:55:19 + --> tests/ui/deref_addrof.rs:60:19 | LL | let _repeat = *&[0; 64]; | ^^^^^^^^^ help: try: `[0; 64]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:66:17 + --> tests/ui/deref_addrof.rs:71:17 | LL | inline!(*& $(@expr self)) | ^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` @@ -64,12 +64,36 @@ LL | inline!(*& $(@expr self)) = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:71:17 + --> tests/ui/deref_addrof.rs:76:17 | LL | inline!(*&mut $(@expr self)) | ^^^^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` | = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 11 previous errors +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:108:9 + | +LL | (*&mut a.padding) = [1; size_of::()]; + | ^^^^^^^^^^^^^^^^^ help: try: `a.padding` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:110:9 + | +LL | (*&mut a.tup).1 = (); + | ^^^^^^^^^^^^^ help: try: `a.tup` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:112:10 + | +LL | **&mut a.prim = 0; + | ^^^^^^^^^^^^ help: try: `a.prim` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:122:10 + | +LL | **&raw mut a.prim = 0; + | ^^^^^^^^^^^^^^^^ help: try: `a.prim` + +error: aborting due to 15 previous errors From ff496ad34fc823bc063293edbf48f14261c6e28b Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 7 May 2025 16:28:26 +0200 Subject: [PATCH 0077/2093] Lint more cases when the explicit dereference can be kept in place --- clippy_lints/src/reference.rs | 54 ++++++++++++++++++++++++++++------- tests/ui/deref_addrof.fixed | 20 +++++++++---- tests/ui/deref_addrof.rs | 10 ++++++- tests/ui/deref_addrof.stderr | 34 ++++++++++++++++++++-- 4 files changed, 99 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 42d1dcaee36b..0b8993ee1e11 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; use clippy_utils::ty::adjust_derefs_manually_drop; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, Node, UnOp}; +use rustc_hir::{Expr, ExprKind, HirId, Mutability, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::{BytePos, Span}; @@ -45,10 +45,18 @@ impl LateLintPass<'_> for DerefAddrOf { // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section. // See #12854 for details. && !matches!(addrof_target.kind, ExprKind::Array(_)) - && !is_manually_drop_through_union(cx, addrof_target) && deref_target.span.eq_ctxt(e.span) && !addrof_target.span.from_expansion() { + // If this expression is an explicit `DerefMut` of a `ManuallyDrop` reached through a + // union, we may remove the reference if we are at the point where the implicit + // dereference would take place. Otherwise, we should not lint. + let keep_deref = match is_manually_drop_through_union(cx, e.hir_id, addrof_target) { + ManuallyDropThroughUnion::Directly => true, + ManuallyDropThroughUnion::Indirect => return, + ManuallyDropThroughUnion::No => false, + }; + let mut applicability = Applicability::MachineApplicable; let sugg = if e.span.from_expansion() { if let Some(macro_source) = e.span.get_source_text(cx) { @@ -97,7 +105,11 @@ impl LateLintPass<'_> for DerefAddrOf { e.span, "immediately dereferencing a reference", "try", - sugg.to_string(), + if keep_deref { + format!("(*{sugg})") + } else { + sugg.to_string() + }, applicability, ); } @@ -105,21 +117,43 @@ impl LateLintPass<'_> for DerefAddrOf { } } -/// Check if `expr` is part of an access to a `ManuallyDrop` entity reached through a union -fn is_manually_drop_through_union(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if is_reached_through_union(cx, expr) { +/// Is this a `ManuallyDrop` reached through a union, and when is `DerefMut` called on it? +enum ManuallyDropThroughUnion { + /// `ManuallyDrop` reached through a union and immediately explicitely dereferenced + Directly, + /// `ManuallyDrop` reached through a union, and dereferenced later on + Indirect, + /// Any other situation + No, +} + +/// Check if `addrof_target` is part of an access to a `ManuallyDrop` entity reached through a +/// union, and when it is dereferenced using `DerefMut` starting from `expr_id` and going up. +fn is_manually_drop_through_union( + cx: &LateContext<'_>, + expr_id: HirId, + addrof_target: &Expr<'_>, +) -> ManuallyDropThroughUnion { + if is_reached_through_union(cx, addrof_target) { let typeck = cx.typeck_results(); - for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) { - if let Node::Expr(expr) = node { + for (idx, id) in std::iter::once(expr_id) + .chain(cx.tcx.hir_parent_id_iter(expr_id)) + .enumerate() + { + if let Node::Expr(expr) = cx.tcx.hir_node(id) { if adjust_derefs_manually_drop(typeck.expr_adjustments(expr), typeck.expr_ty(expr)) { - return true; + return if idx == 0 { + ManuallyDropThroughUnion::Directly + } else { + ManuallyDropThroughUnion::Indirect + }; } } else { break; } } } - false + ManuallyDropThroughUnion::No } /// Checks whether `expr` denotes an object reached through a union diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 6b200edc38ba..ae8ed0dc1140 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -112,15 +112,23 @@ fn issue14386() { *a.prim = 0; //~^ deref_addrof - (*&mut a.data).num = 42; - (*&mut a.tup).0.num = 42; - (*&mut a.indirect.md)[3] = 1; - (*&mut a.indirect_arr[1].md)[3] = 1; - (*&mut a.indirect_ref.md)[3] = 1; + (*a.data).num = 42; + //~^ deref_addrof + (*a.indirect.md)[3] = 1; + //~^ deref_addrof + (*a.indirect_arr[1].md)[3] = 1; + //~^ deref_addrof + (*a.indirect_ref.md)[3] = 1; + //~^ deref_addrof // Check that raw pointers are properly considered as well *a.prim = 0; //~^ deref_addrof - (*&raw mut a.data).num = 42; + (*a.data).num = 42; + //~^ deref_addrof + + // Do not lint, as the dereference happens later, we cannot + // just remove `&mut` + (*&mut a.tup).0.num = 42; } } diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index abc9e819adef..4ff014059167 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -113,14 +113,22 @@ fn issue14386() { //~^ deref_addrof (*&mut a.data).num = 42; - (*&mut a.tup).0.num = 42; + //~^ deref_addrof (*&mut a.indirect.md)[3] = 1; + //~^ deref_addrof (*&mut a.indirect_arr[1].md)[3] = 1; + //~^ deref_addrof (*&mut a.indirect_ref.md)[3] = 1; + //~^ deref_addrof // Check that raw pointers are properly considered as well **&raw mut a.prim = 0; //~^ deref_addrof (*&raw mut a.data).num = 42; + //~^ deref_addrof + + // Do not lint, as the dereference happens later, we cannot + // just remove `&mut` + (*&mut a.tup).0.num = 42; } } diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 3c422a4040bb..adfa542765c0 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -90,10 +90,40 @@ LL | **&mut a.prim = 0; | ^^^^^^^^^^^^ help: try: `a.prim` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:122:10 + --> tests/ui/deref_addrof.rs:115:9 + | +LL | (*&mut a.data).num = 42; + | ^^^^^^^^^^^^^^ help: try: `(*a.data)` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:117:9 + | +LL | (*&mut a.indirect.md)[3] = 1; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect.md)` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:119:9 + | +LL | (*&mut a.indirect_arr[1].md)[3] = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_arr[1].md)` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:121:9 + | +LL | (*&mut a.indirect_ref.md)[3] = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_ref.md)` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:125:10 | LL | **&raw mut a.prim = 0; | ^^^^^^^^^^^^^^^^ help: try: `a.prim` -error: aborting due to 15 previous errors +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:127:9 + | +LL | (*&raw mut a.data).num = 42; + | ^^^^^^^^^^^^^^^^^^ help: try: `(*a.data)` + +error: aborting due to 20 previous errors From 7224dffc65e7082d2366aa2ca3f7620f93f5439b Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 7 May 2025 17:39:58 +0200 Subject: [PATCH 0078/2093] Do not lint data coming from macro Suggesting to remove `*&` or `*&mut` in a macro may be incorrect, as it would require tracking all possible macro usages. In some cases, it is not possible to remove the dereference or the reference. For example, in the following code, the `drmut!()` macro will be linted against while called as `drmut!(d)`, and the suggestion would be to remove the `*&mut`. That would make `drmut!(u.data).num = 1` invalid, as a `ManuallyDrop` object coming through a union cannot be implicitely dereferenced through `DerefMut`. ```rust use std::mem::ManuallyDrop; #[derive(Copy, Clone)] struct Data { num: u64, } union Union { data: ManuallyDrop, } macro_rules! drmut { ($e:expr) => { *&mut $e }; } fn f(mut u: Union, mut d: Data) { unsafe { drmut!(u.data).num = 1; } drmut!(d).num = 1; } ``` --- clippy_lints/src/reference.rs | 88 +++++++++++------------------------ tests/ui/deref_addrof.fixed | 32 ++++++------- tests/ui/deref_addrof.rs | 32 ++++++------- tests/ui/deref_addrof.stderr | 54 ++++++++------------- 4 files changed, 73 insertions(+), 133 deletions(-) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 0b8993ee1e11..3bbcad12a319 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::source::snippet; +use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::adjust_derefs_manually_drop; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, Mutability, Node, UnOp}; +use rustc_hir::{Expr, ExprKind, HirId, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -40,79 +40,43 @@ declare_lint_pass!(DerefAddrOf => [DEREF_ADDROF]); impl LateLintPass<'_> for DerefAddrOf { fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { - if let ExprKind::Unary(UnOp::Deref, deref_target) = e.kind - && let ExprKind::AddrOf(_, mutability, addrof_target) = deref_target.kind + if !e.span.from_expansion() + && let ExprKind::Unary(UnOp::Deref, deref_target) = e.kind + && !deref_target.span.from_expansion() + && let ExprKind::AddrOf(_, _, addrof_target) = deref_target.kind // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section. // See #12854 for details. && !matches!(addrof_target.kind, ExprKind::Array(_)) && deref_target.span.eq_ctxt(e.span) && !addrof_target.span.from_expansion() { + let mut applicability = Applicability::MachineApplicable; + let mut sugg = || Sugg::hir_with_applicability(cx, addrof_target, "_", &mut applicability); + // If this expression is an explicit `DerefMut` of a `ManuallyDrop` reached through a // union, we may remove the reference if we are at the point where the implicit // dereference would take place. Otherwise, we should not lint. - let keep_deref = match is_manually_drop_through_union(cx, e.hir_id, addrof_target) { - ManuallyDropThroughUnion::Directly => true, + let sugg = match is_manually_drop_through_union(cx, e.hir_id, addrof_target) { + ManuallyDropThroughUnion::Directly => sugg().deref(), ManuallyDropThroughUnion::Indirect => return, - ManuallyDropThroughUnion::No => false, + ManuallyDropThroughUnion::No => sugg(), }; - let mut applicability = Applicability::MachineApplicable; - let sugg = if e.span.from_expansion() { - if let Some(macro_source) = e.span.get_source_text(cx) { - // Remove leading whitespace from the given span - // e.g: ` $visitor` turns into `$visitor` - let trim_leading_whitespaces = |span: Span| { - span.get_source_text(cx) - .and_then(|snip| { - #[expect(clippy::cast_possible_truncation)] - snip.find(|c: char| !c.is_whitespace()) - .map(|pos| span.lo() + BytePos(pos as u32)) - }) - .map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) - }; - - let mut generate_snippet = |pattern: &str| { - #[expect(clippy::cast_possible_truncation)] - macro_source.rfind(pattern).map(|pattern_pos| { - let rpos = pattern_pos + pattern.len(); - let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); - let span = trim_leading_whitespaces(span_after_ref); - snippet_with_applicability(cx, span, "_", &mut applicability) - }) - }; - - if mutability == Mutability::Mut { - generate_snippet("mut") - } else { - generate_snippet("&") - } - } else { - Some(snippet_with_applicability(cx, e.span, "_", &mut applicability)) - } + let sugg = if has_enclosing_paren(snippet(cx, e.span, "")) { + sugg.maybe_paren() } else { - Some(snippet_with_applicability( - cx, - addrof_target.span, - "_", - &mut applicability, - )) + sugg }; - if let Some(sugg) = sugg { - span_lint_and_sugg( - cx, - DEREF_ADDROF, - e.span, - "immediately dereferencing a reference", - "try", - if keep_deref { - format!("(*{sugg})") - } else { - sugg.to_string() - }, - applicability, - ); - } + + span_lint_and_sugg( + cx, + DEREF_ADDROF, + e.span, + "immediately dereferencing a reference", + "try", + sugg.to_string(), + applicability, + ); } } } diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index ae8ed0dc1140..ffe7f7d14408 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -1,5 +1,3 @@ -//@aux-build:proc_macros.rs - #![allow( dangerous_implicit_autorefs, clippy::explicit_auto_deref, @@ -8,9 +6,6 @@ )] #![warn(clippy::deref_addrof)] -extern crate proc_macros; -use proc_macros::inline_macros; - fn get_number() -> usize { 10 } @@ -61,21 +56,22 @@ fn main() { //~^ deref_addrof // do NOT lint for array as semantic differences with/out `*&`. let _arr = *&[0, 1, 2, 3, 4]; -} -#[derive(Copy, Clone)] -pub struct S; -#[inline_macros] -impl S { - pub fn f(&self) -> &Self { - inline!($(@expr self)) - //~^ deref_addrof - } - #[allow(unused_mut)] // mut will be unused, once the macro is fixed - pub fn f_mut(mut self) -> Self { - inline!($(@expr self)) - //~^ deref_addrof + // Do not lint when text comes from macro + macro_rules! mac { + (dr) => { + *&0 + }; + (dr $e:expr) => { + *&$e + }; + (r $e:expr) => { + &$e + }; } + let b = mac!(dr); + let b = mac!(dr a); + let b = *mac!(r a); } fn issue14386() { diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 4ff014059167..bc253716affd 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -1,5 +1,3 @@ -//@aux-build:proc_macros.rs - #![allow( dangerous_implicit_autorefs, clippy::explicit_auto_deref, @@ -8,9 +6,6 @@ )] #![warn(clippy::deref_addrof)] -extern crate proc_macros; -use proc_macros::inline_macros; - fn get_number() -> usize { 10 } @@ -61,21 +56,22 @@ fn main() { //~^ deref_addrof // do NOT lint for array as semantic differences with/out `*&`. let _arr = *&[0, 1, 2, 3, 4]; -} -#[derive(Copy, Clone)] -pub struct S; -#[inline_macros] -impl S { - pub fn f(&self) -> &Self { - inline!(*& $(@expr self)) - //~^ deref_addrof - } - #[allow(unused_mut)] // mut will be unused, once the macro is fixed - pub fn f_mut(mut self) -> Self { - inline!(*&mut $(@expr self)) - //~^ deref_addrof + // Do not lint when text comes from macro + macro_rules! mac { + (dr) => { + *&0 + }; + (dr $e:expr) => { + *&$e + }; + (r $e:expr) => { + &$e + }; } + let b = mac!(dr); + let b = mac!(dr a); + let b = *mac!(r a); } fn issue14386() { diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index adfa542765c0..65dd904a8f75 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -1,5 +1,5 @@ error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:28:13 + --> tests/ui/deref_addrof.rs:23:13 | LL | let b = *&a; | ^^^ help: try: `a` @@ -8,122 +8,106 @@ LL | let b = *&a; = help: to override `-D warnings` add `#[allow(clippy::deref_addrof)]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:31:13 + --> tests/ui/deref_addrof.rs:26:13 | LL | let b = *&get_number(); | ^^^^^^^^^^^^^^ help: try: `get_number()` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:37:13 + --> tests/ui/deref_addrof.rs:32:13 | LL | let b = *&bytes[1..2][0]; | ^^^^^^^^^^^^^^^^ help: try: `bytes[1..2][0]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:42:13 + --> tests/ui/deref_addrof.rs:37:13 | LL | let b = *&(a); | ^^^^^ help: try: `(a)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:45:13 + --> tests/ui/deref_addrof.rs:40:13 | LL | let b = *(&a); | ^^^^^ help: try: `a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:49:13 + --> tests/ui/deref_addrof.rs:44:13 | LL | let b = *((&a)); | ^^^^^^^ help: try: `a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:52:13 + --> tests/ui/deref_addrof.rs:47:13 | LL | let b = *&&a; | ^^^^ help: try: `&a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:55:14 + --> tests/ui/deref_addrof.rs:50:14 | LL | let b = **&aref; | ^^^^^^ help: try: `aref` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:60:19 + --> tests/ui/deref_addrof.rs:55:19 | LL | let _repeat = *&[0; 64]; | ^^^^^^^^^ help: try: `[0; 64]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:71:17 - | -LL | inline!(*& $(@expr self)) - | ^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` - | - = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:76:17 - | -LL | inline!(*&mut $(@expr self)) - | ^^^^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` - | - = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:108:9 + --> tests/ui/deref_addrof.rs:104:9 | LL | (*&mut a.padding) = [1; size_of::()]; | ^^^^^^^^^^^^^^^^^ help: try: `a.padding` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:110:9 + --> tests/ui/deref_addrof.rs:106:9 | LL | (*&mut a.tup).1 = (); | ^^^^^^^^^^^^^ help: try: `a.tup` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:112:10 + --> tests/ui/deref_addrof.rs:108:10 | LL | **&mut a.prim = 0; | ^^^^^^^^^^^^ help: try: `a.prim` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:115:9 + --> tests/ui/deref_addrof.rs:111:9 | LL | (*&mut a.data).num = 42; | ^^^^^^^^^^^^^^ help: try: `(*a.data)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:117:9 + --> tests/ui/deref_addrof.rs:113:9 | LL | (*&mut a.indirect.md)[3] = 1; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect.md)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:119:9 + --> tests/ui/deref_addrof.rs:115:9 | LL | (*&mut a.indirect_arr[1].md)[3] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_arr[1].md)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:121:9 + --> tests/ui/deref_addrof.rs:117:9 | LL | (*&mut a.indirect_ref.md)[3] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_ref.md)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:125:10 + --> tests/ui/deref_addrof.rs:121:10 | LL | **&raw mut a.prim = 0; | ^^^^^^^^^^^^^^^^ help: try: `a.prim` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:127:9 + --> tests/ui/deref_addrof.rs:123:9 | LL | (*&raw mut a.data).num = 42; | ^^^^^^^^^^^^^^^^^^ help: try: `(*a.data)` -error: aborting due to 20 previous errors +error: aborting due to 18 previous errors From f4b3415271faa86e0d93cebfad9703b590a55983 Mon Sep 17 00:00:00 2001 From: Bruno Roy Date: Thu, 31 Jul 2025 18:52:33 -0400 Subject: [PATCH 0079/2093] [Doc] Add links to the various collections Add a few links to the collections mentioned in the module doc for Collections. --- library/std/src/collections/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index 889ed3c53803..6104a02c739b 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -26,7 +26,7 @@ //! should be considered. Detailed discussions of strengths and weaknesses of //! individual collections can be found on their own documentation pages. //! -//! ### Use a `Vec` when: +//! ### Use a [`Vec`] when: //! * You want to collect items up to be processed or sent elsewhere later, and //! don't care about any properties of the actual values being stored. //! * You want a sequence of elements in a particular order, and will only be @@ -35,25 +35,25 @@ //! * You want a resizable array. //! * You want a heap-allocated array. //! -//! ### Use a `VecDeque` when: +//! ### Use a [`VecDeque`] when: //! * You want a [`Vec`] that supports efficient insertion at both ends of the //! sequence. //! * You want a queue. //! * You want a double-ended queue (deque). //! -//! ### Use a `LinkedList` when: +//! ### Use a [`LinkedList`] when: //! * You want a [`Vec`] or [`VecDeque`] of unknown size, and can't tolerate //! amortization. //! * You want to efficiently split and append lists. //! * You are *absolutely* certain you *really*, *truly*, want a doubly linked //! list. //! -//! ### Use a `HashMap` when: +//! ### Use a [`HashMap`] when: //! * You want to associate arbitrary keys with an arbitrary value. //! * You want a cache. //! * You want a map, with no extra functionality. //! -//! ### Use a `BTreeMap` when: +//! ### Use a [`BTreeMap`] when: //! * You want a map sorted by its keys. //! * You want to be able to get a range of entries on-demand. //! * You're interested in what the smallest or largest key-value pair is. @@ -65,7 +65,7 @@ //! * There is no meaningful value to associate with your keys. //! * You just want a set. //! -//! ### Use a `BinaryHeap` when: +//! ### Use a [`BinaryHeap`] when: //! //! * You want to store a bunch of elements, but only ever want to process the //! "biggest" or "most important" one at any given time. From 24c770ba1c53c4c04acdba945cd9f59d687f4d5b Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 1 Aug 2025 00:32:56 +0000 Subject: [PATCH 0080/2093] aarch64: Make `outline-atomics` a known target feature This is a feature used by LLVM that is enabled for our `aarch64-linux` targets, which we would like to configure on in `std`. Thus, mark `outline-atomics` a known feature. It is left unstable for now. --- compiler/rustc_target/src/target_features.rs | 4 ++++ tests/assembly-llvm/asm/aarch64-outline-atomics.rs | 4 ++++ tests/ui/check-cfg/target_feature.stderr | 1 + 3 files changed, 9 insertions(+) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index b2af99228fe6..7551d5a66fc9 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -248,6 +248,10 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("mte", Stable, &[]), // FEAT_AdvSimd & FEAT_FP ("neon", Stable, &[]), + // Backend option to turn atomic operations into an intrinsic call when `lse` is not known to be + // available, so the intrinsic can do runtime LSE feature detection rather than unconditionally + // using slower non-LSE operations. Unstable since it doesn't need to user-togglable. + ("outline-atomics", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_PAUTH (address authentication) ("paca", Stable, &[]), // FEAT_PAUTH (generic authentication) diff --git a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs index 22599c18dcf3..1177c1e68ed5 100644 --- a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs +++ b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs @@ -8,6 +8,10 @@ use std::sync::atomic::AtomicI32; use std::sync::atomic::Ordering::*; +// Verify config on outline-atomics works (it is always enabled on aarch64-linux). +#[cfg(not(target_feature = "outline-atomics"))] +compile_error!("outline-atomics is not enabled"); + pub fn compare_exchange(a: &AtomicI32) { // On AArch64 LLVM should outline atomic operations. // CHECK: __aarch64_cas4_relax diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index f422919983b7..e730492fd9b0 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -183,6 +183,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `nnp-assist` `nontrapping-fptoint` `nvic` +`outline-atomics` `paca` `pacg` `pan` From 613080b5f1d58a2496d9f8dc8c314d5432fe3d6f Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Thu, 31 Jul 2025 23:46:04 -0400 Subject: [PATCH 0081/2093] add Option::reduce --- library/core/src/option.rs | 36 ++++++++++++++++++++++++++++++++++ library/coretests/tests/lib.rs | 1 + 2 files changed, 37 insertions(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 0e481519f76f..f3ddc05bc1a6 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1961,6 +1961,42 @@ impl Option { _ => None, } } + + /// Reduces two options into one, using the provided function if both are `Some`. + /// + /// If `self` is `Some(s)` and `other` is `Some(o)`, this method returns `Some(f(s, o))`. + /// Otherwise, if only one of `self` and `other` is `Some`, that one is returned. + /// If both `self` and `other` are `None`, `None` is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_reduce)] + /// + /// let s12 = Some(12); + /// let s17 = Some(17); + /// let n = None; + /// let f = |a, b| a + b; + /// + /// assert_eq!(s12.reduce(s17, f), Some(29)); + /// assert_eq!(s12.reduce(n, f), Some(12)); + /// assert_eq!(n.reduce(s17, f), Some(17)); + /// assert_eq!(n.reduce(n, f), None); + /// ``` + #[unstable(feature = "option_reduce", issue = "144273")] + pub fn reduce(self, other: Option, f: F) -> Option + where + T: Into, + U: Into, + F: FnOnce(T, U) -> R, + { + match (self, other) { + (Some(a), Some(b)) => Some(f(a, b)), + (Some(a), _) => Some(a.into()), + (_, Some(b)) => Some(b.into()), + _ => None, + } + } } impl Option<(T, U)> { diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 4cfac9ecc2ab..3f817ab128bb 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -77,6 +77,7 @@ #![feature(never_type)] #![feature(next_index)] #![feature(numfmt)] +#![feature(option_reduce)] #![feature(pattern)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] From ad61b3113c7ce43c60134bf5441397b82898a78c Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 31 Jul 2025 22:53:27 -0500 Subject: [PATCH 0082/2093] Correct some grammar in integer documentation Update "between" to "among" (more than two items), connect the "which" dependent clause to the independent part, and remove the redundant "here". --- library/core/src/num/int_macros.rs | 9 +++------ library/core/src/num/saturating.rs | 4 ++-- library/core/src/num/uint_macros.rs | 13 +++++-------- library/core/src/num/wrapping.rs | 4 ++-- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 5683d5ec92dc..7fa2a3e45f37 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2512,8 +2512,7 @@ macro_rules! int_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `i32` is used here. + /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` /// #![feature(bigint_helper_methods)] @@ -2543,8 +2542,7 @@ macro_rules! int_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `i32` is used here. + /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` /// #![feature(bigint_helper_methods)] @@ -2581,8 +2579,7 @@ macro_rules! int_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `i32` is used here. + /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` /// #![feature(bigint_helper_methods)] diff --git a/library/core/src/num/saturating.rs b/library/core/src/num/saturating.rs index 4460e430aecf..e3a79dca7646 100644 --- a/library/core/src/num/saturating.rs +++ b/library/core/src/num/saturating.rs @@ -660,8 +660,8 @@ macro_rules! saturating_int_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `i16` is used here. + /// Please note that this example is shared among integer types, which is why `i16` + /// is used. /// /// ``` /// use std::num::Saturating; diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 584cd60fbe5c..84d8de665b23 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2136,8 +2136,7 @@ macro_rules! uint_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `u8` is used here. + /// Please note that this example is shared among integer types, which is why `u8` is used. /// /// ``` /// assert_eq!(10u8.wrapping_mul(12), 120); @@ -2627,8 +2626,8 @@ macro_rules! uint_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. + /// Please note that this example is shared among integer types, which is why why `u32` + /// is used. /// /// ``` /// assert_eq!(5u32.overflowing_mul(2), (10, false)); @@ -2654,8 +2653,7 @@ macro_rules! uint_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. + /// Please note that this example is shared among integer types, which is why `u32` is used. /// /// ``` /// #![feature(bigint_helper_methods)] @@ -2685,8 +2683,7 @@ macro_rules! uint_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. + /// Please note that this example is shared among integer types, which is why `u32` is used. /// /// ``` /// #![feature(bigint_helper_methods)] diff --git a/library/core/src/num/wrapping.rs b/library/core/src/num/wrapping.rs index c460f38bd2e4..e91280e056dd 100644 --- a/library/core/src/num/wrapping.rs +++ b/library/core/src/num/wrapping.rs @@ -677,8 +677,8 @@ macro_rules! wrapping_int_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `i16` is used here. + /// Please note that this example is shared among integer types, which is why `i16` + /// is used. /// /// Basic usage: /// From 67cce09bfb87c896ec83117d0d61f847cdb8de60 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 1 Aug 2025 05:06:37 +0000 Subject: [PATCH 0083/2093] Prepare for merging from rust-lang/rust This updates the rust-version file to adcb3d3b4cd3b7c4cde642f3ed537037f293738e. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 1ced6098acf4..bf69accaf063 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -32e7a4b92b109c24e9822c862a7c74436b50e564 +adcb3d3b4cd3b7c4cde642f3ed537037f293738e From 0ff984894b263ba3efaee10a32ece57e50b2320a Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 1 Aug 2025 05:14:44 +0000 Subject: [PATCH 0084/2093] fmt --- src/tools/miri/src/machine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 00c3373bb0f3..757244c11a69 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -12,10 +12,10 @@ use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use rustc_abi::{Align, ExternAbi, Size}; use rustc_apfloat::{Float, FloatConvert}; -use rustc_hir::attrs::InlineAttr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[allow(unused)] use rustc_data_structures::static_assert_size; +use rustc_hir::attrs::InlineAttr; use rustc_middle::mir; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ From 6ab38e446a1e4053f515922d4d1456d3a95c1a36 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 1 Aug 2025 08:57:31 +0200 Subject: [PATCH 0085/2093] update rustup PR message --- src/tools/miri/.github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 4e55200cddf0..7d79c384f85a 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -198,7 +198,7 @@ jobs: BRANCH="rustup-$(date -u +%Y-%m-%d)" git switch -c $BRANCH git push -u origin $BRANCH - gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.' + gh pr create -B master --title 'Automatic Rustup' --body "Update \`rustc\` to https://github.com/rust-lang/rust/commit/$(cat rust-version)." env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} From a067c6a3c6ac4db7652e1722acf2648ef65b0229 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Sun, 1 Jun 2025 12:51:17 +0200 Subject: [PATCH 0086/2093] refactor `unreachable/expr_cast.rs` test --- tests/ui/reachable/expr_cast.rs | 15 +++++++-------- tests/ui/reachable/expr_cast.stderr | 26 ++++++++++++++------------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/tests/ui/reachable/expr_cast.rs b/tests/ui/reachable/expr_cast.rs index e8e477ea4f68..58ebe2a7b9fc 100644 --- a/tests/ui/reachable/expr_cast.rs +++ b/tests/ui/reachable/expr_cast.rs @@ -1,13 +1,12 @@ -#![allow(unused_variables)] -#![allow(unused_assignments)] -#![allow(dead_code)] +//@ edition: 2024 #![deny(unreachable_code)] -#![feature(never_type, type_ascription)] fn a() { - // the cast is unreachable: - let x = {return} as !; //~ ERROR unreachable - //~| ERROR non-primitive cast + _ = {return} as u32; //~ error: unreachable } -fn main() { } +fn b() { + (return) as u32; //~ error: unreachable +} + +fn main() {} diff --git a/tests/ui/reachable/expr_cast.stderr b/tests/ui/reachable/expr_cast.stderr index 6643f1784a17..cf711d4436e0 100644 --- a/tests/ui/reachable/expr_cast.stderr +++ b/tests/ui/reachable/expr_cast.stderr @@ -1,24 +1,26 @@ error: unreachable expression - --> $DIR/expr_cast.rs:9:13 + --> $DIR/expr_cast.rs:5:9 | -LL | let x = {return} as !; - | ^------^^^^^^ - | || - | |any code following this expression is unreachable - | unreachable expression +LL | _ = {return} as u32; + | ^------^^^^^^^^ + | || + | |any code following this expression is unreachable + | unreachable expression | note: the lint level is defined here - --> $DIR/expr_cast.rs:4:9 + --> $DIR/expr_cast.rs:2:9 | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ -error[E0605]: non-primitive cast: `()` as `!` - --> $DIR/expr_cast.rs:9:13 +error: unreachable expression + --> $DIR/expr_cast.rs:9:5 | -LL | let x = {return} as !; - | ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +LL | (return) as u32; + | --------^^^^^^^ + | | + | unreachable expression + | any code following this expression is unreachable error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0605`. From d2e133d96a2a7b0063cfd514a35304e4908dcb6f Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Sun, 1 Jun 2025 12:51:17 +0200 Subject: [PATCH 0087/2093] don't warn on explicit casts of never to any --- compiler/rustc_hir_typeck/src/expr.rs | 3 +++ tests/ui/reachable/expr_cast.rs | 13 ++++++++-- tests/ui/reachable/expr_cast.stderr | 26 ------------------- tests/ui/reachable/unreachable-try-pattern.rs | 2 +- .../reachable/unreachable-try-pattern.stderr | 11 ++++---- 5 files changed, 20 insertions(+), 35 deletions(-) delete mode 100644 tests/ui/reachable/expr_cast.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 454ec7ddcafb..74136b546282 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -290,6 +290,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ExprKind::Let(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {} + // Do not warn on `as` casts from never to any, + // they are sometimes required to appeal typeck. + ExprKind::Cast(_, _) => {} // If `expr` is a result of desugaring the try block and is an ok-wrapped // diverging expression (e.g. it arose from desugaring of `try { return }`), // we skip issuing a warning because it is autogenerated code. diff --git a/tests/ui/reachable/expr_cast.rs b/tests/ui/reachable/expr_cast.rs index 58ebe2a7b9fc..aa412c99b2e9 100644 --- a/tests/ui/reachable/expr_cast.rs +++ b/tests/ui/reachable/expr_cast.rs @@ -1,12 +1,21 @@ +//@ check-pass //@ edition: 2024 +// +// Check that we don't warn on `as` casts of never to any as unreachable. +// While they *are* unreachable, sometimes they are required to appeal typeck. #![deny(unreachable_code)] fn a() { - _ = {return} as u32; //~ error: unreachable + _ = {return} as u32; } fn b() { - (return) as u32; //~ error: unreachable + (return) as u32; +} + +// example that needs an explicit never-to-any `as` cast +fn example() -> impl Iterator { + todo!() as std::iter::Empty<_> } fn main() {} diff --git a/tests/ui/reachable/expr_cast.stderr b/tests/ui/reachable/expr_cast.stderr deleted file mode 100644 index cf711d4436e0..000000000000 --- a/tests/ui/reachable/expr_cast.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: unreachable expression - --> $DIR/expr_cast.rs:5:9 - | -LL | _ = {return} as u32; - | ^------^^^^^^^^ - | || - | |any code following this expression is unreachable - | unreachable expression - | -note: the lint level is defined here - --> $DIR/expr_cast.rs:2:9 - | -LL | #![deny(unreachable_code)] - | ^^^^^^^^^^^^^^^^ - -error: unreachable expression - --> $DIR/expr_cast.rs:9:5 - | -LL | (return) as u32; - | --------^^^^^^^ - | | - | unreachable expression - | any code following this expression is unreachable - -error: aborting due to 2 previous errors - diff --git a/tests/ui/reachable/unreachable-try-pattern.rs b/tests/ui/reachable/unreachable-try-pattern.rs index 22cbfb95af08..1358722e229c 100644 --- a/tests/ui/reachable/unreachable-try-pattern.rs +++ b/tests/ui/reachable/unreachable-try-pattern.rs @@ -18,7 +18,7 @@ fn bar(x: Result) -> Result { fn foo(x: Result) -> Result { let y = (match x { Ok(n) => Ok(n as u32), Err(e) => Err(e) })?; //~^ WARN unreachable pattern - //~| WARN unreachable expression + //~| WARN unreachable call Ok(y) } diff --git a/tests/ui/reachable/unreachable-try-pattern.stderr b/tests/ui/reachable/unreachable-try-pattern.stderr index 40b116131057..468af427249c 100644 --- a/tests/ui/reachable/unreachable-try-pattern.stderr +++ b/tests/ui/reachable/unreachable-try-pattern.stderr @@ -1,11 +1,10 @@ -warning: unreachable expression - --> $DIR/unreachable-try-pattern.rs:19:36 +warning: unreachable call + --> $DIR/unreachable-try-pattern.rs:19:33 | LL | let y = (match x { Ok(n) => Ok(n as u32), Err(e) => Err(e) })?; - | -^^^^^^^ - | | - | unreachable expression - | any code following this expression is unreachable + | ^^ - any code following this expression is unreachable + | | + | unreachable call | note: the lint level is defined here --> $DIR/unreachable-try-pattern.rs:3:9 From 4ad8606d79a7cb9693f315fdd979fdb03d72318e Mon Sep 17 00:00:00 2001 From: "Pascal S. de Kloe" Date: Tue, 8 Jul 2025 17:56:25 +0200 Subject: [PATCH 0088/2093] fmt with table lookup for binary, octal and hex * correct buffer size * no trait abstraction * similar to decimal --- library/core/src/fmt/num.rs | 181 ++++++++++++------------------------ 1 file changed, 57 insertions(+), 124 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index a52749d43259..cdabaeb72654 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -10,9 +10,6 @@ use crate::{fmt, ptr, slice, str}; trait DisplayInt: PartialEq + PartialOrd + Div + Rem + Sub + Copy { - fn zero() -> Self; - fn from_u8(u: u8) -> Self; - fn to_u8(&self) -> u8; #[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))] fn to_u32(&self) -> u32; fn to_u64(&self) -> u64; @@ -22,9 +19,6 @@ trait DisplayInt: macro_rules! impl_int { ($($t:ident)*) => ( $(impl DisplayInt for $t { - fn zero() -> Self { 0 } - fn from_u8(u: u8) -> Self { u as Self } - fn to_u8(&self) -> u8 { *self as u8 } #[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))] fn to_u32(&self) -> u32 { *self as u32 } fn to_u64(&self) -> u64 { *self as u64 } @@ -38,137 +32,76 @@ impl_int! { u8 u16 u32 u64 u128 usize } -/// A type that represents a specific radix -/// -/// # Safety -/// -/// `digit` must return an ASCII character. -#[doc(hidden)] -unsafe trait GenericRadix: Sized { - /// The number of digits. - const BASE: u8; - - /// A radix-specific prefix string. - const PREFIX: &'static str; - - /// Converts an integer to corresponding radix digit. - fn digit(x: u8) -> u8; - - /// Format an unsigned integer using the radix using a formatter. - fn fmt_int(&self, mut x: T, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // The radix can be as low as 2, so we need a buffer of at least 128 - // characters for a base 2 number. - let zero = T::zero(); - let mut buf = [MaybeUninit::::uninit(); 128]; - let mut offset = buf.len(); - let base = T::from_u8(Self::BASE); - - // Accumulate each digit of the number from the least significant - // to the most significant figure. - loop { - let n = x % base; // Get the current place value. - x = x / base; // Deaccumulate the number. - offset -= 1; - buf[offset].write(Self::digit(n.to_u8())); // Store the digit in the buffer. - if x == zero { - // No more digits left to accumulate. - break; - }; - } - - // SAFETY: Starting from `offset`, all elements of the slice have been set. - let digits = unsafe { slice_buffer_to_str(&buf, offset) }; - f.pad_integral(true, Self::PREFIX, digits) - } -} - -/// A binary (base 2) radix -#[derive(Clone, PartialEq)] -struct Binary; - -/// An octal (base 8) radix -#[derive(Clone, PartialEq)] -struct Octal; - -/// A hexadecimal (base 16) radix, formatted with lower-case characters -#[derive(Clone, PartialEq)] -struct LowerHex; - -/// A hexadecimal (base 16) radix, formatted with upper-case characters -#[derive(Clone, PartialEq)] -struct UpperHex; - -macro_rules! radix { - ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => { - unsafe impl GenericRadix for $T { - const BASE: u8 = $base; - const PREFIX: &'static str = $prefix; - fn digit(x: u8) -> u8 { - match x { - $($x => $conv,)+ - x => panic!("number not in the range 0..={}: {}", Self::BASE - 1, x), +/// Formatting of integers with a non-decimal radix. +macro_rules! radix_integer { + (fmt::$Trait:ident for $Signed:ident and $Unsigned:ident, $prefix:literal, $dig_tab:literal) => { + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::$Trait for $Unsigned { + /// Format unsigned integers in the radix. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Check macro arguments at compile time. + const { + assert!($Unsigned::MIN == 0, "need unsigned"); + assert!($dig_tab.is_ascii(), "need single-byte entries"); } + + // ASCII digits in ascending order are used as a lookup table. + const DIG_TAB: &[u8] = $dig_tab; + const BASE: $Unsigned = DIG_TAB.len() as $Unsigned; + const MAX_DIG_N: usize = $Unsigned::MAX.ilog(BASE) as usize + 1; + + // Buffer digits of self with right alignment. + let mut buf = [MaybeUninit::::uninit(); MAX_DIG_N]; + // Count the number of bytes in buf that are not initialized. + let mut offset = buf.len(); + + // Accumulate each digit of the number from the least + // significant to the most significant figure. + let mut remain = *self; + loop { + let digit = remain % BASE; + remain /= BASE; + + offset -= 1; + // SAFETY: `remain` will reach 0 and we will break before `offset` wraps + unsafe { core::hint::assert_unchecked(offset < buf.len()) } + buf[offset].write(DIG_TAB[digit as usize]); + if remain == 0 { + break; + } + } + + // SAFETY: Starting from `offset`, all elements of the slice have been set. + let digits = unsafe { slice_buffer_to_str(&buf, offset) }; + f.pad_integral(true, $prefix, digits) } } - } -} -radix! { Binary, 2, "0b", x @ 0 ..= 1 => b'0' + x } -radix! { Octal, 8, "0o", x @ 0 ..= 7 => b'0' + x } -radix! { LowerHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'a' + (x - 10) } -radix! { UpperHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'A' + (x - 10) } - -macro_rules! int_base { - (fmt::$Trait:ident for $T:ident -> $Radix:ident) => { #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::$Trait for $T { + impl fmt::$Trait for $Signed { + /// Format signed integers in the two’s-complement form. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - $Radix.fmt_int(*self, f) + fmt::$Trait::fmt(&self.cast_unsigned(), f) } } }; } -macro_rules! integer { - ($Int:ident, $Uint:ident) => { - int_base! { fmt::Binary for $Uint -> Binary } - int_base! { fmt::Octal for $Uint -> Octal } - int_base! { fmt::LowerHex for $Uint -> LowerHex } - int_base! { fmt::UpperHex for $Uint -> UpperHex } - - // Format signed integers as unsigned (two’s complement representation). - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::Binary for $Int { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Binary::fmt(&self.cast_unsigned(), f) - } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::Octal for $Int { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Octal::fmt(&self.cast_unsigned(), f) - } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::LowerHex for $Int { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::LowerHex::fmt(&self.cast_unsigned(), f) - } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::UpperHex for $Int { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.cast_unsigned(), f) - } - } +/// Formatting of integers with a non-decimal radix. +macro_rules! radix_integers { + ($Signed:ident, $Unsigned:ident) => { + radix_integer! { fmt::Binary for $Signed and $Unsigned, "0b", b"01" } + radix_integer! { fmt::Octal for $Signed and $Unsigned, "0o", b"01234567" } + radix_integer! { fmt::LowerHex for $Signed and $Unsigned, "0x", b"0123456789abcdef" } + radix_integer! { fmt::UpperHex for $Signed and $Unsigned, "0x", b"0123456789ABCDEF" } }; } -integer! { isize, usize } -integer! { i8, u8 } -integer! { i16, u16 } -integer! { i32, u32 } -integer! { i64, u64 } -integer! { i128, u128 } +radix_integers! { isize, usize } +radix_integers! { i8, u8 } +radix_integers! { i16, u16 } +radix_integers! { i32, u32 } +radix_integers! { i64, u64 } +radix_integers! { i128, u128 } macro_rules! impl_Debug { ($($T:ident)*) => { From 74c6fdf171e04e368e6e2179158396a4ead6c634 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 27 Jul 2025 18:01:40 +0200 Subject: [PATCH 0089/2093] c-variadic: multiple ABIs in the same program for arm --- .../src/directives/directive_names.rs | 1 + .../same-program-multiple-abis-arm.rs | 77 +++++++++++++++++++ ...s => same-program-multiple-abis-x86_64.rs} | 0 3 files changed, 78 insertions(+) create mode 100644 tests/ui/c-variadic/same-program-multiple-abis-arm.rs rename tests/ui/c-variadic/{same-program-multiple-abis.rs => same-program-multiple-abis-x86_64.rs} (100%) diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 7fc76a42e0ca..cc4e81c357ad 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -193,6 +193,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-bpf", "only-cdb", "only-dist", + "only-eabihf", "only-elf", "only-emscripten", "only-gnu", diff --git a/tests/ui/c-variadic/same-program-multiple-abis-arm.rs b/tests/ui/c-variadic/same-program-multiple-abis-arm.rs new file mode 100644 index 000000000000..1b0bdecabfbc --- /dev/null +++ b/tests/ui/c-variadic/same-program-multiple-abis-arm.rs @@ -0,0 +1,77 @@ +#![feature(extended_varargs_abi_support)] +//@ run-pass +//@ only-arm +//@ ignore-thumb (this test uses arm assembly) +//@ only-eabihf (the assembly below requires float hardware support) + +// Check that multiple c-variadic calling conventions can be used in the same program. +// +// Clang and gcc reject defining functions with a non-default calling convention and a variable +// argument list, so C programs that use multiple c-variadic calling conventions are unlikely +// to come up. Here we validate that our codegen backends do in fact generate correct code. + +extern "C" { + fn variadic_c(_: f64, _: ...) -> f64; +} + +extern "aapcs" { + fn variadic_aapcs(_: f64, _: ...) -> f64; +} + +fn main() { + unsafe { + assert_eq!(variadic_c(1.0, 2.0, 3.0), 1.0 + 2.0 + 3.0); + assert_eq!(variadic_aapcs(1.0, 2.0, 3.0), 1.0 + 2.0 + 3.0); + } +} + +// This assembly was generated using https://godbolt.org/z/xcW6a1Tj5, and corresponds to the +// following code compiled for the `armv7-unknown-linux-gnueabihf` target: +// +// ```rust +// #![feature(c_variadic)] +// +// #[unsafe(no_mangle)] +// unsafe extern "C" fn variadic(a: f64, mut args: ...) -> f64 { +// let b = args.arg::(); +// let c = args.arg::(); +// +// a + b + c +// } +// ``` +// +// This function uses floats (and passes one normal float argument) because the aapcs and C calling +// conventions differ in how floats are passed, e.g. https://godbolt.org/z/sz799f51x. However, for +// c-variadic functions, both ABIs actually behave the same, based on: +// +// https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#65parameter-passing +// +// > A variadic function is always marshaled as for the base standard. +// +// https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#7the-standard-variants +// +// > This section applies only to non-variadic functions. For a variadic function the base standard +// > is always used both for argument passing and result return. +core::arch::global_asm!( + r#" +{variadic_c}: +{variadic_aapcs}: + sub sp, sp, #12 + stmib sp, {{r2, r3}} + vmov d0, r0, r1 + add r0, sp, #4 + vldr d1, [sp, #4] + add r0, r0, #15 + bic r0, r0, #7 + vadd.f64 d0, d0, d1 + add r1, r0, #8 + str r1, [sp] + vldr d1, [r0] + vadd.f64 d0, d0, d1 + vmov r0, r1, d0 + add sp, sp, #12 + bx lr + "#, + variadic_c = sym variadic_c, + variadic_aapcs = sym variadic_aapcs, +); diff --git a/tests/ui/c-variadic/same-program-multiple-abis.rs b/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs similarity index 100% rename from tests/ui/c-variadic/same-program-multiple-abis.rs rename to tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs From c0c48984b05e244fc93144d0c1d48db318639b3b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 2 Aug 2025 17:28:17 +0000 Subject: [PATCH 0090/2093] Method probe: check doc aliases after edit distance. --- compiler/rustc_hir_typeck/src/method/probe.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 1f3969bd93c3..f5584e0596a6 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -2373,17 +2373,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if !self.is_relevant_kind_for_mode(x.kind) { return false; } - if self.matches_by_doc_alias(x.def_id) { - return true; - } - match edit_distance_with_substrings( + if let Some(d) = edit_distance_with_substrings( name.as_str(), x.name().as_str(), max_dist, ) { - Some(d) => d > 0, - None => false, + return d > 0; } + self.matches_by_doc_alias(x.def_id) }) .copied() .collect() From e772f7ab3ee7d5f217c60b640b95f8bfc84d9536 Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Wed, 23 Oct 2024 20:13:56 -0400 Subject: [PATCH 0091/2093] corrected lifetime in core::panic::Location::file return type --- library/core/src/panic/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 6ef7d5a22a30..e81dc5eb5304 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -183,7 +183,7 @@ impl<'a> Location<'a> { #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] #[rustc_const_stable(feature = "const_location_fields", since = "1.79.0")] - pub const fn file(&self) -> &str { + pub const fn file(&self) -> &'a str { // SAFETY: The filename is valid. unsafe { self.filename.as_ref() } } From 9c4a35e0e705dbe93dcfbba9fbde9d3dbfc13e11 Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Wed, 23 Oct 2024 23:49:23 -0400 Subject: [PATCH 0092/2093] added regression test for `core::panic::Location::file`'s lifetime --- library/core/tests/panic/location.rs | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 library/core/tests/panic/location.rs diff --git a/library/core/tests/panic/location.rs b/library/core/tests/panic/location.rs new file mode 100644 index 000000000000..35017612f00c --- /dev/null +++ b/library/core/tests/panic/location.rs @@ -0,0 +1,39 @@ +use core::panic::Location; + +// Note: Some of the following tests depend on the source location, +// so please be careful when editing this file. + +#[test] +fn location_const_caller() { + const _CALLER_REFERENCE: &Location<'static> = Location::caller(); + const _CALLER: Location<'static> = *Location::caller(); +} + +#[test] +fn location_const_file() { + const CALLER: &Location<'static> = Location::caller(); + const FILE: &str = CALLER.file(); + assert_eq!(FILE, file!()); +} + +#[test] +fn location_const_line() { + const CALLER: &Location<'static> = Location::caller(); + const LINE: u32 = CALLER.line(); + assert_eq!(LINE, 21); +} + +#[test] +fn location_const_column() { + const CALLER: &Location<'static> = Location::caller(); + const COLUMN: u32 = CALLER.column(); + assert_eq!(COLUMN, 40); +} + +#[test] +fn location_file_lifetime<'x>() { + // Verify that the returned `&str`s lifetime is derived from the generic + // lifetime 'a, not the lifetime of `&self`, when calling `Location::file`. + // Test failure is indicated by a compile failure, not a runtime panic. + let _: for<'a> fn(&'a Location<'x>) -> &'x str = Location::file; +} From 557737062d1a65b25f9bbb257f07755078e5cdae Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Wed, 23 Oct 2024 20:13:56 -0400 Subject: [PATCH 0093/2093] corrected lifetime in core::panic::Location::file return type --- library/core/src/panic/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index e81dc5eb5304..cafdcfa2c2e0 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -195,7 +195,7 @@ impl<'a> Location<'a> { #[must_use] #[unstable(feature = "file_with_nul", issue = "141727")] #[inline] - pub const fn file_with_nul(&self) -> &CStr { + pub const fn file_with_nul(&self) -> &'a CStr { let filename = self.filename.as_ptr(); // SAFETY: The filename is valid for `filename_len+1` bytes, so this addition can't From 8a0438fee86b7dc566d0758b3aee050b84edc842 Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Sat, 2 Aug 2025 16:53:06 -0400 Subject: [PATCH 0094/2093] moved new test to updated test location --- library/core/tests/panic/location.rs | 39 ----------------------- library/coretests/tests/panic/location.rs | 8 +++++ 2 files changed, 8 insertions(+), 39 deletions(-) delete mode 100644 library/core/tests/panic/location.rs diff --git a/library/core/tests/panic/location.rs b/library/core/tests/panic/location.rs deleted file mode 100644 index 35017612f00c..000000000000 --- a/library/core/tests/panic/location.rs +++ /dev/null @@ -1,39 +0,0 @@ -use core::panic::Location; - -// Note: Some of the following tests depend on the source location, -// so please be careful when editing this file. - -#[test] -fn location_const_caller() { - const _CALLER_REFERENCE: &Location<'static> = Location::caller(); - const _CALLER: Location<'static> = *Location::caller(); -} - -#[test] -fn location_const_file() { - const CALLER: &Location<'static> = Location::caller(); - const FILE: &str = CALLER.file(); - assert_eq!(FILE, file!()); -} - -#[test] -fn location_const_line() { - const CALLER: &Location<'static> = Location::caller(); - const LINE: u32 = CALLER.line(); - assert_eq!(LINE, 21); -} - -#[test] -fn location_const_column() { - const CALLER: &Location<'static> = Location::caller(); - const COLUMN: u32 = CALLER.column(); - assert_eq!(COLUMN, 40); -} - -#[test] -fn location_file_lifetime<'x>() { - // Verify that the returned `&str`s lifetime is derived from the generic - // lifetime 'a, not the lifetime of `&self`, when calling `Location::file`. - // Test failure is indicated by a compile failure, not a runtime panic. - let _: for<'a> fn(&'a Location<'x>) -> &'x str = Location::file; -} diff --git a/library/coretests/tests/panic/location.rs b/library/coretests/tests/panic/location.rs index 910001bcc1c5..2174ac854e94 100644 --- a/library/coretests/tests/panic/location.rs +++ b/library/coretests/tests/panic/location.rs @@ -47,6 +47,14 @@ fn location_const_column() { assert_eq!(COLUMN, 40); } +#[test] +fn location_file_lifetime<'x>() { + // Verify that the returned `&str`s lifetime is derived from the generic + // lifetime 'a, not the lifetime of `&self`, when calling `Location::file`. + // Test failure is indicated by a compile failure, not a runtime panic. + let _: for<'a> fn(&'a Location<'x>) -> &'x str = Location::file; +} + #[test] fn location_debug() { let f = format!("{:?}", Location::caller()); From 792ec3bdd42800bfbf16ee1d570efc398bf30f30 Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Sat, 2 Aug 2025 17:34:59 -0400 Subject: [PATCH 0095/2093] updated line number in test --- library/coretests/tests/panic/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/coretests/tests/panic/location.rs b/library/coretests/tests/panic/location.rs index 2174ac854e94..a7db05a15c68 100644 --- a/library/coretests/tests/panic/location.rs +++ b/library/coretests/tests/panic/location.rs @@ -59,7 +59,7 @@ fn location_file_lifetime<'x>() { fn location_debug() { let f = format!("{:?}", Location::caller()); assert!(f.contains(&format!("{:?}", file!()))); - assert!(f.contains("52")); + assert!(f.contains("60")); assert!(f.contains("29")); } From a7162e416e73afd4fc4c72f171b8166bb9d35b8c Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 2 Aug 2025 23:42:57 +0200 Subject: [PATCH 0096/2093] Remove references to two unknown lints in config `option_map_unwrap_or` and `seek_rewind` are not current lints, and cannot be referenced as having the `msrv` configuration option. --- book/src/lint_configuration.md | 2 -- clippy_config/src/conf.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 7f16f3a98105..6d554170f1fe 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -873,7 +873,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) * [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics) * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) -* [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) @@ -881,7 +880,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`repeat_vec_with_capacity`](https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity) * [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) -* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) * [`to_digit_is_some`](https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 8167d75583ee..a9987b4e2ec5 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -775,7 +775,6 @@ define_Conf! { needless_borrow, non_std_lazy_statics, option_as_ref_deref, - option_map_unwrap_or, ptr_as_ptr, question_mark, redundant_field_names, @@ -783,7 +782,6 @@ define_Conf! { repeat_vec_with_capacity, same_item_push, seek_from_current, - seek_rewind, to_digit_is_some, transmute_ptr_to_ref, tuple_array_conversions, From 07b04a2e04348283acf9565f3de2c500cce4df9a Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 2 Aug 2025 23:52:39 +0200 Subject: [PATCH 0097/2093] Check that all configuration options reference existing lints --- tests/config-consistency.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/config-consistency.rs diff --git a/tests/config-consistency.rs b/tests/config-consistency.rs new file mode 100644 index 000000000000..9e7ca26c7d40 --- /dev/null +++ b/tests/config-consistency.rs @@ -0,0 +1,30 @@ +#![feature(rustc_private)] + +// This test checks that all lints defined in `clippy_config::conf` in `#[lints]` +// attributes exist as Clippy lints. +// +// This test is a no-op if run as part of the compiler test suite +// and will always succeed. + +use std::collections::HashSet; + +#[test] +fn config_consistency() { + if option_env!("RUSTC_TEST_SUITE").is_some() { + return; + } + + let lint_names: HashSet = clippy_lints::declared_lints::LINTS + .iter() + .map(|lint_info| lint_info.lint.name.strip_prefix("clippy::").unwrap().to_lowercase()) + .collect(); + for conf in clippy_config::get_configuration_metadata() { + for lint in conf.lints { + assert!( + lint_names.contains(*lint), + "Configuration option {} references lint `{lint}` which does not exist", + conf.name + ); + } + } +} From 064825e734e9472925664ad681e055602ea62397 Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Fri, 1 Aug 2025 20:13:48 +0200 Subject: [PATCH 0098/2093] Add suggestion to `cast_sign_loss` and `cast_possible_wrap` using the `cast_{un,}signed()` methods --- clippy_lints/src/casts/cast_possible_wrap.rs | 30 ++++++- clippy_lints/src/casts/cast_sign_loss.rs | 29 ++++++- clippy_lints/src/casts/mod.rs | 4 +- clippy_lints/src/casts/utils.rs | 32 ++++++++ clippy_utils/src/msrvs.rs | 2 +- tests/ui/cast.stderr | 82 ++++++++++---------- 6 files changed, 131 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index e26c03ccda93..5a8b3e6d9a41 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -1,4 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -16,7 +19,14 @@ enum EmitState { LintOnPtrSize(u64), } -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_op: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + msrv: Msrv, +) { let (Some(from_nbits), Some(to_nbits)) = ( utils::int_ty_to_nbits(cx.tcx, cast_from), utils::int_ty_to_nbits(cx.tcx, cast_to), @@ -85,5 +95,23 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca .note("`usize` and `isize` may be as small as 16 bits on some platforms") .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types"); } + + if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST) + && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to) + { + let method = match cast { + utils::CastTo::Signed => "cast_signed()", + utils::CastTo::Unsigned => "cast_unsigned()", + }; + let mut app = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app); + + diag.span_suggestion( + expr.span, + format!("if this is intentional, consider using `{method}` instead"), + format!("{}.{method}", sugg.maybe_paren()), + app, + ); + } }); } diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index a70bd8861919..757d53e394ff 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -2,15 +2,18 @@ use std::convert::Infallible; use std::ops::ControlFlow; use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{method_chain_args, sext, sym}; +use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use rustc_span::Symbol; -use super::CAST_SIGN_LOSS; +use super::{CAST_SIGN_LOSS, utils}; /// A list of methods that can never return a negative value. /// Includes methods that panic rather than returning a negative value. @@ -42,13 +45,33 @@ pub(super) fn check<'cx>( cast_op: &Expr<'_>, cast_from: Ty<'cx>, cast_to: Ty<'_>, + msrv: Msrv, ) { if should_lint(cx, cast_op, cast_from, cast_to) { - span_lint( + span_lint_and_then( cx, CAST_SIGN_LOSS, expr.span, format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"), + |diag| { + if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST) + && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to) + { + let method = match cast { + utils::CastTo::Signed => "cast_signed()", + utils::CastTo::Unsigned => "cast_unsigned()", + }; + let mut app = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app); + + diag.span_suggestion( + expr.span, + format!("if this is intentional, consider using `{method}` instead"), + format!("{}.{method}", sugg.maybe_paren()), + app, + ); + } + }, ); } } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 37accff5eaa8..4df2d81ea355 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -887,9 +887,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if cast_to.is_numeric() { cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { - cast_possible_wrap::check(cx, expr, cast_from, cast_to); + cast_possible_wrap::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_precision_loss::check(cx, expr, cast_from, cast_to); - cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to); + cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to); } diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index d846d78b9ee7..8251e48da5cd 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -60,3 +60,35 @@ pub(super) fn enum_ty_to_nbits(adt: AdtDef<'_>, tcx: TyCtxt<'_>) -> u64 { neg_bits.max(pos_bits).into() } } + +pub(super) enum CastTo { + Signed, + Unsigned, +} +/// Returns `Some` if the type cast is between 2 integral types that differ +/// only in signedness, otherwise `None`. The value of `Some` is which +/// signedness is casted to. +pub(super) fn is_signedness_cast(cast_from: Ty<'_>, cast_to: Ty<'_>) -> Option { + if !cast_from.is_integral() || !cast_to.is_integral() { + return None; + } + if cast_from.is_signed() == cast_to.is_signed() { + return None; + } + if as_uint_ty(cast_from) != as_uint_ty(cast_to) { + return None; + } + + if cast_to.is_signed() { + Some(CastTo::Signed) + } else { + Some(CastTo::Unsigned) + } +} +fn as_uint_ty(ty: Ty<'_>) -> Option { + match ty.kind() { + ty::Uint(uint_ty) => Some(*uint_ty), + ty::Int(int_ty) => Some(int_ty.to_unsigned()), + _ => None, + } +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 24ed4c3a8bec..a38550a7b552 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -24,7 +24,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,88,0 { LET_CHAINS } - 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF } + 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST } 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 1cb30d956679..13e0a5583cdc 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -194,7 +194,7 @@ error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:88:5 | LL | 1u8 as i8; - | ^^^^^^^^^ + | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` | = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` @@ -203,25 +203,25 @@ error: casting `u16` to `i16` may wrap around the value --> tests/ui/cast.rs:91:5 | LL | 1u16 as i16; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u16.cast_signed()` error: casting `u32` to `i32` may wrap around the value --> tests/ui/cast.rs:94:5 | LL | 1u32 as i32; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u32.cast_signed()` error: casting `u64` to `i64` may wrap around the value --> tests/ui/cast.rs:97:5 | LL | 1u64 as i64; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u64.cast_signed()` error: casting `usize` to `isize` may wrap around the value --> tests/ui/cast.rs:100:5 | LL | 1usize as isize; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1usize.cast_signed()` error: casting `usize` to `i8` may truncate the value --> tests/ui/cast.rs:104:5 @@ -321,43 +321,43 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:138:5 | LL | -1i32 as u32; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i32).cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:142:5 | LL | -1isize as usize; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).cast_unsigned()` error: casting `i8` to `u8` may lose the sign of the value --> tests/ui/cast.rs:154:5 | LL | (i8::MIN).abs() as u8; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:159:5 | LL | (-1i64).abs() as u64; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:161:5 | LL | (-1isize).abs() as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:169:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:185:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `i8` may truncate the value --> tests/ui/cast.rs:237:5 @@ -495,79 +495,79 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:438:9 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:444:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:447:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:449:5 | LL | (-2_i32).pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:454:5 | LL | (-5_i32 % 2) as u32; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:457:5 | LL | (-5_i32 % -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:461:5 | LL | (-2_i32 >> 1) as u32; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:465:5 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:467:5 | LL | (x * x * x) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:471:5 | LL | (y * y * y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:474:5 | LL | (y * y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:476:5 | LL | (y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:479:5 | LL | (y / y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()` error: equal expressions as operands to `/` --> tests/ui/cast.rs:479:6 @@ -581,97 +581,97 @@ error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:483:5 | LL | (y + y + y + -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:486:5 | LL | (y + y + y + 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:490:5 | LL | (z + -2) as u16; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:493:5 | LL | (z + z + 2) as u16; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:497:9 | LL | (a * a * b * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:499:9 | LL | (a * b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:502:9 | LL | (a * -b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:505:9 | LL | (a * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:507:9 | LL | (a * -2) as u32; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:510:9 | LL | (a * b * c * -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:513:9 | LL | (a / b) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:515:9 | LL | (a / b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:518:9 | LL | (a / b + b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:521:9 | LL | a.saturating_pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:524:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:532:21 | LL | let _ = i32::MIN as u32; // cast_sign_loss - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `i32::MIN.cast_unsigned()` ... LL | m!(); | ---- in this macro invocation From 0eb16ea97b5161da7874f772e20e49f02af20189 Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Sat, 2 Aug 2025 00:16:08 +0200 Subject: [PATCH 0099/2093] Add test to ensure that suggestions are only emitted if MSRV supports it --- tests/ui/cast.rs | 13 +++++++++++++ tests/ui/cast.stderr | 14 +++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index 525be8216500..fab02bf7b24e 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -569,3 +569,16 @@ fn issue12721() { (255 % 999999u64) as u8; //~^ cast_possible_truncation } + +mod issue14150 { + #[clippy::msrv = "1.87"] + fn msrv_supports_cast_signed() { + _ = 1u8 as i8; + //~^ cast_possible_wrap + } + #[clippy::msrv = "1.86"] + fn msrv_doesnt_supports_cast_signed() { + _ = 1u8 as i8; + //~^ cast_possible_wrap + } +} diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 13e0a5583cdc..f131369df241 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -752,5 +752,17 @@ LL - (255 % 999999u64) as u8; LL + u8::try_from(255 % 999999u64); | -error: aborting due to 92 previous errors +error: casting `u8` to `i8` may wrap around the value + --> tests/ui/cast.rs:576:13 + | +LL | _ = 1u8 as i8; + | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` + +error: casting `u8` to `i8` may wrap around the value + --> tests/ui/cast.rs:581:13 + | +LL | _ = 1u8 as i8; + | ^^^^^^^^^ + +error: aborting due to 94 previous errors From 4e286bb8fb305d6ba864ff8b2c806b4fd189657a Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Sun, 3 Aug 2025 13:44:56 +0200 Subject: [PATCH 0100/2093] Apply suggestions from code review --- clippy_lints/src/casts/cast_possible_wrap.rs | 2 +- clippy_lints/src/casts/cast_sign_loss.rs | 2 +- clippy_lints/src/casts/utils.rs | 23 +----- tests/ui/cast.stderr | 84 ++++++++++---------- 4 files changed, 47 insertions(+), 64 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index 5a8b3e6d9a41..9eaa6e4cf26e 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -108,7 +108,7 @@ pub(super) fn check( diag.span_suggestion( expr.span, - format!("if this is intentional, consider using `{method}` instead"), + format!("if this is intentional, use `{method}` instead"), format!("{}.{method}", sugg.maybe_paren()), app, ); diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 757d53e394ff..f870d27b796e 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -66,7 +66,7 @@ pub(super) fn check<'cx>( diag.span_suggestion( expr.span, - format!("if this is intentional, consider using `{method}` instead"), + format!("if this is intentional, use `{method}` instead"), format!("{}.{method}", sugg.maybe_paren()), app, ); diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index 8251e48da5cd..707fc2a8eed3 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -69,26 +69,9 @@ pub(super) enum CastTo { /// only in signedness, otherwise `None`. The value of `Some` is which /// signedness is casted to. pub(super) fn is_signedness_cast(cast_from: Ty<'_>, cast_to: Ty<'_>) -> Option { - if !cast_from.is_integral() || !cast_to.is_integral() { - return None; - } - if cast_from.is_signed() == cast_to.is_signed() { - return None; - } - if as_uint_ty(cast_from) != as_uint_ty(cast_to) { - return None; - } - - if cast_to.is_signed() { - Some(CastTo::Signed) - } else { - Some(CastTo::Unsigned) - } -} -fn as_uint_ty(ty: Ty<'_>) -> Option { - match ty.kind() { - ty::Uint(uint_ty) => Some(*uint_ty), - ty::Int(int_ty) => Some(int_ty.to_unsigned()), + match (cast_from.kind(), cast_to.kind()) { + (ty::Int(from), ty::Uint(to)) if from.to_unsigned() == *to => Some(CastTo::Unsigned), + (ty::Uint(from), ty::Int(to)) if *from == to.to_unsigned() => Some(CastTo::Signed), _ => None, } } diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index f131369df241..8c48855123f9 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -194,7 +194,7 @@ error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:88:5 | LL | 1u8 as i8; - | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` + | ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()` | = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` @@ -203,25 +203,25 @@ error: casting `u16` to `i16` may wrap around the value --> tests/ui/cast.rs:91:5 | LL | 1u16 as i16; - | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u16.cast_signed()` + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u16.cast_signed()` error: casting `u32` to `i32` may wrap around the value --> tests/ui/cast.rs:94:5 | LL | 1u32 as i32; - | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u32.cast_signed()` + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u32.cast_signed()` error: casting `u64` to `i64` may wrap around the value --> tests/ui/cast.rs:97:5 | LL | 1u64 as i64; - | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u64.cast_signed()` + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u64.cast_signed()` error: casting `usize` to `isize` may wrap around the value --> tests/ui/cast.rs:100:5 | LL | 1usize as isize; - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1usize.cast_signed()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1usize.cast_signed()` error: casting `usize` to `i8` may truncate the value --> tests/ui/cast.rs:104:5 @@ -321,43 +321,43 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:138:5 | LL | -1i32 as u32; - | ^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i32).cast_unsigned()` + | ^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i32).cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:142:5 | LL | -1isize as usize; - | ^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).cast_unsigned()` + | ^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).cast_unsigned()` error: casting `i8` to `u8` may lose the sign of the value --> tests/ui/cast.rs:154:5 | LL | (i8::MIN).abs() as u8; - | ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:159:5 | LL | (-1i64).abs() as u64; - | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:161:5 | LL | (-1isize).abs() as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:169:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:185:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `i8` may truncate the value --> tests/ui/cast.rs:237:5 @@ -495,79 +495,79 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:438:9 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:444:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:447:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:449:5 | LL | (-2_i32).pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:454:5 | LL | (-5_i32 % 2) as u32; - | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:457:5 | LL | (-5_i32 % -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:461:5 | LL | (-2_i32 >> 1) as u32; - | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:465:5 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:467:5 | LL | (x * x * x) as u32; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:471:5 | LL | (y * y * y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:474:5 | LL | (y * y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:476:5 | LL | (y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:479:5 | LL | (y / y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()` error: equal expressions as operands to `/` --> tests/ui/cast.rs:479:6 @@ -581,97 +581,97 @@ error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:483:5 | LL | (y + y + y + -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:486:5 | LL | (y + y + y + 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:490:5 | LL | (z + -2) as u16; - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:493:5 | LL | (z + z + 2) as u16; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:497:9 | LL | (a * a * b * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:499:9 | LL | (a * b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:502:9 | LL | (a * -b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:505:9 | LL | (a * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:507:9 | LL | (a * -2) as u32; - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:510:9 | LL | (a * b * c * -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:513:9 | LL | (a / b) as u32; - | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b).cast_unsigned()` + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:515:9 | LL | (a / b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:518:9 | LL | (a / b + b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:521:9 | LL | a.saturating_pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:524:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:532:21 | LL | let _ = i32::MIN as u32; // cast_sign_loss - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `i32::MIN.cast_unsigned()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `i32::MIN.cast_unsigned()` ... LL | m!(); | ---- in this macro invocation @@ -756,7 +756,7 @@ error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:576:13 | LL | _ = 1u8 as i8; - | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` + | ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()` error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:581:13 From bf644e9d14d4e088303041d4136daefaa8ae6f86 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 2 Aug 2025 19:03:46 +0000 Subject: [PATCH 0101/2093] Use codegen_fn_attrs to check if function is naked. --- compiler/rustc_hir_typeck/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index aae870f7ee3e..72fd8db300be 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -46,12 +46,12 @@ use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{HirId, HirIdMap, Node, find_attr}; +use rustc_hir::{HirId, HirIdMap, Node}; use rustc_hir_analysis::check::{check_abi, check_custom_abi}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; @@ -174,7 +174,7 @@ fn typeck_with_inspect<'tcx>( .map(|(idx, ty)| fcx.normalize(arg_span(idx), ty)), ); - if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Naked(..)) { + if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NAKED) { naked_functions::typeck_naked_fn(tcx, def_id, body); } From b5a4e5d73fb7fd47caa1b22ffe8527c13f895c21 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Mon, 4 Aug 2025 01:24:22 +0500 Subject: [PATCH 0102/2093] remove gates --- library/alloc/src/collections/btree/map/entry.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index ea8fa363c380..b16aad0bb03d 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -275,7 +275,6 @@ impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> { /// # Examples /// /// ``` - /// #![feature(btree_entry_insert)] /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); @@ -284,7 +283,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> { /// assert_eq!(entry.key(), &"poneyland"); /// ``` #[inline] - #[unstable(feature = "btree_entry_insert", issue = "65225")] + #[stable(feature = "btree_entry_insert", since = "CURRENT_RUSTC_VERSION")] pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { match self { Occupied(mut entry) => { @@ -383,7 +382,6 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> { /// # Examples /// /// ``` - /// #![feature(btree_entry_insert)] /// use std::collections::BTreeMap; /// use std::collections::btree_map::Entry; /// @@ -395,7 +393,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> { /// } /// assert_eq!(map["poneyland"], 37); /// ``` - #[unstable(feature = "btree_entry_insert", issue = "65225")] + #[stable(feature = "btree_entry_insert", since = "CURRENT_RUSTC_VERSION")] pub fn insert_entry(mut self, value: V) -> OccupiedEntry<'a, K, V, A> { let handle = match self.handle { None => { From 4c4b8b23c6935e719ca01b614922024abb694656 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Mon, 4 Aug 2025 01:06:04 +0500 Subject: [PATCH 0103/2093] remove gate --- library/std/src/path.rs | 3 +-- library/std/tests/path.rs | 8 +------- src/tools/clippy/tests/missing-test-files.rs | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 055e7f814800..5dd52a23e97c 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2678,7 +2678,6 @@ impl Path { /// # Examples /// /// ``` - /// # #![feature(path_file_prefix)] /// use std::path::Path; /// /// assert_eq!("foo", Path::new("foo.rs").file_prefix().unwrap()); @@ -2691,7 +2690,7 @@ impl Path { /// /// [`Path::file_stem`]: Path::file_stem /// - #[unstable(feature = "path_file_prefix", issue = "86319")] + #[stable(feature = "path_file_prefix", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn file_prefix(&self) -> Option<&OsStr> { self.file_name().map(split_file_at_dot).and_then(|(before, _after)| Some(before)) diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index 901d2770f203..e1576a0d4231 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -1,10 +1,4 @@ -#![feature( - clone_to_uninit, - path_add_extension, - path_file_prefix, - maybe_uninit_slice, - normalize_lexically -)] +#![feature(clone_to_uninit, path_add_extension, maybe_uninit_slice, normalize_lexically)] use std::clone::CloneToUninit; use std::ffi::OsStr; diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs index 565dcd73f582..63f960c92fa3 100644 --- a/src/tools/clippy/tests/missing-test-files.rs +++ b/src/tools/clippy/tests/missing-test-files.rs @@ -1,6 +1,6 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::assertions_on_constants)] -#![feature(path_file_prefix)] +#![cfg_attr(bootstrap, feature(path_file_prefix))] use std::cmp::Ordering; use std::ffi::OsStr; From 849f71f02564f1c726848efa4c70673128cd64d2 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Mon, 4 Aug 2025 01:06:04 +0500 Subject: [PATCH 0104/2093] remove gate --- tests/missing-test-files.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 565dcd73f582..63f960c92fa3 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -1,6 +1,6 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::assertions_on_constants)] -#![feature(path_file_prefix)] +#![cfg_attr(bootstrap, feature(path_file_prefix))] use std::cmp::Ordering; use std::ffi::OsStr; From 607ecca1fbe884a7d032ef1ec1d98853ce284a3d Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 4 Aug 2025 05:09:06 +0000 Subject: [PATCH 0105/2093] Prepare for merging from rust-lang/rust This updates the rust-version file to 07b7dc90ee4df5815dbb91ef8e98cb93571230f5. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index bf69accaf063..8cf5873eab42 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -adcb3d3b4cd3b7c4cde642f3ed537037f293738e +07b7dc90ee4df5815dbb91ef8e98cb93571230f5 From c4b91bd9f87391c1dda3784205c0dd10b54ec2e8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 Aug 2025 10:49:43 +0200 Subject: [PATCH 0106/2093] Merge commit '482e8540a1b757ed7bccc2041c5400f051fdb01e' into subtree-update_cg_gcc_2025-08-04 --- build_system/src/abi_test.rs | 2 +- build_system/src/fuzz.rs | 8 +- ...1-Add-stdarch-Cargo.toml-for-testing.patch | 23 +++--- rust-toolchain | 2 +- src/allocator.rs | 12 ++- src/builder.rs | 26 ++++-- src/int.rs | 76 ++++++++++++++--- src/intrinsic/archs.rs | 60 ++++++++++++++ src/intrinsic/mod.rs | 9 ++- src/intrinsic/simd.rs | 81 +++++++++++++++++++ src/lib.rs | 5 +- tests/failing-lto-tests.txt | 2 +- tests/failing-run-make-tests.txt | 1 - tests/failing-ui-tests.txt | 8 +- tools/cspell_dicts/rustc_codegen_gcc.txt | 3 + 15 files changed, 274 insertions(+), 44 deletions(-) diff --git a/build_system/src/abi_test.rs b/build_system/src/abi_test.rs index 3c1531be27a5..a85886d87f36 100644 --- a/build_system/src/abi_test.rs +++ b/build_system/src/abi_test.rs @@ -31,7 +31,7 @@ pub fn run() -> Result<(), String> { Some("clones/abi-cafe".as_ref()), true, ) - .map_err(|err| (format!("Git clone failed with message: {err:?}!")))?; + .map_err(|err| format!("Git clone failed with message: {err:?}!"))?; // Configure abi-cafe to use the exact same rustc version we use - this is crucial. // Otherwise, the concept of ABI compatibility becomes meanignless. std::fs::copy("rust-toolchain", "clones/abi-cafe/rust-toolchain") diff --git a/build_system/src/fuzz.rs b/build_system/src/fuzz.rs index 453211366b31..9714ce29af90 100644 --- a/build_system/src/fuzz.rs +++ b/build_system/src/fuzz.rs @@ -43,18 +43,18 @@ pub fn run() -> Result<(), String> { "--start" => { start = str::parse(&args.next().ok_or_else(|| "Fuzz start not provided!".to_string())?) - .map_err(|err| (format!("Fuzz start not a number {err:?}!")))?; + .map_err(|err| format!("Fuzz start not a number {err:?}!"))?; } "--count" => { count = str::parse(&args.next().ok_or_else(|| "Fuzz count not provided!".to_string())?) - .map_err(|err| (format!("Fuzz count not a number {err:?}!")))?; + .map_err(|err| format!("Fuzz count not a number {err:?}!"))?; } "-j" | "--jobs" => { threads = str::parse( &args.next().ok_or_else(|| "Fuzz thread count not provided!".to_string())?, ) - .map_err(|err| (format!("Fuzz thread count not a number {err:?}!")))?; + .map_err(|err| format!("Fuzz thread count not a number {err:?}!"))?; } _ => return Err(format!("Unknown option {arg}")), } @@ -66,7 +66,7 @@ pub fn run() -> Result<(), String> { Some("clones/rustlantis".as_ref()), true, ) - .map_err(|err| (format!("Git clone failed with message: {err:?}!")))?; + .map_err(|err| format!("Git clone failed with message: {err:?}!"))?; // Ensure that we are on the newest rustlantis commit. let cmd: &[&dyn AsRef] = &[&"git", &"pull", &"origin"]; diff --git a/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch b/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch index 9cc377850b9b..3a8c37a8b8d9 100644 --- a/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch +++ b/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch @@ -1,29 +1,28 @@ -From b8f3eed3053c9333b5dfbeaeb2a6a65a4b3156df Mon Sep 17 00:00:00 2001 -From: Antoni Boucher -Date: Tue, 29 Aug 2023 13:06:34 -0400 +From 190e26c9274b3c93a9ee3516b395590e6bd9213b Mon Sep 17 00:00:00 2001 +From: None +Date: Sun, 3 Aug 2025 19:54:56 -0400 Subject: [PATCH] Patch 0001-Add-stdarch-Cargo.toml-for-testing.patch --- - library/stdarch/Cargo.toml | 23 +++++++++++++++++++++++ - 1 file changed, 23 insertions(+) + library/stdarch/Cargo.toml | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) create mode 100644 library/stdarch/Cargo.toml diff --git a/library/stdarch/Cargo.toml b/library/stdarch/Cargo.toml new file mode 100644 -index 0000000..4c63700 +index 0000000..bd6725c --- /dev/null +++ b/library/stdarch/Cargo.toml -@@ -0,0 +1,21 @@ +@@ -0,0 +1,20 @@ +[workspace] +resolver = "1" +members = [ -+ "crates/core_arch", -+ "crates/std_detect", -+ "crates/stdarch-gen-arm", ++ "crates/*", + #"examples/" +] +exclude = [ -+ "crates/wasm-assert-instr-tests" ++ "crates/wasm-assert-instr-tests", ++ "rust_programs", +] + +[profile.release] @@ -36,5 +35,5 @@ index 0000000..4c63700 +opt-level = 3 +incremental = true -- -2.42.0 +2.50.1 diff --git a/rust-toolchain b/rust-toolchain index 2fe8ec4647fa..058e734be5cf 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-07-04" +channel = "nightly-2025-08-03" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/src/allocator.rs b/src/allocator.rs index 0d8dc93274f9..2a95a7368aac 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -99,11 +99,15 @@ fn create_const_value_function( let func = context.new_function(None, FunctionType::Exported, output, &[], name, false); #[cfg(feature = "master")] - func.add_attribute(FnAttribute::Visibility(symbol_visibility_to_gcc( - tcx.sess.default_visibility(), - ))); + { + func.add_attribute(FnAttribute::Visibility(symbol_visibility_to_gcc( + tcx.sess.default_visibility(), + ))); - func.add_attribute(FnAttribute::AlwaysInline); + // FIXME(antoyo): cg_llvm sets AlwaysInline, but AlwaysInline is different in GCC and using + // it here will causes linking errors when using LTO. + func.add_attribute(FnAttribute::Inline); + } if tcx.sess.must_emit_unwind_tables() { // TODO(antoyo): emit unwind tables. diff --git a/src/builder.rs b/src/builder.rs index 4aee211e2efa..34ade3d025f8 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -540,9 +540,15 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn ret(&mut self, mut value: RValue<'gcc>) { let expected_return_type = self.current_func().get_return_type(); - if !expected_return_type.is_compatible_with(value.get_type()) { - // NOTE: due to opaque pointers now being used, we need to cast here. - value = self.context.new_cast(self.location, value, expected_return_type); + let value_type = value.get_type(); + if !expected_return_type.is_compatible_with(value_type) { + // NOTE: due to opaque pointers now being used, we need to (bit)cast here. + if self.is_native_int_type(value_type) && self.is_native_int_type(expected_return_type) + { + value = self.context.new_cast(self.location, value, expected_return_type); + } else { + value = self.context.new_bitcast(self.location, value, expected_return_type); + } } self.llbb().end_with_return(self.location, value); } @@ -1279,11 +1285,19 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn intcast( &mut self, - value: RValue<'gcc>, + mut value: RValue<'gcc>, dest_typ: Type<'gcc>, - _is_signed: bool, + is_signed: bool, ) -> RValue<'gcc> { - // NOTE: is_signed is for value, not dest_typ. + let value_type = value.get_type(); + if is_signed && !value_type.is_signed(self.cx) { + let signed_type = value_type.to_signed(self.cx); + value = self.gcc_int_cast(value, signed_type); + } else if !is_signed && value_type.is_signed(self.cx) { + let unsigned_type = value_type.to_unsigned(self.cx); + value = self.gcc_int_cast(value, unsigned_type); + } + self.gcc_int_cast(value, dest_typ) } diff --git a/src/int.rs b/src/int.rs index 6f21ce9352b5..9fb7f6bad684 100644 --- a/src/int.rs +++ b/src/int.rs @@ -4,12 +4,15 @@ // cSpell:words cmpti divti modti mulodi muloti udivti umodti -use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp}; +use gccjit::{ + BinaryOp, CType, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp, +}; use rustc_abi::{CanonAbi, Endian, ExternAbi}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, BuilderMethods, OverflowOp}; use rustc_middle::ty::{self, Ty}; use rustc_target::callconv::{ArgAbi, ArgAttributes, FnAbi, PassMode}; +use rustc_type_ir::{Interner, TyKind}; use crate::builder::{Builder, ToGccComp}; use crate::common::{SignType, TypeReflection}; @@ -167,9 +170,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { if a_type.is_vector() { // Vector types need to be bitcast. // TODO(antoyo): perhaps use __builtin_convertvector for vector casting. - b = self.context.new_bitcast(self.location, b, a.get_type()); + b = self.context.new_bitcast(self.location, b, a_type); } else { - b = self.context.new_cast(self.location, b, a.get_type()); + b = self.context.new_cast(self.location, b, a_type); } } self.context.new_binary_op(self.location, operation, a_type, a, b) @@ -216,13 +219,22 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { operation_name: &str, signed: bool, a: RValue<'gcc>, - b: RValue<'gcc>, + mut b: RValue<'gcc>, ) -> RValue<'gcc> { let a_type = a.get_type(); let b_type = b.get_type(); if (self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type)) || (a_type.is_vector() && b_type.is_vector()) { + if !a_type.is_compatible_with(b_type) { + if a_type.is_vector() { + // Vector types need to be bitcast. + // TODO(antoyo): perhaps use __builtin_convertvector for vector casting. + b = self.context.new_bitcast(self.location, b, a_type); + } else { + b = self.context.new_cast(self.location, b, a_type); + } + } self.context.new_binary_op(self.location, operation, a_type, a, b) } else { debug_assert!(a_type.dyncast_array().is_some()); @@ -351,6 +363,11 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { // TODO(antoyo): is it correct to use rhs type instead of the parameter typ? .new_local(self.location, rhs.get_type(), "binopResult") .get_address(self.location); + let new_type = type_kind_to_gcc_type(new_kind); + let new_type = self.context.new_c_type(new_type); + let lhs = self.context.new_cast(self.location, lhs, new_type); + let rhs = self.context.new_cast(self.location, rhs, new_type); + let res = self.context.new_cast(self.location, res, new_type.make_pointer()); let overflow = self.overflow_call(intrinsic, &[lhs, rhs, res], None); (res.dereference(self.location).to_rvalue(), overflow) } @@ -477,11 +494,27 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let lhs_low = self.context.new_cast(self.location, self.low(lhs), unsigned_type); let rhs_low = self.context.new_cast(self.location, self.low(rhs), unsigned_type); + let mut lhs_high = self.high(lhs); + let mut rhs_high = self.high(rhs); + + match op { + IntPredicate::IntUGT + | IntPredicate::IntUGE + | IntPredicate::IntULT + | IntPredicate::IntULE => { + lhs_high = self.context.new_cast(self.location, lhs_high, unsigned_type); + rhs_high = self.context.new_cast(self.location, rhs_high, unsigned_type); + } + // TODO(antoyo): we probably need to handle signed comparison for unsigned + // integers. + _ => (), + } + let condition = self.context.new_comparison( self.location, ComparisonOp::LessThan, - self.high(lhs), - self.high(rhs), + lhs_high, + rhs_high, ); self.llbb().end_with_conditional(self.location, condition, block1, block2); @@ -495,8 +528,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let condition = self.context.new_comparison( self.location, ComparisonOp::GreaterThan, - self.high(lhs), - self.high(rhs), + lhs_high, + rhs_high, ); block2.end_with_conditional(self.location, condition, block3, block4); @@ -620,7 +653,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } } - pub fn gcc_xor(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + pub fn gcc_xor(&self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> { let a_type = a.get_type(); let b_type = b.get_type(); if a_type.is_vector() && b_type.is_vector() { @@ -628,6 +661,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { a ^ b } else if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) { + if !a_type.is_compatible_with(b_type) { + b = self.context.new_cast(self.location, b, a_type); + } a ^ b } else { self.concat_low_high_rvalues( @@ -1042,3 +1078,25 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { self.context.new_array_constructor(None, typ, &values) } } + +fn type_kind_to_gcc_type(kind: TyKind) -> CType { + use rustc_middle::ty::IntTy::*; + use rustc_middle::ty::UintTy::*; + use rustc_middle::ty::{Int, Uint}; + + match kind { + Int(I8) => CType::Int8t, + Int(I16) => CType::Int16t, + Int(I32) => CType::Int32t, + Int(I64) => CType::Int64t, + Int(I128) => CType::Int128t, + + Uint(U8) => CType::UInt8t, + Uint(U16) => CType::UInt16t, + Uint(U32) => CType::UInt32t, + Uint(U64) => CType::UInt64t, + Uint(U128) => CType::UInt128t, + + _ => unimplemented!("Kind: {:?}", kind), + } +} diff --git a/src/intrinsic/archs.rs b/src/intrinsic/archs.rs index 915ed875e32f..d1b2a93243d2 100644 --- a/src/intrinsic/archs.rs +++ b/src/intrinsic/archs.rs @@ -95,8 +95,11 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "cubema" => "__builtin_amdgcn_cubema", "cubesc" => "__builtin_amdgcn_cubesc", "cubetc" => "__builtin_amdgcn_cubetc", + "cvt.f16.bf8" => "__builtin_amdgcn_cvt_f16_bf8", + "cvt.f16.fp8" => "__builtin_amdgcn_cvt_f16_fp8", "cvt.f32.bf8" => "__builtin_amdgcn_cvt_f32_bf8", "cvt.f32.fp8" => "__builtin_amdgcn_cvt_f32_fp8", + "cvt.f32.fp8.e5m3" => "__builtin_amdgcn_cvt_f32_fp8_e5m3", "cvt.off.f32.i4" => "__builtin_amdgcn_cvt_off_f32_i4", "cvt.pk.bf8.f32" => "__builtin_amdgcn_cvt_pk_bf8_f32", "cvt.pk.f16.bf8" => "__builtin_amdgcn_cvt_pk_f16_bf8", @@ -181,6 +184,12 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "dot4.f32.fp8.bf8" => "__builtin_amdgcn_dot4_f32_fp8_bf8", "dot4.f32.fp8.fp8" => "__builtin_amdgcn_dot4_f32_fp8_fp8", "ds.add.gs.reg.rtn" => "__builtin_amdgcn_ds_add_gs_reg_rtn", + "ds.atomic.async.barrier.arrive.b64" => { + "__builtin_amdgcn_ds_atomic_async_barrier_arrive_b64" + } + "ds.atomic.barrier.arrive.rtn.b64" => { + "__builtin_amdgcn_ds_atomic_barrier_arrive_rtn_b64" + } "ds.bpermute" => "__builtin_amdgcn_ds_bpermute", "ds.bpermute.fi.b32" => "__builtin_amdgcn_ds_bpermute_fi_b32", "ds.gws.barrier" => "__builtin_amdgcn_ds_gws_barrier", @@ -198,8 +207,32 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "fdot2.f16.f16" => "__builtin_amdgcn_fdot2_f16_f16", "fdot2.f32.bf16" => "__builtin_amdgcn_fdot2_f32_bf16", "fdot2c.f32.bf16" => "__builtin_amdgcn_fdot2c_f32_bf16", + "flat.prefetch" => "__builtin_amdgcn_flat_prefetch", "fmul.legacy" => "__builtin_amdgcn_fmul_legacy", + "global.load.async.to.lds.b128" => { + "__builtin_amdgcn_global_load_async_to_lds_b128" + } + "global.load.async.to.lds.b32" => { + "__builtin_amdgcn_global_load_async_to_lds_b32" + } + "global.load.async.to.lds.b64" => { + "__builtin_amdgcn_global_load_async_to_lds_b64" + } + "global.load.async.to.lds.b8" => "__builtin_amdgcn_global_load_async_to_lds_b8", "global.load.lds" => "__builtin_amdgcn_global_load_lds", + "global.prefetch" => "__builtin_amdgcn_global_prefetch", + "global.store.async.from.lds.b128" => { + "__builtin_amdgcn_global_store_async_from_lds_b128" + } + "global.store.async.from.lds.b32" => { + "__builtin_amdgcn_global_store_async_from_lds_b32" + } + "global.store.async.from.lds.b64" => { + "__builtin_amdgcn_global_store_async_from_lds_b64" + } + "global.store.async.from.lds.b8" => { + "__builtin_amdgcn_global_store_async_from_lds_b8" + } "groupstaticsize" => "__builtin_amdgcn_groupstaticsize", "iglp.opt" => "__builtin_amdgcn_iglp_opt", "implicit.buffer.ptr" => "__builtin_amdgcn_implicit_buffer_ptr", @@ -291,6 +324,7 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "s.incperflevel" => "__builtin_amdgcn_s_incperflevel", "s.memrealtime" => "__builtin_amdgcn_s_memrealtime", "s.memtime" => "__builtin_amdgcn_s_memtime", + "s.monitor.sleep" => "__builtin_amdgcn_s_monitor_sleep", "s.sendmsg" => "__builtin_amdgcn_s_sendmsg", "s.sendmsghalt" => "__builtin_amdgcn_s_sendmsghalt", "s.setprio" => "__builtin_amdgcn_s_setprio", @@ -300,11 +334,15 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "s.sleep.var" => "__builtin_amdgcn_s_sleep_var", "s.ttracedata" => "__builtin_amdgcn_s_ttracedata", "s.ttracedata.imm" => "__builtin_amdgcn_s_ttracedata_imm", + "s.wait.asynccnt" => "__builtin_amdgcn_s_wait_asynccnt", "s.wait.event.export.ready" => "__builtin_amdgcn_s_wait_event_export_ready", + "s.wait.tensorcnt" => "__builtin_amdgcn_s_wait_tensorcnt", "s.waitcnt" => "__builtin_amdgcn_s_waitcnt", "sad.hi.u8" => "__builtin_amdgcn_sad_hi_u8", "sad.u16" => "__builtin_amdgcn_sad_u16", "sad.u8" => "__builtin_amdgcn_sad_u8", + "sat.pk4.i4.i8" => "__builtin_amdgcn_sat_pk4_i4_i8", + "sat.pk4.u4.u8" => "__builtin_amdgcn_sat_pk4_u4_u8", "sched.barrier" => "__builtin_amdgcn_sched_barrier", "sched.group.barrier" => "__builtin_amdgcn_sched_group_barrier", "sdot2" => "__builtin_amdgcn_sdot2", @@ -346,8 +384,13 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "smfmac.i32.16x16x64.i8" => "__builtin_amdgcn_smfmac_i32_16x16x64_i8", "smfmac.i32.32x32x32.i8" => "__builtin_amdgcn_smfmac_i32_32x32x32_i8", "smfmac.i32.32x32x64.i8" => "__builtin_amdgcn_smfmac_i32_32x32x64_i8", + "struct.ptr.buffer.load.lds" => "__builtin_amdgcn_struct_ptr_buffer_load_lds", "sudot4" => "__builtin_amdgcn_sudot4", "sudot8" => "__builtin_amdgcn_sudot8", + "tensor.load.to.lds" => "__builtin_amdgcn_tensor_load_to_lds", + "tensor.load.to.lds.d2" => "__builtin_amdgcn_tensor_load_to_lds_d2", + "tensor.store.from.lds" => "__builtin_amdgcn_tensor_store_from_lds", + "tensor.store.from.lds.d2" => "__builtin_amdgcn_tensor_store_from_lds_d2", "udot2" => "__builtin_amdgcn_udot2", "udot4" => "__builtin_amdgcn_udot4", "udot8" => "__builtin_amdgcn_udot8", @@ -6326,6 +6369,23 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { } s390(name, full_name) } + "spv" => { + #[allow(non_snake_case)] + fn spv(name: &str, full_name: &str) -> &'static str { + match name { + // spv + "num.subgroups" => "__builtin_spirv_num_subgroups", + "subgroup.id" => "__builtin_spirv_subgroup_id", + "subgroup.local.invocation.id" => { + "__builtin_spirv_subgroup_local_invocation_id" + } + "subgroup.max.size" => "__builtin_spirv_subgroup_max_size", + "subgroup.size" => "__builtin_spirv_subgroup_size", + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), + } + } + spv(name, full_name) + } "ve" => { #[allow(non_snake_case)] fn ve(name: &str, full_name: &str) -> &'static str { diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs index 0753ac1aeb84..eb0a5336a1f1 100644 --- a/src/intrinsic/mod.rs +++ b/src/intrinsic/mod.rs @@ -925,10 +925,17 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { // TODO(antoyo): use width? let arg_type = arg.get_type(); let result_type = self.u32_type; + let arg = if arg_type.is_signed(self.cx) { + let new_type = arg_type.to_unsigned(self.cx); + self.gcc_int_cast(arg, new_type) + } else { + arg + }; + let arg_type = arg.get_type(); let count_leading_zeroes = // TODO(antoyo): write a new function Type::is_compatible_with(&Type) and use it here // instead of using is_uint(). - if arg_type.is_uint(self.cx) { + if arg_type.is_uchar(self.cx) || arg_type.is_ushort(self.cx) || arg_type.is_uint(self.cx) { "__builtin_clz" } else if arg_type.is_ulong(self.cx) { diff --git a/src/intrinsic/simd.rs b/src/intrinsic/simd.rs index 350915a277e3..fdc15d580eff 100644 --- a/src/intrinsic/simd.rs +++ b/src/intrinsic/simd.rs @@ -206,6 +206,28 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ); } + #[cfg(feature = "master")] + if name == sym::simd_funnel_shl { + return Ok(simd_funnel_shift( + bx, + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + true, + )); + } + + #[cfg(feature = "master")] + if name == sym::simd_funnel_shr { + return Ok(simd_funnel_shift( + bx, + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + false, + )); + } + if name == sym::simd_bswap { return Ok(simd_bswap(bx, args[0].immediate())); } @@ -1434,3 +1456,62 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( unimplemented!("simd {}", name); } + +#[cfg(feature = "master")] +fn simd_funnel_shift<'a, 'gcc, 'tcx>( + bx: &mut Builder<'a, 'gcc, 'tcx>, + a: RValue<'gcc>, + b: RValue<'gcc>, + shift: RValue<'gcc>, + shift_left: bool, +) -> RValue<'gcc> { + use crate::common::SignType; + + let a_type = a.get_type(); + let vector_type = a_type.unqualified().dyncast_vector().expect("vector type"); + let num_units = vector_type.get_num_units(); + let elem_type = vector_type.get_element_type(); + + let (new_int_type, int_shift_val, int_mask) = if elem_type.is_compatible_with(bx.u8_type) + || elem_type.is_compatible_with(bx.i8_type) + { + (bx.u16_type, 8, u8::MAX as u64) + } else if elem_type.is_compatible_with(bx.u16_type) || elem_type.is_compatible_with(bx.i16_type) + { + (bx.u32_type, 16, u16::MAX as u64) + } else if elem_type.is_compatible_with(bx.u32_type) || elem_type.is_compatible_with(bx.i32_type) + { + (bx.u64_type, 32, u32::MAX as u64) + } else if elem_type.is_compatible_with(bx.u64_type) || elem_type.is_compatible_with(bx.i64_type) + { + (bx.u128_type, 64, u64::MAX) + } else { + unimplemented!("funnel shift on {:?}", elem_type); + }; + + let int_mask = bx.context.new_rvalue_from_long(new_int_type, int_mask as i64); + let int_shift_val = bx.context.new_rvalue_from_int(new_int_type, int_shift_val); + let mut elements = vec![]; + let unsigned_type = elem_type.to_unsigned(bx); + for i in 0..num_units { + let index = bx.context.new_rvalue_from_int(bx.int_type, i as i32); + let a_val = bx.context.new_vector_access(None, a, index).to_rvalue(); + let a_val = bx.context.new_bitcast(None, a_val, unsigned_type); + // TODO: we probably need to use gcc_int_cast instead. + let a_val = bx.gcc_int_cast(a_val, new_int_type); + let b_val = bx.context.new_vector_access(None, b, index).to_rvalue(); + let b_val = bx.context.new_bitcast(None, b_val, unsigned_type); + let b_val = bx.gcc_int_cast(b_val, new_int_type); + let shift_val = bx.context.new_vector_access(None, shift, index).to_rvalue(); + let shift_val = bx.gcc_int_cast(shift_val, new_int_type); + let mut val = a_val << int_shift_val | b_val; + if shift_left { + val = (val << shift_val) >> int_shift_val; + } else { + val = (val >> shift_val) & int_mask; + } + let val = bx.gcc_int_cast(val, elem_type); + elements.push(val); + } + bx.context.new_rvalue_from_vector(None, a_type, &elements) +} diff --git a/src/lib.rs b/src/lib.rs index 613315f77a6b..b11f11d38e3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,7 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_symbol_mangling; extern crate rustc_target; +extern crate rustc_type_ir; // This prevents duplicating functions and statics that are already part of the host rustc process. #[allow(unused_extern_crates)] @@ -362,9 +363,9 @@ impl WriteBackendMethods for GccCodegenBackend { _exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, - diff_fncs: Vec, + diff_functions: Vec, ) -> Result, FatalError> { - if !diff_fncs.is_empty() { + if !diff_functions.is_empty() { unimplemented!(); } diff --git a/tests/failing-lto-tests.txt b/tests/failing-lto-tests.txt index b9126fb73a77..bf0633f73200 100644 --- a/tests/failing-lto-tests.txt +++ b/tests/failing-lto-tests.txt @@ -28,6 +28,6 @@ tests/ui/macros/macro-comma-behavior-rpass.rs tests/ui/macros/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs tests/ui/macros/stringify.rs -tests/ui/reexport-test-harness-main.rs tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test.rs tests/ui/binding/fn-arg-incomplete-pattern-drop-order.rs +tests/ui/lto/debuginfo-lto-alloc.rs diff --git a/tests/failing-run-make-tests.txt b/tests/failing-run-make-tests.txt index 842533cd3c62..29032b321fa7 100644 --- a/tests/failing-run-make-tests.txt +++ b/tests/failing-run-make-tests.txt @@ -6,7 +6,6 @@ tests/run-make/doctests-keep-binaries/ tests/run-make/doctests-runtool/ tests/run-make/emit-shared-files/ tests/run-make/exit-code/ -tests/run-make/issue-22131/ tests/run-make/issue-64153/ tests/run-make/llvm-ident/ tests/run-make/native-link-modifier-bundle/ diff --git a/tests/failing-ui-tests.txt b/tests/failing-ui-tests.txt index 6979c04d5343..41fb4729c07d 100644 --- a/tests/failing-ui-tests.txt +++ b/tests/failing-ui-tests.txt @@ -10,11 +10,10 @@ tests/ui/iterators/iter-sum-overflow-overflow-checks.rs tests/ui/mir/mir_drop_order.rs tests/ui/mir/mir_let_chains_drop_order.rs tests/ui/mir/mir_match_guard_let_chains_drop_order.rs -tests/ui/oom_unwind.rs +tests/ui/panics/oom-panic-unwind.rs tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs tests/ui/panic-runtime/abort.rs tests/ui/panic-runtime/link-to-abort.rs -tests/ui/unwind-no-uwtable.rs tests/ui/parser/unclosed-delimiter-in-dep.rs tests/ui/consts/missing_span_in_backtrace.rs tests/ui/drop/dynamic-drop.rs @@ -82,3 +81,8 @@ tests/ui/coroutine/panic-drops.rs tests/ui/coroutine/panic-safe.rs tests/ui/process/nofile-limit.rs tests/ui/simd/intrinsic/generic-arithmetic-pass.rs +tests/ui/linking/no-gc-encapsulation-symbols.rs +tests/ui/panics/unwind-force-no-unwind-tables.rs +tests/ui/attributes/fn-align-dyn.rs +tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs +tests/ui/explicit-tail-calls/recursion-etc.rs diff --git a/tools/cspell_dicts/rustc_codegen_gcc.txt b/tools/cspell_dicts/rustc_codegen_gcc.txt index 31023e50ffa1..4fb018b3ecd8 100644 --- a/tools/cspell_dicts/rustc_codegen_gcc.txt +++ b/tools/cspell_dicts/rustc_codegen_gcc.txt @@ -8,6 +8,7 @@ clzll cmse codegened csky +ctfe ctlz ctpop cttz @@ -25,6 +26,7 @@ fwrapv gimple hrtb immediates +interner liblto llbb llcx @@ -47,6 +49,7 @@ mavx mcmodel minimumf minnumf +miri monomorphization monomorphizations monomorphized From 1c793602ae4183d4b114e62affb4c7035cb130ad Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Mon, 4 Aug 2025 09:47:33 -0400 Subject: [PATCH 0107/2093] Reenable m68k CI --- .github/workflows/m68k.yml | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/.github/workflows/m68k.yml b/.github/workflows/m68k.yml index 759d0d59e268..176ee8628b09 100644 --- a/.github/workflows/m68k.yml +++ b/.github/workflows/m68k.yml @@ -91,11 +91,7 @@ jobs: ./y.sh prepare --only-libcore --cross ./y.sh build --sysroot --features compiler-builtins-no-f16-f128 --target-triple m68k-unknown-linux-gnu ./y.sh test --mini-tests --target-triple m68k-unknown-linux-gnu - # FIXME: since https://github.com/rust-lang/rust/pull/140809, we cannot run programs for architectures not - # supported by the object crate, since this adds a dependency on symbols.o for the panic runtime. - # And as such, a wrong order of the object files in the linker command now fails with an undefined reference - # to some symbols like __rustc::rust_panic. - #CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu ./y.sh test --cargo-tests --target-triple m68k-unknown-linux-gnu + CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu ./y.sh test --cargo-tests --target-triple m68k-unknown-linux-gnu ./y.sh clean all - name: Prepare dependencies @@ -104,23 +100,21 @@ jobs: git config --global user.name "User" ./y.sh prepare --cross - # FIXME: We cannot run programs for architectures not supported by the object crate. See comment above. - #- name: Run tests - #run: | - #./y.sh test --target-triple m68k-unknown-linux-gnu --release --clean --build-sysroot --sysroot-features compiler-builtins-no-f16-f128 ${{ matrix.commands }} + - name: Run tests + run: | + ./y.sh test --target-triple m68k-unknown-linux-gnu --release --clean --build-sysroot --sysroot-features compiler-builtins-no-f16-f128 ${{ matrix.commands }} - # FIXME: We cannot run programs for architectures not supported by the object crate. See comment above. - #- name: Run Hello World! - #run: | - #./y.sh build --target-triple m68k-unknown-linux-gnu + - name: Run Hello World! + run: | + ./y.sh build --target-triple m68k-unknown-linux-gnu - #vm_dir=$(pwd)/vm - #cd tests/hello-world - #CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ../../y.sh cargo build --target m68k-unknown-linux-gnu - #sudo cp target/m68k-unknown-linux-gnu/debug/hello_world $vm_dir/home/ - #sudo chroot $vm_dir qemu-m68k-static /home/hello_world > hello_world_stdout - #expected_output="40" - #test $(cat hello_world_stdout) == $expected_output || (echo "Output differs. Actual output: $(cat hello_world_stdout)"; exit 1) + vm_dir=$(pwd)/vm + cd tests/hello-world + CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ../../y.sh cargo build --target m68k-unknown-linux-gnu + sudo cp target/m68k-unknown-linux-gnu/debug/hello_world $vm_dir/home/ + sudo chroot $vm_dir qemu-m68k-static /home/hello_world > hello_world_stdout + expected_output="40" + test $(cat hello_world_stdout) == $expected_output || (echo "Output differs. Actual output: $(cat hello_world_stdout)"; exit 1) # Summary job for the merge queue. # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! From aa51e18ae6c86e7c44f3d77ea40a2a9c4e967235 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Mon, 4 Aug 2025 10:06:57 -0400 Subject: [PATCH 0108/2093] Remove outdated comment --- src/intrinsic/simd.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/intrinsic/simd.rs b/src/intrinsic/simd.rs index fdc15d580eff..41363d6313d6 100644 --- a/src/intrinsic/simd.rs +++ b/src/intrinsic/simd.rs @@ -1497,7 +1497,6 @@ fn simd_funnel_shift<'a, 'gcc, 'tcx>( let index = bx.context.new_rvalue_from_int(bx.int_type, i as i32); let a_val = bx.context.new_vector_access(None, a, index).to_rvalue(); let a_val = bx.context.new_bitcast(None, a_val, unsigned_type); - // TODO: we probably need to use gcc_int_cast instead. let a_val = bx.gcc_int_cast(a_val, new_int_type); let b_val = bx.context.new_vector_access(None, b, index).to_rvalue(); let b_val = bx.context.new_bitcast(None, b_val, unsigned_type); From d85a64531673c42ef8a7e214305751f27da53bac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 4 Aug 2025 16:22:05 +0200 Subject: [PATCH 0109/2093] Require approval from t-infra instead of t-release on tier bumps --- src/doc/rustc/src/target-tier-policy.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/doc/rustc/src/target-tier-policy.md b/src/doc/rustc/src/target-tier-policy.md index a0acfdf0e4ab..28d3dc32a63e 100644 --- a/src/doc/rustc/src/target-tier-policy.md +++ b/src/doc/rustc/src/target-tier-policy.md @@ -534,10 +534,10 @@ tests, and will reject patches that fail to build or pass the testsuite on a target. We hold tier 1 targets to our highest standard of requirements. A proposed new tier 1 target must be reviewed and approved by the compiler team -based on these requirements. In addition, the release team must approve the -viability and value of supporting the target. For a tier 1 target, this will +based on these requirements. In addition, the infra team must approve the +viability of supporting the target. For a tier 1 target, this will typically take place via a full RFC proposing the target, to be jointly -reviewed and approved by the compiler team and release team. +reviewed and approved by the compiler team and infra team. In addition, the infrastructure team must approve the integration of the target into Continuous Integration (CI), and the tier 1 CI-related requirements. This @@ -617,7 +617,7 @@ including the infrastructure team in the RFC proposing the target. A tier 1 target may be demoted if it no longer meets these requirements but still meets the requirements for a lower tier. Any proposal for demotion of a tier 1 target requires a full RFC process, with approval by the compiler and -release teams. Any such proposal will be communicated widely to the Rust +infra teams. Any such proposal will be communicated widely to the Rust community, both when initially proposed and before being dropped from a stable release. A tier 1 target is highly unlikely to be directly removed without first being demoted to tier 2 or tier 3. (The amount of time between such @@ -628,7 +628,7 @@ planned and scheduled action.) Raising the baseline expectations of a tier 1 target (such as the minimum CPU features or OS version required) requires the approval of the compiler and -release teams, and should be widely communicated as well, but does not +infra teams, and should be widely communicated as well, but does not necessarily require a full RFC. ### Tier 1 with host tools @@ -638,11 +638,11 @@ host (such as `rustc` and `cargo`). This allows the target to be used as a development platform, not just a compilation target. A proposed new tier 1 target with host tools must be reviewed and approved by -the compiler team based on these requirements. In addition, the release team -must approve the viability and value of supporting host tools for the target. +the compiler team based on these requirements. In addition, the infra team +must approve the viability of supporting host tools for the target. For a tier 1 target, this will typically take place via a full RFC proposing the target, to be jointly reviewed and approved by the compiler team and -release team. +infra team. In addition, the infrastructure team must approve the integration of the target's host tools into Continuous Integration (CI), and the CI-related @@ -697,7 +697,7 @@ target with host tools may be demoted (including having its host tools dropped, or being demoted to tier 2 with host tools) if it no longer meets these requirements but still meets the requirements for a lower tier. Any proposal for demotion of a tier 1 target (with or without host tools) requires a full -RFC process, with approval by the compiler and release teams. Any such proposal +RFC process, with approval by the compiler and infra teams. Any such proposal will be communicated widely to the Rust community, both when initially proposed and before being dropped from a stable release. From 6e75abd0aa4f22f6291f1a25ff81e0c6b959ad26 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sun, 20 Jul 2025 22:35:43 +0530 Subject: [PATCH 0110/2093] std: sys: io: io_slice: Add UEFI types UEFI networking APIs do support vectored read/write. While the types for UDP4, UDP6, TCP4 and TCP6 are defined separately, they are essentially the same C struct. So we can map IoSlice and IoSliceMut to have the same binary representation. Since all UEFI networking types for read/write are DSTs, `IoSlice` and `IoSliceMut` will need to be copied to the end of the transmit/receive structures. So having the same binary representation just allows us to do a single memcpy instead of having to loop and set the DST. Signed-off-by: Ayush Singh --- library/std/src/sys/io/io_slice/uefi.rs | 74 +++++++++++++++ library/std/src/sys/io/mod.rs | 3 + library/std/src/sys/pal/uefi/tests.rs | 119 ++++++++++++++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 library/std/src/sys/io/io_slice/uefi.rs diff --git a/library/std/src/sys/io/io_slice/uefi.rs b/library/std/src/sys/io/io_slice/uefi.rs new file mode 100644 index 000000000000..909cfbea0b7b --- /dev/null +++ b/library/std/src/sys/io/io_slice/uefi.rs @@ -0,0 +1,74 @@ +//! A buffer type used with `Write::write_vectored` for UEFI Networking APIs. Vectored writing to +//! File is not supported as of UEFI Spec 2.11. + +use crate::marker::PhantomData; +use crate::slice; + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct IoSlice<'a> { + len: u32, + data: *const u8, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + let len = buf.len().try_into().unwrap(); + Self { len, data: buf.as_ptr(), _p: PhantomData } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + self.len = u32::try_from(n) + .ok() + .and_then(|n| self.len.checked_sub(n)) + .expect("advancing IoSlice beyond its length"); + unsafe { self.data = self.data.add(n) }; + } + + #[inline] + pub const fn as_slice(&self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self.data, self.len as usize) } + } +} + +#[repr(C)] +pub struct IoSliceMut<'a> { + len: u32, + data: *mut u8, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + let len = buf.len().try_into().unwrap(); + Self { len, data: buf.as_mut_ptr(), _p: PhantomData } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + self.len = u32::try_from(n) + .ok() + .and_then(|n| self.len.checked_sub(n)) + .expect("advancing IoSlice beyond its length"); + unsafe { self.data = self.data.add(n) }; + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.data, self.len as usize) } + } + + #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.data, self.len as usize) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.data, self.len as usize) } + } +} diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 4d0365d42fd9..ae75f4d97b43 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -11,6 +11,9 @@ mod io_slice { } else if #[cfg(target_os = "wasi")] { mod wasi; pub use wasi::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; } else { mod unsupported; pub use unsupported::*; diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs index 38658cc4e9ac..49e75a1a70d7 100644 --- a/library/std/src/sys/pal/uefi/tests.rs +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -1,5 +1,6 @@ use super::alloc::*; use super::time::*; +use crate::io::{IoSlice, IoSliceMut}; use crate::time::Duration; #[test] @@ -39,3 +40,121 @@ fn epoch() { }; assert_eq!(system_time_internal::uefi_time_to_duration(t), Duration::new(0, 0)); } + +// UEFI IoSlice and IoSliceMut Tests +// +// Strictly speaking, vectored read/write types for UDP4, UDP6, TCP4, TCP6 are defined +// separately in the UEFI Spec. However, they have the same signature. These tests just ensure +// that `IoSlice` and `IoSliceMut` are compatible with the vectored types for all the +// networking protocols. + +unsafe fn to_slice(val: &T) -> &[u8] { + let len = size_of_val(val); + unsafe { crate::slice::from_raw_parts(crate::ptr::from_ref(val).cast(), len) } +} + +#[test] +fn io_slice_single() { + let mut data = [0, 1, 2, 3, 4]; + + let tcp4_frag = r_efi::protocols::tcp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let tcp6_frag = r_efi::protocols::tcp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp4_frag = r_efi::protocols::udp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp6_frag = r_efi::protocols::udp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let io_slice = IoSlice::new(&data); + + unsafe { + assert_eq!(to_slice(&io_slice), to_slice(&tcp4_frag)); + assert_eq!(to_slice(&io_slice), to_slice(&tcp6_frag)); + assert_eq!(to_slice(&io_slice), to_slice(&udp4_frag)); + assert_eq!(to_slice(&io_slice), to_slice(&udp6_frag)); + } +} + +#[test] +fn io_slice_mut_single() { + let mut data = [0, 1, 2, 3, 4]; + + let tcp4_frag = r_efi::protocols::tcp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let tcp6_frag = r_efi::protocols::tcp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp4_frag = r_efi::protocols::udp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp6_frag = r_efi::protocols::udp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let io_slice_mut = IoSliceMut::new(&mut data); + + unsafe { + assert_eq!(to_slice(&io_slice_mut), to_slice(&tcp4_frag)); + assert_eq!(to_slice(&io_slice_mut), to_slice(&tcp6_frag)); + assert_eq!(to_slice(&io_slice_mut), to_slice(&udp4_frag)); + assert_eq!(to_slice(&io_slice_mut), to_slice(&udp6_frag)); + } +} + +#[test] +fn io_slice_multi() { + let mut data = [0, 1, 2, 3, 4]; + + let tcp4_frag = r_efi::protocols::tcp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let rhs = + [tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag]; + let lhs = [ + IoSlice::new(&data), + IoSlice::new(&data), + IoSlice::new(&data), + IoSlice::new(&data), + IoSlice::new(&data), + ]; + + unsafe { + assert_eq!(to_slice(&lhs), to_slice(&rhs)); + } +} + +#[test] +fn io_slice_basic() { + let data = [0, 1, 2, 3, 4]; + let mut io_slice = IoSlice::new(&data); + + assert_eq!(data, io_slice.as_slice()); + io_slice.advance(2); + assert_eq!(&data[2..], io_slice.as_slice()); +} + +#[test] +fn io_slice_mut_basic() { + let data = [0, 1, 2, 3, 4]; + let mut data_clone = [0, 1, 2, 3, 4]; + let mut io_slice_mut = IoSliceMut::new(&mut data_clone); + + assert_eq!(data, io_slice_mut.as_slice()); + assert_eq!(data, io_slice_mut.as_mut_slice()); + + io_slice_mut.advance(2); + assert_eq!(&data[2..], io_slice_mut.into_slice()); +} From fa6986ebe749bc59d569e3f75d0b5764f2601d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 1 Aug 2025 21:19:32 +0000 Subject: [PATCH 0111/2093] Suggest bounds in more cases, accounting for type parameters referenced in predicate --- .../src/error_reporting/traits/suggestions.rs | 77 ++++++++++++++++--- compiler/rustc_trait_selection/src/lib.rs | 1 + ...ng_goal_with_repeat_expr_anon_const.stderr | 4 - .../suggest-complex-bound-on-method.fixed | 23 ++++++ .../bound/suggest-complex-bound-on-method.rs | 23 ++++++ .../suggest-complex-bound-on-method.stderr | 22 ++++++ 6 files changed, 136 insertions(+), 14 deletions(-) create mode 100644 tests/ui/traits/bound/suggest-complex-bound-on-method.fixed create mode 100644 tests/ui/traits/bound/suggest-complex-bound-on-method.rs create mode 100644 tests/ui/traits/bound/suggest-complex-bound-on-method.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index c182fd99b17b..d6997c8525ef 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -31,8 +31,8 @@ use rustc_middle::ty::print::{ }; use rustc_middle::ty::{ self, AdtKind, GenericArgs, InferTy, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeVisitableExt, TypeckResults, Upcast, suggest_arbitrary_trait_bound, - suggest_constraining_type_param, + TypeSuperFoldable, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, TypeckResults, Upcast, + suggest_arbitrary_trait_bound, suggest_constraining_type_param, }; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; @@ -262,6 +262,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => (false, None), }; + let mut finder = ParamFinder { .. }; + finder.visit_binder(&trait_pred); + // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we // don't suggest `T: Sized + ?Sized`. loop { @@ -410,6 +413,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } + hir::Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Fn(..), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + generics, + trait_item_def_id: None, + kind: hir::ImplItemKind::Fn(..), + .. + }) if finder.can_suggest_bound(generics) => { + // Missing generic type parameter bound. + suggest_arbitrary_trait_bound( + self.tcx, + generics, + err, + trait_pred, + associated_ty, + ); + } hir::Node::Item(hir::Item { kind: hir::ItemKind::Struct(_, generics, _) @@ -422,7 +445,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | hir::ItemKind::Const(_, generics, _, _) | hir::ItemKind::TraitAlias(_, generics, _), .. - }) if !param_ty => { + }) if finder.can_suggest_bound(generics) => { // Missing generic type parameter bound. if suggest_arbitrary_trait_bound( self.tcx, @@ -5034,8 +5057,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S(T);` // is not. Look for invalid "bare" parameter uses, and suggest using indirection. - let mut visitor = - FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false }; + let mut visitor = FindTypeParam { param: param.name.ident().name, .. }; visitor.visit_item(item); if visitor.invalid_spans.is_empty() { return false; @@ -5198,7 +5220,7 @@ fn hint_missing_borrow<'tcx>( /// Used to suggest replacing associated types with an explicit type in `where` clauses. #[derive(Debug)] pub struct SelfVisitor<'v> { - pub paths: Vec<&'v hir::Ty<'v>>, + pub paths: Vec<&'v hir::Ty<'v>> = Vec::new(), pub name: Option, } @@ -5568,7 +5590,7 @@ fn point_at_assoc_type_restriction( ); // Search for the associated type `Self::{name}`, get // its type and suggest replacing the bound with it. - let mut visitor = SelfVisitor { paths: vec![], name: Some(name) }; + let mut visitor = SelfVisitor { name: Some(name), .. }; visitor.visit_trait_ref(trait_ref); for path in visitor.paths { err.span_suggestion_verbose( @@ -5579,7 +5601,7 @@ fn point_at_assoc_type_restriction( ); } } else { - let mut visitor = SelfVisitor { paths: vec![], name: None }; + let mut visitor = SelfVisitor { name: None, .. }; visitor.visit_trait_ref(trait_ref); let span: MultiSpan = visitor.paths.iter().map(|p| p.span).collect::>().into(); @@ -5609,8 +5631,8 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec) { /// `param: ?Sized` would be a valid constraint. struct FindTypeParam { param: rustc_span::Symbol, - invalid_spans: Vec, - nested: bool, + invalid_spans: Vec = Vec::new(), + nested: bool = false, } impl<'v> Visitor<'v> for FindTypeParam { @@ -5648,3 +5670,38 @@ impl<'v> Visitor<'v> for FindTypeParam { } } } + +/// Look for type parameters in predicates. We use this to identify whether a bound is suitable in +/// on a given item. +struct ParamFinder { + params: Vec = Vec::new(), +} + +impl<'tcx> TypeVisitor> for ParamFinder { + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + match t.kind() { + ty::Param(p) => self.params.push(p.name), + _ => {} + } + t.super_visit_with(self) + } +} + +impl ParamFinder { + /// Whether the `hir::Generics` of the current item can suggest the evaluated bound because its + /// references to type parameters are present in the generics. + fn can_suggest_bound(&self, generics: &hir::Generics<'_>) -> bool { + if self.params.is_empty() { + // There are no references to type parameters at all, so suggesting the bound + // would be reasonable. + return true; + } + generics.params.iter().any(|p| match p.name { + hir::ParamName::Plain(p_name) => { + // All of the parameters in the bound can be referenced in the current item. + self.params.iter().any(|p| *p == p_name.name || *p == kw::SelfUpper) + } + _ => true, + }) + } +} diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index e2b22f7bab74..fc0cf8f140a7 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -19,6 +19,7 @@ #![feature(assert_matches)] #![feature(associated_type_defaults)] #![feature(box_patterns)] +#![feature(default_field_values)] #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iterator_try_reduce)] diff --git a/tests/ui/const-generics/failing_goal_with_repeat_expr_anon_const.stderr b/tests/ui/const-generics/failing_goal_with_repeat_expr_anon_const.stderr index 12de4f1dc300..cf5b55a0ce29 100644 --- a/tests/ui/const-generics/failing_goal_with_repeat_expr_anon_const.stderr +++ b/tests/ui/const-generics/failing_goal_with_repeat_expr_anon_const.stderr @@ -21,10 +21,6 @@ LL | [0u8; std::mem::size_of::()] == Self::P; | ^^ no implementation for `[u8; std::mem::size_of::()] == ::A` | = help: the trait `PartialEq<::A>` is not implemented for `[u8; std::mem::size_of::()]` -help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement - | -LL | pub trait T where [u8; std::mem::size_of::()]: PartialEq<::A> { - | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/traits/bound/suggest-complex-bound-on-method.fixed b/tests/ui/traits/bound/suggest-complex-bound-on-method.fixed new file mode 100644 index 000000000000..719835af0a9f --- /dev/null +++ b/tests/ui/traits/bound/suggest-complex-bound-on-method.fixed @@ -0,0 +1,23 @@ +//@ run-rustfix +#![allow(dead_code)] +struct Application; +// https://github.com/rust-lang/rust/issues/144734 +trait Trait { + type Error: std::error::Error; + + fn run(&self) -> Result<(), Self::Error>; +} + +#[derive(Debug)] +enum ApplicationError { + Quit, +} + +impl Application { + fn thing(&self, t: T) -> Result<(), ApplicationError> where ApplicationError: From<::Error> { + t.run()?; //~ ERROR E0277 + Ok(()) + } +} + +fn main() {} diff --git a/tests/ui/traits/bound/suggest-complex-bound-on-method.rs b/tests/ui/traits/bound/suggest-complex-bound-on-method.rs new file mode 100644 index 000000000000..39cec571242d --- /dev/null +++ b/tests/ui/traits/bound/suggest-complex-bound-on-method.rs @@ -0,0 +1,23 @@ +//@ run-rustfix +#![allow(dead_code)] +struct Application; +// https://github.com/rust-lang/rust/issues/144734 +trait Trait { + type Error: std::error::Error; + + fn run(&self) -> Result<(), Self::Error>; +} + +#[derive(Debug)] +enum ApplicationError { + Quit, +} + +impl Application { + fn thing(&self, t: T) -> Result<(), ApplicationError> { + t.run()?; //~ ERROR E0277 + Ok(()) + } +} + +fn main() {} diff --git a/tests/ui/traits/bound/suggest-complex-bound-on-method.stderr b/tests/ui/traits/bound/suggest-complex-bound-on-method.stderr new file mode 100644 index 000000000000..ac45f0495411 --- /dev/null +++ b/tests/ui/traits/bound/suggest-complex-bound-on-method.stderr @@ -0,0 +1,22 @@ +error[E0277]: `?` couldn't convert the error to `ApplicationError` + --> $DIR/suggest-complex-bound-on-method.rs:18:16 + | +LL | t.run()?; + | -----^ the trait `From<::Error>` is not implemented for `ApplicationError` + | | + | this can't be annotated with `?` because it has type `Result<_, ::Error>` + | +note: `ApplicationError` needs to implement `From<::Error>` + --> $DIR/suggest-complex-bound-on-method.rs:12:1 + | +LL | enum ApplicationError { + | ^^^^^^^^^^^^^^^^^^^^^ + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | fn thing(&self, t: T) -> Result<(), ApplicationError> where ApplicationError: From<::Error> { + | +++++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 9916ce362f712b7aa91cd7576a5dcfc575ffd621 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 1 Aug 2025 15:31:16 +0200 Subject: [PATCH 0112/2093] fix `#[loop_match]` on diverging loop this generated invalid MIR before --- .../rustc_mir_build/src/builder/expr/into.rs | 2 +- tests/ui/loop-match/diverges.rs | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/ui/loop-match/diverges.rs diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 82b883a99a11..ac87f671699d 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -345,7 +345,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr_span, |this| { this.lower_match_arms( - destination, + state_place, scrutinee_place_builder, scrutinee_span, arms, diff --git a/tests/ui/loop-match/diverges.rs b/tests/ui/loop-match/diverges.rs new file mode 100644 index 000000000000..f1b3ffb2076b --- /dev/null +++ b/tests/ui/loop-match/diverges.rs @@ -0,0 +1,44 @@ +//@ build-pass +//@ compile-flags: -Zvalidate-mir +#![allow(incomplete_features)] +#![feature(loop_match)] +#![crate_type = "lib"] + +// Test that a #[loop_match] without an explicit break from the loop generates valid MIR. + +fn break_to_block_unit() -> u8 { + let mut state = 0; + #[loop_match] + loop { + state = 'blk: { + match state { + _ => 'b: { + break 'b 2; + } + } + } + } +} + +fn break_to_block_value() -> u8 { + let mut state = 0u8; + #[loop_match] + 'a: loop { + state = 'blk: { + match state { + _ => break 'blk state, + } + } + } +} + +fn infinite_a(mut state: u8) { + #[loop_match] + loop { + state = 'blk: { + match state { + a => a, + } + } + } +} From 2db126d651b6803c9b37fd021376ce6e5dd5a09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 1 Mar 2025 22:27:16 +0000 Subject: [PATCH 0113/2093] Include whitespace in "remove `|`" suggestion and make it hidden --- compiler/rustc_errors/src/diagnostic.rs | 2 +- compiler/rustc_parse/messages.ftl | 4 +- compiler/rustc_parse/src/errors.rs | 17 +++- compiler/rustc_parse/src/parser/pat.rs | 18 ++-- .../issue-64879-trailing-before-guard.fixed | 18 ++++ .../issue-64879-trailing-before-guard.rs | 5 +- .../issue-64879-trailing-before-guard.stderr | 24 +++--- .../ui/or-patterns/remove-leading-vert.fixed | 26 +++--- tests/ui/or-patterns/remove-leading-vert.rs | 2 +- .../ui/or-patterns/remove-leading-vert.stderr | 85 +------------------ ...30779-never-arm-no-oatherwise-block.stderr | 6 -- 11 files changed, 77 insertions(+), 130 deletions(-) create mode 100644 tests/ui/or-patterns/issue-64879-trailing-before-guard.fixed diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 96c7ba6ed27b..0034a30be08a 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1112,7 +1112,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { .map(|snippet| { debug_assert!( !(sp.is_empty() && snippet.is_empty()), - "Span must not be empty and have no suggestion" + "Span `{sp:?}` must not be empty and have no suggestion" ); Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] } }) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 859118a4adee..83c5ac449c20 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -851,8 +851,8 @@ parse_too_many_hashes = too many `#` symbols: raw strings may be delimited by up parse_too_short_hex_escape = numeric character escape is too short -parse_trailing_vert_not_allowed = a trailing `|` is not allowed in an or-pattern - .suggestion = remove the `{$token}` +parse_trailing_vert_not_allowed = a trailing `{$token}` is not allowed in an or-pattern +parse_trailing_vert_not_allowed_suggestion = remove the `{$token}` parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto` parse_trait_alias_cannot_be_const = trait aliases cannot be `const` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 4aaaba01faeb..871d4a53b5c1 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2620,7 +2620,7 @@ pub(crate) enum TopLevelOrPatternNotAllowedSugg { parse_sugg_remove_leading_vert_in_pattern, code = "", applicability = "machine-applicable", - style = "verbose" + style = "tool-only" )] RemoveLeadingVert { #[primary_span] @@ -2653,12 +2653,25 @@ pub(crate) struct UnexpectedVertVertInPattern { pub start: Option, } +#[derive(Subdiagnostic)] +#[suggestion( + parse_trailing_vert_not_allowed, + code = "", + applicability = "machine-applicable", + style = "tool-only" +)] +pub(crate) struct TrailingVertSuggestion { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_trailing_vert_not_allowed)] pub(crate) struct TrailingVertNotAllowed { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] pub span: Span, + #[subdiagnostic] + pub suggestion: TrailingVertSuggestion, #[label(parse_label_while_parsing_or_pattern_here)] pub start: Option, pub token: Token, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 64653ee2a04c..a5d16ee42315 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -25,10 +25,10 @@ use crate::errors::{ GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, ParenRangeSuggestion, PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, - TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, UnexpectedExpressionInPattern, - UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, - UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, - UnexpectedVertVertInPattern, WrapInParens, + TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, TrailingVertSuggestion, + UnexpectedExpressionInPattern, UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, + UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, + UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens, }; use crate::parser::expr::{DestructuredFloat, could_be_unclosed_char_literal}; use crate::{exp, maybe_recover_from_interpolated_ty_qpath}; @@ -268,10 +268,9 @@ impl<'a> Parser<'a> { if let PatKind::Or(pats) = &pat.kind { let span = pat.span; - let sub = if pats.len() == 1 { - Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert { - span: span.with_hi(span.lo() + BytePos(1)), - }) + let sub = if let [_] = &pats[..] { + let span = span.with_hi(span.lo() + BytePos(1)); + Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert { span }) } else { Some(TopLevelOrPatternNotAllowedSugg::WrapInParens { span, @@ -363,6 +362,9 @@ impl<'a> Parser<'a> { self.dcx().emit_err(TrailingVertNotAllowed { span: self.token.span, start: lo, + suggestion: TrailingVertSuggestion { + span: self.prev_token.span.shrink_to_hi().with_hi(self.token.span.hi()), + }, token: self.token, note_double_vert: self.token.kind == token::OrOr, }); diff --git a/tests/ui/or-patterns/issue-64879-trailing-before-guard.fixed b/tests/ui/or-patterns/issue-64879-trailing-before-guard.fixed new file mode 100644 index 000000000000..0c65f709d663 --- /dev/null +++ b/tests/ui/or-patterns/issue-64879-trailing-before-guard.fixed @@ -0,0 +1,18 @@ +// In this regression test we check that a trailing `|` in an or-pattern just +// before the `if` token of a `match` guard will receive parser recovery with +// an appropriate error message. +//@ run-rustfix +#![allow(dead_code)] + +enum E { A, B } + +fn main() { + match E::A { + E::A | + E::B //~ ERROR a trailing `|` is not allowed in an or-pattern + if true => { + let _recovery_witness: i32 = 0i32; //~ ERROR mismatched types + } + _ => {} + } +} diff --git a/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs b/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs index 181c770096a5..d7da564c2e1d 100644 --- a/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs +++ b/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs @@ -1,6 +1,8 @@ // In this regression test we check that a trailing `|` in an or-pattern just // before the `if` token of a `match` guard will receive parser recovery with // an appropriate error message. +//@ run-rustfix +#![allow(dead_code)] enum E { A, B } @@ -9,7 +11,8 @@ fn main() { E::A | E::B | //~ ERROR a trailing `|` is not allowed in an or-pattern if true => { - let recovery_witness: bool = 0; //~ ERROR mismatched types + let _recovery_witness: i32 = 0u32; //~ ERROR mismatched types } + _ => {} } } diff --git a/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr b/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr index 91db3d049f62..238c76080dc4 100644 --- a/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr +++ b/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr @@ -1,24 +1,24 @@ error: a trailing `|` is not allowed in an or-pattern - --> $DIR/issue-64879-trailing-before-guard.rs:10:14 + --> $DIR/issue-64879-trailing-before-guard.rs:12:14 | LL | E::A | | ---- while parsing this or-pattern starting here LL | E::B | | ^ - | -help: remove the `|` - | -LL - E::B | -LL + E::B - | error[E0308]: mismatched types - --> $DIR/issue-64879-trailing-before-guard.rs:12:42 + --> $DIR/issue-64879-trailing-before-guard.rs:14:42 + | +LL | let _recovery_witness: i32 = 0u32; + | --- ^^^^ expected `i32`, found `u32` + | | + | expected due to this + | +help: change the type of the numeric literal from `u32` to `i32` + | +LL - let _recovery_witness: i32 = 0u32; +LL + let _recovery_witness: i32 = 0i32; | -LL | let recovery_witness: bool = 0; - | ---- ^ expected `bool`, found integer - | | - | expected due to this error: aborting due to 2 previous errors diff --git a/tests/ui/or-patterns/remove-leading-vert.fixed b/tests/ui/or-patterns/remove-leading-vert.fixed index 2851b8f18c54..aa7975dc508a 100644 --- a/tests/ui/or-patterns/remove-leading-vert.fixed +++ b/tests/ui/or-patterns/remove-leading-vert.fixed @@ -23,26 +23,26 @@ fn leading() { #[cfg(false)] fn trailing() { - let ( A ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern - let (a ,): (E,); //~ ERROR a trailing `|` is not allowed in an or-pattern - let ( A | B ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern - let [ A | B ]: [E; 1]; //~ ERROR a trailing `|` is not allowed in an or-pattern - let S { f: B }; //~ ERROR a trailing `|` is not allowed in an or-pattern - let ( A | B ): E; //~ ERROR unexpected token `||` in pattern + let ( A ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern + let (a,): (E,); //~ ERROR a trailing `|` is not allowed in an or-pattern + let ( A | B ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern + let [ A | B ]: [E; 1]; //~ ERROR a trailing `|` is not allowed in an or-pattern + let S { f: B }; //~ ERROR a trailing `|` is not allowed in an or-pattern + let ( A | B ): E; //~ ERROR unexpected token `||` in pattern //~^ ERROR a trailing `|` is not allowed in an or-pattern match A { - A => {} //~ ERROR a trailing `|` is not allowed in an or-pattern - A => {} //~ ERROR a trailing `|` is not allowed in an or-pattern - A | B => {} //~ ERROR unexpected token `||` in pattern + A => {} //~ ERROR a trailing `|` is not allowed in an or-pattern + A => {} //~ ERROR a trailing `||` is not allowed in an or-pattern + A | B => {} //~ ERROR unexpected token `||` in pattern //~^ ERROR a trailing `|` is not allowed in an or-pattern - | A | B => {} + | A | B => {} //~^ ERROR a trailing `|` is not allowed in an or-pattern } // These test trailing-vert in `let` bindings, but they also test that we don't emit a // duplicate suggestion that would confuse rustfix. - let a : u8 = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern - let a = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern - let a ; //~ ERROR a trailing `|` is not allowed in an or-pattern + let a : u8 = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern + let a = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern + let a ; //~ ERROR a trailing `|` is not allowed in an or-pattern } diff --git a/tests/ui/or-patterns/remove-leading-vert.rs b/tests/ui/or-patterns/remove-leading-vert.rs index 1e1dbfbc6e66..1b4eb669fbb4 100644 --- a/tests/ui/or-patterns/remove-leading-vert.rs +++ b/tests/ui/or-patterns/remove-leading-vert.rs @@ -32,7 +32,7 @@ fn trailing() { //~^ ERROR a trailing `|` is not allowed in an or-pattern match A { A | => {} //~ ERROR a trailing `|` is not allowed in an or-pattern - A || => {} //~ ERROR a trailing `|` is not allowed in an or-pattern + A || => {} //~ ERROR a trailing `||` is not allowed in an or-pattern A || B | => {} //~ ERROR unexpected token `||` in pattern //~^ ERROR a trailing `|` is not allowed in an or-pattern | A | B | => {} diff --git a/tests/ui/or-patterns/remove-leading-vert.stderr b/tests/ui/or-patterns/remove-leading-vert.stderr index 0323c64f0423..29450153ba45 100644 --- a/tests/ui/or-patterns/remove-leading-vert.stderr +++ b/tests/ui/or-patterns/remove-leading-vert.stderr @@ -3,12 +3,6 @@ error: function parameters require top-level or-patterns in parentheses | LL | fn fun1( | A: E) {} | ^^^ - | -help: remove the `|` - | -LL - fn fun1( | A: E) {} -LL + fn fun1( A: E) {} - | error: unexpected `||` before function parameter --> $DIR/remove-leading-vert.rs:12:14 @@ -78,12 +72,6 @@ LL | let ( A | ): E; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let ( A | ): E; -LL + let ( A ): E; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:27:12 @@ -92,12 +80,6 @@ LL | let (a |,): (E,); | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let (a |,): (E,); -LL + let (a ,): (E,); - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:28:17 @@ -106,12 +88,6 @@ LL | let ( A | B | ): E; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let ( A | B | ): E; -LL + let ( A | B ): E; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:29:17 @@ -120,12 +96,6 @@ LL | let [ A | B | ]: [E; 1]; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let [ A | B | ]: [E; 1]; -LL + let [ A | B ]: [E; 1]; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:30:18 @@ -134,12 +104,6 @@ LL | let S { f: B | }; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let S { f: B | }; -LL + let S { f: B }; - | error: unexpected token `||` in pattern --> $DIR/remove-leading-vert.rs:31:13 @@ -162,12 +126,6 @@ LL | let ( A || B | ): E; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let ( A || B | ): E; -LL + let ( A || B ): E; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:34:11 @@ -176,14 +134,8 @@ LL | A | => {} | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - A | => {} -LL + A => {} - | -error: a trailing `|` is not allowed in an or-pattern +error: a trailing `||` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:35:11 | LL | A || => {} @@ -192,11 +144,6 @@ LL | A || => {} | while parsing this or-pattern starting here | = note: alternatives in or-patterns are separated with `|`, not `||` -help: remove the `||` - | -LL - A || => {} -LL + A => {} - | error: unexpected token `||` in pattern --> $DIR/remove-leading-vert.rs:36:11 @@ -219,12 +166,6 @@ LL | A || B | => {} | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - A || B | => {} -LL + A || B => {} - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:38:17 @@ -233,12 +174,6 @@ LL | | A | B | => {} | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - | A | B | => {} -LL + | A | B => {} - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:45:11 @@ -247,12 +182,6 @@ LL | let a | : u8 = 0; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let a | : u8 = 0; -LL + let a : u8 = 0; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:46:11 @@ -261,12 +190,6 @@ LL | let a | = 0; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let a | = 0; -LL + let a = 0; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:47:11 @@ -275,12 +198,6 @@ LL | let a | ; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let a | ; -LL + let a ; - | error: aborting due to 21 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr index 26731e29ffc5..5f4a5f31e34f 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr +++ b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr @@ -5,12 +5,6 @@ LL | ! | | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - ! | -LL + ! - | error: a never pattern is always unreachable --> $DIR/ICE-130779-never-arm-no-oatherwise-block.rs:10:20 From 970ac40300bc75de9f5ef2edf77e93be5bd0769d Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sat, 2 Aug 2025 23:10:46 +0500 Subject: [PATCH 0114/2093] updated doc comment --- src/librustdoc/clean/types.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 89d5acb985b1..cc3faa3d612a 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1534,10 +1534,10 @@ impl Type { matches!(self, Type::Path { path: Path { res: Res::Def(DefKind::TyAlias, _), .. } }) } - /// Check if two types are "the same" for documentation purposes. + /// Check if this type is a subtype of another type for documentation purposes. /// /// This is different from `Eq`, because it knows that things like - /// `Placeholder` are possible matches for everything. + /// `Infer` and generics have special subtyping rules. /// /// This relation is not commutative when generics are involved: /// @@ -1548,8 +1548,8 @@ impl Type { /// let cache = Cache::new(false); /// let generic = Type::Generic(rustc_span::symbol::sym::Any); /// let unit = Type::Primitive(PrimitiveType::Unit); - /// assert!(!generic.is_same(&unit, &cache)); - /// assert!(unit.is_same(&generic, &cache)); + /// assert!(!generic.is_doc_subtype_of(&unit, &cache)); + /// assert!(unit.is_doc_subtype_of(&generic, &cache)); /// ``` /// /// An owned type is also the same as its borrowed variants (this is commutative), From dccc075d05f0c28fab5775f7efa5550f24382ec6 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 4 Aug 2025 23:47:40 +0000 Subject: [PATCH 0115/2093] Check for rustc_has_incoherent_inherent_impls in incoherent_impls query. --- compiler/rustc_hir_typeck/src/method/probe.rs | 21 +++---------------- compiler/rustc_middle/src/ty/trait_def.rs | 7 +++++++ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index f5584e0596a6..51de661a2bb5 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -777,31 +777,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.assemble_inherent_candidates_from_object(generalized_self_ty); self.assemble_inherent_impl_candidates_for_type(p.def_id(), receiver_steps); - if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty( - raw_self_ty, - receiver_steps, - ); - } + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps); } ty::Adt(def, _) => { let def_id = def.did(); self.assemble_inherent_impl_candidates_for_type(def_id, receiver_steps); - if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty( - raw_self_ty, - receiver_steps, - ); - } + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps); } ty::Foreign(did) => { self.assemble_inherent_impl_candidates_for_type(did, receiver_steps); - if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty( - raw_self_ty, - receiver_steps, - ); - } + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps); } ty::Param(_) => { self.assemble_inherent_candidates_from_param(raw_self_ty); diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 59e2b2a034de..4e38d969192f 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -6,6 +6,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_macros::{Decodable, Encodable, HashStable}; +use rustc_span::symbol::sym; use tracing::debug; use crate::query::LocalCrate; @@ -239,6 +240,12 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait /// Query provider for `incoherent_impls`. pub(super) fn incoherent_impls_provider(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] { + if let Some(def_id) = simp.def() + && !tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) + { + return &[]; + } + let mut impls = Vec::new(); for cnum in iter::once(LOCAL_CRATE).chain(tcx.crates(()).iter().copied()) { for &impl_def_id in tcx.crate_incoherent_impls((cnum, simp)) { From 5073886181cb95bfe899a26404c57d2944f2a4d4 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 2 Aug 2025 18:21:16 +0000 Subject: [PATCH 0116/2093] Gate checking rustc attrs on the feature. --- compiler/rustc_hir_typeck/src/callee.rs | 6 ++++-- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 16 ++++++++++++---- compiler/rustc_hir_typeck/src/upvar.rs | 2 +- compiler/rustc_hir_typeck/src/writeback.rs | 3 ++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 48bb45de53eb..e19dd83f81d8 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -511,7 +511,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Untranslatable diagnostics are okay for rustc internals #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] - if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) { + if self.has_rustc_attrs + && self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) + { let predicates = self.tcx.predicates_of(def_id); let predicates = predicates.instantiate(self.tcx, args); for (predicate, predicate_span) in predicates { @@ -894,7 +896,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // If we have `rustc_do_not_const_check`, do not check `[const]` bounds. - if self.tcx.has_attr(self.body_id, sym::rustc_do_not_const_check) { + if self.has_rustc_attrs && self.tcx.has_attr(self.body_id, sym::rustc_do_not_const_check) { return; } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 0c6226ce71e7..74f27e85cba2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -126,6 +126,10 @@ pub(crate) struct FnCtxt<'a, 'tcx> { /// These are stored here so we may collect them when canonicalizing user /// type ascriptions later. pub(super) trait_ascriptions: RefCell>>>, + + /// Whether the current crate enables the `rustc_attrs` feature. + /// This allows to skip processing attributes in many places. + pub(super) has_rustc_attrs: bool, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -154,6 +158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { diverging_fallback_behavior, diverging_block_behavior, trait_ascriptions: Default::default(), + has_rustc_attrs: root_ctxt.tcx.features().rustc_attrs(), } } @@ -525,10 +530,13 @@ fn parse_never_type_options_attr( let mut fallback = None; let mut block = None; - let items = tcx - .get_attr(CRATE_DEF_ID, sym::rustc_never_type_options) - .map(|attr| attr.meta_item_list().unwrap()) - .unwrap_or_default(); + let items = if tcx.features().rustc_attrs() { + tcx.get_attr(CRATE_DEF_ID, sym::rustc_never_type_options) + .map(|attr| attr.meta_item_list().unwrap()) + } else { + None + }; + let items = items.unwrap_or_default(); for item in items { if item.has_name(sym::fallback) && fallback.is_none() { diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index df38c3a12143..8c0ec78e0af9 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -1747,7 +1747,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn should_log_capture_analysis(&self, closure_def_id: LocalDefId) -> bool { - self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) + self.has_rustc_attrs && self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) } fn log_capture_analysis_first_pass( diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 093de950d636..574406ecc8a2 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -45,7 +45,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This attribute causes us to dump some writeback information // in the form of errors, which is used for unit tests. - let rustc_dump_user_args = self.tcx.has_attr(item_def_id, sym::rustc_dump_user_args); + let rustc_dump_user_args = + self.has_rustc_attrs && self.tcx.has_attr(item_def_id, sym::rustc_dump_user_args); let mut wbcx = WritebackCx::new(self, body, rustc_dump_user_args); for param in body.params { From 6b7f8fbcc584f4dc46081b5c690aeca7def88019 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 5 Aug 2025 01:55:20 +0000 Subject: [PATCH 0117/2093] Promote crash test. --- tests/crashes/124352.rs | 4 ---- .../malformed-never-type-options.rs | 8 ++++++++ .../malformed-never-type-options.stderr | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) delete mode 100644 tests/crashes/124352.rs create mode 100644 tests/ui/attributes/malformed-never-type-options.rs create mode 100644 tests/ui/attributes/malformed-never-type-options.stderr diff --git a/tests/crashes/124352.rs b/tests/crashes/124352.rs deleted file mode 100644 index e9eb4419e6a4..000000000000 --- a/tests/crashes/124352.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ known-bug: #124352 -#![rustc_never_type_options(: Unsize = "hi")] - -fn main() {} diff --git a/tests/ui/attributes/malformed-never-type-options.rs b/tests/ui/attributes/malformed-never-type-options.rs new file mode 100644 index 000000000000..0c384be0e226 --- /dev/null +++ b/tests/ui/attributes/malformed-never-type-options.rs @@ -0,0 +1,8 @@ +//! Regression test for #124352 +//! The `rustc_*` attribute is malformed, but ICEing without a `feature(rustc_attrs)` is still bad. + +#![rustc_never_type_options(: Unsize = "hi")] +//~^ ERROR expected unsuffixed literal, found `:` +//~| ERROR use of an internal attribute + +fn main() {} diff --git a/tests/ui/attributes/malformed-never-type-options.stderr b/tests/ui/attributes/malformed-never-type-options.stderr new file mode 100644 index 000000000000..0d2ff4881f2a --- /dev/null +++ b/tests/ui/attributes/malformed-never-type-options.stderr @@ -0,0 +1,19 @@ +error: expected unsuffixed literal, found `:` + --> $DIR/malformed-never-type-options.rs:4:29 + | +LL | #![rustc_never_type_options(: Unsize = "hi")] + | ^ + +error[E0658]: use of an internal attribute + --> $DIR/malformed-never-type-options.rs:4:1 + | +LL | #![rustc_never_type_options(: Unsize = "hi")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + = note: the `#[rustc_never_type_options]` attribute is an internal implementation detail that will never be stable + = note: `rustc_never_type_options` is used to experiment with never type fallback and work on never type stabilization + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. From bdaabc17b664952ce0b9666b9b57188a6126935b Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Mon, 4 Aug 2025 20:47:43 +0200 Subject: [PATCH 0118/2093] Add test for "missing function argument" on multiline call --- .../fn/fn-arg-count-mismatch-diagnostics.rs | 12 +++++++ .../fn-arg-count-mismatch-diagnostics.stderr | 32 +++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs index b2f80ba1bf62..3b12ea1a7368 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs @@ -46,9 +46,21 @@ impl Bar { } } +fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} + fn main() { foo(1, 2, 3); //~^ ERROR function takes 4 arguments but 3 bar(1, 2, 3); //~^ ERROR function takes 6 arguments but 3 + + let variable_name = 42; + function_with_lots_of_arguments( + variable_name, + variable_name, + variable_name, + variable_name, + variable_name, + ); + //~^^^^^^^ ERROR this function takes 6 arguments but 5 arguments were supplied [E0061] } diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr index 6af7671af03b..494d9bf60a65 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr @@ -52,7 +52,7 @@ LL | <$from>::$method(8, /* u8 */) | ++++++++++ error[E0061]: this function takes 4 arguments but 3 arguments were supplied - --> $DIR/fn-arg-count-mismatch-diagnostics.rs:50:5 + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:52:5 | LL | foo(1, 2, 3); | ^^^--------- argument #4 of type `isize` is missing @@ -68,7 +68,7 @@ LL | foo(1, 2, 3, /* isize */); | +++++++++++++ error[E0061]: this function takes 6 arguments but 3 arguments were supplied - --> $DIR/fn-arg-count-mismatch-diagnostics.rs:52:5 + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:54:5 | LL | bar(1, 2, 3); | ^^^--------- three arguments of type `i32`, `i32`, and `i32` are missing @@ -83,6 +83,32 @@ help: provide the arguments LL | bar(1, 2, 3, /* i32 */, /* i32 */, /* i32 */); | +++++++++++++++++++++++++++++++++ -error: aborting due to 5 previous errors +error[E0061]: this function takes 6 arguments but 5 arguments were supplied + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:58:5 + | +LL | function_with_lots_of_arguments( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | variable_name, +LL | variable_name, + | ------------- argument #2 of type `char` is missing + | +note: function defined here + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:49:4 + | +LL | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- +help: provide the argument + | +LL - function_with_lots_of_arguments( +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - ); +LL + function_with_lots_of_arguments(variable_name, /* char */, variable_name, variable_name, variable_name, variable_name); + | + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0061`. From 2136c500cdbdd08fe7c25927b5d3ce790d75f68b Mon Sep 17 00:00:00 2001 From: Sebastian Poeplau Date: Fri, 1 Aug 2025 12:39:50 +0200 Subject: [PATCH 0119/2093] Preserve the .debug_gdb_scripts section Make sure that compiler and linker don't optimize the section's contents away by adding the global holding the data to "llvm.used". The volatile load in the main shim is retained because "llvm.used", which translates to SHF_GNU_RETAIN on ELF targets, requires a reasonably recent linker; emitting the volatile load ensures compatibility with older linkers, at least when libstd is used. Pretty printers in dylib dependencies are now emitted by the main crate instead of the dylib; apart from matching how rlibs are handled, this approach has the advantage that `omit_gdb_pretty_printer_section` keeps working with dylib dependencies. --- src/debuginfo.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/debuginfo.rs b/src/debuginfo.rs index 4c8585192a1b..4c0b64395232 100644 --- a/src/debuginfo.rs +++ b/src/debuginfo.rs @@ -254,7 +254,8 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { // TODO(antoyo): implement. } - fn debuginfo_finalize(&self) { + fn debuginfo_finalize(&mut self) { + // TODO: emit section `.debug_gdb_scripts`. self.context.set_debug_info(true) } From 566cdab16d74f29837b0978f25159c9ee7c51440 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 1 Aug 2025 21:04:53 +1000 Subject: [PATCH 0120/2093] Clarify `value_path_str_with_args`. The use of `print_value_path` means the value namespace is always used and the `guess_def_namespace` call is unnecessary. This commit removes the `guess_def_namespace` call and hard-codes `ValueNS`. It also changes the `print_value_path` to `print_def_path` for consistency with `def_path_str_with_args`. --- compiler/rustc_middle/src/ty/print/pretty.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index b381d62be47e..c150aa65b9be 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2131,8 +2131,6 @@ impl<'a, 'tcx> FmtPrinter<'a, 'tcx> { } } -// HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always -// (but also some things just print a `DefId` generally so maybe we need this?) fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace { match tcx.def_key(def_id).disambiguated_data.data { DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::OpaqueTy => { @@ -2157,6 +2155,7 @@ impl<'t> TyCtxt<'t> { self.def_path_str_with_args(def_id, &[]) } + /// For this one we determine the appropriate namespace for the `def_id`. pub fn def_path_str_with_args( self, def_id: impl IntoQueryParam, @@ -2169,16 +2168,17 @@ impl<'t> TyCtxt<'t> { FmtPrinter::print_string(self, ns, |p| p.print_def_path(def_id, args)).unwrap() } + /// For this one we always use value namespace. pub fn value_path_str_with_args( self, def_id: impl IntoQueryParam, args: &'t [GenericArg<'t>], ) -> String { let def_id = def_id.into_query_param(); - let ns = guess_def_namespace(self, def_id); + let ns = Namespace::ValueNS; debug!("value_path_str: def_id={:?}, ns={:?}", def_id, ns); - FmtPrinter::print_string(self, ns, |p| p.print_value_path(def_id, args)).unwrap() + FmtPrinter::print_string(self, ns, |p| p.print_def_path(def_id, args)).unwrap() } } From b0c36dd4c7519d295239b69127ff84f51f14acc3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 1 Aug 2025 23:58:46 +1000 Subject: [PATCH 0121/2093] Rename most of the printers. Three of them are named `AbsolutePathPrinter`, which is confusing, so give those names that better indicate how they are used. And then there is `SymbolPrinter` and `SymbolMangler`, which are renamed as `LegacySymbolMangler` and `V0SymbolMangler`, better indicating their similarity. --- compiler/rustc_const_eval/src/util/type_name.rs | 10 +++++----- compiler/rustc_lint/src/context.rs | 6 +++--- compiler/rustc_symbol_mangling/src/legacy.rs | 10 +++++----- compiler/rustc_symbol_mangling/src/v0.rs | 12 ++++++------ .../src/error_reporting/infer/mod.rs | 6 +++--- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index e6b9759819f1..573c734e931d 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -7,12 +7,12 @@ use rustc_middle::bug; use rustc_middle::ty::print::{PrettyPrinter, PrintError, Printer}; use rustc_middle::ty::{self, GenericArg, GenericArgKind, Ty, TyCtxt}; -struct AbsolutePathPrinter<'tcx> { +struct TypeNamePrinter<'tcx> { tcx: TyCtxt<'tcx>, path: String, } -impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { +impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -133,7 +133,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { } } -impl<'tcx> PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> { +impl<'tcx> PrettyPrinter<'tcx> for TypeNamePrinter<'tcx> { fn should_print_region(&self, _region: ty::Region<'_>) -> bool { false } @@ -157,7 +157,7 @@ impl<'tcx> PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> { } } -impl Write for AbsolutePathPrinter<'_> { +impl Write for TypeNamePrinter<'_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { self.path.push_str(s); Ok(()) @@ -165,7 +165,7 @@ impl Write for AbsolutePathPrinter<'_> { } pub fn type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> String { - let mut p = AbsolutePathPrinter { tcx, path: String::new() }; + let mut p = TypeNamePrinter { tcx, path: String::new() }; p.print_type(ty).unwrap(); p.path } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 11181d10af5e..0dd50af2efcd 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -745,12 +745,12 @@ impl<'tcx> LateContext<'tcx> { /// } /// ``` pub fn get_def_path(&self, def_id: DefId) -> Vec { - struct AbsolutePathPrinter<'tcx> { + struct LintPathPrinter<'tcx> { tcx: TyCtxt<'tcx>, path: Vec, } - impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { + impl<'tcx> Printer<'tcx> for LintPathPrinter<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -853,7 +853,7 @@ impl<'tcx> LateContext<'tcx> { } } - let mut p = AbsolutePathPrinter { tcx: self.tcx, path: vec![] }; + let mut p = LintPathPrinter { tcx: self.tcx, path: vec![] }; p.print_def_path(def_id, &[]).unwrap(); p.path } diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index aa8292c05044..f8bab0f5d0c1 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -58,7 +58,7 @@ pub(super) fn mangle<'tcx>( let hash = get_symbol_hash(tcx, instance, instance_ty, instantiating_crate); - let mut p = SymbolPrinter { tcx, path: SymbolPath::new(), keep_within_component: false }; + let mut p = LegacySymbolMangler { tcx, path: SymbolPath::new(), keep_within_component: false }; p.print_def_path( def_id, if let ty::InstanceKind::DropGlue(_, _) @@ -213,7 +213,7 @@ impl SymbolPath { } } -struct SymbolPrinter<'tcx> { +struct LegacySymbolMangler<'tcx> { tcx: TyCtxt<'tcx>, path: SymbolPath, @@ -228,7 +228,7 @@ struct SymbolPrinter<'tcx> { // `PrettyPrinter` aka pretty printing of e.g. types in paths, // symbol names should have their own printing machinery. -impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { +impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -453,7 +453,7 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { } } -impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> { +impl<'tcx> PrettyPrinter<'tcx> for LegacySymbolMangler<'tcx> { fn should_print_region(&self, _region: ty::Region<'_>) -> bool { false } @@ -489,7 +489,7 @@ impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> { } } -impl fmt::Write for SymbolPrinter<'_> { +impl fmt::Write for LegacySymbolMangler<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { // Name sanitation. LLVM will happily accept identifiers with weird names, but // gas doesn't! diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index c2458ae814b7..2aad5f315f36 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -33,7 +33,7 @@ pub(super) fn mangle<'tcx>( let args = tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), instance.args); let prefix = "_R"; - let mut p: SymbolMangler<'_> = SymbolMangler { + let mut p: V0SymbolMangler<'_> = V0SymbolMangler { tcx, start_offset: prefix.len(), is_exportable, @@ -88,7 +88,7 @@ pub fn mangle_internal_symbol<'tcx>(tcx: TyCtxt<'tcx>, item_name: &str) -> Strin } let prefix = "_R"; - let mut p: SymbolMangler<'_> = SymbolMangler { + let mut p: V0SymbolMangler<'_> = V0SymbolMangler { tcx, start_offset: prefix.len(), is_exportable: false, @@ -131,7 +131,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>( trait_ref: ty::ExistentialTraitRef<'tcx>, ) -> String { // FIXME(flip1995): See comment in `mangle_typeid_for_fnabi`. - let mut p = SymbolMangler { + let mut p = V0SymbolMangler { tcx, start_offset: 0, is_exportable: false, @@ -159,7 +159,7 @@ struct BinderLevel { lifetime_depths: Range, } -struct SymbolMangler<'tcx> { +struct V0SymbolMangler<'tcx> { tcx: TyCtxt<'tcx>, binders: Vec, out: String, @@ -173,7 +173,7 @@ struct SymbolMangler<'tcx> { consts: FxHashMap, usize>, } -impl<'tcx> SymbolMangler<'tcx> { +impl<'tcx> V0SymbolMangler<'tcx> { fn push(&mut self, s: &str) { self.out.push_str(s); } @@ -272,7 +272,7 @@ impl<'tcx> SymbolMangler<'tcx> { } } -impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { +impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index ed8229154a9b..0aefb4580f86 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -224,12 +224,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { use ty::GenericArg; use ty::print::Printer; - struct AbsolutePathPrinter<'tcx> { + struct ConflictingPathPrinter<'tcx> { tcx: TyCtxt<'tcx>, segments: Vec, } - impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { + impl<'tcx> Printer<'tcx> for ConflictingPathPrinter<'tcx> { fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { self.tcx } @@ -300,7 +300,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // let _ = [{struct Foo; Foo}, {struct Foo; Foo}]; if did1.krate != did2.krate { let abs_path = |def_id| { - let mut p = AbsolutePathPrinter { tcx: self.tcx, segments: vec![] }; + let mut p = ConflictingPathPrinter { tcx: self.tcx, segments: vec![] }; p.print_def_path(def_id, &[]).map(|_| p.segments) }; From d76eef481e2deaf3635da967e8afc5ac63c4c13e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 2 Aug 2025 00:03:28 +1000 Subject: [PATCH 0122/2093] Simplify multiple things. - `same_path` can just be a `bool`. - `expected` and `found` are only needed inside the block. - Neaten a comment. --- .../src/error_reporting/infer/mod.rs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 0aefb4580f86..fd204412564f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -304,24 +304,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { p.print_def_path(def_id, &[]).map(|_| p.segments) }; - // We compare strings because DefPath can be different - // for imported and non-imported crates + // We compare strings because DefPath can be different for imported and + // non-imported crates. let expected_str = self.tcx.def_path_str(did1); let found_str = self.tcx.def_path_str(did2); let Ok(expected_abs) = abs_path(did1) else { return false }; let Ok(found_abs) = abs_path(did2) else { return false }; - let same_path = || -> Result<_, PrintError> { - Ok(expected_str == found_str || expected_abs == found_abs) - }; - // We want to use as unique a type path as possible. If both types are "locally - // known" by the same name, we use the "absolute path" which uses the original - // crate name instead. - let (expected, found) = if expected_str == found_str { - (join_path_syms(&expected_abs), join_path_syms(&found_abs)) - } else { - (expected_str.clone(), found_str.clone()) - }; - if same_path().unwrap_or(false) { + let same_path = expected_str == found_str || expected_abs == found_abs; + if same_path { + // We want to use as unique a type path as possible. If both types are "locally + // known" by the same name, we use the "absolute path" which uses the original + // crate name instead. + let (expected, found) = if expected_str == found_str { + (join_path_syms(&expected_abs), join_path_syms(&found_abs)) + } else { + (expected_str.clone(), found_str.clone()) + }; + // We've displayed "expected `a::b`, found `a::b`". We add context to // differentiate the different cases where that might happen. let expected_crate_name = self.tcx.crate_name(did1.krate); From 3a993a611c9f32e34e341794ce19026ad3df2c0f Mon Sep 17 00:00:00 2001 From: Raoul Strackx Date: Tue, 5 Aug 2025 14:51:02 +0200 Subject: [PATCH 0123/2093] Ignore sleep_until test on SGX --- library/std/tests/thread.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs index 32561dd6ab6a..29f220d8a70a 100644 --- a/library/std/tests/thread.rs +++ b/library/std/tests/thread.rs @@ -19,6 +19,7 @@ fn sleep_very_long() { } #[test] +#[cfg_attr(target_env = "sgx", ignore = "Time within SGX enclave cannot be trusted")] fn sleep_until() { let now = Instant::now(); let period = Duration::from_millis(100); From f7e46f9d603010be83b3302500c65130a5186782 Mon Sep 17 00:00:00 2001 From: Marco Cavenati Date: Tue, 5 Aug 2025 15:35:59 +0200 Subject: [PATCH 0124/2093] fix(unicode-table-generator): fix duplicated unique indices unicode-table-generator panicked while populating distinct_indices because of duplicated indices. This was introduced by swapping the order of canonical_words.push(...) and canonical_words.len(). --- src/tools/unicode-table-generator/src/raw_emitter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs index e9e0efc45944..03ed9499e26c 100644 --- a/src/tools/unicode-table-generator/src/raw_emitter.rs +++ b/src/tools/unicode-table-generator/src/raw_emitter.rs @@ -341,7 +341,7 @@ impl Canonicalized { for &w in unique_words { unique_mapping.entry(w).or_insert_with(|| { canonical_words.push(w); - UniqueMapping::Canonical(canonical_words.len()) + UniqueMapping::Canonical(canonical_words.len() - 1) }); } assert_eq!(canonicalized_words.len() + canonical_words.len(), unique_words.len()); From 7a113811fab70d9d1932ad32ea392b9f100186ef Mon Sep 17 00:00:00 2001 From: Zihan Date: Mon, 4 Aug 2025 21:44:16 -0400 Subject: [PATCH 0125/2093] fix &str type check in `from_str_radix_10` changelog: none Signed-off-by: Zihan --- clippy_lints/src/from_str_radix_10.rs | 4 ++-- tests/ui/from_str_radix_10.fixed | 10 ++++++++++ tests/ui/from_str_radix_10.rs | 10 ++++++++++ tests/ui/from_str_radix_10.stderr | 14 +++++++++++++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index b816963cc825..d5873b3f85aa 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def}; @@ -89,5 +89,5 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { /// Checks if a Ty is `String` or `&str` fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_lang_item(cx, ty, LangItem::String) || is_type_diagnostic_item(cx, ty, sym::str) + is_type_lang_item(cx, ty, LangItem::String) || ty.peel_refs().is_str() } diff --git a/tests/ui/from_str_radix_10.fixed b/tests/ui/from_str_radix_10.fixed index 4b8fd778685e..47d24167e56c 100644 --- a/tests/ui/from_str_radix_10.fixed +++ b/tests/ui/from_str_radix_10.fixed @@ -74,3 +74,13 @@ fn issue_12731() { let _ = u32::from_str_radix("123", 10); } } + +fn fix_str_ref_check() { + #![allow(clippy::needless_borrow)] + let s = "1"; + let _ = s.parse::().unwrap(); + //~^ from_str_radix_10 + let s_ref = &s; + let _ = s_ref.parse::().unwrap(); + //~^ from_str_radix_10 +} diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 89002b11a995..952e19b57a00 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -74,3 +74,13 @@ fn issue_12731() { let _ = u32::from_str_radix("123", 10); } } + +fn fix_str_ref_check() { + #![allow(clippy::needless_borrow)] + let s = "1"; + let _ = u32::from_str_radix(&s, 10).unwrap(); + //~^ from_str_radix_10 + let s_ref = &s; + let _ = u32::from_str_radix(&s_ref, 10).unwrap(); + //~^ from_str_radix_10 +} diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr index c693e8f50ff6..d4e6c3657f20 100644 --- a/tests/ui/from_str_radix_10.stderr +++ b/tests/ui/from_str_radix_10.stderr @@ -49,5 +49,17 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` LL | i32::from_str_radix(&stringier, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::()` -error: aborting due to 8 previous errors +error: this call to `from_str_radix` can be replaced with a call to `str::parse` + --> tests/ui/from_str_radix_10.rs:81:13 + | +LL | let _ = u32::from_str_radix(&s, 10).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.parse::()` + +error: this call to `from_str_radix` can be replaced with a call to `str::parse` + --> tests/ui/from_str_radix_10.rs:84:13 + | +LL | let _ = u32::from_str_radix(&s_ref, 10).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s_ref.parse::()` + +error: aborting due to 10 previous errors From b038197b161e61a04fcf77e28f5284853640631c Mon Sep 17 00:00:00 2001 From: Ross MacArthur Date: Tue, 5 Aug 2025 17:05:23 +0200 Subject: [PATCH 0126/2093] Stabilize `core::iter::chain` --- library/core/src/iter/adapters/chain.rs | 4 +--- library/core/src/iter/adapters/mod.rs | 2 +- library/core/src/iter/mod.rs | 2 +- library/coretests/tests/lib.rs | 1 - 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/library/core/src/iter/adapters/chain.rs b/library/core/src/iter/adapters/chain.rs index dad3d79acb18..943b88e23305 100644 --- a/library/core/src/iter/adapters/chain.rs +++ b/library/core/src/iter/adapters/chain.rs @@ -45,8 +45,6 @@ impl Chain { /// # Examples /// /// ``` -/// #![feature(iter_chain)] -/// /// use std::iter::chain; /// /// let a = [1, 2, 3]; @@ -62,7 +60,7 @@ impl Chain { /// assert_eq!(iter.next(), Some(6)); /// assert_eq!(iter.next(), None); /// ``` -#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] pub fn chain(a: A, b: B) -> Chain where A: IntoIterator, diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 2a0ef0189d16..6c6de0a4e5c9 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -32,7 +32,7 @@ mod zip; pub use self::array_chunks::ArrayChunks; #[unstable(feature = "std_internals", issue = "none")] pub use self::by_ref_sized::ByRefSized; -#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] pub use self::chain::chain; #[stable(feature = "iter_cloned", since = "1.1.0")] pub use self::cloned::Cloned; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 56ca1305b601..bc07324f5204 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -404,7 +404,7 @@ pub use self::adapters::StepBy; pub use self::adapters::TrustedRandomAccess; #[unstable(feature = "trusted_random_access", issue = "none")] pub use self::adapters::TrustedRandomAccessNoCoerce; -#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] pub use self::adapters::chain; pub(crate) use self::adapters::try_process; #[stable(feature = "iter_zip", since = "1.59.0")] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 0a9c0c61c958..b15ee88b8d77 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -59,7 +59,6 @@ #![feature(isolate_most_least_significant_one)] #![feature(iter_advance_by)] #![feature(iter_array_chunks)] -#![feature(iter_chain)] #![feature(iter_collect_into)] #![feature(iter_intersperse)] #![feature(iter_is_partitioned)] From 9c0cfd262f4c1197d9993a78ad0fbfc04a9c774c Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 5 Aug 2025 08:59:08 +0000 Subject: [PATCH 0127/2093] Fix description of unsigned `checked_exact_div` Like its signed counterpart, this function does not panic. Also, fix the examples to document how it returns Some/None. --- library/core/src/num/uint_macros.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 584cd60fbe5c..ac5cbcec0b19 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1103,23 +1103,17 @@ macro_rules! uint_impl { self / rhs } - /// Checked integer division without remainder. Computes `self / rhs`. - /// - /// # Panics - /// - /// This function will panic if `rhs == 0` or `self % rhs != 0`. + /// Checked integer division without remainder. Computes `self / rhs`, + /// returning `None` if `rhs == 0` or if `self % rhs != 0`. /// /// # Examples /// /// ``` /// #![feature(exact_div)] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")] - /// ``` - /// - /// ```should_panic - /// #![feature(exact_div)] - #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(2), Some(32));")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(32), Some(2));")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(0), None);")] + #[doc = concat!("assert_eq!(65", stringify!($SelfT), ".checked_exact_div(2), None);")] /// ``` #[unstable( feature = "exact_div", From 9a1d38047fe367c32685b07a9ac41da4675b892d Mon Sep 17 00:00:00 2001 From: "Chai T. Rex" Date: Thu, 3 Jul 2025 23:38:17 -0400 Subject: [PATCH 0128/2093] Add `u8`-as-ASCII methods to `core::ascii::Char` --- library/core/src/ascii/ascii_char.rs | 604 ++++++++++++++++++++++++++- 1 file changed, 603 insertions(+), 1 deletion(-) diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index 054ddf844700..bde4b92c2715 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -445,7 +445,7 @@ pub enum AsciiChar { } impl AsciiChar { - /// Creates an ascii character from the byte `b`, + /// Creates an ASCII character from the byte `b`, /// or returns `None` if it's too large. #[unstable(feature = "ascii_char", issue = "110998")] #[inline] @@ -540,6 +540,608 @@ impl AsciiChar { pub const fn as_str(&self) -> &str { crate::slice::from_ref(self).as_str() } + + /// Makes a copy of the value in its upper case equivalent. + /// + /// Letters 'a' to 'z' are mapped to 'A' to 'Z'. + /// + /// To uppercase the value in-place, use [`make_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let lowercase_a = ascii::Char::SmallA; + /// + /// assert_eq!( + /// ascii::Char::CapitalA, + /// lowercase_a.to_uppercase(), + /// ); + /// ``` + /// + /// [`make_uppercase`]: Self::make_uppercase + #[must_use = "to uppercase the value in-place, use `make_uppercase()`"] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn to_uppercase(self) -> Self { + let uppercase_byte = self.to_u8().to_ascii_uppercase(); + // SAFETY: Toggling the 6th bit won't convert ASCII to non-ASCII. + unsafe { Self::from_u8_unchecked(uppercase_byte) } + } + + /// Makes a copy of the value in its lower case equivalent. + /// + /// Letters 'A' to 'Z' are mapped to 'a' to 'z'. + /// + /// To lowercase the value in-place, use [`make_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// + /// assert_eq!( + /// ascii::Char::SmallA, + /// uppercase_a.to_lowercase(), + /// ); + /// ``` + /// + /// [`make_lowercase`]: Self::make_lowercase + #[must_use = "to lowercase the value in-place, use `make_lowercase()`"] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn to_lowercase(self) -> Self { + let lowercase_byte = self.to_u8().to_ascii_lowercase(); + // SAFETY: Setting the 6th bit won't convert ASCII to non-ASCII. + unsafe { Self::from_u8_unchecked(lowercase_byte) } + } + + /// Checks that two values are a case-insensitive match. + /// + /// This is equivalent to `to_lowercase(a) == to_lowercase(b)`. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let lowercase_a = ascii::Char::SmallA; + /// let uppercase_a = ascii::Char::CapitalA; + /// + /// assert!(lowercase_a.eq_ignore_case(uppercase_a)); + /// ``` + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn eq_ignore_case(self, other: Self) -> bool { + // FIXME(const-hack) `arg.to_u8().to_ascii_lowercase()` -> `arg.to_lowercase()` + // once `PartialEq` is const for `Self`. + self.to_u8().to_ascii_lowercase() == other.to_u8().to_ascii_lowercase() + } + + /// Converts this value to its upper case equivalent in-place. + /// + /// Letters 'a' to 'z' are mapped to 'A' to 'Z'. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let mut letter_a = ascii::Char::SmallA; + /// + /// letter_a.make_uppercase(); + /// + /// assert_eq!(ascii::Char::CapitalA, letter_a); + /// ``` + /// + /// [`to_uppercase`]: Self::to_uppercase + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn make_uppercase(&mut self) { + *self = self.to_uppercase(); + } + + /// Converts this value to its lower case equivalent in-place. + /// + /// Letters 'A' to 'Z' are mapped to 'a' to 'z'. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let mut letter_a = ascii::Char::CapitalA; + /// + /// letter_a.make_lowercase(); + /// + /// assert_eq!(ascii::Char::SmallA, letter_a); + /// ``` + /// + /// [`to_lowercase`]: Self::to_lowercase + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn make_lowercase(&mut self) { + *self = self.to_lowercase(); + } + + /// Checks if the value is an alphabetic character: + /// + /// - 0x41 'A' ..= 0x5A 'Z', or + /// - 0x61 'a' ..= 0x7A 'z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(uppercase_a.is_alphabetic()); + /// assert!(uppercase_g.is_alphabetic()); + /// assert!(a.is_alphabetic()); + /// assert!(g.is_alphabetic()); + /// assert!(!zero.is_alphabetic()); + /// assert!(!percent.is_alphabetic()); + /// assert!(!space.is_alphabetic()); + /// assert!(!lf.is_alphabetic()); + /// assert!(!esc.is_alphabetic()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_alphabetic(self) -> bool { + self.to_u8().is_ascii_alphabetic() + } + + /// Checks if the value is an uppercase character: + /// 0x41 'A' ..= 0x5A 'Z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(uppercase_a.is_uppercase()); + /// assert!(uppercase_g.is_uppercase()); + /// assert!(!a.is_uppercase()); + /// assert!(!g.is_uppercase()); + /// assert!(!zero.is_uppercase()); + /// assert!(!percent.is_uppercase()); + /// assert!(!space.is_uppercase()); + /// assert!(!lf.is_uppercase()); + /// assert!(!esc.is_uppercase()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_uppercase(self) -> bool { + self.to_u8().is_ascii_uppercase() + } + + /// Checks if the value is a lowercase character: + /// 0x61 'a' ..= 0x7A 'z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_lowercase()); + /// assert!(!uppercase_g.is_lowercase()); + /// assert!(a.is_lowercase()); + /// assert!(g.is_lowercase()); + /// assert!(!zero.is_lowercase()); + /// assert!(!percent.is_lowercase()); + /// assert!(!space.is_lowercase()); + /// assert!(!lf.is_lowercase()); + /// assert!(!esc.is_lowercase()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_lowercase(self) -> bool { + self.to_u8().is_ascii_lowercase() + } + + /// Checks if the value is an alphanumeric character: + /// + /// - 0x41 'A' ..= 0x5A 'Z', or + /// - 0x61 'a' ..= 0x7A 'z', or + /// - 0x30 '0' ..= 0x39 '9'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(uppercase_a.is_alphanumeric()); + /// assert!(uppercase_g.is_alphanumeric()); + /// assert!(a.is_alphanumeric()); + /// assert!(g.is_alphanumeric()); + /// assert!(zero.is_alphanumeric()); + /// assert!(!percent.is_alphanumeric()); + /// assert!(!space.is_alphanumeric()); + /// assert!(!lf.is_alphanumeric()); + /// assert!(!esc.is_alphanumeric()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_alphanumeric(self) -> bool { + self.to_u8().is_ascii_alphanumeric() + } + + /// Checks if the value is a decimal digit: + /// 0x30 '0' ..= 0x39 '9'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_digit()); + /// assert!(!uppercase_g.is_digit()); + /// assert!(!a.is_digit()); + /// assert!(!g.is_digit()); + /// assert!(zero.is_digit()); + /// assert!(!percent.is_digit()); + /// assert!(!space.is_digit()); + /// assert!(!lf.is_digit()); + /// assert!(!esc.is_digit()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_digit(self) -> bool { + self.to_u8().is_ascii_digit() + } + + /// Checks if the value is an octal digit: + /// 0x30 '0' ..= 0x37 '7'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants, is_ascii_octdigit)] + /// + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let a = ascii::Char::SmallA; + /// let zero = ascii::Char::Digit0; + /// let seven = ascii::Char::Digit7; + /// let eight = ascii::Char::Digit8; + /// let percent = ascii::Char::PercentSign; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_octdigit()); + /// assert!(!a.is_octdigit()); + /// assert!(zero.is_octdigit()); + /// assert!(seven.is_octdigit()); + /// assert!(!eight.is_octdigit()); + /// assert!(!percent.is_octdigit()); + /// assert!(!lf.is_octdigit()); + /// assert!(!esc.is_octdigit()); + /// ``` + #[must_use] + // This is blocked on two unstable features. Please ensure both are + // stabilized before marking this method as stable. + #[unstable(feature = "ascii_char", issue = "110998")] + // #[unstable(feature = "is_ascii_octdigit", issue = "101288")] + #[inline] + pub const fn is_octdigit(self) -> bool { + self.to_u8().is_ascii_octdigit() + } + + /// Checks if the value is a hexadecimal digit: + /// + /// - 0x30 '0' ..= 0x39 '9', or + /// - 0x41 'A' ..= 0x46 'F', or + /// - 0x61 'a' ..= 0x66 'f'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(uppercase_a.is_hexdigit()); + /// assert!(!uppercase_g.is_hexdigit()); + /// assert!(a.is_hexdigit()); + /// assert!(!g.is_hexdigit()); + /// assert!(zero.is_hexdigit()); + /// assert!(!percent.is_hexdigit()); + /// assert!(!space.is_hexdigit()); + /// assert!(!lf.is_hexdigit()); + /// assert!(!esc.is_hexdigit()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_hexdigit(self) -> bool { + self.to_u8().is_ascii_hexdigit() + } + + /// Checks if the value is a punctuation character: + /// + /// - 0x21 ..= 0x2F `! " # $ % & ' ( ) * + , - . /`, or + /// - 0x3A ..= 0x40 `: ; < = > ? @`, or + /// - 0x5B ..= 0x60 `` [ \ ] ^ _ ` ``, or + /// - 0x7B ..= 0x7E `{ | } ~` + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_punctuation()); + /// assert!(!uppercase_g.is_punctuation()); + /// assert!(!a.is_punctuation()); + /// assert!(!g.is_punctuation()); + /// assert!(!zero.is_punctuation()); + /// assert!(percent.is_punctuation()); + /// assert!(!space.is_punctuation()); + /// assert!(!lf.is_punctuation()); + /// assert!(!esc.is_punctuation()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_punctuation(self) -> bool { + self.to_u8().is_ascii_punctuation() + } + + /// Checks if the value is a graphic character: + /// 0x21 '!' ..= 0x7E '~'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(uppercase_a.is_graphic()); + /// assert!(uppercase_g.is_graphic()); + /// assert!(a.is_graphic()); + /// assert!(g.is_graphic()); + /// assert!(zero.is_graphic()); + /// assert!(percent.is_graphic()); + /// assert!(!space.is_graphic()); + /// assert!(!lf.is_graphic()); + /// assert!(!esc.is_graphic()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_graphic(self) -> bool { + self.to_u8().is_ascii_graphic() + } + + /// Checks if the value is a whitespace character: + /// 0x20 SPACE, 0x09 HORIZONTAL TAB, 0x0A LINE FEED, + /// 0x0C FORM FEED, or 0x0D CARRIAGE RETURN. + /// + /// Rust uses the WhatWG Infra Standard's [definition of ASCII + /// whitespace][infra-aw]. There are several other definitions in + /// wide use. For instance, [the POSIX locale][pct] includes + /// 0x0B VERTICAL TAB as well as all the above characters, + /// but—from the very same specification—[the default rule for + /// "field splitting" in the Bourne shell][bfs] considers *only* + /// SPACE, HORIZONTAL TAB, and LINE FEED as whitespace. + /// + /// If you are writing a program that will process an existing + /// file format, check what that format's definition of whitespace is + /// before using this function. + /// + /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace + /// [pct]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_whitespace()); + /// assert!(!uppercase_g.is_whitespace()); + /// assert!(!a.is_whitespace()); + /// assert!(!g.is_whitespace()); + /// assert!(!zero.is_whitespace()); + /// assert!(!percent.is_whitespace()); + /// assert!(space.is_whitespace()); + /// assert!(lf.is_whitespace()); + /// assert!(!esc.is_whitespace()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_whitespace(self) -> bool { + self.to_u8().is_ascii_whitespace() + } + + /// Checks if the value is a control character: + /// 0x00 NUL ..= 0x1F UNIT SEPARATOR, or 0x7F DELETE. + /// Note that most whitespace characters are control + /// characters, but SPACE is not. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_control()); + /// assert!(!uppercase_g.is_control()); + /// assert!(!a.is_control()); + /// assert!(!g.is_control()); + /// assert!(!zero.is_control()); + /// assert!(!percent.is_control()); + /// assert!(!space.is_control()); + /// assert!(lf.is_control()); + /// assert!(esc.is_control()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_control(self) -> bool { + self.to_u8().is_ascii_control() + } + + /// Returns an iterator that produces an escaped version of a + /// character. + /// + /// The behavior is identical to + /// [`ascii::escape_default`](crate::ascii::escape_default). + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let zero = ascii::Char::Digit0; + /// let tab = ascii::Char::CharacterTabulation; + /// let cr = ascii::Char::CarriageReturn; + /// let lf = ascii::Char::LineFeed; + /// let apostrophe = ascii::Char::Apostrophe; + /// let double_quote = ascii::Char::QuotationMark; + /// let backslash = ascii::Char::ReverseSolidus; + /// + /// assert_eq!("0", zero.escape_ascii().to_string()); + /// assert_eq!("\\t", tab.escape_ascii().to_string()); + /// assert_eq!("\\r", cr.escape_ascii().to_string()); + /// assert_eq!("\\n", lf.escape_ascii().to_string()); + /// assert_eq!("\\'", apostrophe.escape_ascii().to_string()); + /// assert_eq!("\\\"", double_quote.escape_ascii().to_string()); + /// assert_eq!("\\\\", backslash.escape_ascii().to_string()); + /// ``` + #[must_use = "this returns the escaped character as an iterator, \ + without modifying the original"] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub fn escape_ascii(self) -> super::EscapeDefault { + super::escape_default(self.to_u8()) + } } macro_rules! into_int_impl { From d811581ba7b8f577b537d33446856eb6c6ad41fa Mon Sep 17 00:00:00 2001 From: "Chai T. Rex" Date: Fri, 4 Jul 2025 23:32:58 -0400 Subject: [PATCH 0129/2093] Add `std::ascii::Char` `MIN` and `MAX` constants --- library/core/src/ascii/ascii_char.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index bde4b92c2715..419e4694594f 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -445,6 +445,14 @@ pub enum AsciiChar { } impl AsciiChar { + /// The character with the lowest ASCII code. + #[unstable(feature = "ascii_char", issue = "110998")] + pub const MIN: Self = Self::Null; + + /// The character with the highest ASCII code. + #[unstable(feature = "ascii_char", issue = "110998")] + pub const MAX: Self = Self::Delete; + /// Creates an ASCII character from the byte `b`, /// or returns `None` if it's too large. #[unstable(feature = "ascii_char", issue = "110998")] From 070425e54d36fd20d363d6994d8ffdfa44ec160d Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Tue, 5 Aug 2025 16:26:42 +0200 Subject: [PATCH 0130/2093] Add aarch64_be-unknown-none-softfloat target Signed-off-by: Jens Reidel --- compiler/rustc_target/src/spec/mod.rs | 1 + .../aarch64_be_unknown_none_softfloat.rs | 43 +++++++++++ src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 1 + .../aarch64_be-unknown-none-softfloat.md | 74 +++++++++++++++++++ tests/assembly-llvm/targets/targets-elf.rs | 3 + 6 files changed, 123 insertions(+) create mode 100644 compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs create mode 100644 src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 033590e01a67..126d17b97584 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2146,6 +2146,7 @@ supported_targets! { ("aarch64-unknown-none", aarch64_unknown_none), ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat), + ("aarch64_be-unknown-none-softfloat", aarch64_be_unknown_none_softfloat), ("aarch64-unknown-nuttx", aarch64_unknown_nuttx), ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs new file mode 100644 index 000000000000..7f918e850809 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs @@ -0,0 +1,43 @@ +// Generic big-endian AArch64 target for bare-metal code - Floating point disabled +// +// Can be used in conjunction with the `target-feature` and +// `target-cpu` compiler flags to opt-in more hardware-specific +// features. +// +// For example, `-C target-cpu=cortex-a53`. +use rustc_abi::Endian; + +use crate::spec::{ + Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, + TargetMetadata, TargetOptions, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + abi: "softfloat".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + features: "+v8a,+strict-align,-neon,-fp-armv8".into(), + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(128), + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, + stack_probes: StackProbeType::Inline, + panic_strategy: PanicStrategy::Abort, + endian: Endian::Big, + ..Default::default() + }; + Target { + llvm_target: "aarch64_be-unknown-none".into(), + metadata: TargetMetadata { + description: Some("Bare ARM64 (big-endian), softfloat".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + data_layout: "E-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: "aarch64".into(), + options: opts, + } +} diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 7c688e32bc0d..83e51ca63696 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -47,6 +47,7 @@ - [\*-apple-visionos](platform-support/apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md) + - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md) - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md) - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md) - [arm-none-eabi](platform-support/arm-none-eabi.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 65b706301538..005be9c4d662 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -273,6 +273,7 @@ target | std | host | notes `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian) `aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI) [`aarch64_be-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD (big-endian) +[`aarch64_be-unknown-none-softfloat`](platform-support/aarch64_be-unknown-none-softfloat.md) | * | | Bare big-endian ARM64, softfloat [`amdgcn-amd-amdhsa`](platform-support/amdgcn-amd-amdhsa.md) | * | | `-Ctarget-cpu=gfx...` to specify [the AMD GPU] to compile for [`arm64_32-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | Arm Apple WatchOS 64-bit with 32-bit pointers [`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md) | ✓ | ✓ | ARM64e Apple Darwin diff --git a/src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md b/src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md new file mode 100644 index 000000000000..a28ddcdf7f2f --- /dev/null +++ b/src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md @@ -0,0 +1,74 @@ +# aarch64_be-unknown-none-softfloat + +**Tier: 3** + +Target for freestanding/bare-metal big-endian ARM64 binaries in ELF format: +firmware, kernels, etc. + +## Target maintainers + +[@Gelbpunkt](https://github.com/Gelbpunkt) + +## Requirements + +This target is cross-compiled. There is no support for `std`. There is no +default allocator, but it's possible to use `alloc` by supplying an allocator. + +The target does not assume existence of a FPU and does not make use of any +non-GPR register. This allows the generated code to run in environments, such +as kernels, which may need to avoid the use of such registers or which may have +special considerations about the use of such registers (e.g. saving and +restoring them to avoid breaking userspace code using the same registers). You +can change code generation to use additional CPU features via the +`-C target-feature=` codegen options to rustc, or via the `#[target_feature]` +mechanism within Rust code. + +By default, code generated with the soft-float target should run on any +big-endian ARM64 hardware, enabling additional target features may raise this +baseline. + +`extern "C"` uses the [architecture's standard calling convention][aapcs64]. + +[aapcs64]: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst + +The targets generate binaries in the ELF format. Any alternate formats or +special considerations for binary layout will require linker options or linker +scripts. + +## Building the target + +You can build Rust with support for the target by adding it to the `target` +list in `bootstrap.toml`: + +```toml +[build] +target = ["aarch64_be-unknown-none-softfloat"] +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for +this target, you will first need to build Rust with the target enabled (see +"Building the target" above). + +## Cross-compilation + +For cross builds, you will need an appropriate ARM64 C/C++ toolchain for +linking, or if you want to compile C code along with Rust (such as for Rust +crates with C dependencies). + +Rust *may* be able to use an `aarch64_be-unknown-linux-{gnu,musl}-` toolchain +with appropriate standalone flags to build for this target (depending on the +assumptions of that toolchain, see below), or you may wish to use a separate +`aarch64_be-unknown-none-softfloat` toolchain. + +On some ARM64 hosts that use ELF binaries, you *may* be able to use the host C +toolchain, if it does not introduce assumptions about the host environment that +don't match the expectations of a standalone environment. Otherwise, you may +need a separate toolchain for standalone/freestanding development, just as when +cross-compiling from a non-ARM64 platform. + +## Testing + +As the target supports a variety of different environments and does not support +`std`, it does not support running the Rust test suite. diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index edf16548e7de..11c7567cfa13 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -10,6 +10,9 @@ //@ revisions: aarch64_be_unknown_netbsd //@ [aarch64_be_unknown_netbsd] compile-flags: --target aarch64_be-unknown-netbsd //@ [aarch64_be_unknown_netbsd] needs-llvm-components: aarch64 +//@ revisions: aarch64_be_unknown_none_softfloat +//@ [aarch64_be_unknown_none_softfloat] compile-flags: --target aarch64_be-unknown-none-softfloat +//@ [aarch64_be_unknown_none_softfloat] needs-llvm-components: aarch64 //@ revisions: aarch64_kmc_solid_asp3 //@ [aarch64_kmc_solid_asp3] compile-flags: --target aarch64-kmc-solid_asp3 //@ [aarch64_kmc_solid_asp3] needs-llvm-components: aarch64 From dfd38f0d36d04c1d080578481546029e4ebbebca Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 2 Aug 2025 02:18:47 +1000 Subject: [PATCH 0131/2093] Add comments to `Printer`. These details took me some time to work out. --- compiler/rustc_middle/src/ty/print/mod.rs | 58 ++++++++++++++++------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 8a125c7fe284..e404206d19e8 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -21,18 +21,16 @@ pub trait Print<'tcx, P> { fn print(&self, p: &mut P) -> Result<(), PrintError>; } -/// Interface for outputting user-facing "type-system entities" -/// (paths, types, lifetimes, constants, etc.) as a side-effect -/// (e.g. formatting, like `PrettyPrinter` implementors do) or by -/// constructing some alternative representation (e.g. an AST), -/// which the associated types allow passing through the methods. -/// -/// For pretty-printing/formatting in particular, see `PrettyPrinter`. -// -// FIXME(eddyb) find a better name; this is more general than "printing". +/// A trait that "prints" user-facing type system entities: paths, types, lifetimes, constants, +/// etc. "Printing" here means building up a representation of the entity's path, usually as a +/// `String` (e.g. "std::io::Read") or a `Vec` (e.g. `[sym::std, sym::io, sym::Read]`). The +/// representation is built up by appending one or more pieces. The specific details included in +/// the built-up representation depend on the purpose of the printer. The more advanced printers +/// also rely on the `PrettyPrinter` sub-trait. pub trait Printer<'tcx>: Sized { fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; + /// Appends a representation of an entity with a normal path, e.g. "std::io::Read". fn print_def_path( &mut self, def_id: DefId, @@ -41,6 +39,7 @@ pub trait Printer<'tcx>: Sized { self.default_print_def_path(def_id, args) } + /// Like `print_def_path`, but for `DefPathData::Impl`. fn print_impl_path( &mut self, impl_def_id: DefId, @@ -66,25 +65,40 @@ pub trait Printer<'tcx>: Sized { self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref) } + /// Appends a representation of a region. fn print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), PrintError>; + /// Appends a representation of a type. fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError>; + /// Appends a representation of a list of `PolyExistentialPredicate`s. fn print_dyn_existential( &mut self, predicates: &'tcx ty::List>, ) -> Result<(), PrintError>; + /// Appends a representation of a const. fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError>; + /// Appends a representation of a crate name, e.g. `std`, or even ``. fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError>; - fn path_qualified( + /// Appends a representation of a (full or partial) simple path, in two parts. `print_prefix`, + /// when called, appends the representation of the leading segments. The rest of the method + /// appends the representation of the final segment, the details of which are in + /// `disambiguated_data`. + /// + /// E.g. `std::io` + `Read` -> `std::io::Read`. + fn path_append( &mut self, - self_ty: Ty<'tcx>, - trait_ref: Option>, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, + disambiguated_data: &DisambiguatedDefPathData, ) -> Result<(), PrintError>; + /// Similar to `path_append`, but the final segment is an `impl` segment. + /// + /// E.g. `slice` + `` -> `slice::`, which may then be further appended to, + /// giving a longer path representation such as `slice::::to_vec_in::ConvertVec`. fn path_append_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, @@ -92,18 +106,26 @@ pub trait Printer<'tcx>: Sized { trait_ref: Option>, ) -> Result<(), PrintError>; - fn path_append( - &mut self, - print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - disambiguated_data: &DisambiguatedDefPathData, - ) -> Result<(), PrintError>; - + /// Appends a representation of a path ending in generic args, in two parts. `print_prefix`, + /// when called, appends the leading segments. The rest of the method appends the + /// representation of the generic args. (Some printers choose to skip appending the generic + /// args.) + /// + /// E.g. `ImplementsTraitForUsize` + `` -> `ImplementsTraitForUsize`. fn path_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], ) -> Result<(), PrintError>; + /// Appends a representation of a qualified path segment, e.g. `>`. + /// If `trait_ref` is `None`, it may fall back to simpler forms, e.g. `>` or just `Foo`. + fn path_qualified( + &mut self, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result<(), PrintError>; + fn should_truncate(&mut self) -> bool { false } From 69bcd79ab78fb02798406424ab64d209bfed1880 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 3 Aug 2025 20:38:11 +1000 Subject: [PATCH 0132/2093] Move `should_truncate` from trait `Printer` to sub-trait `PrettyPrinter`. It's not used in `Printer`. --- compiler/rustc_middle/src/ty/print/mod.rs | 4 ---- compiler/rustc_middle/src/ty/print/pretty.rs | 12 ++++++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index e404206d19e8..c0ad1ba38460 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -126,10 +126,6 @@ pub trait Printer<'tcx>: Sized { trait_ref: Option>, ) -> Result<(), PrintError>; - fn should_truncate(&mut self) -> bool { - false - } - // Defaults (should not be overridden): #[instrument(skip(self), level = "debug")] diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c150aa65b9be..394b94999bff 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -333,6 +333,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { f: impl FnOnce(&mut Self) -> Result<(), PrintError>, ) -> Result<(), PrintError>; + fn should_truncate(&mut self) -> bool { + false + } + /// Returns `true` if the region should be printed in /// optional positions, e.g., `&'a T` or `dyn Tr + 'b`. /// This is typically the case for all non-`'_` regions. @@ -2295,10 +2299,6 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } } - fn should_truncate(&mut self) -> bool { - !self.type_length_limit.value_within_limit(self.printed_type_count) - } - fn print_dyn_existential( &mut self, predicates: &'tcx ty::List>, @@ -2487,6 +2487,10 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } + fn should_truncate(&mut self) -> bool { + !self.type_length_limit.value_within_limit(self.printed_type_count) + } + fn should_print_region(&self, region: ty::Region<'tcx>) -> bool { let highlight = self.region_highlight_mode; if highlight.region_highlighted(region).is_some() { From b8adda6194dfe3296fff73812dcaa88c58a4f0f1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 3 Aug 2025 21:02:28 +1000 Subject: [PATCH 0133/2093] Rename some `Printer` methods. I find these name clearer, and starting them all with `print_` makes things more consistent. --- .../rustc_const_eval/src/util/type_name.rs | 10 ++--- compiler/rustc_lint/src/context.rs | 10 ++--- compiler/rustc_middle/src/ty/print/mod.rs | 30 ++++++++------- compiler/rustc_middle/src/ty/print/pretty.rs | 37 ++++++++++--------- compiler/rustc_symbol_mangling/src/legacy.rs | 17 +++++---- compiler/rustc_symbol_mangling/src/v0.rs | 16 ++++---- .../src/error_reporting/infer/mod.rs | 10 ++--- .../error_reporting/infer/note_and_explain.rs | 2 +- 8 files changed, 71 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 573c734e931d..2f74f998fd04 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -73,12 +73,12 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { self.pretty_print_dyn_existential(predicates) } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.path.push_str(self.tcx.crate_name(cnum).as_str()); Ok(()) } - fn path_qualified( + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -86,7 +86,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { self.pretty_path_qualified(self_ty, trait_ref) } - fn path_append_impl( + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -105,7 +105,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { ) } - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -117,7 +117,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { Ok(()) } - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 0dd50af2efcd..d9163d94710e 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -774,12 +774,12 @@ impl<'tcx> LateContext<'tcx> { unreachable!(); // because `path_generic_args` ignores the `GenericArgs` } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.path = vec![self.tcx.crate_name(cnum)]; Ok(()) } - fn path_qualified( + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -800,7 +800,7 @@ impl<'tcx> LateContext<'tcx> { }) } - fn path_append_impl( + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -825,7 +825,7 @@ impl<'tcx> LateContext<'tcx> { Ok(()) } - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -844,7 +844,7 @@ impl<'tcx> LateContext<'tcx> { Ok(()) } - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _args: &[GenericArg<'tcx>], diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index c0ad1ba38460..4ca483eafe3c 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -81,7 +81,7 @@ pub trait Printer<'tcx>: Sized { fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError>; /// Appends a representation of a crate name, e.g. `std`, or even ``. - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError>; + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError>; /// Appends a representation of a (full or partial) simple path, in two parts. `print_prefix`, /// when called, appends the representation of the leading segments. The rest of the method @@ -89,17 +89,17 @@ pub trait Printer<'tcx>: Sized { /// `disambiguated_data`. /// /// E.g. `std::io` + `Read` -> `std::io::Read`. - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, ) -> Result<(), PrintError>; - /// Similar to `path_append`, but the final segment is an `impl` segment. + /// Similar to `print_path_with_simple`, but the final segment is an `impl` segment. /// /// E.g. `slice` + `` -> `slice::`, which may then be further appended to, /// giving a longer path representation such as `slice::::to_vec_in::ConvertVec`. - fn path_append_impl( + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -112,7 +112,7 @@ pub trait Printer<'tcx>: Sized { /// args.) /// /// E.g. `ImplementsTraitForUsize` + `` -> `ImplementsTraitForUsize`. - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], @@ -120,7 +120,7 @@ pub trait Printer<'tcx>: Sized { /// Appends a representation of a qualified path segment, e.g. `>`. /// If `trait_ref` is `None`, it may fall back to simpler forms, e.g. `>` or just `Foo`. - fn path_qualified( + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -140,7 +140,7 @@ pub trait Printer<'tcx>: Sized { match key.disambiguated_data.data { DefPathData::CrateRoot => { assert!(key.parent.is_none()); - self.path_crate(def_id.krate) + self.print_crate_name(def_id.krate) } DefPathData::Impl => self.print_impl_path(def_id, args), @@ -164,7 +164,7 @@ pub trait Printer<'tcx>: Sized { )) = self.tcx().coroutine_kind(def_id) && args.len() > parent_args.len() { - return self.path_generic_args( + return self.print_path_with_generic_args( |p| p.print_def_path(def_id, parent_args), &args[..parent_args.len() + 1][..1], ); @@ -186,7 +186,7 @@ pub trait Printer<'tcx>: Sized { _ => { if !generics.is_own_empty() && args.len() >= generics.count() { let args = generics.own_args_no_defaults(self.tcx(), args); - return self.path_generic_args( + return self.print_path_with_generic_args( |p| p.print_def_path(def_id, parent_args), args, ); @@ -202,7 +202,7 @@ pub trait Printer<'tcx>: Sized { && self.tcx().generics_of(parent_def_id).parent_count == 0; } - self.path_append( + self.print_path_with_simple( |p: &mut Self| { if trait_qualify_parent { let trait_ref = ty::TraitRef::new( @@ -210,7 +210,7 @@ pub trait Printer<'tcx>: Sized { parent_def_id, parent_args.iter().copied(), ); - p.path_qualified(trait_ref.self_ty(), Some(trait_ref)) + p.print_path_with_qualified(trait_ref.self_ty(), Some(trait_ref)) } else { p.print_def_path(parent_def_id, parent_args) } @@ -253,11 +253,15 @@ pub trait Printer<'tcx>: Sized { // If the impl is not co-located with either self-type or // trait-type, then fallback to a format that identifies // the module more clearly. - self.path_append_impl(|p| p.print_def_path(parent_def_id, &[]), self_ty, impl_trait_ref) + self.print_path_with_impl( + |p| p.print_def_path(parent_def_id, &[]), + self_ty, + impl_trait_ref, + ) } else { // Otherwise, try to give a good form that would be valid language // syntax. Preferably using associated item notation. - self.path_qualified(self_ty, impl_trait_ref) + self.print_path_with_qualified(self_ty, impl_trait_ref) } } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 394b94999bff..a7ff1b518331 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -474,7 +474,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // path to the crate followed by the path to the item within the crate. if let Some(cnum) = def_id.as_crate_root() { if cnum == LOCAL_CRATE { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } @@ -498,7 +498,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // or avoid ending up with `ExternCrateSource::Extern`, // for the injected `std`/`core`. if span.is_dummy() { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } @@ -512,13 +512,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { return Ok(true); } (ExternCrateSource::Path, LOCAL_CRATE) => { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } _ => {} }, None => { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } } @@ -628,7 +628,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { return Ok(false); } callers.push(visible_parent); - // HACK(eddyb) this bypasses `path_append`'s prefix printing to avoid + // HACK(eddyb) this bypasses `print_path_with_simple`'s prefix printing to avoid // knowing ahead of time whether the entire path will succeed or not. // To support printers that do not implement `PrettyPrinter`, a `Vec` or // linked list on the stack would need to be built, before any printing. @@ -637,7 +637,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { true => {} } callers.pop(); - self.path_append(|_| Ok(()), &DisambiguatedDefPathData { data, disambiguator: 0 })?; + self.print_path_with_simple( + |_| Ok(()), + &DisambiguatedDefPathData { data, disambiguator: 0 }, + )?; Ok(true) } @@ -1312,10 +1315,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { alias_ty: ty::AliasTerm<'tcx>, ) -> Result<(), PrintError> { let def_key = self.tcx().def_key(alias_ty.def_id); - self.path_generic_args( + self.print_path_with_generic_args( |p| { - p.path_append( - |p| p.path_qualified(alias_ty.self_ty(), None), + p.print_path_with_simple( + |p| p.print_path_with_qualified(alias_ty.self_ty(), None), &def_key.disambiguated_data, ) }, @@ -1400,7 +1403,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } } - // HACK(eddyb) this duplicates `FmtPrinter`'s `path_generic_args`, + // HACK(eddyb) this duplicates `FmtPrinter`'s `print_path_with_generic_args`, // in order to place the projections inside the `<...>`. if !resugared { let principal_with_self = @@ -2234,7 +2237,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { self.print_def_path(parent_def_id, &[])?; - // HACK(eddyb) copy of `path_append` to avoid + // HACK(eddyb) copy of `print_path_with_simple` to avoid // constructing a `DisambiguatedDefPathData`. if !self.empty_path { write!(self, "::")?; @@ -2310,7 +2313,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { self.pretty_print_const(ct, false) } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.empty_path = true; if cnum == LOCAL_CRATE { if self.tcx.sess.at_least_rust_2018() { @@ -2327,7 +2330,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn path_qualified( + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -2337,7 +2340,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn path_append_impl( + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -2359,7 +2362,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -2390,7 +2393,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], @@ -3229,7 +3232,7 @@ define_print! { // The args don't contain the self ty (as it has been erased) but the corresp. // generics do as the trait always has a self ty param. We need to offset. let args = &self.args[p.tcx().generics_of(self.def_id).parent_count - 1..]; - p.path_generic_args(|p| write!(p, "{name}"), args)?; + p.print_path_with_generic_args(|p| write!(p, "{name}"), args)?; write!(p, " = ")?; self.term.print(p)?; } diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index f8bab0f5d0c1..350546d4b665 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -218,8 +218,8 @@ struct LegacySymbolMangler<'tcx> { path: SymbolPath, // When `true`, `finalize_pending_component` isn't used. - // This is needed when recursing into `path_qualified`, - // or `path_generic_args`, as any nested paths are + // This is needed when recursing into `print_path_with_qualified`, + // or `print_path_with_generic_args`, as any nested paths are // logically within one component. keep_within_component: bool, } @@ -303,11 +303,12 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { Ok(()) } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.write_str(self.tcx.crate_name(cnum).as_str())?; Ok(()) } - fn path_qualified( + + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -329,7 +330,7 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { } } - fn path_append_impl( + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -352,7 +353,8 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { trait_ref, ) } - fn path_append( + + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -375,7 +377,8 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { Ok(()) } - fn path_generic_args( + + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 2aad5f315f36..0cbd48ba08c4 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -365,7 +365,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // Encode impl generic params if the generic parameters contain non-region parameters // and this isn't an inherent impl. if impl_trait_ref.is_some() && args.iter().any(|a| a.has_non_region_param()) { - self.path_generic_args( + self.print_path_with_generic_args( |this| { this.path_append_ns( |p| p.print_def_path(parent_def_id, &[]), @@ -786,7 +786,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { None => { self.push("S"); for (field_def, field) in iter::zip(&variant_def.fields, fields) { - // HACK(eddyb) this mimics `path_append`, + // HACK(eddyb) this mimics `print_path_with_simple`, // instead of simply using `field_def.ident`, // just to be able to handle disambiguators. let disambiguated_field = @@ -819,7 +819,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { Ok(()) } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.push("C"); if !self.is_exportable { let stable_crate_id = self.tcx.def_path_hash(cnum.as_def_id()).stable_crate_id(); @@ -830,7 +830,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { Ok(()) } - fn path_qualified( + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -843,7 +843,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { self.print_def_path(trait_ref.def_id, trait_ref.args) } - fn path_append_impl( + fn print_path_with_impl( &mut self, _: impl FnOnce(&mut Self) -> Result<(), PrintError>, _: Ty<'tcx>, @@ -853,7 +853,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { unreachable!() } - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -873,7 +873,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { DefPathData::SyntheticCoroutineBody => 's', DefPathData::NestedStatic => 'n', - // These should never show up as `path_append` arguments. + // These should never show up as `print_path_with_simple` arguments. DefPathData::CrateRoot | DefPathData::Use | DefPathData::GlobalAsm @@ -896,7 +896,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { ) } - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index fd204412564f..839a8007bf20 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -253,12 +253,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { unreachable!(); // because `path_generic_args` ignores the `GenericArgs` } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.segments = vec![self.tcx.crate_name(cnum)]; Ok(()) } - fn path_qualified( + fn print_path_with_qualified( &mut self, _self_ty: Ty<'tcx>, _trait_ref: Option>, @@ -266,7 +266,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Err(fmt::Error) } - fn path_append_impl( + fn print_path_with_impl( &mut self, _print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _self_ty: Ty<'tcx>, @@ -275,7 +275,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Err(fmt::Error) } - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -285,7 +285,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Ok(()) } - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _args: &[GenericArg<'tcx>], diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 129d0963a75a..b9284c07d830 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -947,7 +947,7 @@ fn foo(&self) -> Self::T { String::new() } pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String { FmtPrinter::print_string(self.tcx, hir::def::Namespace::TypeNS, |p| { - p.path_generic_args(|_| Ok(()), args) + p.print_path_with_generic_args(|_| Ok(()), args) }) .expect("could not write to `String`.") } From 42a1042f9b915b48d1e7176802cb130d029b3b48 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 3 Aug 2025 21:14:01 +1000 Subject: [PATCH 0134/2093] Rename some `PrettyPrinter` methods. More consistency. --- .../rustc_const_eval/src/util/type_name.rs | 4 +- compiler/rustc_middle/src/mir/pretty.rs | 4 +- compiler/rustc_middle/src/ty/print/pretty.rs | 52 +++++++++---------- compiler/rustc_symbol_mangling/src/legacy.rs | 6 +-- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 2f74f998fd04..d50507f65eb1 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -83,7 +83,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - self.pretty_path_qualified(self_ty, trait_ref) + self.pretty_print_path_with_qualified(self_ty, trait_ref) } fn print_path_with_impl( @@ -92,7 +92,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - self.pretty_path_append_impl( + self.pretty_print_path_with_impl( |cx| { print_prefix(cx)?; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index ed067d491276..a3949308eb4c 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1969,7 +1969,7 @@ fn pretty_print_const_value_tcx<'tcx>( let args = tcx.lift(args).unwrap(); let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); p.print_alloc_ids = true; - p.print_value_path(variant_def.def_id, args)?; + p.pretty_print_value_path(variant_def.def_id, args)?; fmt.write_str(&p.into_buffer())?; match variant_def.ctor_kind() { @@ -2011,7 +2011,7 @@ fn pretty_print_const_value_tcx<'tcx>( (ConstValue::ZeroSized, ty::FnDef(d, s)) => { let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); p.print_alloc_ids = true; - p.print_value_path(*d, s)?; + p.pretty_print_value_path(*d, s)?; fmt.write_str(&p.into_buffer())?; return Ok(()); } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index a7ff1b518331..46876cef1d0f 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -245,7 +245,7 @@ impl<'tcx> RegionHighlightMode<'tcx> { /// Trait for printers that pretty-print using `fmt::Write` to the printer. pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { /// Like `print_def_path` but for value paths. - fn print_value_path( + fn pretty_print_value_path( &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], @@ -253,7 +253,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.print_def_path(def_id, args) } - fn print_in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> + fn pretty_print_in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable>, { @@ -644,7 +644,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Ok(true) } - fn pretty_path_qualified( + fn pretty_print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -679,7 +679,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { }) } - fn pretty_path_append_impl( + fn pretty_print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -746,7 +746,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } sig.print(self)?; write!(self, " {{")?; - self.print_value_path(def_id, args)?; + self.pretty_print_value_path(def_id, args)?; write!(self, "}}")?; } } @@ -1393,7 +1393,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if let ty::Tuple(tys) = principal.args.type_at(0).kind() { let mut projections = predicates.projection_bounds(); if let (Some(proj), None) = (projections.next(), projections.next()) { - p.pretty_fn_sig( + p.pretty_print_fn_sig( tys, false, proj.skip_binder().term.as_type().expect("Return type was a const"), @@ -1495,7 +1495,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Ok(()) } - fn pretty_fn_sig( + fn pretty_print_fn_sig( &mut self, inputs: &[Ty<'tcx>], c_variadic: bool, @@ -1532,7 +1532,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { match self.tcx().def_kind(def) { DefKind::Const | DefKind::AssocConst => { - self.print_value_path(def, args)?; + self.pretty_print_value_path(def, args)?; } DefKind::AnonConst => { if def.is_local() @@ -1541,13 +1541,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { { write!(self, "{snip}")?; } else { - // Do not call `print_value_path` as if a parent of this anon const is - // an impl it will attempt to print out the impl trait ref i.e. `::{constant#0}`. This would cause printing to enter an - // infinite recursion if the anon const is in the self type i.e. - // `impl Default for [T; 32 - 1 - 1 - 1] {` where we would - // try to print - // `<[T; /* print constant#0 again */] as // Default>::{constant#0}`. + // Do not call `pretty_print_value_path` as if a parent of this anon + // const is an impl it will attempt to print out the impl trait ref + // i.e. `::{constant#0}`. This would cause printing to + // enter an infinite recursion if the anon const is in the self type + // i.e. `impl Default for [T; 32 - 1 - 1 - 1] {` where we + // would try to print `<[T; /* print constant#0 again */] as // + // Default>::{constant#0}`. write!( self, "{}::{}", @@ -1749,7 +1749,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.tcx().try_get_global_alloc(prov.alloc_id()) { self.typed_value( - |this| this.print_value_path(instance.def_id(), instance.args), + |this| this.pretty_print_value_path(instance.def_id(), instance.args), |this| this.print_type(ty), " as ", )?; @@ -1943,7 +1943,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let variant_idx = contents.variant.expect("destructed const of adt without variant idx"); let variant_def = &def.variant(variant_idx); - self.print_value_path(variant_def.def_id, args)?; + self.pretty_print_value_path(variant_def.def_id, args)?; match variant_def.ctor_kind() { Some(CtorKind::Const) => {} Some(CtorKind::Fn) => { @@ -1979,7 +1979,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } (_, ty::FnDef(def_id, args)) => { // Never allowed today, but we still encounter them in invalid const args. - self.print_value_path(def_id, args)?; + self.pretty_print_value_path(def_id, args)?; return Ok(()); } // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading @@ -2000,7 +2000,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Ok(()) } - fn pretty_closure_as_impl( + fn pretty_print_closure_as_impl( &mut self, closure: ty::ClosureArgs>, ) -> Result<(), PrintError> { @@ -2335,7 +2335,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - self.pretty_path_qualified(self_ty, trait_ref)?; + self.pretty_print_path_with_qualified(self_ty, trait_ref)?; self.empty_path = false; Ok(()) } @@ -2346,7 +2346,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - self.pretty_path_append_impl( + self.pretty_print_path_with_impl( |p| { print_prefix(p)?; if !p.empty_path { @@ -2424,7 +2424,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id)) } - fn print_value_path( + fn pretty_print_value_path( &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], @@ -2436,7 +2436,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn print_in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> + fn pretty_print_in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable>, { @@ -2899,7 +2899,7 @@ where T: Print<'tcx, P> + TypeFoldable>, { fn print(&self, p: &mut P) -> Result<(), PrintError> { - p.print_in_binder(self) + p.pretty_print_in_binder(self) } } @@ -3097,7 +3097,7 @@ define_print! { } write!(p, "fn")?; - p.pretty_fn_sig(self.inputs(), self.c_variadic, self.output())?; + p.pretty_print_fn_sig(self.inputs(), self.c_variadic, self.output())?; } ty::TraitRef<'tcx> { @@ -3321,7 +3321,7 @@ define_print_and_forward_display! { } PrintClosureAsImpl<'tcx> { - p.pretty_closure_as_impl(self.closure)?; + p.pretty_print_closure_as_impl(self.closure)?; } ty::ParamTy { diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 350546d4b665..923318ef3036 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -313,7 +313,7 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - // Similar to `pretty_path_qualified`, but for the other + // Similar to `pretty_print_path_with_qualified`, but for the other // types that are printed as paths (see `print_type` above). match self_ty.kind() { ty::FnDef(..) @@ -326,7 +326,7 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { self.print_type(self_ty) } - _ => self.pretty_path_qualified(self_ty, trait_ref), + _ => self.pretty_print_path_with_qualified(self_ty, trait_ref), } } @@ -336,7 +336,7 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - self.pretty_path_append_impl( + self.pretty_print_path_with_impl( |cx| { print_prefix(cx)?; From 2472d19625fa39ed635a211be397319d2b62cb24 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 Aug 2025 13:38:26 +0000 Subject: [PATCH 0135/2093] Include allocator module in LLVM_passes timer --- compiler/rustc_codegen_ssa/src/back/write.rs | 7 +-- compiler/rustc_codegen_ssa/src/base.rs | 56 ++++++++++---------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index aa29afb7f5b1..a2ae03a7611b 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -110,7 +110,6 @@ pub struct ModuleConfig { pub lint_llvm_ir: bool, pub no_prepopulate_passes: bool, pub no_builtins: bool, - pub time_module: bool, pub vectorize_loop: bool, pub vectorize_slp: bool, pub merge_functions: bool, @@ -228,10 +227,6 @@ impl ModuleConfig { no_prepopulate_passes: sess.opts.cg.no_prepopulate_passes, no_builtins: no_builtins || sess.target.no_builtins, - // Exclude metadata and allocator modules from time_passes output, - // since they throw off the "LLVM passes" measurement. - time_module: if_regular!(true, false), - // Copy what clang does by turning on loop vectorization at O2 and // slp vectorization at O3. vectorize_loop: !sess.opts.cg.no_vectorize_loops @@ -1740,7 +1735,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>( llvm_start_time: &mut Option>, work: WorkItem, ) { - if cgcx.config(work.module_kind()).time_module && llvm_start_time.is_none() { + if llvm_start_time.is_none() { *llvm_start_time = Some(cgcx.prof.verbose_generic_activity("LLVM_passes")); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index b4556ced0b3f..10a25bb3caaa 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -681,33 +681,6 @@ pub fn codegen_crate( let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu, autodiff_items); - // Codegen an allocator shim, if necessary. - if let Some(kind) = allocator_kind_for_codegen(tcx) { - let llmod_id = - cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); - let module_llvm = tcx.sess.time("write_allocator_module", || { - backend.codegen_allocator( - tcx, - &llmod_id, - kind, - // If allocator_kind is Some then alloc_error_handler_kind must - // also be Some. - tcx.alloc_error_handler_kind(()).unwrap(), - ) - }); - - ongoing_codegen.wait_for_signal_to_codegen_item(); - ongoing_codegen.check_for_errors(tcx.sess); - - // These modules are generally cheap and won't throw off scheduling. - let cost = 0; - submit_codegened_module_to_llvm( - &ongoing_codegen.coordinator, - ModuleCodegen::new_allocator(llmod_id, module_llvm), - cost, - ); - } - // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization // by, for example, preventing a large CGU from being processed last and @@ -823,6 +796,35 @@ pub fn codegen_crate( } } + // Codegen an allocator shim, if necessary. + // Do this last to ensure the LLVM_passes timer doesn't start while no CGUs have been codegened + // yet for the backend to optimize. + if let Some(kind) = allocator_kind_for_codegen(tcx) { + let llmod_id = + cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); + let module_llvm = tcx.sess.time("write_allocator_module", || { + backend.codegen_allocator( + tcx, + &llmod_id, + kind, + // If allocator_kind is Some then alloc_error_handler_kind must + // also be Some. + tcx.alloc_error_handler_kind(()).unwrap(), + ) + }); + + ongoing_codegen.wait_for_signal_to_codegen_item(); + ongoing_codegen.check_for_errors(tcx.sess); + + // These modules are generally cheap and won't throw off scheduling. + let cost = 0; + submit_codegened_module_to_llvm( + &ongoing_codegen.coordinator, + ModuleCodegen::new_allocator(llmod_id, module_llvm), + cost, + ); + } + ongoing_codegen.codegen_finished(tcx); // Since the main thread is sometimes blocked during codegen, we keep track From ee38bc06ce68d3f426f191a012ab529eceecb700 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:03:35 +0000 Subject: [PATCH 0136/2093] Remove unused field from ModuleConfig --- compiler/rustc_codegen_ssa/src/back/write.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index a2ae03a7611b..17dcca753987 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -76,12 +76,9 @@ pub struct ModuleConfig { /// Names of additional optimization passes to run. pub passes: Vec, /// Some(level) to optimize at a certain level, or None to run - /// absolutely no optimizations (used for the metadata module). + /// absolutely no optimizations (used for the allocator module). pub opt_level: Option, - /// Some(level) to optimize binary size, or None to not affect program size. - pub opt_size: Option, - pub pgo_gen: SwitchWithOptPath, pub pgo_use: Option, pub pgo_sample_use: Option, @@ -170,7 +167,6 @@ impl ModuleConfig { passes: if_regular!(sess.opts.cg.passes.clone(), vec![]), opt_level: opt_level_and_size, - opt_size: opt_level_and_size, pgo_gen: if_regular!( sess.opts.cg.profile_generate.clone(), From a7e8fe73111a96871feadda1eefa2b3f616cdb8e Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Wed, 6 Aug 2025 16:52:43 +0200 Subject: [PATCH 0137/2093] Clarify EOF handling for `BufRead::skip_until` --- library/std/src/io/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index d351ee5e739d..ff0e29e04c25 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2461,7 +2461,7 @@ pub trait BufRead: Read { /// delimiter or EOF is found. /// /// If successful, this function will return the total number of bytes read, - /// including the delimiter byte. + /// including the delimiter byte if found. /// /// This is useful for efficiently skipping data such as NUL-terminated strings /// in binary file formats without buffering. @@ -2489,7 +2489,7 @@ pub trait BufRead: Read { /// ``` /// use std::io::{self, BufRead}; /// - /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0"); + /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0!"); /// /// // read name /// let mut name = Vec::new(); @@ -2509,6 +2509,11 @@ pub trait BufRead: Read { /// .expect("reading from cursor won't fail"); /// assert_eq!(num_bytes, 11); /// assert_eq!(animal, b"Crustacean\0"); + /// + /// // reach EOF + /// let num_bytes = cursor.skip_until(b'\0') + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 1); /// ``` #[stable(feature = "bufread_skip_until", since = "1.83.0")] fn skip_until(&mut self, byte: u8) -> Result { From 1e271d6ed13756db08160daaa327b99b7fe6099e Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Tue, 5 Aug 2025 00:11:01 +0200 Subject: [PATCH 0138/2093] Allow function argument mismatch suggestion to be multiline --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 50 ++++++++++++++++--- .../argument-suggestions/issue-100478.stderr | 16 +++--- .../fn-arg-count-mismatch-diagnostics.stderr | 12 ++--- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 36abd7c85550..b80a2af31007 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1589,26 +1589,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`. full_call_span.shrink_to_hi() }; + + // Controls how the arguments should be listed in the suggestion. + enum ArgumentsFormatting { + SingleLine, + Multiline { fallback_indent: String, brace_indent: String }, + } + let arguments_formatting = { + let mut provided_inputs = matched_inputs.iter().filter_map(|a| *a); + if let Some(brace_indent) = source_map.indentation_before(suggestion_span) + && let Some(first_idx) = provided_inputs.by_ref().next() + && let Some(last_idx) = provided_inputs.by_ref().next() + && let (_, first_span) = provided_arg_tys[first_idx] + && let (_, last_span) = provided_arg_tys[last_idx] + && source_map.is_multiline(first_span.to(last_span)) + && let Some(fallback_indent) = source_map.indentation_before(first_span) + { + ArgumentsFormatting::Multiline { fallback_indent, brace_indent } + } else { + ArgumentsFormatting::SingleLine + } + }; + let mut suggestion = "(".to_owned(); let mut needs_comma = false; for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { if needs_comma { - suggestion += ", "; - } else { - needs_comma = true; + suggestion += ","; } - let suggestion_text = if let Some(provided_idx) = provided_idx + match &arguments_formatting { + ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ", + ArgumentsFormatting::SingleLine => {} + ArgumentsFormatting::Multiline { .. } => suggestion += "\n", + } + needs_comma = true; + let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx && let (_, provided_span) = provided_arg_tys[*provided_idx] && let Ok(arg_text) = source_map.span_to_snippet(provided_span) { - arg_text + (Some(provided_span), arg_text) } else { // Propose a placeholder of the correct type let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - ty_to_snippet(expected_ty, expected_idx) + (None, ty_to_snippet(expected_ty, expected_idx)) }; + if let ArgumentsFormatting::Multiline { fallback_indent, .. } = + &arguments_formatting + { + let indent = suggestion_span + .and_then(|span| source_map.indentation_before(span)) + .unwrap_or_else(|| fallback_indent.clone()); + suggestion += &indent; + } suggestion += &suggestion_text; } + if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting { + suggestion += ",\n"; + suggestion += &brace_indent; + } suggestion += ")"; err.span_suggestion_verbose( suggestion_span, diff --git a/tests/ui/argument-suggestions/issue-100478.stderr b/tests/ui/argument-suggestions/issue-100478.stderr index 8889a0ab5df0..81dbff2f0008 100644 --- a/tests/ui/argument-suggestions/issue-100478.stderr +++ b/tests/ui/argument-suggestions/issue-100478.stderr @@ -75,12 +75,16 @@ LL | fn foo(p1: T1, p2: Arc, p3: T3, p4: Arc, p5: T5, p6: T6, p7: T7, p8 | ^^^ ----------- help: provide the argument | -LL - foo( -LL - -LL - p1, //p2, -LL - p3, p4, p5, p6, p7, p8, -LL - ); -LL + foo(p1, /* Arc */, p3, p4, p5, p6, p7, p8); +LL ~ foo( +LL + p1, +LL + /* Arc */, +LL + p3, +LL + p4, +LL + p5, +LL + p6, +LL + p7, +LL + p8, +LL ~ ); | error: aborting due to 4 previous errors diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr index 494d9bf60a65..dda9b398a833 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr @@ -99,14 +99,10 @@ LL | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- help: provide the argument | -LL - function_with_lots_of_arguments( -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - ); -LL + function_with_lots_of_arguments(variable_name, /* char */, variable_name, variable_name, variable_name, variable_name); +LL | function_with_lots_of_arguments( +LL | variable_name, +LL ~ /* char */, +LL ~ variable_name, | error: aborting due to 6 previous errors From 82aac02c5267a427fdc5e8e1abbba98fc11c832c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:01:07 +0000 Subject: [PATCH 0139/2093] Revert "Preserve the .debug_gdb_scripts section" This reverts commit 868bdde25b030e0b71a29a5dbc04a891036e702e. --- src/debuginfo.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/debuginfo.rs b/src/debuginfo.rs index 4c0b64395232..4c8585192a1b 100644 --- a/src/debuginfo.rs +++ b/src/debuginfo.rs @@ -254,8 +254,7 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { // TODO(antoyo): implement. } - fn debuginfo_finalize(&mut self) { - // TODO: emit section `.debug_gdb_scripts`. + fn debuginfo_finalize(&self) { self.context.set_debug_info(true) } From cece1b95defd25f29f17da2483b5b2b79b829105 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 6 Aug 2025 19:54:58 +0200 Subject: [PATCH 0140/2093] Reuse previous `Vec` allocation in loop --- clippy_lints/src/doc/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index d27d68d38664..eca3bc390d77 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1139,12 +1139,12 @@ fn check_doc<'a, Events: Iterator, Range in_footnote_definition = true, End(TagEnd::FootnoteDefinition) => in_footnote_definition = false, From decdb950bf016391145b582cd0756d3e67481ad6 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 7 Aug 2025 01:05:01 -0500 Subject: [PATCH 0141/2093] symcheck: Store the section name in `SymInfo` if available Currently `SymInfo` stores a `Section`, which is just an index: SymInfo { section: Section( SectionIndex( 539, ), ), ... }, Look up and store the section name instead if possible, with a fallback to the `Section` debug printing. This makes output more clear and will allow us to filter by section name. --- .../crates/symbol-check/src/main.rs | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/library/compiler-builtins/crates/symbol-check/src/main.rs b/library/compiler-builtins/crates/symbol-check/src/main.rs index 1312a7179703..beb568a0f847 100644 --- a/library/compiler-builtins/crates/symbol-check/src/main.rs +++ b/library/compiler-builtins/crates/symbol-check/src/main.rs @@ -9,7 +9,7 @@ use std::process::{Command, Stdio}; use object::read::archive::{ArchiveFile, ArchiveMember}; use object::{ - File as ObjFile, Object, ObjectSymbol, Symbol, SymbolKind, SymbolScope, SymbolSection, + File as ObjFile, Object, ObjectSection, ObjectSymbol, Symbol, SymbolKind, SymbolScope, }; use serde_json::Value; @@ -154,7 +154,7 @@ struct SymInfo { name: String, kind: SymbolKind, scope: SymbolScope, - section: SymbolSection, + section: String, is_undefined: bool, is_global: bool, is_local: bool, @@ -165,12 +165,22 @@ struct SymInfo { } impl SymInfo { - fn new(sym: &Symbol, member: &ArchiveMember) -> Self { + fn new(sym: &Symbol, obj: &ObjFile, member: &ArchiveMember) -> Self { + // Include the section name if possible. Fall back to the `Section` debug impl if not. + let section = sym.section(); + let section_name = sym + .section() + .index() + .and_then(|idx| obj.section_by_index(idx).ok()) + .and_then(|sec| sec.name().ok()) + .map(ToString::to_string) + .unwrap_or_else(|| format!("{section:?}")); + Self { name: sym.name().expect("missing name").to_owned(), kind: sym.kind(), scope: sym.scope(), - section: sym.section(), + section: section_name, is_undefined: sym.is_undefined(), is_global: sym.is_global(), is_local: sym.is_local(), @@ -192,13 +202,13 @@ fn verify_no_duplicates(archive: &Archive) { let mut dups = Vec::new(); let mut found_any = false; - archive.for_each_symbol(|symbol, member| { + archive.for_each_symbol(|symbol, obj, member| { // Only check defined globals if !symbol.is_global() || symbol.is_undefined() { return; } - let sym = SymInfo::new(&symbol, member); + let sym = SymInfo::new(&symbol, obj, member); // x86-32 includes multiple copies of thunk symbols if sym.name.starts_with("__x86.get_pc_thunk") { @@ -244,7 +254,7 @@ fn verify_core_symbols(archive: &Archive) { let mut undefined = Vec::new(); let mut has_symbols = false; - archive.for_each_symbol(|symbol, member| { + archive.for_each_symbol(|symbol, obj, member| { has_symbols = true; // Find only symbols from `core` @@ -252,7 +262,7 @@ fn verify_core_symbols(archive: &Archive) { return; } - let sym = SymInfo::new(&symbol, member); + let sym = SymInfo::new(&symbol, obj, member); if sym.is_undefined { undefined.push(sym); } else { @@ -304,9 +314,9 @@ impl Archive { } /// For a given archive, do something with each symbol. - fn for_each_symbol(&self, mut f: impl FnMut(Symbol, &ArchiveMember)) { + fn for_each_symbol(&self, mut f: impl FnMut(Symbol, &ObjFile, &ArchiveMember)) { self.for_each_object(|obj, member| { - obj.symbols().for_each(|sym| f(sym, member)); + obj.symbols().for_each(|sym| f(sym, &obj, member)); }); } } From ab0def2739744ae31d5738263acd0cc0e00e1a16 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 7 Aug 2025 03:27:16 -0500 Subject: [PATCH 0142/2093] symcheck: Ignore symbols in `.debug_gdb_scripts` Since [1], our object files may now contain a GDB script section. These symbols wind up with multiple instances in the archive but are weak, so we can safely ignore them in our duplicates check. This resolves the current CI failures. [1]: https://github.com/rust-lang/rust/pull/143679 --- library/compiler-builtins/crates/symbol-check/src/main.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/compiler-builtins/crates/symbol-check/src/main.rs b/library/compiler-builtins/crates/symbol-check/src/main.rs index beb568a0f847..129c959f23a9 100644 --- a/library/compiler-builtins/crates/symbol-check/src/main.rs +++ b/library/compiler-builtins/crates/symbol-check/src/main.rs @@ -215,6 +215,11 @@ fn verify_no_duplicates(archive: &Archive) { return; } + // GDB pretty printing symbols may show up more than once but are weak. + if sym.section == ".debug_gdb_scripts" && sym.is_weak { + return; + } + // Windows has symbols for literal numeric constants, string literals, and MinGW pseudo- // relocations. These are allowed to have repeated definitions. let win_allowed_dup_pfx = ["__real@", "__xmm@", "??_C@_", ".refptr"]; From 186ea6e1e6cbd0edf35ae6aca4abf441856f168f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 7 Aug 2025 10:41:43 +0200 Subject: [PATCH 0143/2093] Only run the pull workflow once per week --- src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml index 04d6469aeaa4..5ff3118960da 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -3,8 +3,8 @@ name: rustc-pull on: workflow_dispatch: schedule: - # Run at 04:00 UTC every Monday and Thursday - - cron: '0 4 * * 1,4' + # Run at 04:00 UTC every Monday + - cron: '0 4 * * 1' jobs: pull: From 098d06b0300e7ec3d4174904c2d6a9d4444a30a2 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:13:04 +0000 Subject: [PATCH 0144/2093] Prevent name collisions with internal implementation details The implementation of the linkage attribute inside extern blocks defines symbols starting with _rust_extern_with_linkage_. If someone tries to also define this symbol you will get a symbol conflict or even an ICE. By adding an unpredictable component to the symbol name, this becomes less of an issue. --- src/consts.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/consts.rs b/src/consts.rs index c04c75e1b11f..873f1f1951c1 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -6,6 +6,7 @@ use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; use rustc_hir::def::DefKind; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::interpret::{ self, ConstAllocation, ErrorHandled, Scalar as InterpScalar, read_target_uint, @@ -384,8 +385,8 @@ fn check_and_apply_linkage<'gcc, 'tcx>( // linkage and there are no definitions), then // `extern_with_linkage_foo` will instead be initialized to // zero. - let mut real_name = "_rust_extern_with_linkage_".to_string(); - real_name.push_str(sym); + let real_name = + format!("_rust_extern_with_linkage_{:016x}_{sym}", cx.tcx.stable_crate_id(LOCAL_CRATE)); let global2 = cx.define_global(&real_name, gcc_type, is_tls, attrs.link_section); // TODO(antoyo): set linkage. let value = cx.const_ptrcast(global1.get_address(None), gcc_type); From eb15cf0a30eec3166088d6a0c5a8fc6854571371 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 7 Aug 2025 17:05:15 +0200 Subject: [PATCH 0145/2093] Merge commit '334fb906aef13d20050987b13448f37391bb97a2' into clippy-subtree-update --- .github/ISSUE_TEMPLATE/new_lint.yml | 21 + .github/driver.sh | 6 +- CHANGELOG.md | 101 ++++- CONTRIBUTING.md | 42 +- Cargo.toml | 2 +- book/src/lint_configuration.md | 2 +- clippy_config/Cargo.toml | 2 +- clippy_config/src/conf.rs | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/approx_const.rs | 3 +- .../src/attrs/duplicated_attributes.rs | 44 ++- clippy_lints/src/attrs/inline_always.rs | 3 +- clippy_lints/src/attrs/repr_attributes.rs | 3 +- clippy_lints/src/blocks_in_conditions.rs | 3 +- clippy_lints/src/booleans.rs | 3 +- clippy_lints/src/casts/cast_lossless.rs | 6 + clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/cloned_ref_to_slice_refs.rs | 2 +- clippy_lints/src/coerce_container_to_any.rs | 2 +- clippy_lints/src/cognitive_complexity.rs | 2 +- clippy_lints/src/collapsible_if.rs | 53 ++- clippy_lints/src/copies.rs | 6 +- clippy_lints/src/declared_lints.rs | 2 +- .../src/default_union_representation.rs | 3 +- clippy_lints/src/deprecated_lints.rs | 2 + clippy_lints/src/doc/broken_link.rs | 76 ++-- clippy_lints/src/doc/mod.rs | 2 +- .../src/doc/suspicious_doc_comments.rs | 2 +- .../src/doc/too_long_first_doc_paragraph.rs | 2 +- clippy_lints/src/empty_with_brackets.rs | 4 +- clippy_lints/src/eta_reduction.rs | 5 +- clippy_lints/src/exhaustive_items.rs | 5 +- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/format_args.rs | 7 +- clippy_lints/src/formatting.rs | 28 +- clippy_lints/src/four_forward_slashes.rs | 15 + clippy_lints/src/functions/must_use.rs | 2 +- clippy_lints/src/if_not_else.rs | 2 +- clippy_lints/src/incompatible_msrv.rs | 26 +- clippy_lints/src/index_refutable_slice.rs | 4 +- clippy_lints/src/infallible_try_from.rs | 6 +- clippy_lints/src/inline_fn_without_body.rs | 5 +- clippy_lints/src/let_with_type_underscore.rs | 11 +- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/macro_use.rs | 5 +- clippy_lints/src/manual_assert.rs | 16 +- clippy_lints/src/manual_async_fn.rs | 2 +- clippy_lints/src/manual_non_exhaustive.rs | 5 +- .../src/matches/match_single_binding.rs | 246 ++++++++++-- clippy_lints/src/matches/single_match.rs | 8 +- clippy_lints/src/methods/implicit_clone.rs | 4 +- clippy_lints/src/methods/ip_constant.rs | 25 +- .../iter_on_single_or_empty_collections.rs | 69 ++-- clippy_lints/src/methods/mod.rs | 2 +- .../src/methods/unnecessary_map_or.rs | 14 +- .../src/methods/unnecessary_sort_by.rs | 80 ++-- .../src/methods/unnecessary_to_owned.rs | 4 +- clippy_lints/src/min_ident_chars.rs | 79 +++- clippy_lints/src/missing_inline.rs | 5 +- clippy_lints/src/needless_bool.rs | 21 +- clippy_lints/src/needless_pass_by_value.rs | 4 +- clippy_lints/src/no_mangle_with_rust_abi.rs | 2 +- clippy_lints/src/non_std_lazy_statics.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 9 +- clippy_lints/src/pass_by_ref_or_value.rs | 5 +- clippy_lints/src/redundant_else.rs | 2 +- clippy_lints/src/return_self_not_must_use.rs | 5 +- clippy_lints/src/semicolon_block.rs | 36 +- clippy_lints/src/std_instead_of_core.rs | 3 +- clippy_lints/src/strings.rs | 121 ------ clippy_lints/src/unit_types/let_unit_value.rs | 109 +++-- clippy_lints/src/unit_types/mod.rs | 17 +- clippy_lints/src/utils/author.rs | 6 +- clippy_lints/src/wildcard_imports.rs | 2 +- .../derive_deserialize_allowing_unknown.rs | 5 +- clippy_test_deps/Cargo.lock | 7 - clippy_test_deps/Cargo.toml | 1 - clippy_utils/Cargo.toml | 2 +- clippy_utils/README.md | 2 +- clippy_utils/src/attrs.rs | 4 +- clippy_utils/src/lib.rs | 116 ++++-- clippy_utils/src/msrvs.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 2 +- clippy_utils/src/source.rs | 7 +- clippy_utils/src/sugg.rs | 127 ++++-- clippy_utils/src/sym.rs | 1 - clippy_utils/src/ty/mod.rs | 7 +- declare_clippy_lint/Cargo.toml | 2 +- lintcheck/src/input.rs | 2 +- lintcheck/src/json.rs | 2 +- lintcheck/src/output.rs | 2 +- rust-toolchain.toml | 2 +- tests/compile-test.rs | 11 +- tests/symbols-used.rs | 2 +- .../fail_file_attr/Cargo.stderr | 2 +- .../ui-cargo/duplicate_mod/fail/Cargo.stderr | 10 +- .../lint_groups_priority/fail/Cargo.stderr | 6 +- .../wildcard_imports/wildcard_imports.fixed | 11 + .../wildcard_imports/wildcard_imports.rs | 11 + .../wildcard_imports/wildcard_imports.stderr | 6 +- tests/ui/cast_lossless_integer_unfixable.rs | 17 + tests/ui/collapsible_else_if.fixed | 22 ++ tests/ui/collapsible_else_if.rs | 24 ++ tests/ui/collapsible_else_if.stderr | 11 +- tests/ui/collapsible_if.fixed | 18 + tests/ui/collapsible_if.rs | 19 + tests/ui/collapsible_if.stderr | 20 +- tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 18 +- tests/ui/doc/doc-fixable.fixed | 2 +- tests/ui/doc/doc-fixable.rs | 2 +- tests/ui/duplicated_attributes.rs | 2 +- tests/ui/duplicated_attributes.stderr | 23 +- tests/ui/empty_structs_with_brackets.fixed | 14 + tests/ui/empty_structs_with_brackets.rs | 14 + tests/ui/empty_structs_with_brackets.stderr | 10 +- tests/ui/four_forward_slashes_bare_cr.rs | 6 + tests/ui/four_forward_slashes_bare_cr.stderr | 14 + tests/ui/implicit_clone.fixed | 6 + tests/ui/implicit_clone.rs | 6 + tests/ui/implicit_clone.stderr | 14 +- .../slice_indexing_in_macro.fixed | 12 +- .../slice_indexing_in_macro.rs | 12 +- .../slice_indexing_in_macro.stderr | 11 +- tests/ui/indexing_slicing_slice.rs | 1 - tests/ui/indexing_slicing_slice.stderr | 38 +- tests/ui/infallible_try_from.stderr | 4 +- tests/ui/ip_constant.fixed | 14 + tests/ui/ip_constant.rs | 14 + tests/ui/ip_constant.stderr | 80 +++- tests/ui/iter_on_single_items.fixed | 24 ++ tests/ui/iter_on_single_items.rs | 24 ++ tests/ui/let_unit.fixed | 11 + tests/ui/let_unit.rs | 10 + tests/ui/let_unit.stderr | 16 +- tests/ui/let_with_type_underscore.fixed | 12 + tests/ui/let_with_type_underscore.rs | 12 + tests/ui/let_with_type_underscore.stderr | 50 ++- tests/ui/manual_assert.edition2018.stderr | 4 +- tests/ui/manual_assert.edition2021.stderr | 4 +- tests/ui/manual_strip.rs | 2 +- tests/ui/manual_unwrap_or_default.fixed | 2 +- tests/ui/manual_unwrap_or_default.rs | 2 +- tests/ui/map_identity.fixed | 64 ++- tests/ui/map_identity.rs | 64 ++- tests/ui/map_identity.stderr | 38 +- tests/ui/match_single_binding.fixed | 62 +++ tests/ui/match_single_binding.rs | 76 ++++ tests/ui/match_single_binding.stderr | 116 +++++- tests/ui/min_ident_chars.rs | 49 +++ tests/ui/min_ident_chars.stderr | 80 +++- tests/ui/needless_collect_indirect.rs | 3 +- tests/ui/needless_collect_indirect.stderr | 32 +- tests/ui/nonminimal_bool.rs | 18 + tests/ui/nonminimal_bool.stderr | 18 +- tests/ui/option_if_let_else.fixed | 12 + tests/ui/option_if_let_else.rs | 16 + tests/ui/option_if_let_else.stderr | 19 +- tests/ui/search_is_some.rs | 15 + tests/ui/search_is_some.stderr | 17 +- tests/ui/search_is_some_fixable_some.fixed | 7 + tests/ui/search_is_some_fixable_some.rs | 7 + tests/ui/search_is_some_fixable_some.stderr | 8 +- tests/ui/string_to_string.rs | 21 - tests/ui/string_to_string.stderr | 28 -- tests/ui/string_to_string_in_map.fixed | 20 - tests/ui/string_to_string_in_map.rs | 20 - tests/ui/string_to_string_in_map.stderr | 38 -- tests/ui/suspicious_else_formatting.rs | 10 +- tests/ui/suspicious_else_formatting.stderr | 6 +- tests/ui/unnecessary_clippy_cfg.rs | 3 +- tests/ui/unnecessary_clippy_cfg.stderr | 46 +-- tests/ui/unnecessary_map_or.fixed | 20 + tests/ui/unnecessary_map_or.rs | 20 + tests/ui/unnecessary_map_or.stderr | 30 +- tests/ui/unnecessary_sort_by.stderr | 12 +- triagebot.toml | 1 - util/gh-pages/index_template.html | 37 +- util/gh-pages/script.js | 13 +- util/gh-pages/style.css | 371 +++++++++++++++--- 181 files changed, 2803 insertions(+), 1027 deletions(-) create mode 100644 tests/ui/cast_lossless_integer_unfixable.rs create mode 100644 tests/ui/four_forward_slashes_bare_cr.rs create mode 100644 tests/ui/four_forward_slashes_bare_cr.stderr delete mode 100644 tests/ui/string_to_string.rs delete mode 100644 tests/ui/string_to_string.stderr delete mode 100644 tests/ui/string_to_string_in_map.fixed delete mode 100644 tests/ui/string_to_string_in_map.rs delete mode 100644 tests/ui/string_to_string_in_map.stderr diff --git a/.github/ISSUE_TEMPLATE/new_lint.yml b/.github/ISSUE_TEMPLATE/new_lint.yml index 464740640e0c..a8202f6378fd 100644 --- a/.github/ISSUE_TEMPLATE/new_lint.yml +++ b/.github/ISSUE_TEMPLATE/new_lint.yml @@ -48,3 +48,24 @@ body: ``` validations: required: true + - type: textarea + id: comparison + attributes: + label: Comparison with existing lints + description: | + What makes this lint different from any existing lints that are similar, and how are those differences useful? + + You can [use this playground template to see what existing lints are triggered by the bad code][playground] + (make sure to use "Tools > Clippy" and not "Build"). + You can also look through the list of [rustc's allowed-by-default lints][allowed-by-default], + as those won't show up in the playground above. + + [allowed-by-default]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html + + [playground]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&code=%23%21%5Bwarn%28clippy%3A%3Apedantic%29%5D%0A%23%21%5Bwarn%28clippy%3A%3Anursery%29%5D%0A%23%21%5Bwarn%28clippy%3A%3Arestriction%29%5D%0A%23%21%5Bwarn%28clippy%3A%3Aall%29%5D%0A%23%21%5Ballow%28clippy%3A%3Ablanket_clippy_restriction_lints%2C+reason+%3D+%22testing+to+see+if+any+restriction+lints+match+given+code%22%29%5D%0A%0A%2F%2F%21+Template+that+can+be+used+to+see+what+clippy+lints+a+given+piece+of+code+would+trigger + placeholder: Unlike `clippy::...`, the proposed lint would... + - type: textarea + id: context + attributes: + label: Additional Context + description: Any additional context that you believe may be relevant. diff --git a/.github/driver.sh b/.github/driver.sh index 5a81b4112918..2874aaf2110c 100755 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -47,9 +47,9 @@ unset CARGO_MANIFEST_DIR # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 # FIXME: How to match the clippy invocation in compile-test.rs? -./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/string_to_string.rs 2>string_to_string.stderr && exit 1 -sed -e "/= help: for/d" string_to_string.stderr > normalized.stderr -diff -u normalized.stderr tests/ui/string_to_string.stderr +./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/char_lit_as_u8.rs 2>char_lit_as_u8.stderr && exit 1 +sed -e "/= help: for/d" char_lit_as_u8.stderr > normalized.stderr +diff -u normalized.stderr tests/ui/char_lit_as_u8.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same SYSROOT=$(rustc --print sysroot) diff --git a/CHANGELOG.md b/CHANGELOG.md index a92fbdc767bd..bc60b1c57f7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,105 @@ document. ## Unreleased / Beta / In Rust Nightly -[03a5b6b9...master](https://github.com/rust-lang/rust-clippy/compare/03a5b6b9...master) +[4ef75291...master](https://github.com/rust-lang/rust-clippy/compare/4ef75291...master) + +## Rust 1.89 + +Current stable, released 2025-08-07 + +[View all 137 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-05-01T16%3A52%3A57Z..2025-06-13T08%3A33%3A27Z+base%3Amaster) + +### New Lints + +* Added [`coerce_container_to_any`] to `nursery` [#14812](https://github.com/rust-lang/rust-clippy/pull/14812) +* Added [`ip_constant`] to `pedantic` [#14878](https://github.com/rust-lang/rust-clippy/pull/14878) +* Added [`infallible_try_from`] to `suspicious` [#14813](https://github.com/rust-lang/rust-clippy/pull/14813) +* Added [`doc_suspicious_footnotes`] to `suspicious` [#14708](https://github.com/rust-lang/rust-clippy/pull/14708) +* Added [`pointer_format`] to `restriction` [#14792](https://github.com/rust-lang/rust-clippy/pull/14792) +* Added [`useless_concat`] to `complexity` [#13829](https://github.com/rust-lang/rust-clippy/pull/13829) +* Added [`cloned_ref_to_slice_refs`] to `perf` [#14284](https://github.com/rust-lang/rust-clippy/pull/14284) +* Added [`confusing_method_to_numeric_cast`] to `suspicious` [#13979](https://github.com/rust-lang/rust-clippy/pull/13979) + +### Moves and Deprecations + +* Removed superseded lints: `transmute_float_to_int`, `transmute_int_to_char`, + `transmute_int_to_float`, `transmute_num_to_bytes` (now in rustc) + [#14703](https://github.com/rust-lang/rust-clippy/pull/14703) + +### Enhancements + +* [`module_name_repetitions`] added `allow_exact_repetitions` configuration option + [#14261](https://github.com/rust-lang/rust-clippy/pull/14261) +* [`missing_docs_in_private_items`] added `allow_unused` config for underscored fields + [#14453](https://github.com/rust-lang/rust-clippy/pull/14453) +* [`unnecessary_unwrap`] fixed being emitted twice in closure + [#14763](https://github.com/rust-lang/rust-clippy/pull/14763) +* [`needless_return`] lint span no longer wraps to previous line + [#14790](https://github.com/rust-lang/rust-clippy/pull/14790) +* [`trivial-copy-size-limit`] now defaults to `target_pointer_width` + [#13319](https://github.com/rust-lang/rust-clippy/pull/13319) +* [`integer_division`] fixed false negative for NonZero denominators + [#14664](https://github.com/rust-lang/rust-clippy/pull/14664) +* [`arbitrary_source_item_ordering`] no longer lints inside items with `#[repr]` attribute + [#14610](https://github.com/rust-lang/rust-clippy/pull/14610) +* [`excessive_precision`] no longer triggers on exponent with leading zeros + [#14824](https://github.com/rust-lang/rust-clippy/pull/14824) +* [`to_digit_is_some`] no longer lints in const contexts when MSRV is below 1.87 + [#14771](https://github.com/rust-lang/rust-clippy/pull/14771) + +### False Positive Fixes + +* [`std_instead_of_core`] fixed FP when part of the `use` cannot be replaced + [#15016](https://github.com/rust-lang/rust-clippy/pull/15016) +* [`unused_unit`] fixed FP for `Fn` bounds + [#14962](https://github.com/rust-lang/rust-clippy/pull/14962) +* [`unnecessary_debug_formatting`] fixed FP inside `Debug` impl + [#14955](https://github.com/rust-lang/rust-clippy/pull/14955) +* [`assign_op_pattern`] fixed FP on unstable const trait + [#14886](https://github.com/rust-lang/rust-clippy/pull/14886) +* [`useless_conversion`] fixed FP when using `.into_iter().any()` + [#14800](https://github.com/rust-lang/rust-clippy/pull/14800) +* [`collapsible_if`] fixed FP on block stmt before expr + [#14730](https://github.com/rust-lang/rust-clippy/pull/14730) +* [`manual_unwrap_or_default`] fixed FP on ref binding + [#14731](https://github.com/rust-lang/rust-clippy/pull/14731) +* [`unused_async`] fixed FP on default impl + [#14720](https://github.com/rust-lang/rust-clippy/pull/14720) +* [`manual_slice_fill`] fixed FP on `IndexMut` overload + [#14719](https://github.com/rust-lang/rust-clippy/pull/14719) +* [`unnecessary_to_owned`] fixed FP when map key is a reference + [#14834](https://github.com/rust-lang/rust-clippy/pull/14834) + +### ICE Fixes + +* [`mutable_key_type`] fixed ICE when infinitely associated generic types are used + [#14965](https://github.com/rust-lang/rust-clippy/pull/14965) +* [`zero_sized_map_values`] fixed ICE while computing type layout + [#14837](https://github.com/rust-lang/rust-clippy/pull/14837) +* [`useless_asref`] fixed ICE on trait method + [#14830](https://github.com/rust-lang/rust-clippy/pull/14830) +* [`manual_slice_size_calculation`] fixed ICE in suggestion and triggers in `const` context + [#14804](https://github.com/rust-lang/rust-clippy/pull/14804) +* [`missing_const_for_fn`]: fix ICE with some compilation options + [#14776](https://github.com/rust-lang/rust-clippy/pull/14776) + +### Documentation Improvements + +* [`manual_contains`] improved documentation wording + [#14917](https://github.com/rust-lang/rust-clippy/pull/14917) + +### Performance improvements + +* [`strlen_on_c_strings`] optimized by 99.75% (31M → 76k instructions) + [#15043](https://github.com/rust-lang/rust-clippy/pull/15043) +* Reduced documentation lints execution time by 85% (7.5% → 1% of total runtime) + [#14870](https://github.com/rust-lang/rust-clippy/pull/14870) +* [`unit_return_expecting_ord`] optimized to reduce binder instantiation from 95k to 10k calls + [#14905](https://github.com/rust-lang/rust-clippy/pull/14905) +* [`doc_markdown`] optimized by 50% + [#14693](https://github.com/rust-lang/rust-clippy/pull/14693) +* Refactor and speed up `cargo dev fmt` + [#14638](https://github.com/rust-lang/rust-clippy/pull/14638) ## Rust 1.88 @@ -6260,6 +6358,7 @@ Released 2018-09-13 [`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma +[`possible_missing_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_else [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`precedence_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence_bits [`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72ab08792d37..42ed624ec212 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -199,26 +199,34 @@ currently. Between writing new lints, fixing issues, reviewing pull requests and responding to issues there may not always be enough time to stay on top of it all. -Our highest priority is fixing [ICEs][I-ICE] and [bugs][C-bug], for example -an ICE in a popular crate that many other crates depend on. We don't -want Clippy to crash on your code and we want it to be as reliable as the -suggestions from Rust compiler errors. +To find things to fix, go to the [tracking issue][tracking_issue], find an issue that you like, +and claim it with `@rustbot claim`. -We have prioritization labels and a sync-blocker label, which are described below. -- [P-low][p-low]: Requires attention (fix/response/evaluation) by a team member but isn't urgent. -- [P-medium][p-medium]: Should be addressed by a team member until the next sync. -- [P-high][p-high]: Should be immediately addressed and will require an out-of-cycle sync or a backport. -- [L-sync-blocker][l-sync-blocker]: An issue that "blocks" a sync. -Or rather: before the sync this should be addressed, -e.g. by removing a lint again, so it doesn't hit beta/stable. +As a general metric and always taking into account your skill and knowledge level, you can use this guide: + +- 🟥 [ICEs][search_ice], these are compiler errors that causes Clippy to panic and crash. Usually involves high-level +debugging, sometimes interacting directly with the upstream compiler. Difficult to fix but a great challenge that +improves a lot developer workflows! + +- 🟧 [Suggestion causes bug][sugg_causes_bug], Clippy suggested code that changed logic in some silent way. +Unacceptable, as this may have disastrous consequences. Easier to fix than ICEs + +- 🟨 [Suggestion causes error][sugg_causes_error], Clippy suggested code snippet that caused a compiler error +when applied. We need to make sure that Clippy doesn't suggest using a variable twice at the same time or similar +easy-to-happen occurrences. + +- 🟩 [False positives][false_positive], a lint should not have fired, the easiest of them all, as this is "just" +identifying the root of a false positive and making an exception for those cases. + +Note that false negatives do not have priority unless the case is very clear, as they are a feature-request in a +trench coat. [triage]: https://forge.rust-lang.org/release/triage-procedure.html -[I-ICE]: https://github.com/rust-lang/rust-clippy/labels/I-ICE -[C-bug]: https://github.com/rust-lang/rust-clippy/labels/C-bug -[p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low -[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium -[p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high -[l-sync-blocker]: https://github.com/rust-lang/rust-clippy/labels/L-sync-blocker +[search_ice]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc+state%3Aopen+label%3A%22I-ICE%22 +[sugg_causes_bug]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-bug +[sugg_causes_error]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-error%20 +[false_positive]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-false-positive +[tracking_issue]: https://github.com/rust-lang/rust-clippy/issues/15086 ## Contributions diff --git a/Cargo.toml b/Cargo.toml index f25f9ab54dd2..daf1c98cdc94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.90" +version = "0.1.91" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 992ed2c6aaaa..7f16f3a98105 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -555,7 +555,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 858366c8a5c4..6ad2cf0d0b10 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.90" +version = "0.1.91" edition = "2024" publish = false diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 555f54bcfb8b..8167d75583ee 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -44,7 +44,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", - "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", + "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase", diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index c03cc99b581f..b9e6de79cc05 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.90" +version = "0.1.91" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index ab47e3097524..a3710ca51655 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -2,8 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; -use rustc_hir::RustcVersion; -use rustc_hir::{HirId, Lit}; +use rustc_hir::{HirId, Lit, RustcVersion}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{Span, symbol}; diff --git a/clippy_lints/src/attrs/duplicated_attributes.rs b/clippy_lints/src/attrs/duplicated_attributes.rs index c2406bcfb647..c956738edf0e 100644 --- a/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/clippy_lints/src/attrs/duplicated_attributes.rs @@ -2,6 +2,7 @@ use super::DUPLICATED_ATTRIBUTES; use clippy_utils::diagnostics::span_lint_and_then; use itertools::Itertools; use rustc_ast::{Attribute, MetaItem}; +use rustc_ast_pretty::pprust::path_to_string; use rustc_data_structures::fx::FxHashMap; use rustc_lint::EarlyContext; use rustc_span::{Span, Symbol, sym}; @@ -35,31 +36,38 @@ fn check_duplicated_attr( if attr.span.from_expansion() { return; } - let Some(ident) = attr.ident() else { return }; - let name = ident.name; - if name == sym::doc || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason { - // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg - // conditions are the same. - // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. - return; - } - if let Some(direct_parent) = parent.last() - && *direct_parent == sym::cfg_trace - && [sym::all, sym::not, sym::any].contains(&name) - { - // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one - // level `cfg`, we leave. - return; + let attr_path = if let Some(ident) = attr.ident() { + ident.name + } else { + Symbol::intern(&path_to_string(&attr.path)) + }; + if let Some(ident) = attr.ident() { + let name = ident.name; + if name == sym::doc || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason + { + // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg + // conditions are the same. + // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. + return; + } + if let Some(direct_parent) = parent.last() + && *direct_parent == sym::cfg_trace + && [sym::all, sym::not, sym::any].contains(&name) + { + // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one + // level `cfg`, we leave. + return; + } } if let Some(value) = attr.value_str() { emit_if_duplicated( cx, attr, attr_paths, - format!("{}:{name}={value}", parent.iter().join(":")), + format!("{}:{attr_path}={value}", parent.iter().join(":")), ); } else if let Some(sub_attrs) = attr.meta_item_list() { - parent.push(name); + parent.push(attr_path); for sub_attr in sub_attrs { if let Some(meta) = sub_attr.meta_item() { check_duplicated_attr(cx, meta, attr_paths, parent); @@ -67,7 +75,7 @@ fn check_duplicated_attr( } parent.pop(); } else { - emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}", parent.iter().join(":"))); + emit_if_duplicated(cx, attr, attr_paths, format!("{}:{attr_path}", parent.iter().join(":"))); } } diff --git a/clippy_lints/src/attrs/inline_always.rs b/clippy_lints/src/attrs/inline_always.rs index 409bb698665f..fb86ae8da9d6 100644 --- a/clippy_lints/src/attrs/inline_always.rs +++ b/clippy_lints/src/attrs/inline_always.rs @@ -1,8 +1,7 @@ use super::INLINE_ALWAYS; use clippy_utils::diagnostics::span_lint; use rustc_hir::attrs::{AttributeKind, InlineAttr}; -use rustc_hir::find_attr; -use rustc_hir::Attribute; +use rustc_hir::{Attribute, find_attr}; use rustc_lint::LateContext; use rustc_span::Span; use rustc_span::symbol::Symbol; diff --git a/clippy_lints/src/attrs/repr_attributes.rs b/clippy_lints/src/attrs/repr_attributes.rs index 4ece3ed44fdf..8a530e8cff2e 100644 --- a/clippy_lints/src/attrs/repr_attributes.rs +++ b/clippy_lints/src/attrs/repr_attributes.rs @@ -1,6 +1,5 @@ use rustc_hir::attrs::{AttributeKind, ReprAttr}; -use rustc_hir::find_attr; -use rustc_hir::Attribute; +use rustc_hir::{Attribute, find_attr}; use rustc_lint::LateContext; use rustc_span::Span; diff --git a/clippy_lints/src/blocks_in_conditions.rs b/clippy_lints/src/blocks_in_conditions.rs index 011962846cb3..10fe1a4174f8 100644 --- a/clippy_lints/src/blocks_in_conditions.rs +++ b/clippy_lints/src/blocks_in_conditions.rs @@ -100,8 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { cond.span, BRACED_EXPR_MESSAGE, "try", - snippet_block_with_applicability(cx, ex.span, "..", Some(expr.span), &mut applicability) - .to_string(), + snippet_block_with_applicability(cx, ex.span, "..", Some(expr.span), &mut applicability), applicability, ); } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index ba1135d745ae..64aeb27df693 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -7,10 +7,9 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{eq_expr_value, sym}; use rustc_ast::ast::LitKind; -use rustc_hir::RustcVersion; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; -use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, RustcVersion, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index c1d6cec1b62e..c924fba6b5c8 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -25,6 +25,12 @@ pub(super) fn check( return; } + // If the `as` is from a macro and the casting type is from macro input, whether it is lossless is + // dependent on the input + if expr.span.from_expansion() && !cast_to_hir.span.eq_ctxt(expr.span) { + return; + } + span_lint_and_then( cx, CAST_LOSSLESS, diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 37accff5eaa8..dcc439a272cf 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -807,7 +807,7 @@ declare_clippy_lint! { /// ```no_run /// let _ = u16::MAX as usize; /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.89.0"] pub CONFUSING_METHOD_TO_NUMERIC_CAST, suspicious, "casting a primitive method pointer to any integer type" diff --git a/clippy_lints/src/cloned_ref_to_slice_refs.rs b/clippy_lints/src/cloned_ref_to_slice_refs.rs index e33a8e0fb742..72ab292ee3c6 100644 --- a/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// let data_ref = &data; /// take_slice(slice::from_ref(data_ref)); /// ``` - #[clippy::version = "1.87.0"] + #[clippy::version = "1.89.0"] pub CLONED_REF_TO_SLICE_REFS, perf, "cloning a reference for slice references" diff --git a/clippy_lints/src/coerce_container_to_any.rs b/clippy_lints/src/coerce_container_to_any.rs index 6217fc4c8977..2e3acb7748e2 100644 --- a/clippy_lints/src/coerce_container_to_any.rs +++ b/clippy_lints/src/coerce_container_to_any.rs @@ -41,7 +41,7 @@ declare_clippy_lint! { /// // Succeeds since we have a &dyn Any to the inner u32! /// assert_eq!(dyn_any_of_u32.downcast_ref::(), Some(&0u32)); /// ``` - #[clippy::version = "1.88.0"] + #[clippy::version = "1.89.0"] pub COERCE_CONTAINER_TO_ANY, nursery, "coercing to `&dyn Any` when dereferencing could produce a `dyn Any` without coercion is usually not intended" diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 518535e8c8bf..8f1c02965244 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// * [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) #[clippy::version = "1.35.0"] pub COGNITIVE_COMPLEXITY, - nursery, + restriction, "functions that should be split up into multiple functions", @eval_always = true } diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 1854d86c53b2..e3103e2d3016 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -136,6 +136,9 @@ impl CollapsibleIf { return; } + // Peel off any parentheses. + let (_, else_block_span, _) = peel_parens(cx.tcx.sess.source_map(), else_.span); + // Prevent "elseif" // Check that the "else" is followed by whitespace let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { @@ -152,7 +155,7 @@ impl CollapsibleIf { if requires_space { " " } else { "" }, snippet_block_with_applicability( cx, - else_.span, + else_block_span, "..", Some(else_block.span), &mut applicability @@ -187,7 +190,8 @@ impl CollapsibleIf { .with_leading_whitespace(cx) .into_span() }; - let inner_if = inner.span.split_at(2).0; + let (paren_start, inner_if_span, paren_end) = peel_parens(cx.tcx.sess.source_map(), inner.span); + let inner_if = inner_if_span.split_at(2).0; let mut sugg = vec![ // Remove the outer then block `{` (then_open_bracket, String::new()), @@ -196,6 +200,17 @@ impl CollapsibleIf { // Replace inner `if` by `&&` (inner_if, String::from("&&")), ]; + + if !paren_start.is_empty() { + // Remove any leading parentheses '(' + sugg.push((paren_start, String::new())); + } + + if !paren_end.is_empty() { + // Remove any trailing parentheses ')' + sugg.push((paren_end, String::new())); + } + sugg.extend(parens_around(check)); sugg.extend(parens_around(check_inner)); @@ -285,3 +300,37 @@ fn span_extract_keyword(sm: &SourceMap, span: Span, keyword: &str) -> Option (Span, Span, Span) { + use crate::rustc_span::Pos; + + let start = span.shrink_to_lo(); + let end = span.shrink_to_hi(); + + let snippet = sm.span_to_snippet(span).unwrap(); + if let Some((trim_start, _, trim_end)) = peel_parens_str(&snippet) { + let mut data = span.data(); + data.lo = data.lo + BytePos::from_usize(trim_start); + data.hi = data.hi - BytePos::from_usize(trim_end); + span = data.span(); + } + + (start.with_hi(span.lo()), span, end.with_lo(span.hi())) +} + +fn peel_parens_str(snippet: &str) -> Option<(usize, &str, usize)> { + let trimmed = snippet.trim(); + if !(trimmed.starts_with('(') && trimmed.ends_with(')')) { + return None; + } + + let trim_start = (snippet.len() - snippet.trim_start().len()) + 1; + let trim_end = (snippet.len() - snippet.trim_end().len()) + 1; + + let inner = snippet.get(trim_start..snippet.len() - trim_end)?; + Some(match peel_parens_str(inner) { + None => (trim_start, inner, trim_end), + Some((start, inner, end)) => (trim_start + start, inner, trim_end + end), + }) +} diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 4bd34527d21f..4fdb497950f8 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -235,9 +235,9 @@ fn lint_branches_sharing_code<'tcx>( let cond_snippet = reindent_multiline(&snippet(cx, cond_span, "_"), false, None); let cond_indent = indent_of(cx, cond_span); let moved_snippet = reindent_multiline(&snippet(cx, span, "_"), true, None); - let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{"; + let suggestion = moved_snippet + "\n" + &cond_snippet + "{"; let suggestion = reindent_multiline(&suggestion, true, cond_indent); - (replace_span, suggestion.to_string()) + (replace_span, suggestion) }); let end_suggestion = res.end_span(last_block, sm).map(|span| { let moved_snipped = reindent_multiline(&snippet(cx, span, "_"), true, None); @@ -253,7 +253,7 @@ fn lint_branches_sharing_code<'tcx>( .then_some(range.start - 4..range.end) }) .map_or(span, |range| range.with_ctxt(span.ctxt())); - (span, suggestion.to_string()) + (span, suggestion.clone()) }); let (span, msg, end_span) = match (&start_suggestion, &end_suggestion) { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index c3f8e02b4c06..e1cb08e361ca 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -178,6 +178,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO, crate::format_push_string::FORMAT_PUSH_STRING_INFO, crate::formatting::POSSIBLE_MISSING_COMMA_INFO, + crate::formatting::POSSIBLE_MISSING_ELSE_INFO, crate::formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING_INFO, crate::formatting::SUSPICIOUS_ELSE_FORMATTING_INFO, crate::formatting::SUSPICIOUS_UNARY_OP_FORMATTING_INFO, @@ -690,7 +691,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO, crate::strings::STRING_LIT_AS_BYTES_INFO, crate::strings::STRING_SLICE_INFO, - crate::strings::STRING_TO_STRING_INFO, crate::strings::STR_TO_STRING_INFO, crate::strings::TRIM_SPLIT_WHITESPACE_INFO, crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO, diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index f41255e54db6..df6525ce040e 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::attrs::{AttributeKind, ReprAttr}; -use rustc_hir::find_attr; -use rustc_hir::{HirId, Item, ItemKind}; +use rustc_hir::{HirId, Item, ItemKind, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, FieldDef}; diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 5204f73ea0a9..88aebc3e6a16 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -34,6 +34,8 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "pre 1.29.0"] ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), + #[clippy::version = "1.90.0"] + ("clippy::string_to_string", "`clippy:implicit_clone` covers those cases"), #[clippy::version = "pre 1.29.0"] ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/doc/broken_link.rs b/clippy_lints/src/doc/broken_link.rs index 4af10510023d..8878fa9180fe 100644 --- a/clippy_lints/src/doc/broken_link.rs +++ b/clippy_lints/src/doc/broken_link.rs @@ -19,52 +19,52 @@ pub fn check(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragm } fn warn_if_broken_link(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragments: &[DocFragment]) { - if let Some((span, _)) = source_span_for_markdown_range(cx.tcx, doc, &bl.span, fragments) { - let mut len = 0; + let mut len = 0; - // grab raw link data - let (_, raw_link) = doc.split_at(bl.span.start); + // grab raw link data + let (_, raw_link) = doc.split_at(bl.span.start); - // strip off link text part - let raw_link = match raw_link.split_once(']') { - None => return, - Some((prefix, suffix)) => { - len += prefix.len() + 1; - suffix - }, - }; + // strip off link text part + let raw_link = match raw_link.split_once(']') { + None => return, + Some((prefix, suffix)) => { + len += prefix.len() + 1; + suffix + }, + }; - let raw_link = match raw_link.split_once('(') { - None => return, - Some((prefix, suffix)) => { - if !prefix.is_empty() { - // there is text between ']' and '(' chars, so it is not a valid link - return; - } - len += prefix.len() + 1; - suffix - }, - }; + let raw_link = match raw_link.split_once('(') { + None => return, + Some((prefix, suffix)) => { + if !prefix.is_empty() { + // there is text between ']' and '(' chars, so it is not a valid link + return; + } + len += prefix.len() + 1; + suffix + }, + }; - if raw_link.starts_with("(http") { - // reduce chances of false positive reports - // by limiting this checking only to http/https links. + if raw_link.starts_with("(http") { + // reduce chances of false positive reports + // by limiting this checking only to http/https links. + return; + } + + for c in raw_link.chars() { + if c == ')' { + // it is a valid link return; } - for c in raw_link.chars() { - if c == ')' { - // it is a valid link - return; - } - - if c == '\n' { - report_broken_link(cx, span, len); - break; - } - - len += 1; + if c == '\n' + && let Some((span, _)) = source_span_for_markdown_range(cx.tcx, doc, &bl.span, fragments) + { + report_broken_link(cx, span, len); + break; } + + len += 1; } } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index ea0da0d24675..d27d68d38664 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -662,7 +662,7 @@ declare_clippy_lint! { /// /// [^1]: defined here /// fn my_fn() {} /// ``` - #[clippy::version = "1.88.0"] + #[clippy::version = "1.89.0"] pub DOC_SUSPICIOUS_FOOTNOTES, suspicious, "looks like a link or footnote ref, but with no definition" diff --git a/clippy_lints/src/doc/suspicious_doc_comments.rs b/clippy_lints/src/doc/suspicious_doc_comments.rs index 1e7d1f92fa31..47d91b80e7ee 100644 --- a/clippy_lints/src/doc/suspicious_doc_comments.rs +++ b/clippy_lints/src/doc/suspicious_doc_comments.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::AttrStyle; use rustc_ast::token::CommentKind; -use rustc_hir::attrs::AttributeKind; use rustc_errors::Applicability; use rustc_hir::Attribute; +use rustc_hir::attrs::AttributeKind; use rustc_lint::LateContext; use rustc_span::Span; diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 32ba696b3ec7..674690e7e31d 100644 --- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -1,5 +1,5 @@ -use rustc_hir::attrs::AttributeKind; use rustc_errors::Applicability; +use rustc_hir::attrs::AttributeKind; use rustc_hir::{Attribute, Item, ItemKind}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/empty_with_brackets.rs b/clippy_lints/src/empty_with_brackets.rs index f2757407ba57..e7230ebf8cba 100644 --- a/clippy_lints/src/empty_with_brackets.rs +++ b/clippy_lints/src/empty_with_brackets.rs @@ -93,11 +93,11 @@ impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VA impl LateLintPass<'_> for EmptyWithBrackets { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { // FIXME: handle `struct $name {}` - if let ItemKind::Struct(ident, _, var_data) = &item.kind + if let ItemKind::Struct(ident, generics, var_data) = &item.kind && !item.span.from_expansion() && !ident.span.from_expansion() && has_brackets(var_data) - && let span_after_ident = item.span.with_lo(ident.span.hi()) + && let span_after_ident = item.span.with_lo(generics.span.hi()) && has_no_fields(cx, var_data, span_after_ident) { span_lint_and_then( diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index ba539d05b6be..e467246741ce 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -7,10 +7,9 @@ use clippy_utils::{ get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, }; use rustc_abi::ExternAbi; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_errors::Applicability; -use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind, find_attr}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 5f40e5764435..ac61ce705eb7 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::indent_of; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_errors::Applicability; -use rustc_hir::{Item, ItemKind}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{Item, ItemKind, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index d5abaa547e8e..84d39dd81c91 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -148,7 +148,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su .into(); suggestion = match suggestion { - Sugg::MaybeParen(_) => Sugg::MaybeParen(op), + Sugg::MaybeParen(_) | Sugg::UnOp(UnOp::Neg, _) => Sugg::MaybeParen(op), _ => Sugg::NonParen(op), }; } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index af4202422e44..3359aa603239 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -17,12 +17,11 @@ use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, FormatPlaceholder, FormatTrait, }; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::{find_attr,RustcVersion}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; -use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{Expr, ExprKind, LangItem, RustcVersion, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::{self, GenericArg, List, TraitRef, Ty, TyCtxt, Upcast}; @@ -223,7 +222,7 @@ declare_clippy_lint! { /// ``` /// /// [pointer format]: https://doc.rust-lang.org/std/fmt/index.html#formatting-traits - #[clippy::version = "1.88.0"] + #[clippy::version = "1.89.0"] pub POINTER_FORMAT, restriction, "formatting a pointer" diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 4b482f7b233b..1c751643becb 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -91,6 +91,31 @@ declare_clippy_lint! { "suspicious formatting of `else`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for an `if` expression followed by either a block or another `if` that + /// looks like it should have an `else` between them. + /// + /// ### Why is this bad? + /// This is probably some refactoring remnant, even if the code is correct, it + /// might look confusing. + /// + /// ### Example + /// ```rust,ignore + /// if foo { + /// } { // looks like an `else` is missing here + /// } + /// + /// if foo { + /// } if bar { // looks like an `else` is missing here + /// } + /// ``` + #[clippy::version = "1.90.0"] + pub POSSIBLE_MISSING_ELSE, + suspicious, + "possibly missing `else`" +} + declare_clippy_lint! { /// ### What it does /// Checks for possible missing comma in an array. It lints if @@ -116,6 +141,7 @@ declare_lint_pass!(Formatting => [ SUSPICIOUS_ASSIGNMENT_FORMATTING, SUSPICIOUS_UNARY_OP_FORMATTING, SUSPICIOUS_ELSE_FORMATTING, + POSSIBLE_MISSING_ELSE, POSSIBLE_MISSING_COMMA ]); @@ -307,7 +333,7 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { span_lint_and_note( cx, - SUSPICIOUS_ELSE_FORMATTING, + POSSIBLE_MISSING_ELSE, else_span, format!("this looks like {looks_like} but the `else` is missing"), None, diff --git a/clippy_lints/src/four_forward_slashes.rs b/clippy_lints/src/four_forward_slashes.rs index 8822b87f92f7..a7b0edeb7991 100644 --- a/clippy_lints/src/four_forward_slashes.rs +++ b/clippy_lints/src/four_forward_slashes.rs @@ -1,4 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt as _; +use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::Item; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -81,6 +83,14 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { "turn these into doc comments by removing one `/`" }; + // If the comment contains a bare CR (not followed by a LF), do not propose an auto-fix + // as bare CR are not allowed in doc comments. + if span.check_source_text(cx, contains_bare_cr) { + diag.help(msg) + .note("bare CR characters are not allowed in doc comments"); + return; + } + diag.multipart_suggestion( msg, bad_comments @@ -97,3 +107,8 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { } } } + +/// Checks if `text` contains any CR not followed by a LF +fn contains_bare_cr(text: &str) -> bool { + text.bytes().tuple_windows().any(|(a, b)| a == b'\r' && b != b'\n') +} diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 55ca0d9ecb71..8de68bfcb511 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -14,7 +14,7 @@ use clippy_utils::source::snippet_indent; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{return_ty, trait_ref_of_method}; -use rustc_hir::attrs::{AttributeKind}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; use rustc_span::Symbol; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index 25fed0d4dd1b..e8afa69b537e 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -81,7 +81,7 @@ impl LateLintPass<'_> for IfNotElse { e.span, msg, "try", - make_sugg(cx, &cond.kind, cond_inner.span, els.span, "..", Some(e.span)).to_string(), + make_sugg(cx, &cond.kind, cond_inner.span, els.span, "..", Some(e.span)), Applicability::MachineApplicable, ), _ => span_lint_and_help(cx, IF_NOT_ELSE, e.span, msg, None, help), diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 85ebc830d3bc..89988be58758 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -2,14 +2,13 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::{is_in_const_context, is_in_test}; -use rustc_hir::{RustcVersion, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; -use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, QPath}; +use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, QPath, RustcVersion, StabilityLevel, StableSince}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::def_id::DefId; +use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::{ExpnKind, Span, sym}; declare_clippy_lint! { @@ -83,16 +82,22 @@ pub struct IncompatibleMsrv { msrv: Msrv, availability_cache: FxHashMap<(DefId, bool), Availability>, check_in_tests: bool, + core_crate: Option, } impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]); impl IncompatibleMsrv { - pub fn new(conf: &'static Conf) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { msrv: conf.msrv, availability_cache: FxHashMap::default(), check_in_tests: conf.check_incompatible_msrv_in_tests, + core_crate: tcx + .crates(()) + .iter() + .find(|krate| tcx.crate_name(**krate) == sym::core) + .copied(), } } @@ -140,23 +145,16 @@ impl IncompatibleMsrv { // We don't check local items since their MSRV is supposed to always be valid. return; } - if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = span.ctxt().outer_expn_data().kind { + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = expn_data.kind { // Desugared expressions get to cheat and stability is ignored. // Intentionally not using `.from_expansion()`, since we do still care about macro expansions return; } - // Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the // macros may have existed prior to the checked MSRV, but their expansion with a recent compiler // might use recent functions or methods. Compiling with an older compiler would not use those. - if span.from_expansion() - && cx.tcx.crate_name(def_id.krate) == sym::core - && span - .ctxt() - .outer_expn_data() - .macro_def_id - .is_some_and(|def_id| cx.tcx.crate_name(def_id.krate) == sym::core) - { + if Some(def_id.krate) == self.core_crate && expn_data.macro_def_id.map(|did| did.krate) == self.core_crate { return; } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 3d131a7825af..8f9f71a14769 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_copy; -use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local, sym}; +use clippy_utils::{is_lint_allowed, path_to_local}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -71,7 +71,7 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) - && (!expr.span.from_expansion() || is_expn_of(expr.span, sym::if_chain).is_some()) + && !expr.span.from_expansion() && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id) && let found_slices = find_slice_values(cx, let_pat) && !found_slices.is_empty() diff --git a/clippy_lints/src/infallible_try_from.rs b/clippy_lints/src/infallible_try_from.rs index f7cdf05359a3..589c294a678a 100644 --- a/clippy_lints/src/infallible_try_from.rs +++ b/clippy_lints/src/infallible_try_from.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// - /// Infalliable conversions should be implemented via `From` with the blanket conversion. + /// Infallible conversions should be implemented via `From` with the blanket conversion. /// /// ### Example /// ```no_run @@ -35,7 +35,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.88.0"] + #[clippy::version = "1.89.0"] pub INFALLIBLE_TRY_FROM, suspicious, "TryFrom with infallible Error type" @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom { cx, INFALLIBLE_TRY_FROM, span, - "infallible TryFrom impl; consider implementing From, instead", + "infallible TryFrom impl; consider implementing From instead", ); } } diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index ee59a4cc8cb7..e416ac079d6f 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::DiagExt; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_errors::Applicability; -use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{TraitFn, TraitItem, TraitItemKind, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 1917ca24a05b..5b0f95ffc377 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; +use clippy_utils::source::{IntoSpan, SpanRangeExt}; use rustc_errors::Applicability; use rustc_hir::{LetStmt, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -30,9 +31,15 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { if let Some(ty) = local.ty // Ensure that it has a type defined && let TyKind::Infer(()) = &ty.kind // that type is '_' && local.span.eq_ctxt(ty.span) - && !local.span.in_external_macro(cx.tcx.sess.source_map()) + && let sm = cx.tcx.sess.source_map() + && !local.span.in_external_macro(sm) && !is_from_proc_macro(cx, ty) { + let span_to_remove = sm + .span_extend_to_prev_char_before(ty.span, ':', true) + .with_leading_whitespace(cx) + .into_span(); + span_lint_and_then( cx, LET_WITH_TYPE_UNDERSCORE, @@ -40,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { "variable declared with type underscore", |diag| { diag.span_suggestion_verbose( - ty.span.with_lo(local.pat.span.hi()), + span_to_remove, "remove the explicit type `_` declaration", "", Applicability::MachineApplicable, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 914aa6b9b804..844bc1b0e390 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -522,7 +522,8 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod)); store.register_late_pass(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf))); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(unit_types::UnitTypes::new(format_args.clone()))); store.register_late_pass(move |_| Box::new(loops::Loops::new(conf))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf))); @@ -663,7 +664,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); store.register_late_pass(|_| Box::new(empty_drop::EmptyDrop)); store.register_late_pass(|_| Box::new(strings::StrToString)); - store.register_late_pass(|_| Box::new(strings::StringToString)); store.register_late_pass(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); @@ -796,7 +796,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf))); store.register_late_pass(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf))); - store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(conf))); + store.register_late_pass(move |tcx| Box::new(incompatible_msrv::IncompatibleMsrv::new(tcx, conf))); store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf))); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 8a253ae5810f..2ccff7680976 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -202,7 +202,7 @@ fn all_spans_after_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Vec { .iter() .skip_while(|inner| inner.hir_id != stmt.hir_id) .map(stmt_source_span) - .chain(if let Some(e) = block.expr { vec![e.span] } else { vec![] }) + .chain(block.expr.map(|e| e.span)) .collect(); } diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index bf89556fbb61..8989793625aa 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{self as hir, AmbigArg}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{self as hir, AmbigArg, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::Span; diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index ea6b01a053a3..76cb22864779 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call}; -use clippy_utils::{is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; +use clippy_utils::{higher, is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, UnOp}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; @@ -35,7 +35,7 @@ declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); impl<'tcx> LateLintPass<'tcx> for ManualAssert { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if let ExprKind::If(cond, then, None) = expr.kind + if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr) && !matches!(cond.kind, ExprKind::Let(_)) && !expr.span.from_expansion() && let then = peel_blocks_with_stmt(then) @@ -51,19 +51,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { && !is_else_clause(cx.tcx, expr) { let mut applicability = Applicability::MachineApplicable; - let cond = cond.peel_drop_temps(); let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); if !comments.is_empty() { comments += "\n"; } - let (cond, not) = match cond.kind { - ExprKind::Unary(UnOp::Not, e) => (e, ""), - _ => (cond, "!"), - }; - let cond_sugg = - sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability).maybe_paren(); + let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; - let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip}){semicolon}"); + let sugg = format!("assert!({cond_sugg}, {format_args_snip}){semicolon}"); // we show to the user the suggestion without the comments, but when applying the fix, include the // comments in the block span_lint_and_then( diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index abd1ac954cda..ba1ad599e116 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos]) }; - let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)).to_string(); + let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)); diag.multipart_suggestion( "make the function `async` and return the output of the future directly", diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 6b0f74468492..0d783fde3313 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -4,12 +4,11 @@ use clippy_utils::is_doc_hidden; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_indent; use itertools::Itertools; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 6a76c6cedd0d..82d5310663ee 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -1,11 +1,16 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::HirNode; -use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_context}; -use clippy_utils::{is_refutable, peel_blocks}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet, snippet_block_with_context, snippet_with_context}; +use clippy_utils::{is_expr_identity_of_pat, is_refutable, peel_blocks}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind, StmtKind}; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_path, walk_stmt}; +use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, Node, PatKind, Path, Stmt, StmtKind}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use super::MATCH_SINGLE_BINDING; @@ -26,9 +31,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e let match_body = peel_blocks(arms[0].body); let mut app = Applicability::MaybeIncorrect; let ctxt = expr.span.ctxt(); - let mut snippet_body = snippet_block_with_context(cx, match_body.span, ctxt, "..", Some(expr.span), &mut app) - .0 - .to_string(); + let mut snippet_body = snippet_block_with_context(cx, match_body.span, ctxt, "..", Some(expr.span), &mut app).0; // Do we need to add ';' to suggestion ? if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) @@ -50,10 +53,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e cx, (ex, expr), (bind_names, matched_vars), - &snippet_body, + snippet_body, &mut app, Some(span), true, + is_var_binding_used_later(cx, expr, &arms[0]), ); span_lint_and_sugg( @@ -78,15 +82,28 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e snippet_with_context(cx, pat_span, ctxt, "..", &mut app).0 ), ), + None if is_expr_identity_of_pat(cx, arms[0].pat, ex, false) => { + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + expr.span, + "this match could be replaced by its body itself", + "consider using the match body instead", + snippet_body, + Applicability::MachineApplicable, + ); + return; + }, None => { let sugg = sugg_with_curlies( cx, (ex, expr), (bind_names, matched_vars), - &snippet_body, + snippet_body, &mut app, None, true, + is_var_binding_used_later(cx, expr, &arms[0]), ); (expr.span, sugg) }, @@ -108,10 +125,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e cx, (ex, expr), (bind_names, matched_vars), - &snippet_body, + snippet_body, &mut app, None, false, + true, ); span_lint_and_sugg( @@ -139,6 +157,125 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e } } +struct VarBindingVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + identifiers: FxHashSet, +} + +impl<'tcx> Visitor<'tcx> for VarBindingVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; + + fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) -> Self::Result { + if let Res::Local(_) = path.res + && let [segment] = path.segments + && self.identifiers.contains(&segment.ident.name) + { + return ControlFlow::Break(()); + } + + walk_path(self, path) + } + + fn visit_block(&mut self, block: &'tcx Block<'tcx>) -> Self::Result { + let before = self.identifiers.clone(); + walk_block(self, block)?; + self.identifiers = before; + ControlFlow::Continue(()) + } + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) -> Self::Result { + if let StmtKind::Let(let_stmt) = stmt.kind { + if let Some(init) = let_stmt.init { + self.visit_expr(init)?; + } + + let_stmt.pat.each_binding(|_, _, _, ident| { + self.identifiers.remove(&ident.name); + }); + } + walk_stmt(self, stmt) + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result { + match expr.kind { + ExprKind::If( + Expr { + kind: ExprKind::Let(let_expr), + .. + }, + then, + else_, + ) => { + self.visit_expr(let_expr.init)?; + let before = self.identifiers.clone(); + let_expr.pat.each_binding(|_, _, _, ident| { + self.identifiers.remove(&ident.name); + }); + + self.visit_expr(then)?; + self.identifiers = before; + if let Some(else_) = else_ { + self.visit_expr(else_)?; + } + ControlFlow::Continue(()) + }, + ExprKind::Closure(closure) => { + let body = self.cx.tcx.hir_body(closure.body); + let before = self.identifiers.clone(); + for param in body.params { + param.pat.each_binding(|_, _, _, ident| { + self.identifiers.remove(&ident.name); + }); + } + self.visit_expr(body.value)?; + self.identifiers = before; + ControlFlow::Continue(()) + }, + ExprKind::Match(expr, arms, _) => { + self.visit_expr(expr)?; + for arm in arms { + let before = self.identifiers.clone(); + arm.pat.each_binding(|_, _, _, ident| { + self.identifiers.remove(&ident.name); + }); + if let Some(guard) = arm.guard { + self.visit_expr(guard)?; + } + self.visit_expr(arm.body)?; + self.identifiers = before; + } + ControlFlow::Continue(()) + }, + _ => walk_expr(self, expr), + } + } +} + +fn is_var_binding_used_later(cx: &LateContext<'_>, expr: &Expr<'_>, arm: &Arm<'_>) -> bool { + let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) else { + return false; + }; + let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id) else { + return false; + }; + + let mut identifiers = FxHashSet::default(); + arm.pat.each_binding(|_, _, _, ident| { + identifiers.insert(ident.name); + }); + + let mut visitor = VarBindingVisitor { cx, identifiers }; + block + .stmts + .iter() + .skip_while(|s| s.hir_id != stmt.hir_id) + .skip(1) + .any(|stmt| matches!(visitor.visit_stmt(stmt), ControlFlow::Break(()))) + || block + .expr + .is_some_and(|expr| matches!(visitor.visit_expr(expr), ControlFlow::Break(()))) +} + /// Returns true if the `ex` match expression is in a local (`let`) or assign expression fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option { if let Node::Expr(parent_arm_expr) = cx.tcx.parent_hir_node(ex.hir_id) { @@ -161,47 +298,66 @@ fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option(cx: &LateContext<'a>, match_expr: &Expr<'a>) -> bool { +fn expr_in_nested_block(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool { + if let Node::Block(block) = cx.tcx.parent_hir_node(match_expr.hir_id) { + return block + .expr + .map_or_else(|| matches!(block.stmts, [_]), |_| block.stmts.is_empty()); + } + false +} + +fn expr_must_have_curlies(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool { let parent = cx.tcx.parent_hir_node(match_expr.hir_id); - matches!( - parent, - Node::Expr(Expr { - kind: ExprKind::Closure { .. }, - .. - }) | Node::AnonConst(..) + if let Node::Expr(Expr { + kind: ExprKind::Closure(..) | ExprKind::Binary(..), + .. + }) + | Node::AnonConst(..) = parent + { + return true; + } + + if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) + && let ExprKind::Match(..) = arm.body.kind + { + return true; + } + + false +} + +fn indent_of_nth_line(snippet: &str, nth: usize) -> Option { + snippet + .lines() + .nth(nth) + .and_then(|s| s.find(|c: char| !c.is_whitespace())) +} + +fn reindent_snippet_if_in_block(snippet_body: &str, has_assignment: bool) -> String { + if has_assignment || !snippet_body.starts_with('{') { + return reindent_multiline(snippet_body, true, indent_of_nth_line(snippet_body, 1)); + } + + let snippet_body = snippet_body.trim_start_matches('{').trim_end_matches('}').trim(); + reindent_multiline( + snippet_body, + false, + indent_of_nth_line(snippet_body, 0).map(|indent| indent.saturating_sub(4)), ) } +#[expect(clippy::too_many_arguments)] fn sugg_with_curlies<'a>( cx: &LateContext<'a>, (ex, match_expr): (&Expr<'a>, &Expr<'a>), (bind_names, matched_vars): (Span, Span), - snippet_body: &str, + mut snippet_body: String, applicability: &mut Applicability, assignment: Option, needs_var_binding: bool, + is_var_binding_used_later: bool, ) -> String { - let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); - - let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); - if expr_parent_requires_curlies(cx, match_expr) { - cbrace_end = format!("\n{indent}}}"); - // Fix body indent due to the closure - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{indent}"); - } - - // If the parent is already an arm, and the body is another match statement, - // we need curly braces around suggestion - if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) - && let ExprKind::Match(..) = arm.body.kind - { - cbrace_end = format!("\n{indent}}}"); - // Fix body indent due to the match - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{indent}"); - } - let assignment_str = assignment.map_or_else(String::new, |span| { let mut s = snippet(cx, span, "..").to_string(); s.push_str(" = "); @@ -221,5 +377,17 @@ fn sugg_with_curlies<'a>( .to_string() }; + let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); + let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); + if !expr_in_nested_block(cx, match_expr) + && ((needs_var_binding && is_var_binding_used_later) || expr_must_have_curlies(cx, match_expr)) + { + cbrace_end = format!("\n{indent}}}"); + // Fix body indent due to the closure + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{indent}"); + snippet_body = reindent_snippet_if_in_block(&snippet_body, !assignment_str.is_empty()); + } + format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") } diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 7e530e98ac4a..bcf079b70070 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -112,9 +112,7 @@ fn report_single_pattern( let (sugg, help) = if is_unit_expr(arm.body) { (String::new(), "`match` expression can be removed") } else { - let mut sugg = snippet_block_with_context(cx, arm.body.span, ctxt, "..", Some(expr.span), &mut app) - .0 - .to_string(); + let mut sugg = snippet_block_with_context(cx, arm.body.span, ctxt, "..", Some(expr.span), &mut app).0; if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) && let StmtKind::Expr(_) = stmt.kind && match arm.body.kind { @@ -127,7 +125,7 @@ fn report_single_pattern( (sugg, "try") }; span_lint_and_then(cx, lint, expr.span, msg, |diag| { - diag.span_suggestion(expr.span, help, sugg.to_string(), app); + diag.span_suggestion(expr.span, help, sugg, app); note(diag); }); return; @@ -188,7 +186,7 @@ fn report_single_pattern( }; span_lint_and_then(cx, lint, expr.span, msg, |diag| { - diag.span_suggestion(expr.span, "try", sugg.to_string(), app); + diag.span_suggestion(expr.span, "try", sugg, app); note(diag); }); } diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index efa8cee58df7..8a976d1b4dc0 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -40,14 +40,12 @@ pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, re } /// Returns true if the named method can be used to clone the receiver. -/// Note that `to_string` is not flagged by `implicit_clone`. So other lints that call -/// `is_clone_like` and that do flag `to_string` must handle it separately. See, e.g., -/// `is_to_owned_like` in `unnecessary_to_owned.rs`. pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: hir::def_id::DefId) -> bool { match method_name { sym::to_os_string => is_diag_item_method(cx, method_def_id, sym::OsStr), sym::to_owned => is_diag_trait_item(cx, method_def_id, sym::ToOwned), sym::to_path_buf => is_diag_item_method(cx, method_def_id, sym::Path), + sym::to_string => is_diag_trait_item(cx, method_def_id, sym::ToString), sym::to_vec => cx .tcx .impl_of_assoc(method_def_id) diff --git a/clippy_lints/src/methods/ip_constant.rs b/clippy_lints/src/methods/ip_constant.rs index 83803fba6a13..a2ac4e54334e 100644 --- a/clippy_lints/src/methods/ip_constant.rs +++ b/clippy_lints/src/methods/ip_constant.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath, Ty, TyKind}; +use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::sym; use smallvec::SmallVec; @@ -9,13 +9,8 @@ use smallvec::SmallVec; use super::IP_CONSTANT; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) { - if let ExprKind::Path(QPath::TypeRelative( - Ty { - kind: TyKind::Path(QPath::Resolved(_, func_path)), - .. - }, - p, - )) = func.kind + if let ExprKind::Path(QPath::TypeRelative(ty, p)) = func.kind + && let TyKind::Path(QPath::Resolved(_, func_path)) = ty.kind && p.ident.name == sym::new && let Some(func_def_id) = func_path.res.opt_def_id() && matches!( @@ -40,13 +35,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args _ => return, }; + let mut sugg = vec![(expr.span.with_lo(p.ident.span.lo()), constant_name.to_owned())]; + let before_span = expr.span.shrink_to_lo().until(ty.span); + if !before_span.is_empty() { + // Remove everything before the type name + sugg.push((before_span, String::new())); + } + span_lint_and_then(cx, IP_CONSTANT, expr.span, "hand-coded well-known IP address", |diag| { - diag.span_suggestion_verbose( - expr.span.with_lo(p.ident.span.lo()), - "use", - constant_name, - Applicability::MachineApplicable, - ); + diag.multipart_suggestion_verbose("use", sugg, Applicability::MachineApplicable); }); } } diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index c0366765234f..83e565562af0 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -2,14 +2,15 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig}; use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirId; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; +use rustc_middle::ty::Binder; use rustc_span::Symbol; use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS}; @@ -32,24 +33,34 @@ impl IterType { fn is_arg_ty_unified_in_fn<'tcx>( cx: &LateContext<'tcx>, - fn_id: DefId, + fn_sig: ExprFnSig<'tcx>, arg_id: HirId, args: impl IntoIterator>, + is_method: bool, ) -> bool { - let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity(); let arg_id_in_args = args.into_iter().position(|e| e.hir_id == arg_id).unwrap(); - let arg_ty_in_args = fn_sig.input(arg_id_in_args).skip_binder(); + let Some(arg_ty_in_args) = fn_sig.input(arg_id_in_args).map(Binder::skip_binder) else { + return false; + }; - cx.tcx.predicates_of(fn_id).predicates.iter().any(|(clause, _)| { - clause - .as_projection_clause() - .and_then(|p| p.map_bound(|p| p.term.as_type()).transpose()) - .is_some_and(|ty| ty.skip_binder() == arg_ty_in_args) - }) || fn_sig - .inputs() - .iter() - .enumerate() - .any(|(i, ty)| i != arg_id_in_args && ty.skip_binder().walk().any(|arg| arg.as_type() == Some(arg_ty_in_args))) + fn_sig + .predicates_id() + .map(|def_id| cx.tcx.predicates_of(def_id)) + .is_some_and(|generics| { + generics.predicates.iter().any(|(clause, _)| { + clause + .as_projection_clause() + .and_then(|p| p.map_bound(|p| p.term.as_type()).transpose()) + .is_some_and(|ty| ty.skip_binder() == arg_ty_in_args) + }) + }) + || (!is_method + && fn_sig.input(arg_id_in_args).is_some_and(|binder| { + binder + .skip_binder() + .walk() + .any(|arg| arg.as_type() == Some(arg_ty_in_args)) + })) } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, recv: &'tcx Expr<'tcx>) { @@ -70,25 +81,16 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) { Some((Node::Expr(parent), child_id)) => match parent.kind { ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false, - ExprKind::Call( - Expr { - kind: ExprKind::Path(path), - hir_id, - .. - }, - args, - ) => cx + ExprKind::Call(recv, args) => { + expr_sig(cx, recv).is_some_and(|fn_sig| is_arg_ty_unified_in_fn(cx, fn_sig, child_id, args, false)) + }, + ExprKind::MethodCall(_name, recv, args, _span) => cx .typeck_results() - .qpath_res(path, *hir_id) - .opt_def_id() - .filter(|fn_id| cx.tcx.def_kind(fn_id).is_fn_like()) - .is_some_and(|fn_id| is_arg_ty_unified_in_fn(cx, fn_id, child_id, args)), - ExprKind::MethodCall(_name, recv, args, _span) => is_arg_ty_unified_in_fn( - cx, - cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(), - child_id, - once(recv).chain(args.iter()), - ), + .type_dependent_def_id(parent.hir_id) + .and_then(|def_id| ty_sig(cx, cx.tcx.type_of(def_id).instantiate_identity())) + .is_some_and(|fn_sig| { + is_arg_ty_unified_in_fn(cx, fn_sig, child_id, once(recv).chain(args.iter()), true) + }), ExprKind::If(_, _, _) | ExprKind::Match(_, _, _) | ExprKind::Closure(_) @@ -96,7 +98,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method | ExprKind::Break(_, _) => true, _ => false, }, - Some((Node::Stmt(_) | Node::LetStmt(_), _)) => false, + Some((Node::LetStmt(let_stmt), _)) => let_stmt.ty.is_some(), + Some((Node::Stmt(_), _)) => false, _ => true, }; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bcd54557331b..49ca81dafc28 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5450,7 +5450,7 @@ impl Methods { implicit_clone::check(cx, name, expr, recv); } }, - (sym::to_os_string | sym::to_path_buf | sym::to_vec, []) => { + (sym::to_os_string | sym::to_path_buf | sym::to_string | sym::to_vec, []) => { implicit_clone::check(cx, name, expr, recv); }, (sym::type_id, []) => { diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index 4a9007c607c8..1f5e3de6e7a2 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -109,10 +109,16 @@ pub(super) fn check<'a>( ); let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { - match parent_expr.kind { - ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_paren(), - ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_paren(), - _ => binop, + if parent_expr.span.eq_ctxt(expr.span) { + match parent_expr.kind { + ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_paren(), + ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_paren(), + _ => binop, + } + } else { + // if our parent expr is created by a macro, then it should be the one taking care of + // parenthesising us if necessary + binop } } else { binop diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index 1de9f6ab4970..fa9d5332ff4f 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -199,44 +199,50 @@ pub(super) fn check<'tcx>( is_unstable: bool, ) { match detect_lint(cx, expr, recv, arg) { - Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg( - cx, - UNNECESSARY_SORT_BY, - expr.span, - "consider using `sort_by_key`", - "try", - format!( - "{}.sort{}_by_key(|{}| {})", - trigger.vec_name, - if is_unstable { "_unstable" } else { "" }, - trigger.closure_arg, - if let Some(std_or_core) = std_or_core(cx) - && trigger.reverse - { - format!("{}::cmp::Reverse({})", std_or_core, trigger.closure_body) - } else { - trigger.closure_body.to_string() - }, - ), - if trigger.reverse { - Applicability::MaybeIncorrect + Some(LintTrigger::SortByKey(trigger)) => { + let method = if is_unstable { + "sort_unstable_by_key" } else { - Applicability::MachineApplicable - }, - ), - Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg( - cx, - UNNECESSARY_SORT_BY, - expr.span, - "consider using `sort`", - "try", - format!( - "{}.sort{}()", - trigger.vec_name, - if is_unstable { "_unstable" } else { "" }, - ), - Applicability::MachineApplicable, - ), + "sort_by_key" + }; + span_lint_and_sugg( + cx, + UNNECESSARY_SORT_BY, + expr.span, + format!("consider using `{method}`"), + "try", + format!( + "{}.{}(|{}| {})", + trigger.vec_name, + method, + trigger.closure_arg, + if let Some(std_or_core) = std_or_core(cx) + && trigger.reverse + { + format!("{}::cmp::Reverse({})", std_or_core, trigger.closure_body) + } else { + trigger.closure_body + }, + ), + if trigger.reverse { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }, + ); + }, + Some(LintTrigger::Sort(trigger)) => { + let method = if is_unstable { "sort_unstable" } else { "sort" }; + span_lint_and_sugg( + cx, + UNNECESSARY_SORT_BY, + expr.span, + format!("consider using `{method}`"), + "try", + format!("{}.{}()", trigger.vec_name, method), + Applicability::MachineApplicable, + ); + }, None => {}, } } diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 54f45263275c..c1f4904af7c4 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -621,8 +621,8 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { - is_clone_like(cx, method_name, method_def_id) - || is_cow_into_owned(cx, method_name, method_def_id) + is_cow_into_owned(cx, method_name, method_def_id) + || (method_name != sym::to_string && is_clone_like(cx, method_name, method_def_id)) || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) } diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index 99f01c8001a5..dbce29a86318 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -4,10 +4,14 @@ use clippy_utils::is_from_proc_macro; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_item, walk_trait_item}; -use rustc_hir::{GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, UsePath}; +use rustc_hir::{ + GenericParamKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, + UsePath, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::Span; +use rustc_span::symbol::Ident; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; declare_clippy_lint! { @@ -32,6 +36,10 @@ declare_clippy_lint! { /// let title = movie.title; /// } /// ``` + /// + /// ### Limitations + /// Trait implementations which use the same function or parameter name as the trait declaration will + /// not be warned about, even if the name is below the configured limit. #[clippy::version = "1.72.0"] pub MIN_IDENT_CHARS, restriction, @@ -76,6 +84,18 @@ impl LateLintPass<'_> for MinIdentChars { return; } + // If the function is declared but not defined in a trait, check_pat isn't called so we need to + // check this explicitly + if matches!(&item.kind, rustc_hir::TraitItemKind::Fn(_, _)) { + let param_names = cx.tcx.fn_arg_idents(item.owner_id.to_def_id()); + for ident in param_names.iter().flatten() { + let str = ident.as_str(); + if self.is_ident_too_short(cx, str, ident.span) { + emit_min_ident_chars(self, cx, str, ident.span); + } + } + } + walk_trait_item(&mut IdentVisitor { conf: self, cx }, item); } @@ -84,6 +104,7 @@ impl LateLintPass<'_> for MinIdentChars { if let PatKind::Binding(_, _, ident, ..) = pat.kind && let str = ident.as_str() && self.is_ident_too_short(cx, str, ident.span) + && is_not_in_trait_impl(cx, pat, ident) { emit_min_ident_chars(self, cx, str, ident.span); } @@ -118,6 +139,11 @@ impl Visitor<'_> for IdentVisitor<'_, '_> { let str = ident.as_str(); if conf.is_ident_too_short(cx, str, ident.span) { + // Check whether the node is part of a `impl` for a trait. + if matches!(cx.tcx.parent_hir_node(hir_id), Node::TraitRef(_)) { + return; + } + // Check whether the node is part of a `use` statement. We don't want to emit a warning if the user // has no control over the type. let usenode = opt_as_use_node(node).or_else(|| { @@ -201,3 +227,52 @@ fn opt_as_use_node(node: Node<'_>) -> Option<&'_ UsePath<'_>> { None } } + +/// Check if a pattern is a function param in an impl block for a trait and that the param name is +/// the same than in the trait definition. +fn is_not_in_trait_impl(cx: &LateContext<'_>, pat: &Pat<'_>, ident: Ident) -> bool { + let parent_node = cx.tcx.parent_hir_node(pat.hir_id); + if !matches!(parent_node, Node::Param(_)) { + return true; + } + + for (_, parent_node) in cx.tcx.hir_parent_iter(pat.hir_id) { + if let Node::ImplItem(impl_item) = parent_node + && matches!(impl_item.kind, ImplItemKind::Fn(_, _)) + { + let impl_parent_node = cx.tcx.parent_hir_node(impl_item.hir_id()); + if let Node::Item(parent_item) = impl_parent_node + && let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = &parent_item.kind + && let Some(name) = get_param_name(impl_item, cx, ident) + { + return name != ident.name; + } + + return true; + } + } + + true +} + +fn get_param_name(impl_item: &ImplItem<'_>, cx: &LateContext<'_>, ident: Ident) -> Option { + if let Some(trait_item_def_id) = impl_item.trait_item_def_id { + let trait_param_names = cx.tcx.fn_arg_idents(trait_item_def_id); + + let ImplItemKind::Fn(_, body_id) = impl_item.kind else { + return None; + }; + + if let Some(param_index) = cx + .tcx + .hir_body_param_idents(body_id) + .position(|param_ident| param_ident.is_some_and(|param_ident| param_ident.span == ident.span)) + && let Some(trait_param_name) = trait_param_names.get(param_index) + && let Some(trait_param_ident) = trait_param_name + { + return Some(trait_param_ident.name); + } + } + + None +} diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index c637fb247ff2..d02952eb4870 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, Attribute}; +use rustc_hir::{self as hir, Attribute, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::AssocItemContainer; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index b3aa1a7286a9..fa5afcc00874 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, - is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_extract_comment, sym, + is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_contains_comment, sym, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -128,14 +128,13 @@ fn condition_needs_parentheses(e: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for NeedlessBool { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use self::Expression::{Bool, RetBool}; - if e.span.from_expansion() || !span_extract_comment(cx.tcx.sess.source_map(), e.span).is_empty() { - return; - } - if let Some(higher::If { - cond, - then, - r#else: Some(r#else), - }) = higher::If::hir(e) + if !e.span.from_expansion() + && let Some(higher::If { + cond, + then, + r#else: Some(else_expr), + }) = higher::If::hir(e) + && !span_contains_comment(cx.tcx.sess.source_map(), e.span) { let reduce = |ret, not| { let mut applicability = Applicability::MachineApplicable; @@ -167,7 +166,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { applicability, ); }; - if let Some((a, b)) = fetch_bool_block(then).and_then(|a| Some((a, fetch_bool_block(r#else)?))) { + if let Some((a, b)) = fetch_bool_block(then).and_then(|a| Some((a, fetch_bool_block(else_expr)?))) { match (a, b) { (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => { span_lint( @@ -193,7 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { } } if let Some((lhs_a, a)) = fetch_assign(then) - && let Some((lhs_b, b)) = fetch_assign(r#else) + && let Some((lhs_b, b)) = fetch_assign(else_expr) && SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 7b057998063f..32ded96c1236 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -311,9 +311,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { /// Functions marked with these attributes must have the exact signature. pub(crate) fn requires_exact_signature(attrs: &[Attribute]) -> bool { - attrs.iter().any(|attr| { - attr.is_proc_macro_attr() - }) + attrs.iter().any(Attribute::is_proc_macro_attr) } #[derive(Default)] diff --git a/clippy_lints/src/no_mangle_with_rust_abi.rs b/clippy_lints/src/no_mangle_with_rust_abi.rs index 791bbbe30a8e..daa2c2877944 100644 --- a/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, snippet_with_applicability}; use rustc_abi::ExternAbi; -use rustc_hir::attrs::AttributeKind; use rustc_errors::Applicability; +use rustc_hir::attrs::AttributeKind; use rustc_hir::{Attribute, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index abee3c44c5a3..7ecde40aee01 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -49,7 +49,7 @@ declare_clippy_lint! { /// Some functions could be replaced as well if we have replaced `Lazy` to `LazyLock`, /// therefore after suggesting replace the type, we need to make sure the function calls can be /// replaced, otherwise the suggestions cannot be applied thus the applicability should be -/// `Unspecified` or `MaybeIncorret`. +/// [`Applicability::Unspecified`] or [`Applicability::MaybeIncorrect`]. static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[ ("once_cell::sync::Lazy::force", Some("std::sync::LazyLock::force")), ("once_cell::sync::Lazy::get", None), // `std::sync::LazyLock::get` is experimental diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 9487cec87efb..3483f3081a58 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -127,7 +127,8 @@ fn try_get_option_occurrence<'tcx>( if_else: &'tcx Expr<'_>, ) -> Option { let cond_expr = match expr.kind { - ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr, + ExprKind::AddrOf(_, _, inner_expr) => inner_expr, + ExprKind::Unary(UnOp::Deref, inner_expr) if !cx.typeck_results().expr_ty(inner_expr).is_raw_ptr() => inner_expr, _ => expr, }; let (inner_pat, is_result) = try_get_inner_pat_and_is_result(cx, pat)?; @@ -223,8 +224,8 @@ fn try_get_option_occurrence<'tcx>( let mut app = Applicability::Unspecified; - let (none_body, is_argless_call) = match none_body.kind { - ExprKind::Call(call_expr, []) if !none_body.span.from_expansion() => (call_expr, true), + let (none_body, can_omit_arg) = match none_body.kind { + ExprKind::Call(call_expr, []) if !none_body.span.from_expansion() && !is_result => (call_expr, true), _ => (none_body, false), }; @@ -241,7 +242,7 @@ fn try_get_option_occurrence<'tcx>( ), none_expr: format!( "{}{}", - if method_sugg == "map_or" || is_argless_call { + if method_sugg == "map_or" || can_omit_arg { "" } else if is_result { "|_| " diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 303c5dfed891..d7b4a03aa537 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -5,13 +5,12 @@ use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; use clippy_utils::{is_self, is_self_ty}; use core::ops::ControlFlow; use rustc_abi::ExternAbi; -use rustc_hir::attrs::{AttributeKind, InlineAttr}; -use rustc_hir::find_attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::attrs::{AttributeKind, InlineAttr}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{BindingMode, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, Node, PatKind}; +use rustc_hir::{BindingMode, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, Node, PatKind, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::layout::LayoutOf; diff --git a/clippy_lints/src/redundant_else.rs b/clippy_lints/src/redundant_else.rs index a3be16ed858e..79353dc9247e 100644 --- a/clippy_lints/src/redundant_else.rs +++ b/clippy_lints/src/redundant_else.rs @@ -97,7 +97,7 @@ impl EarlyLintPass for RedundantElse { els.span.with_lo(then.span.hi()), "redundant else block", "remove the `else` block and move the contents out", - make_sugg(cx, els.span, "..", Some(expr.span)).to_string(), + make_sugg(cx, els.span, "..", Some(expr.span)), app, ); } diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 2cdb8ef3a654..b057396034c0 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_must_use_ty; use clippy_utils::{nth_arg, return_ty}; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind}; +use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::Span; diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index f6c128d4c529..db91c57b1816 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -102,7 +102,7 @@ impl SemicolonBlock { } fn semicolon_outside_block(&self, cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>) { - let insert_span = block.span.with_lo(block.span.hi()); + let insert_span = block.span.shrink_to_hi(); // For macro call semicolon statements (`mac!();`), the statement's span does not actually // include the semicolon itself, so use `mac_call_stmt_semi_span`, which finds the semicolon @@ -144,28 +144,20 @@ impl LateLintPass<'_> for SemicolonBlock { kind: ExprKind::Block(block, _), .. }) if !block.span.from_expansion() && stmt.span.contains(block.span) => { - let Block { - expr: None, - stmts: [.., stmt], - .. - } = block - else { - return; - }; - let &Stmt { - kind: StmtKind::Semi(expr), - .. - } = stmt - else { - return; - }; - self.semicolon_outside_block(cx, block, expr); + if block.expr.is_none() + && let [.., stmt] = block.stmts + && let StmtKind::Semi(expr) = stmt.kind + { + self.semicolon_outside_block(cx, block, expr); + } }, StmtKind::Semi(Expr { - kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _), + kind: ExprKind::Block(block, _), .. }) if !block.span.from_expansion() => { - self.semicolon_inside_block(cx, block, tail, stmt.span); + if let Some(tail) = block.expr { + self.semicolon_inside_block(cx, block, tail, stmt.span); + } }, _ => (), } @@ -173,9 +165,5 @@ impl LateLintPass<'_> for SemicolonBlock { } fn get_line(cx: &LateContext<'_>, span: Span) -> Option { - if let Ok(line) = cx.sess().source_map().lookup_line(span.lo()) { - return Some(line.line); - } - - None + cx.sess().source_map().lookup_line(span.lo()).ok().map(|line| line.line) } diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 50c44a8e75c4..e9534bc63a69 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -2,11 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::Msrv; -use rustc_hir::{StabilityLevel, StableSince}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Block, Body, HirId, Path, PathSegment}; +use rustc_hir::{Block, Body, HirId, Path, PathSegment, StabilityLevel, StableSince}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 1cda6f596f4c..490e6c974ae1 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -13,8 +13,6 @@ use rustc_middle::ty; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -use std::ops::ControlFlow; - declare_clippy_lint! { /// ### What it does /// Checks for string appends of the form `x = x + y` (without @@ -411,125 +409,6 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { } } -declare_clippy_lint! { - /// ### What it does - /// This lint checks for `.to_string()` method calls on values of type `String`. - /// - /// ### Why restrict this? - /// The `to_string` method is also used on other types to convert them to a string. - /// When called on a `String` it only clones the `String`, which can be more specifically - /// expressed with `.clone()`. - /// - /// ### Example - /// ```no_run - /// let msg = String::from("Hello World"); - /// let _ = msg.to_string(); - /// ``` - /// Use instead: - /// ```no_run - /// let msg = String::from("Hello World"); - /// let _ = msg.clone(); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub STRING_TO_STRING, - restriction, - "using `to_string()` on a `String`, which should be `clone()`" -} - -declare_lint_pass!(StringToString => [STRING_TO_STRING]); - -fn is_parent_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - if let Some(parent_expr) = get_parent_expr(cx, expr) - && let ExprKind::MethodCall(name, _, _, parent_span) = parent_expr.kind - && name.ident.name == sym::map - && let Some(caller_def_id) = cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) - && (clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Result) - || clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Option) - || clippy_utils::is_diag_trait_item(cx, caller_def_id, sym::Iterator)) - { - Some(parent_span) - } else { - None - } -} - -fn is_called_from_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - // Look for a closure as parent of `expr`, discarding simple blocks - let parent_closure = cx - .tcx - .hir_parent_iter(expr.hir_id) - .try_fold(expr.hir_id, |child_hir_id, (_, node)| match node { - // Check that the child expression is the only expression in the block - Node::Block(block) if block.stmts.is_empty() && block.expr.map(|e| e.hir_id) == Some(child_hir_id) => { - ControlFlow::Continue(block.hir_id) - }, - Node::Expr(expr) if matches!(expr.kind, ExprKind::Block(..)) => ControlFlow::Continue(expr.hir_id), - Node::Expr(expr) if matches!(expr.kind, ExprKind::Closure(_)) => ControlFlow::Break(Some(expr)), - _ => ControlFlow::Break(None), - }) - .break_value()?; - is_parent_map_like(cx, parent_closure?) -} - -fn suggest_cloned_string_to_string(cx: &LateContext<'_>, span: rustc_span::Span) { - span_lint_and_sugg( - cx, - STRING_TO_STRING, - span, - "`to_string()` called on a `String`", - "try", - "cloned()".to_string(), - Applicability::MachineApplicable, - ); -} - -impl<'tcx> LateLintPass<'tcx> for StringToString { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if expr.span.from_expansion() { - return; - } - - match &expr.kind { - ExprKind::MethodCall(path, self_arg, [], _) => { - if path.ident.name == sym::to_string - && let ty = cx.typeck_results().expr_ty(self_arg) - && is_type_lang_item(cx, ty.peel_refs(), LangItem::String) - { - if let Some(parent_span) = is_called_from_map_like(cx, expr) { - suggest_cloned_string_to_string(cx, parent_span); - } else { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - STRING_TO_STRING, - expr.span, - "`to_string()` called on a `String`", - |diag| { - diag.help("consider using `.clone()`"); - }, - ); - } - } - }, - ExprKind::Path(QPath::TypeRelative(ty, segment)) => { - if segment.ident.name == sym::to_string - && let rustc_hir::TyKind::Path(QPath::Resolved(_, path)) = ty.peel_refs().kind - && let rustc_hir::def::Res::Def(_, def_id) = path.res - && cx - .tcx - .lang_items() - .get(LangItem::String) - .is_some_and(|lang_id| lang_id == def_id) - && let Some(parent_span) = is_parent_map_like(cx, expr) - { - suggest_cloned_string_to_string(cx, parent_span); - } - }, - _ => {}, - } - } -} - declare_clippy_lint! { /// ### What it does /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`. diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 87f184e13ce1..d5b6c1758549 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,17 +1,20 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_with_context; +use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_context}; use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; +use rustc_ast::{FormatArgs, FormatArgumentKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{Visitor, walk_body}; +use rustc_hir::intravisit::{Visitor, walk_body, walk_expr}; use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, LetStmt, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; +use rustc_span::Span; use super::LET_UNIT_VALUE; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorage, local: &'tcx LetStmt<'_>) { // skip `let () = { ... }` if let PatKind::Tuple(fields, ..) = local.pat.kind && fields.is_empty() @@ -73,11 +76,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { let mut suggestions = Vec::new(); // Suggest omitting the `let` binding - if let Some(expr) = &local.init { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, expr.span, local.span.ctxt(), "()", &mut app).0; - suggestions.push((local.span, format!("{snip};"))); - } + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, init.span, local.span.ctxt(), "()", &mut app).0; // If this is a binding pattern, we need to add suggestions to remove any usages // of the variable @@ -85,53 +85,102 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { && let Some(body_id) = cx.enclosing_body.as_ref() { let body = cx.tcx.hir_body(*body_id); - - // Collect variable usages - let mut visitor = UnitVariableCollector::new(binding_hir_id); + let mut visitor = UnitVariableCollector::new(cx, format_args, binding_hir_id); walk_body(&mut visitor, body); - // Add suggestions for replacing variable usages - suggestions.extend(visitor.spans.into_iter().map(|span| (span, "()".to_string()))); + let mut has_in_format_capture = false; + suggestions.extend(visitor.spans.iter().filter_map(|span| match span { + MaybeInFormatCapture::Yes => { + has_in_format_capture = true; + None + }, + MaybeInFormatCapture::No(span) => Some((*span, "()".to_string())), + })); + + if has_in_format_capture { + suggestions.push(( + init.span, + format!("();\n{}", reindent_multiline(&snip, false, indent_of(cx, local.span))), + )); + diag.multipart_suggestion( + "replace variable usages with `()`", + suggestions, + Applicability::MachineApplicable, + ); + return; + } } - // Emit appropriate diagnostic based on whether there are usages of the let binding - if !suggestions.is_empty() { - let message = if suggestions.len() == 1 { - "omit the `let` binding" - } else { - "omit the `let` binding and replace variable usages with `()`" - }; - diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); - } + suggestions.push((local.span, format!("{snip};"))); + let message = if suggestions.len() == 1 { + "omit the `let` binding" + } else { + "omit the `let` binding and replace variable usages with `()`" + }; + diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); }, ); } } } -struct UnitVariableCollector { +struct UnitVariableCollector<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + format_args: &'a FormatArgsStorage, id: HirId, - spans: Vec, + spans: Vec, + macro_call: Option<&'a FormatArgs>, } -impl UnitVariableCollector { - fn new(id: HirId) -> Self { - Self { id, spans: vec![] } +enum MaybeInFormatCapture { + Yes, + No(Span), +} + +impl<'a, 'tcx> UnitVariableCollector<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>, format_args: &'a FormatArgsStorage, id: HirId) -> Self { + Self { + cx, + format_args, + id, + spans: Vec::new(), + macro_call: None, + } } } /** * Collect all instances where a variable is used based on its `HirId`. */ -impl<'tcx> Visitor<'tcx> for UnitVariableCollector { +impl<'tcx> Visitor<'tcx> for UnitVariableCollector<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result { + if let Some(macro_call) = root_macro_call_first_node(self.cx, ex) + && is_format_macro(self.cx, macro_call.def_id) + && let Some(format_args) = self.format_args.get(self.cx, ex, macro_call.expn) + { + let parent_macro_call = self.macro_call; + self.macro_call = Some(format_args); + walk_expr(self, ex); + self.macro_call = parent_macro_call; + return; + } + if let ExprKind::Path(QPath::Resolved(None, path)) = ex.kind && let Res::Local(id) = path.res && id == self.id { - self.spans.push(path.span); + if let Some(macro_call) = self.macro_call + && macro_call.arguments.all_args().iter().any(|arg| { + matches!(arg.kind, FormatArgumentKind::Captured(_)) && find_format_arg_expr(ex, arg).is_some() + }) + { + self.spans.push(MaybeInFormatCapture::Yes); + } else { + self.spans.push(MaybeInFormatCapture::No(path.span)); + } } - rustc_hir::intravisit::walk_expr(self, ex); + + walk_expr(self, ex); } } diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs index e016bd3434b1..4ffcc247acf6 100644 --- a/clippy_lints/src/unit_types/mod.rs +++ b/clippy_lints/src/unit_types/mod.rs @@ -3,9 +3,10 @@ mod unit_arg; mod unit_cmp; mod utils; +use clippy_utils::macros::FormatArgsStorage; use rustc_hir::{Expr, LetStmt}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -96,11 +97,21 @@ declare_clippy_lint! { "passing unit to a function" } -declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]); +pub struct UnitTypes { + format_args: FormatArgsStorage, +} + +impl_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]); + +impl UnitTypes { + pub fn new(format_args: FormatArgsStorage) -> Self { + Self { format_args } + } +} impl<'tcx> LateLintPass<'tcx> for UnitTypes { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { - let_unit_value::check(cx, local); + let_unit_value::check(cx, &self.format_args, local); } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 299317384122..2113cb92137e 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -348,7 +348,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { IntTy::I128 => "I128", }; format!("LitIntType::Signed(IntTy::{t})") - } + }, LitIntType::Unsigned(uint_ty) => { let t = match uint_ty { UintTy::Usize => "Usize", @@ -359,7 +359,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { UintTy::U128 => "U128", }; format!("LitIntType::Unsigned(UintTy::{t})") - } + }, LitIntType::Unsuffixed => String::from("LitIntType::Unsuffixed"), }; kind!("Int({i}, {int_ty})"); @@ -374,7 +374,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { FloatTy::F128 => "F128", }; format!("LitFloatType::Suffixed(FloatTy::{t})") - } + }, LitFloatType::Unsuffixed => String::from("LitFloatType::Unsuffixed"), }; kind!("Float(_, {float_ty})"); diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 22fd15d153ab..a2523b5fb07c 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -118,7 +118,7 @@ impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); impl LateLintPass<'_> for WildcardImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if cx.sess().is_test_crate() { + if cx.sess().is_test_crate() || item.span.in_external_macro(cx.sess().source_map()) { return; } diff --git a/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs b/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs index e0ae0c11cc2d..5e6a40ac2eb6 100644 --- a/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs +++ b/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs @@ -2,12 +2,11 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::paths; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::{AttrStyle, DelimArgs}; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::{ - AttrArgs, AttrItem, AttrPath, Attribute, HirId, Impl, Item, ItemKind, Path, QPath, TraitRef, Ty, TyKind, + AttrArgs, AttrItem, AttrPath, Attribute, HirId, Impl, Item, ItemKind, Path, QPath, TraitRef, Ty, TyKind, find_attr, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_lint_defs::declare_tool_lint; diff --git a/clippy_test_deps/Cargo.lock b/clippy_test_deps/Cargo.lock index 5be404f24e6f..2f987c0137c9 100644 --- a/clippy_test_deps/Cargo.lock +++ b/clippy_test_deps/Cargo.lock @@ -70,7 +70,6 @@ name = "clippy_test_deps" version = "0.1.0" dependencies = [ "futures", - "if_chain", "itertools", "libc", "parking_lot", @@ -182,12 +181,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "io-uring" version = "0.7.8" diff --git a/clippy_test_deps/Cargo.toml b/clippy_test_deps/Cargo.toml index fcedc5d4843c..e449b48bc460 100644 --- a/clippy_test_deps/Cargo.toml +++ b/clippy_test_deps/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" libc = "0.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } -if_chain = "1.0" quote = "1.0.25" syn = { version = "2.0", features = ["full"] } futures = "0.3" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 73291aa8cdf7..bdf7431f29f2 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.90" +version = "0.1.91" edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 19e71f6af1de..6d8dd92d55d6 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-07-25 +nightly-2025-08-07 ``` diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 4ccd9c5300b1..2d42e76dcbc9 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -2,9 +2,9 @@ use crate::source::SpanRangeExt; use crate::{sym, tokenize_with_text}; use rustc_ast::attr; use rustc_ast::attr::AttributeExt; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_errors::Applicability; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_lexer::TokenKind; use rustc_lint::LateContext; use rustc_middle::ty::{AdtDef, TyCtxt}; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 5b9b0ef30014..fc716d86fc62 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -83,19 +83,18 @@ pub use self::hir_utils::{ use core::mem; use core::ops::ControlFlow; use std::collections::hash_map::Entry; -use std::iter::{once, repeat_n}; +use std::iter::{once, repeat_n, zip}; use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; use rustc_abi::Integer; use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_ast::join_path_syms; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; @@ -106,7 +105,7 @@ use rustc_hir::{ CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, - TraitItemKind, TraitRef, TyKind, UnOp, def, + TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr, }; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -582,7 +581,7 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - return false; } - for (x1, x2) in s1.iter().zip(s2.iter()) { + for (x1, x2) in zip(&s1, &s2) { if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() { return false; } @@ -1898,42 +1897,11 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// * `|x| { return x; }` /// * `|(x, y)| (x, y)` /// * `|[x, y]| [x, y]` +/// * `|Foo(bar, baz)| Foo(bar, baz)` +/// * `|Foo { bar, baz }| Foo { bar, baz }` /// /// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead. fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { - fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool { - if cx - .typeck_results() - .pat_binding_modes() - .get(pat.hir_id) - .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_))) - { - // If the parameter is `(x, y)` of type `&(T, T)`, or `[x, y]` of type `&[T; 2]`, then - // due to match ergonomics, the inner patterns become references. Don't consider this - // the identity function as that changes types. - return false; - } - - match (pat.kind, expr.kind) { - (PatKind::Binding(_, id, _, _), _) => { - path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty() - }, - (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup)) - if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() => - { - pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr)) - }, - (PatKind::Slice(before, slice, after), ExprKind::Array(arr)) - if slice.is_none() && before.len() + after.len() == arr.len() => - { - (before.iter().chain(after)) - .zip(arr) - .all(|(pat, expr)| check_pat(cx, pat, expr)) - }, - _ => false, - } - } - let [param] = func.params else { return false; }; @@ -1966,11 +1934,81 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { return false; } }, - _ => return check_pat(cx, param.pat, expr), + _ => return is_expr_identity_of_pat(cx, param.pat, expr, true), } } } +/// Checks if the given expression is an identity representation of the given pattern: +/// * `x` is the identity representation of `x` +/// * `(x, y)` is the identity representation of `(x, y)` +/// * `[x, y]` is the identity representation of `[x, y]` +/// * `Foo(bar, baz)` is the identity representation of `Foo(bar, baz)` +/// * `Foo { bar, baz }` is the identity representation of `Foo { bar, baz }` +/// +/// Note that `by_hir` is used to determine bindings are checked by their `HirId` or by their name. +/// This can be useful when checking patterns in `let` bindings or `match` arms. +pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool { + if cx + .typeck_results() + .pat_binding_modes() + .get(pat.hir_id) + .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_))) + { + // If the parameter is `(x, y)` of type `&(T, T)`, or `[x, y]` of type `&[T; 2]`, then + // due to match ergonomics, the inner patterns become references. Don't consider this + // the identity function as that changes types. + return false; + } + + // NOTE: we're inside a (function) body, so this won't ICE + let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir); + + match (pat.kind, expr.kind) { + (PatKind::Binding(_, id, _, _), _) if by_hir => { + path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty() + }, + (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => { + matches!(path.segments, [ segment] if segment.ident.name == ident.name) + }, + (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup)) + if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() => + { + zip(pats, tup).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir)) + }, + (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => { + zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir)) + }, + (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields)) + if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() => + { + // check ident + if let ExprKind::Path(ident) = &ident.kind + && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id) + // check fields + && zip(field_pats, fields).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr,by_hir)) + { + true + } else { + false + } + }, + (PatKind::Struct(pat_ident, field_pats, false), ExprKind::Struct(ident, fields, hir::StructTailExpr::None)) + if field_pats.len() == fields.len() => + { + // check ident + qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id) + // check fields + && field_pats.iter().all(|field_pat| { + fields.iter().any(|field| { + field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir) + }) + }) + }, + _ => false, + } +} + /// This is the same as [`is_expr_identity_function`], but does not consider closures /// with type annotations for its bindings (or similar) as identity functions: /// * `|x: u8| x` diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 480e0687756f..89a83e2c48f9 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -1,8 +1,8 @@ use crate::sym; use rustc_ast::Attribute; use rustc_ast::attr::AttributeExt; -use rustc_hir::RustcVersion; use rustc_attr_parsing::parse_version; +use rustc_hir::RustcVersion; use rustc_lint::LateContext; use rustc_session::Session; use rustc_span::Symbol; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 79116eba971a..68f0b5ea2558 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -5,10 +5,10 @@ use crate::msrvs::{self, Msrv}; use hir::LangItem; -use rustc_hir::{RustcVersion, StableSince}; use rustc_const_eval::check_consts::ConstCx; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::{RustcVersion, StableSince}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::LateContext; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 7d21336be1cd..e675291b6f3a 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -342,11 +342,8 @@ impl SourceFileRange { /// Attempts to get the text from the source file. This can fail if the source text isn't /// loaded. pub fn as_str(&self) -> Option<&str> { - self.sf - .src - .as_ref() - .map(|src| src.as_str()) - .or_else(|| self.sf.external_src.get().and_then(|src| src.get_source())) + (self.sf.src.as_ref().map(|src| src.as_str())) + .or_else(|| self.sf.external_src.get()?.get_source()) .and_then(|x| x.get(self.range.clone())) } } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 7a24d07fa1df..a63333c9b48f 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -4,11 +4,11 @@ use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; -use rustc_ast::ast; use rustc_ast::util::parser::AssocOp; +use rustc_ast::{UnOp, ast}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; +use rustc_hir::{self as hir, Closure, ExprKind, HirId, MutTy, Node, TyKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; @@ -29,6 +29,11 @@ pub enum Sugg<'a> { /// A binary operator expression, including `as`-casts and explicit type /// coercion. BinOp(AssocOp, Cow<'a, str>, Cow<'a, str>), + /// A unary operator expression. This is used to sometimes represent `!` + /// or `-`, but only if the type with and without the operator is kept identical. + /// It means that doubling the operator can be used to remove it instead, in + /// order to provide better suggestions. + UnOp(UnOp, Box>), } /// Literal constant `0`, for convenience. @@ -40,9 +45,10 @@ pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("")); impl Display for Sugg<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match *self { - Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) => s.fmt(f), - Sugg::BinOp(op, ref lhs, ref rhs) => binop_to_string(op, lhs, rhs).fmt(f), + match self { + Sugg::NonParen(s) | Sugg::MaybeParen(s) => s.fmt(f), + Sugg::BinOp(op, lhs, rhs) => binop_to_string(*op, lhs, rhs).fmt(f), + Sugg::UnOp(op, inner) => write!(f, "{}{}", op.as_str(), inner.clone().maybe_inner_paren()), } } } @@ -100,9 +106,19 @@ impl<'a> Sugg<'a> { applicability: &mut Applicability, ) -> Self { if expr.span.ctxt() == ctxt { - Self::hir_from_snippet(expr, |span| { - snippet_with_context(cx, span, ctxt, default, applicability).0 - }) + if let ExprKind::Unary(op, inner) = expr.kind + && matches!(op, UnOp::Neg | UnOp::Not) + && cx.typeck_results().expr_ty(expr) == cx.typeck_results().expr_ty(inner) + { + Sugg::UnOp( + op, + Box::new(Self::hir_with_context(cx, inner, ctxt, default, applicability)), + ) + } else { + Self::hir_from_snippet(expr, |span| { + snippet_with_context(cx, span, ctxt, default, applicability).0 + }) + } } else { let (snip, _) = snippet_with_context(cx, expr.span, ctxt, default, applicability); Sugg::NonParen(snip) @@ -341,6 +357,7 @@ impl<'a> Sugg<'a> { let sugg = binop_to_string(op, &lhs, &rhs); Sugg::NonParen(format!("({sugg})").into()) }, + Sugg::UnOp(op, inner) => Sugg::NonParen(format!("({}{})", op.as_str(), inner.maybe_inner_paren()).into()), } } @@ -348,6 +365,26 @@ impl<'a> Sugg<'a> { match self { Sugg::NonParen(p) | Sugg::MaybeParen(p) => p.into_owned(), Sugg::BinOp(b, l, r) => binop_to_string(b, &l, &r), + Sugg::UnOp(op, inner) => format!("{}{}", op.as_str(), inner.maybe_inner_paren()), + } + } + + /// Checks if `self` starts with a unary operator. + fn starts_with_unary_op(&self) -> bool { + match self { + Sugg::UnOp(..) => true, + Sugg::BinOp(..) => false, + Sugg::MaybeParen(s) | Sugg::NonParen(s) => s.starts_with(['*', '!', '-', '&']), + } + } + + /// Call `maybe_paren` on `self` if it doesn't start with a unary operator, + /// don't touch it otherwise. + fn maybe_inner_paren(self) -> Self { + if self.starts_with_unary_op() { + self + } else { + self.maybe_paren() } } } @@ -430,10 +467,11 @@ impl Sub for &Sugg<'_> { forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>); forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>); -impl Neg for Sugg<'_> { - type Output = Sugg<'static>; - fn neg(self) -> Sugg<'static> { - match &self { +impl<'a> Neg for Sugg<'a> { + type Output = Sugg<'a>; + fn neg(self) -> Self::Output { + match self { + Self::UnOp(UnOp::Neg, sugg) => *sugg, Self::BinOp(AssocOp::Cast, ..) => Sugg::MaybeParen(format!("-({self})").into()), _ => make_unop("-", self), } @@ -446,19 +484,21 @@ impl<'a> Not for Sugg<'a> { use AssocOp::Binary; use ast::BinOpKind::{Eq, Ge, Gt, Le, Lt, Ne}; - if let Sugg::BinOp(op, lhs, rhs) = self { - let to_op = match op { - Binary(Eq) => Binary(Ne), - Binary(Ne) => Binary(Eq), - Binary(Lt) => Binary(Ge), - Binary(Ge) => Binary(Lt), - Binary(Gt) => Binary(Le), - Binary(Le) => Binary(Gt), - _ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)), - }; - Sugg::BinOp(to_op, lhs, rhs) - } else { - make_unop("!", self) + match self { + Sugg::BinOp(op, lhs, rhs) => { + let to_op = match op { + Binary(Eq) => Binary(Ne), + Binary(Ne) => Binary(Eq), + Binary(Lt) => Binary(Ge), + Binary(Ge) => Binary(Lt), + Binary(Gt) => Binary(Le), + Binary(Le) => Binary(Gt), + _ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)), + }; + Sugg::BinOp(to_op, lhs, rhs) + }, + Sugg::UnOp(UnOp::Not, expr) => *expr, + _ => make_unop("!", self), } } } @@ -491,20 +531,11 @@ impl Display for ParenHelper { /// Builds the string for `` adding parenthesis when necessary. /// /// For convenience, the operator is taken as a string because all unary -/// operators have the same -/// precedence. +/// operators have the same precedence. pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> { - // If the `expr` starts with `op` already, do not add wrap it in + // If the `expr` starts with a unary operator already, do not wrap it in // parentheses. - let expr = if let Sugg::MaybeParen(ref sugg) = expr - && !has_enclosing_paren(sugg) - && sugg.starts_with(op) - { - expr - } else { - expr.maybe_paren() - }; - Sugg::MaybeParen(format!("{op}{expr}").into()) + Sugg::MaybeParen(format!("{op}{}", expr.maybe_inner_paren()).into()) } /// Builds the string for ` ` adding parenthesis when necessary. @@ -753,8 +784,10 @@ pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Opti let mut visitor = DerefDelegate { cx, closure_span: closure.span, + closure_arg_id: closure_body.params[0].pat.hir_id, closure_arg_is_type_annotated_double_ref, next_pos: closure.span.lo(), + checked_borrows: FxHashSet::default(), suggestion_start: String::new(), applicability: Applicability::MachineApplicable, }; @@ -780,10 +813,15 @@ struct DerefDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, /// The span of the input closure to adapt closure_span: Span, + /// The `hir_id` of the closure argument being checked + closure_arg_id: HirId, /// Indicates if the arg of the closure is a type annotated double reference closure_arg_is_type_annotated_double_ref: bool, /// last position of the span to gradually build the suggestion next_pos: BytePos, + /// `hir_id`s that has been checked. This is used to avoid checking the same `hir_id` multiple + /// times when inside macro expansions. + checked_borrows: FxHashSet, /// starting part of the gradually built suggestion suggestion_start: String, /// confidence on the built suggestion @@ -847,9 +885,15 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + #[expect(clippy::too_many_lines)] fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { if let PlaceBase::Local(id) = cmt.place.base { let span = self.cx.tcx.hir_span(cmt.hir_id); + if !self.checked_borrows.insert(cmt.hir_id) { + // already checked this span and hir_id, skip + return; + } + let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None); let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability); @@ -858,7 +902,12 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // full identifier that includes projection (i.e.: `fp.field`) let ident_str_with_proj = snippet(self.cx, span, "..").to_string(); - if cmt.place.projections.is_empty() { + // Make sure to get in all projections if we're on a `matches!` + if let Node::Pat(pat) = self.cx.tcx.hir_node(id) + && pat.hir_id != self.closure_arg_id + { + let _ = write!(self.suggestion_start, "{start_snip}{ident_str_with_proj}"); + } else if cmt.place.projections.is_empty() { // handle item without any projection, that needs an explicit borrowing // i.e.: suggest `&x` instead of `x` let _: fmt::Result = write!(self.suggestion_start, "{start_snip}&{ident_str}"); diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 934be97d94e5..ce7cc9348fbd 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -171,7 +171,6 @@ generate! { has_significant_drop, hidden_glob_reexports, hygiene, - if_chain, insert, inspect, int_roundings, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 02a8eda5893e..d79773f83211 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -6,13 +6,12 @@ use core::ops::ControlFlow; use itertools::Itertools; use rustc_abi::VariantIdx; use rustc_ast::ast::Mutability; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, FnDecl, LangItem, TyKind}; +use rustc_hir::{Expr, FnDecl, LangItem, TyKind, find_attr}; use rustc_hir_analysis::lower_ty; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; @@ -583,7 +582,7 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator { Sig(Binder<'tcx, FnSig<'tcx>>, Option), Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>), diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index bd6b4dfdee4d..ec0e59e70549 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.90" +version = "0.1.91" edition = "2024" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0" diff --git a/lintcheck/src/input.rs b/lintcheck/src/input.rs index 408a2e087af2..1ed059d2fb11 100644 --- a/lintcheck/src/input.rs +++ b/lintcheck/src/input.rs @@ -117,7 +117,7 @@ pub fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) crate_sources.push(CrateWithSource { name: tk.name.clone(), source: CrateSource::CratesIo { - version: version.to_string(), + version: version.clone(), }, file_link: tk.file_link(DEFAULT_DOCS_LINK), options: tk.options.clone(), diff --git a/lintcheck/src/json.rs b/lintcheck/src/json.rs index 808997ff0220..79c1255c5ffe 100644 --- a/lintcheck/src/json.rs +++ b/lintcheck/src/json.rs @@ -66,7 +66,7 @@ impl fmt::Display for Summary { } in &self.0 { let html_id = to_html_id(name); - writeln!(f, "| [`{name}`](#{html_id}) | {added} | {changed} | {removed} |")?; + writeln!(f, "| [`{name}`](#{html_id}) | {added} | {removed} | {changed} |")?; } Ok(()) diff --git a/lintcheck/src/output.rs b/lintcheck/src/output.rs index d7fe0915121d..1ecc3f7c2494 100644 --- a/lintcheck/src/output.rs +++ b/lintcheck/src/output.rs @@ -220,7 +220,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us let same_in_both_hashmaps = old_stats .iter() .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val)) - .map(|(k, v)| (k.to_string(), *v)) + .map(|(k, v)| (k.clone(), *v)) .collect::>(); let mut old_stats_deduped = old_stats; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 0edb80edd04e..ac51ec2d61b5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-07-25" +channel = "nightly-2025-08-07" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 464efc45c6b4..6b6dfd7b81ea 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -73,8 +73,7 @@ fn internal_extern_flags() -> Vec { && INTERNAL_TEST_DEPENDENCIES.contains(&name) { // A dependency may be listed twice if it is available in sysroot, - // and the sysroot dependencies are listed first. As of the writing, - // this only seems to apply to if_chain. + // and the sysroot dependencies are listed first. crates.insert(name, path); } } @@ -434,6 +433,7 @@ fn ui_cargo_toml_metadata() { #[derive(Template)] #[template(path = "index_template.html")] struct Renderer<'a> { + count: usize, lints: &'a Vec, } @@ -513,7 +513,12 @@ impl DiagnosticCollector { fs::write( "util/gh-pages/index.html", - Renderer { lints: &metadata }.render().unwrap(), + Renderer { + count: LINTS.len(), + lints: &metadata, + } + .render() + .unwrap(), ) .unwrap(); }); diff --git a/tests/symbols-used.rs b/tests/symbols-used.rs index bc0456711fb2..a1049ba64d54 100644 --- a/tests/symbols-used.rs +++ b/tests/symbols-used.rs @@ -75,7 +75,7 @@ fn all_symbols_are_used() -> Result<()> { for sym in extra { eprintln!(" - {sym}"); } - Err(format!("extra symbols found — remove them {SYM_FILE}"))?; + Err(format!("extra symbols found — remove them from {SYM_FILE}"))?; } Ok(()) } diff --git a/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.stderr b/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.stderr index 14a6b5047b18..666b842bf808 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.stderr @@ -7,7 +7,7 @@ error: unnecessary structure name repetition note: the lint level is defined here --> src/main.rs:6:9 | -6 | #![deny(clippy::use_self)] + 6 | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ error: unnecessary structure name repetition diff --git a/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr b/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr index b626551e35b9..8db52c47ad54 100644 --- a/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr +++ b/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr @@ -14,10 +14,10 @@ error: file is loaded as a module multiple times: `src/b.rs` error: file is loaded as a module multiple times: `src/c.rs` --> src/main.rs:7:1 | -7 | mod c; + 7 | mod c; | ^^^^^^ first loaded here -8 | / #[path = "c.rs"] -9 | | mod c2; + 8 | / #[path = "c.rs"] + 9 | | mod c2; | |_______^ loaded again here 10 | / #[path = "c.rs"] 11 | | mod c3; @@ -44,8 +44,8 @@ error: file is loaded as a module multiple times: `src/from_other_module.rs` | ::: src/other_module/mod.rs:1:1 | -1 | / #[path = "../from_other_module.rs"] -2 | | mod m; + 1 | / #[path = "../from_other_module.rs"] + 2 | | mod m; | |______^ loaded again here | = help: replace all but one `mod` item with `use` items diff --git a/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr b/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr index 059427d8ee0f..a87339a9a301 100644 --- a/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr +++ b/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr @@ -1,7 +1,7 @@ error: lint group `rust_2018_idioms` has the same priority (0) as a lint --> Cargo.toml:7:1 | -7 | rust_2018_idioms = "warn" + 7 | rust_2018_idioms = "warn" | ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0 ... 12 | unused_attributes = { level = "allow" } @@ -11,8 +11,8 @@ error: lint group `rust_2018_idioms` has the same priority (0) as a lint = note: `#[deny(clippy::lint_groups_priority)]` on by default help: to have lints override the group set `rust_2018_idioms` to a lower priority | -7 - rust_2018_idioms = "warn" -7 + rust_2018_idioms = { level = "warn", priority = -1 } + 7 - rust_2018_idioms = "warn" + 7 + rust_2018_idioms = { level = "warn", priority = -1 } | error: lint group `unused` has the same priority (0) as a lint diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed index 20511cbed165..ec9fbcfd4a37 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed @@ -1,3 +1,4 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs #![warn(clippy::wildcard_imports)] mod prelude { @@ -13,6 +14,10 @@ mod my_crate { pub mod utils { pub fn my_util_fn() {} } + + pub mod utils2 { + pub const SOME_CONST: u32 = 1; + } } pub use utils::{BAR, print}; @@ -22,6 +27,12 @@ use my_crate::utils::my_util_fn; use prelude::FOO; //~^ ERROR: usage of wildcard import +proc_macros::external! { + use my_crate::utils2::*; + + static SOME_STATIC: u32 = SOME_CONST; +} + fn main() { let _ = FOO; let _ = BAR; diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.rs b/tests/ui-toml/wildcard_imports/wildcard_imports.rs index 8d05910f471b..233ee19f89b9 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.rs +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.rs @@ -1,3 +1,4 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs #![warn(clippy::wildcard_imports)] mod prelude { @@ -13,6 +14,10 @@ mod my_crate { pub mod utils { pub fn my_util_fn() {} } + + pub mod utils2 { + pub const SOME_CONST: u32 = 1; + } } pub use utils::*; @@ -22,6 +27,12 @@ use my_crate::utils::*; use prelude::*; //~^ ERROR: usage of wildcard import +proc_macros::external! { + use my_crate::utils2::*; + + static SOME_STATIC: u32 = SOME_CONST; +} + fn main() { let _ = FOO; let _ = BAR; diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.stderr b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr index 5e624dd6c3cd..5d37cb705f51 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.stderr +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:18:9 + --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:23:9 | LL | pub use utils::*; | ^^^^^^^^ help: try: `utils::{BAR, print}` @@ -8,13 +8,13 @@ LL | pub use utils::*; = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]` error: usage of wildcard import - --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:20:5 + --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:25:5 | LL | use my_crate::utils::*; | ^^^^^^^^^^^^^^^^^^ help: try: `my_crate::utils::my_util_fn` error: usage of wildcard import - --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:22:5 + --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:27:5 | LL | use prelude::*; | ^^^^^^^^^^ help: try: `prelude::FOO` diff --git a/tests/ui/cast_lossless_integer_unfixable.rs b/tests/ui/cast_lossless_integer_unfixable.rs new file mode 100644 index 000000000000..db9cbbb5b663 --- /dev/null +++ b/tests/ui/cast_lossless_integer_unfixable.rs @@ -0,0 +1,17 @@ +//@check-pass +#![warn(clippy::cast_lossless)] + +fn issue15348() { + macro_rules! zero { + ($int:ty) => {{ + let data: [u8; 3] = [0, 0, 0]; + data[0] as $int + }}; + } + + let _ = zero!(u8); + let _ = zero!(u16); + let _ = zero!(u32); + let _ = zero!(u64); + let _ = zero!(u128); +} diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index fed75244c6f7..3d709fe9b8e0 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -104,3 +104,25 @@ fn issue14799() { } } } + +fn in_parens() { + let x = "hello"; + let y = "world"; + + if x == "hello" { + print!("Hello "); + } else if y == "world" { println!("world") } else { println!("!") } + //~^^^ collapsible_else_if +} + +fn in_brackets() { + let x = "hello"; + let y = "world"; + + // There is no lint when the inner `if` is in a block. + if x == "hello" { + print!("Hello "); + } else { + { if y == "world" { println!("world") } else { println!("!") } } + } +} diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index e50e781fb698..51868e039086 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -120,3 +120,27 @@ fn issue14799() { } } } + +fn in_parens() { + let x = "hello"; + let y = "world"; + + if x == "hello" { + print!("Hello "); + } else { + (if y == "world" { println!("world") } else { println!("!") }) + } + //~^^^ collapsible_else_if +} + +fn in_brackets() { + let x = "hello"; + let y = "world"; + + // There is no lint when the inner `if` is in a block. + if x == "hello" { + print!("Hello "); + } else { + { if y == "world" { println!("world") } else { println!("!") } } + } +} diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 7d80894cadbb..1a7bcec7fd5d 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -150,5 +150,14 @@ LL | | if false {} LL | | } | |_____^ help: collapse nested if block: `if false {}` -error: aborting due to 8 previous errors +error: this `else { if .. }` block can be collapsed + --> tests/ui/collapsible_else_if.rs:130:12 + | +LL | } else { + | ____________^ +LL | | (if y == "world" { println!("world") } else { println!("!") }) +LL | | } + | |_____^ help: collapse nested if block: `if y == "world" { println!("world") } else { println!("!") }` + +error: aborting due to 9 previous errors diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index 77bc791ea8e9..78354c2d7cf8 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -163,3 +163,21 @@ fn issue14799() { if true {} }; } + +fn in_parens() { + if true + && true { + println!("In parens, linted"); + } + //~^^^^^ collapsible_if +} + +fn in_brackets() { + if true { + { + if true { + println!("In brackets, not linted"); + } + } + } +} diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index d30df157d5eb..5d9afa109569 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -173,3 +173,22 @@ fn issue14799() { if true {} }; } + +fn in_parens() { + if true { + (if true { + println!("In parens, linted"); + }) + } + //~^^^^^ collapsible_if +} + +fn in_brackets() { + if true { + { + if true { + println!("In brackets, not linted"); + } + } + } +} diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index 32c6b0194030..a685cc2e9291 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -190,5 +190,23 @@ LL | // This is a comment, do not collapse code to it LL ~ ; 3 | -error: aborting due to 11 previous errors +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if.rs:178:5 + | +LL | / if true { +LL | | (if true { +LL | | println!("In parens, linted"); +LL | | }) +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if true +LL ~ && true { +LL | println!("In parens, linted"); +LL ~ } + | + +error: aborting due to 12 previous errors diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 6b69bdd29cea..9743a83fb934 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -12,6 +12,7 @@ #![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` #![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` #![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` +#![warn(clippy::string_to_string)] //~ ERROR: lint `clippy::string_to_string` #![warn(clippy::unsafe_vector_initialization)] //~ ERROR: lint `clippy::unsafe_vector_initialization` #![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` #![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 07e59d33d608..cd225da611c4 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -61,35 +61,41 @@ error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can n LL | #![warn(clippy::should_assert_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower +error: lint `clippy::string_to_string` has been removed: `clippy:implicit_clone` covers those cases --> tests/ui/deprecated.rs:15:9 | +LL | #![warn(clippy::string_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower + --> tests/ui/deprecated.rs:16:9 + | LL | #![warn(clippy::unsafe_vector_initialization)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable - --> tests/ui/deprecated.rs:16:9 + --> tests/ui/deprecated.rs:17:9 | LL | #![warn(clippy::unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable - --> tests/ui/deprecated.rs:17:9 + --> tests/ui/deprecated.rs:18:9 | LL | #![warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` - --> tests/ui/deprecated.rs:18:9 + --> tests/ui/deprecated.rs:19:9 | LL | #![warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config - --> tests/ui/deprecated.rs:19:9 + --> tests/ui/deprecated.rs:20:9 | LL | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index 8cf20d8b1a11..bbbd5973036e 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -83,7 +83,7 @@ fn test_units() { /// WebGL WebGL2 WebGPU WebRTC WebSocket WebTransport /// TensorFlow /// TrueType -/// iOS macOS FreeBSD NetBSD OpenBSD +/// iOS macOS FreeBSD NetBSD OpenBSD NixOS /// TeX LaTeX BibTeX BibLaTeX /// MinGW /// CamelCase (see also #2395) diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 5b6f2bd8330c..1077d3580d3c 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -83,7 +83,7 @@ fn test_units() { /// WebGL WebGL2 WebGPU WebRTC WebSocket WebTransport /// TensorFlow /// TrueType -/// iOS macOS FreeBSD NetBSD OpenBSD +/// iOS macOS FreeBSD NetBSD OpenBSD NixOS /// TeX LaTeX BibTeX BibLaTeX /// MinGW /// CamelCase (see also #2395) diff --git a/tests/ui/duplicated_attributes.rs b/tests/ui/duplicated_attributes.rs index 3ca91d6f1829..9a6714995059 100644 --- a/tests/ui/duplicated_attributes.rs +++ b/tests/ui/duplicated_attributes.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macro_attr.rs +#![warn(clippy::duplicated_attributes, clippy::duplicated_attributes)] //~ ERROR: duplicated attribute #![feature(rustc_attrs)] -#![warn(clippy::duplicated_attributes)] #![cfg(any(unix, windows))] #![allow(dead_code)] #![allow(dead_code)] //~ ERROR: duplicated attribute diff --git a/tests/ui/duplicated_attributes.stderr b/tests/ui/duplicated_attributes.stderr index 0903617a8d1f..922939d60dd6 100644 --- a/tests/ui/duplicated_attributes.stderr +++ b/tests/ui/duplicated_attributes.stderr @@ -1,3 +1,22 @@ +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:2:40 + | +LL | #![warn(clippy::duplicated_attributes, clippy::duplicated_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:2:9 + | +LL | #![warn(clippy::duplicated_attributes, clippy::duplicated_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:2:40 + | +LL | #![warn(clippy::duplicated_attributes, clippy::duplicated_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::duplicated-attributes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]` + error: duplicated attribute --> tests/ui/duplicated_attributes.rs:6:10 | @@ -14,8 +33,6 @@ help: remove this attribute | LL | #![allow(dead_code)] | ^^^^^^^^^ - = note: `-D clippy::duplicated-attributes` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]` error: duplicated attribute --> tests/ui/duplicated_attributes.rs:14:9 @@ -34,5 +51,5 @@ help: remove this attribute LL | #[allow(dead_code)] | ^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/empty_structs_with_brackets.fixed b/tests/ui/empty_structs_with_brackets.fixed index 419cf2354f89..22386245ffe6 100644 --- a/tests/ui/empty_structs_with_brackets.fixed +++ b/tests/ui/empty_structs_with_brackets.fixed @@ -32,3 +32,17 @@ macro_rules! empty_struct { empty_struct!(FromMacro); fn main() {} + +mod issue15349 { + trait Bar {} + impl Bar for [u8; 7] {} + + struct Foo; + //~^ empty_structs_with_brackets + impl Foo + where + [u8; N]: Bar<[(); N]>, + { + fn foo() {} + } +} diff --git a/tests/ui/empty_structs_with_brackets.rs b/tests/ui/empty_structs_with_brackets.rs index 90c415c12206..5cb54b661349 100644 --- a/tests/ui/empty_structs_with_brackets.rs +++ b/tests/ui/empty_structs_with_brackets.rs @@ -32,3 +32,17 @@ macro_rules! empty_struct { empty_struct!(FromMacro); fn main() {} + +mod issue15349 { + trait Bar {} + impl Bar for [u8; 7] {} + + struct Foo {} + //~^ empty_structs_with_brackets + impl Foo + where + [u8; N]: Bar<[(); N]>, + { + fn foo() {} + } +} diff --git a/tests/ui/empty_structs_with_brackets.stderr b/tests/ui/empty_structs_with_brackets.stderr index 86ef43aa9600..f662bb9423e3 100644 --- a/tests/ui/empty_structs_with_brackets.stderr +++ b/tests/ui/empty_structs_with_brackets.stderr @@ -16,5 +16,13 @@ LL | struct MyEmptyTupleStruct(); // should trigger lint | = help: remove the brackets -error: aborting due to 2 previous errors +error: found empty brackets on struct declaration + --> tests/ui/empty_structs_with_brackets.rs:40:31 + | +LL | struct Foo {} + | ^^^ + | + = help: remove the brackets + +error: aborting due to 3 previous errors diff --git a/tests/ui/four_forward_slashes_bare_cr.rs b/tests/ui/four_forward_slashes_bare_cr.rs new file mode 100644 index 000000000000..19123cd206e3 --- /dev/null +++ b/tests/ui/four_forward_slashes_bare_cr.rs @@ -0,0 +1,6 @@ +//@no-rustfix +#![warn(clippy::four_forward_slashes)] + +//~v four_forward_slashes +//// nondoc comment with bare CR: ' ' +fn main() {} diff --git a/tests/ui/four_forward_slashes_bare_cr.stderr b/tests/ui/four_forward_slashes_bare_cr.stderr new file mode 100644 index 000000000000..64e70b97db9a --- /dev/null +++ b/tests/ui/four_forward_slashes_bare_cr.stderr @@ -0,0 +1,14 @@ +error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't + --> tests/ui/four_forward_slashes_bare_cr.rs:5:1 + | +LL | / //// nondoc comment with bare CR: '␍' +LL | | fn main() {} + | |_^ + | + = help: make this a doc comment by removing one `/` + = note: bare CR characters are not allowed in doc comments + = note: `-D clippy::four-forward-slashes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::four_forward_slashes)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/implicit_clone.fixed b/tests/ui/implicit_clone.fixed index d60d1cb0ec04..267514c5f3d3 100644 --- a/tests/ui/implicit_clone.fixed +++ b/tests/ui/implicit_clone.fixed @@ -135,4 +135,10 @@ fn main() { } let no_clone = &NoClone; let _ = no_clone.to_owned(); + + let s = String::from("foo"); + let _ = s.clone(); + //~^ implicit_clone + let _ = s.clone(); + //~^ implicit_clone } diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index b96828f28c82..fba954026e7f 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -135,4 +135,10 @@ fn main() { } let no_clone = &NoClone; let _ = no_clone.to_owned(); + + let s = String::from("foo"); + let _ = s.to_owned(); + //~^ implicit_clone + let _ = s.to_string(); + //~^ implicit_clone } diff --git a/tests/ui/implicit_clone.stderr b/tests/ui/implicit_clone.stderr index 1eb6ff1fe429..4cca9b0d0c07 100644 --- a/tests/ui/implicit_clone.stderr +++ b/tests/ui/implicit_clone.stderr @@ -67,5 +67,17 @@ error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenc LL | let _ = pathbuf_ref.to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(**pathbuf_ref).clone()` -error: aborting due to 11 previous errors +error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type + --> tests/ui/implicit_clone.rs:140:13 + | +LL | let _ = s.to_owned(); + | ^^^^^^^^^^^^ help: consider using: `s.clone()` + +error: implicitly cloning a `String` by calling `to_string` on its dereferenced type + --> tests/ui/implicit_clone.rs:142:13 + | +LL | let _ = s.to_string(); + | ^^^^^^^^^^^^^ help: consider using: `s.clone()` + +error: aborting due to 13 previous errors diff --git a/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed b/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed index 72edc539f043..edf6f014d3d0 100644 --- a/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed +++ b/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed @@ -1,8 +1,5 @@ #![deny(clippy::index_refutable_slice)] -extern crate if_chain; -use if_chain::if_chain; - macro_rules! if_let_slice_macro { () => { // This would normally be linted @@ -18,12 +15,9 @@ fn main() { if_let_slice_macro!(); // Do lint this - if_chain! { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some([slice_0, ..]) = slice; + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some([slice_0, ..]) = slice { //~^ ERROR: this binding can be a slice pattern to avoid indexing - then { - println!("{}", slice_0); - } + println!("{}", slice_0); } } diff --git a/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs b/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs index 7b474ba423b9..76d4a2350f50 100644 --- a/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs +++ b/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs @@ -1,8 +1,5 @@ #![deny(clippy::index_refutable_slice)] -extern crate if_chain; -use if_chain::if_chain; - macro_rules! if_let_slice_macro { () => { // This would normally be linted @@ -18,12 +15,9 @@ fn main() { if_let_slice_macro!(); // Do lint this - if_chain! { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice) = slice; + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some(slice) = slice { //~^ ERROR: this binding can be a slice pattern to avoid indexing - then { - println!("{}", slice[0]); - } + println!("{}", slice[0]); } } diff --git a/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr b/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr index 64741abb9114..635e6d19aef2 100644 --- a/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr +++ b/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr @@ -1,8 +1,8 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:23:21 + --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:19:17 | -LL | if let Some(slice) = slice; - | ^^^^^ +LL | if let Some(slice) = slice { + | ^^^^^ | note: the lint level is defined here --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:1:9 @@ -11,10 +11,9 @@ LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the binding and indexed access with a slice pattern | -LL ~ if let Some([slice_0, ..]) = slice; +LL ~ if let Some([slice_0, ..]) = slice { LL | -LL | then { -LL ~ println!("{}", slice_0); +LL ~ println!("{}", slice_0); | error: aborting due to 1 previous error diff --git a/tests/ui/indexing_slicing_slice.rs b/tests/ui/indexing_slicing_slice.rs index cad77f56d03a..dea31530a0b6 100644 --- a/tests/ui/indexing_slicing_slice.rs +++ b/tests/ui/indexing_slicing_slice.rs @@ -1,6 +1,5 @@ //@aux-build: proc_macros.rs -#![warn(clippy::indexing_slicing)] // We also check the out_of_bounds_indexing lint here, because it lints similar things and // we want to avoid false positives. #![warn(clippy::out_of_bounds_indexing)] diff --git a/tests/ui/indexing_slicing_slice.stderr b/tests/ui/indexing_slicing_slice.stderr index e3ef89823e3d..e3d6086544de 100644 --- a/tests/ui/indexing_slicing_slice.stderr +++ b/tests/ui/indexing_slicing_slice.stderr @@ -1,5 +1,5 @@ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:115:6 + --> tests/ui/indexing_slicing_slice.rs:114:6 | LL | &x[index..]; | ^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | &x[index..]; = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]` error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:117:6 + --> tests/ui/indexing_slicing_slice.rs:116:6 | LL | &x[..index]; | ^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | &x[..index]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:119:6 + --> tests/ui/indexing_slicing_slice.rs:118:6 | LL | &x[index_from..index_to]; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | &x[index_from..index_to]; = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:121:6 + --> tests/ui/indexing_slicing_slice.rs:120:6 | LL | &x[index_from..][..index_to]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | &x[index_from..][..index_to]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:121:6 + --> tests/ui/indexing_slicing_slice.rs:120:6 | LL | &x[index_from..][..index_to]; | ^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | &x[index_from..][..index_to]; = help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:124:6 + --> tests/ui/indexing_slicing_slice.rs:123:6 | LL | &x[5..][..10]; | ^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | &x[5..][..10]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:124:8 + --> tests/ui/indexing_slicing_slice.rs:123:8 | LL | &x[5..][..10]; | ^ @@ -58,7 +58,7 @@ LL | &x[5..][..10]; = help: to override `-D warnings` add `#[allow(clippy::out_of_bounds_indexing)]` error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:127:6 + --> tests/ui/indexing_slicing_slice.rs:126:6 | LL | &x[0..][..3]; | ^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | &x[0..][..3]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:129:6 + --> tests/ui/indexing_slicing_slice.rs:128:6 | LL | &x[1..][..5]; | ^^^^^^^^^^^ @@ -74,19 +74,19 @@ LL | &x[1..][..5]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:137:12 + --> tests/ui/indexing_slicing_slice.rs:136:12 | LL | &y[0..=4]; | ^ error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:139:11 + --> tests/ui/indexing_slicing_slice.rs:138:11 | LL | &y[..=4]; | ^ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:145:6 + --> tests/ui/indexing_slicing_slice.rs:144:6 | LL | &v[10..100]; | ^^^^^^^^^^ @@ -94,7 +94,7 @@ LL | &v[10..100]; = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:147:6 + --> tests/ui/indexing_slicing_slice.rs:146:6 | LL | &x[10..][..100]; | ^^^^^^^^^^^^^^ @@ -102,13 +102,13 @@ LL | &x[10..][..100]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:147:8 + --> tests/ui/indexing_slicing_slice.rs:146:8 | LL | &x[10..][..100]; | ^^ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:150:6 + --> tests/ui/indexing_slicing_slice.rs:149:6 | LL | &v[10..]; | ^^^^^^^ @@ -116,7 +116,7 @@ LL | &v[10..]; = help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:152:6 + --> tests/ui/indexing_slicing_slice.rs:151:6 | LL | &v[..100]; | ^^^^^^^^ @@ -124,7 +124,7 @@ LL | &v[..100]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_slice.rs:170:5 + --> tests/ui/indexing_slicing_slice.rs:169:5 | LL | map_with_get[true]; | ^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | map_with_get[true]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_slice.rs:174:5 + --> tests/ui/indexing_slicing_slice.rs:173:5 | LL | s[0]; | ^^^^ @@ -140,7 +140,7 @@ LL | s[0]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_slice.rs:178:5 + --> tests/ui/indexing_slicing_slice.rs:177:5 | LL | y[0]; | ^^^^ diff --git a/tests/ui/infallible_try_from.stderr b/tests/ui/infallible_try_from.stderr index 705b1188489c..d1e0d9e7d3bb 100644 --- a/tests/ui/infallible_try_from.stderr +++ b/tests/ui/infallible_try_from.stderr @@ -1,4 +1,4 @@ -error: infallible TryFrom impl; consider implementing From, instead +error: infallible TryFrom impl; consider implementing From instead --> tests/ui/infallible_try_from.rs:8:1 | LL | impl TryFrom for MyStruct { @@ -10,7 +10,7 @@ LL | type Error = !; = note: `-D clippy::infallible-try-from` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::infallible_try_from)]` -error: infallible TryFrom impl; consider implementing From, instead +error: infallible TryFrom impl; consider implementing From instead --> tests/ui/infallible_try_from.rs:16:1 | LL | impl TryFrom for MyStruct { diff --git a/tests/ui/ip_constant.fixed b/tests/ui/ip_constant.fixed index 2e3389c11938..c94796821394 100644 --- a/tests/ui/ip_constant.fixed +++ b/tests/ui/ip_constant.fixed @@ -48,6 +48,20 @@ fn literal_test3() { //~^ ip_constant } +fn wrapped_in_parens() { + let _ = std::net::Ipv4Addr::LOCALHOST; + //~^ ip_constant + let _ = std::net::Ipv4Addr::BROADCAST; + //~^ ip_constant + let _ = std::net::Ipv4Addr::UNSPECIFIED; + //~^ ip_constant + + let _ = std::net::Ipv6Addr::LOCALHOST; + //~^ ip_constant + let _ = std::net::Ipv6Addr::UNSPECIFIED; + //~^ ip_constant +} + const CONST_U8_0: u8 = 0; const CONST_U8_1: u8 = 1; const CONST_U8_127: u8 = 127; diff --git a/tests/ui/ip_constant.rs b/tests/ui/ip_constant.rs index 15e0b0551bab..69a5c3b4e923 100644 --- a/tests/ui/ip_constant.rs +++ b/tests/ui/ip_constant.rs @@ -48,6 +48,20 @@ fn literal_test3() { //~^ ip_constant } +fn wrapped_in_parens() { + let _ = (std::net::Ipv4Addr::new(127, 0, 0, 1)); + //~^ ip_constant + let _ = (std::net::Ipv4Addr::new(255, 255, 255, 255)); + //~^ ip_constant + let _ = (std::net::Ipv4Addr::new(0, 0, 0, 0)); + //~^ ip_constant + + let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + //~^ ip_constant + let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); + //~^ ip_constant +} + const CONST_U8_0: u8 = 0; const CONST_U8_1: u8 = 1; const CONST_U8_127: u8 = 127; diff --git a/tests/ui/ip_constant.stderr b/tests/ui/ip_constant.stderr index 3e984c6cb3bb..07d912b18a57 100644 --- a/tests/ui/ip_constant.stderr +++ b/tests/ui/ip_constant.stderr @@ -180,9 +180,69 @@ LL - let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); LL + let _ = std::net::Ipv6Addr::UNSPECIFIED; | +error: hand-coded well-known IP address + --> tests/ui/ip_constant.rs:52:13 + | +LL | let _ = (std::net::Ipv4Addr::new(127, 0, 0, 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - let _ = (std::net::Ipv4Addr::new(127, 0, 0, 1)); +LL + let _ = std::net::Ipv4Addr::LOCALHOST; + | + +error: hand-coded well-known IP address + --> tests/ui/ip_constant.rs:54:13 + | +LL | let _ = (std::net::Ipv4Addr::new(255, 255, 255, 255)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - let _ = (std::net::Ipv4Addr::new(255, 255, 255, 255)); +LL + let _ = std::net::Ipv4Addr::BROADCAST; + | + +error: hand-coded well-known IP address + --> tests/ui/ip_constant.rs:56:13 + | +LL | let _ = (std::net::Ipv4Addr::new(0, 0, 0, 0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - let _ = (std::net::Ipv4Addr::new(0, 0, 0, 0)); +LL + let _ = std::net::Ipv4Addr::UNSPECIFIED; + | + +error: hand-coded well-known IP address + --> tests/ui/ip_constant.rs:59:13 + | +LL | let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); +LL + let _ = std::net::Ipv6Addr::LOCALHOST; + | + error: hand-coded well-known IP address --> tests/ui/ip_constant.rs:61:13 | +LL | let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); +LL + let _ = std::net::Ipv6Addr::UNSPECIFIED; + | + +error: hand-coded well-known IP address + --> tests/ui/ip_constant.rs:75:13 + | LL | let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | @@ -193,7 +253,7 @@ LL + let _ = Ipv4Addr::LOCALHOST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:63:13 + --> tests/ui/ip_constant.rs:77:13 | LL | let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -205,7 +265,7 @@ LL + let _ = Ipv4Addr::BROADCAST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:65:13 + --> tests/ui/ip_constant.rs:79:13 | LL | let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +277,7 @@ LL + let _ = Ipv4Addr::UNSPECIFIED; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:69:13 + --> tests/ui/ip_constant.rs:83:13 | LL | let _ = Ipv6Addr::new( | _____________^ @@ -246,7 +306,7 @@ LL + let _ = Ipv6Addr::LOCALHOST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:81:13 + --> tests/ui/ip_constant.rs:95:13 | LL | let _ = Ipv6Addr::new( | _____________^ @@ -275,7 +335,7 @@ LL + let _ = Ipv6Addr::UNSPECIFIED; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:96:13 + --> tests/ui/ip_constant.rs:110:13 | LL | let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -287,7 +347,7 @@ LL + let _ = Ipv4Addr::LOCALHOST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:98:13 + --> tests/ui/ip_constant.rs:112:13 | LL | let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -299,7 +359,7 @@ LL + let _ = Ipv4Addr::BROADCAST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:100:13 + --> tests/ui/ip_constant.rs:114:13 | LL | let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -311,7 +371,7 @@ LL + let _ = Ipv4Addr::UNSPECIFIED; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:104:13 + --> tests/ui/ip_constant.rs:118:13 | LL | let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -323,7 +383,7 @@ LL + let _ = Ipv6Addr::LOCALHOST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:106:13 + --> tests/ui/ip_constant.rs:120:13 | LL | let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -334,5 +394,5 @@ LL - let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1) LL + let _ = Ipv6Addr::LOCALHOST; | -error: aborting due to 25 previous errors +error: aborting due to 30 previous errors diff --git a/tests/ui/iter_on_single_items.fixed b/tests/ui/iter_on_single_items.fixed index b43fad6449c1..044037aac2e3 100644 --- a/tests/ui/iter_on_single_items.fixed +++ b/tests/ui/iter_on_single_items.fixed @@ -66,3 +66,27 @@ fn main() { custom_option::custom_option(); in_macros!(); } + +mod issue14981 { + use std::option::IntoIter; + fn takes_into_iter(_: impl IntoIterator) {} + + fn let_stmt() { + macro_rules! x { + ($e:expr) => { + let _: IntoIter = $e; + }; + } + x!(Some(5).into_iter()); + } + + fn fn_ptr() { + fn some_func(_: IntoIter) -> IntoIter { + todo!() + } + some_func(Some(5).into_iter()); + + const C: fn(IntoIter) -> IntoIter = as IntoIterator>::into_iter; + C(Some(5).into_iter()); + } +} diff --git a/tests/ui/iter_on_single_items.rs b/tests/ui/iter_on_single_items.rs index 625c96d3ef1f..c925d0e480fa 100644 --- a/tests/ui/iter_on_single_items.rs +++ b/tests/ui/iter_on_single_items.rs @@ -66,3 +66,27 @@ fn main() { custom_option::custom_option(); in_macros!(); } + +mod issue14981 { + use std::option::IntoIter; + fn takes_into_iter(_: impl IntoIterator) {} + + fn let_stmt() { + macro_rules! x { + ($e:expr) => { + let _: IntoIter = $e; + }; + } + x!(Some(5).into_iter()); + } + + fn fn_ptr() { + fn some_func(_: IntoIter) -> IntoIter { + todo!() + } + some_func(Some(5).into_iter()); + + const C: fn(IntoIter) -> IntoIter = as IntoIterator>::into_iter; + C(Some(5).into_iter()); + } +} diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 304eacecd942..381d4cac4622 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -198,3 +198,14 @@ pub fn issue12594() { returns_result(res).unwrap(); } } + +fn issue15061() { + fn return_unit() {} + fn do_something(x: ()) {} + + let res = (); + return_unit(); + //~^ let_unit_value + do_something(()); + println!("{res:?}"); +} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index a02cb346ff99..cdfc74991c40 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -198,3 +198,13 @@ pub fn issue12594() { returns_result(res).unwrap(); } } + +fn issue15061() { + fn return_unit() {} + fn do_something(x: ()) {} + + let res = return_unit(); + //~^ let_unit_value + do_something(res); + println!("{res:?}"); +} diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index d743110c99dd..637c9ff686bd 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -68,5 +68,19 @@ LL ~ returns_result(()).unwrap(); LL ~ returns_result(()).unwrap(); | -error: aborting due to 4 previous errors +error: this let-binding has unit value + --> tests/ui/let_unit.rs:206:5 + | +LL | let res = return_unit(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace variable usages with `()` + | +LL ~ let res = (); +LL ~ return_unit(); +LL | +LL ~ do_something(()); + | + +error: aborting due to 5 previous errors diff --git a/tests/ui/let_with_type_underscore.fixed b/tests/ui/let_with_type_underscore.fixed index 7a4af4e3d1e7..6339fe595c21 100644 --- a/tests/ui/let_with_type_underscore.fixed +++ b/tests/ui/let_with_type_underscore.fixed @@ -45,3 +45,15 @@ fn main() { x = (); }; } + +fn issue15377() { + let (a) = 0; + //~^ let_with_type_underscore + let ((a)) = 0; + //~^ let_with_type_underscore + let ((a,)) = (0,); + //~^ let_with_type_underscore + #[rustfmt::skip] + let ( (a ) ) = 0; + //~^ let_with_type_underscore +} diff --git a/tests/ui/let_with_type_underscore.rs b/tests/ui/let_with_type_underscore.rs index a7c2f598b56d..bd85346cf01a 100644 --- a/tests/ui/let_with_type_underscore.rs +++ b/tests/ui/let_with_type_underscore.rs @@ -45,3 +45,15 @@ fn main() { x = (); }; } + +fn issue15377() { + let (a): _ = 0; + //~^ let_with_type_underscore + let ((a)): _ = 0; + //~^ let_with_type_underscore + let ((a,)): _ = (0,); + //~^ let_with_type_underscore + #[rustfmt::skip] + let ( (a ) ): _ = 0; + //~^ let_with_type_underscore +} diff --git a/tests/ui/let_with_type_underscore.stderr b/tests/ui/let_with_type_underscore.stderr index 9179f9922071..c3f6c82397b8 100644 --- a/tests/ui/let_with_type_underscore.stderr +++ b/tests/ui/let_with_type_underscore.stderr @@ -60,5 +60,53 @@ LL - let x : _ = 1; LL + let x = 1; | -error: aborting due to 5 previous errors +error: variable declared with type underscore + --> tests/ui/let_with_type_underscore.rs:50:5 + | +LL | let (a): _ = 0; + | ^^^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + | +LL - let (a): _ = 0; +LL + let (a) = 0; + | + +error: variable declared with type underscore + --> tests/ui/let_with_type_underscore.rs:52:5 + | +LL | let ((a)): _ = 0; + | ^^^^^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + | +LL - let ((a)): _ = 0; +LL + let ((a)) = 0; + | + +error: variable declared with type underscore + --> tests/ui/let_with_type_underscore.rs:54:5 + | +LL | let ((a,)): _ = (0,); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + | +LL - let ((a,)): _ = (0,); +LL + let ((a,)) = (0,); + | + +error: variable declared with type underscore + --> tests/ui/let_with_type_underscore.rs:57:5 + | +LL | let ( (a ) ): _ = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + | +LL - let ( (a ) ): _ = 0; +LL + let ( (a ) ) = 0; + | + +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 221cddf069db..2e9c9045caae 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -167,7 +167,7 @@ LL - comment */ LL - /// Doc comment LL - panic!("panic with comment") // comment after `panic!` LL - } -LL + assert!(!(a > 2), "panic with comment"); +LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement @@ -186,7 +186,7 @@ LL - const BAR: () = if N == 0 { LL - LL - panic!() LL - }; -LL + const BAR: () = assert!(!(N == 0), ); +LL + const BAR: () = assert!(N != 0, ); | error: only a `panic!` in `if`-then statement diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 221cddf069db..2e9c9045caae 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -167,7 +167,7 @@ LL - comment */ LL - /// Doc comment LL - panic!("panic with comment") // comment after `panic!` LL - } -LL + assert!(!(a > 2), "panic with comment"); +LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement @@ -186,7 +186,7 @@ LL - const BAR: () = if N == 0 { LL - LL - panic!() LL - }; -LL + const BAR: () = assert!(!(N == 0), ); +LL + const BAR: () = assert!(N != 0, ); | error: only a `panic!` in `if`-then statement diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index 086b75a39875..0fdaa1c045e0 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -75,7 +75,7 @@ fn main() { s4[2..].to_string(); } - // Don't propose to reuse the `stripped` identifier as it is overriden + // Don't propose to reuse the `stripped` identifier as it is overridden if s.starts_with("ab") { let stripped = &s["ab".len()..]; //~^ ERROR: stripping a prefix manually diff --git a/tests/ui/manual_unwrap_or_default.fixed b/tests/ui/manual_unwrap_or_default.fixed index 41ca44ceef4e..189fe876aa5d 100644 --- a/tests/ui/manual_unwrap_or_default.fixed +++ b/tests/ui/manual_unwrap_or_default.fixed @@ -102,7 +102,7 @@ fn issue_12928() { let y = if let Some(Y(a, ..)) = x { a } else { 0 }; } -// For symetry with `manual_unwrap_or` test +// For symmetry with `manual_unwrap_or` test fn allowed_manual_unwrap_or_zero() -> u32 { Some(42).unwrap_or_default() } diff --git a/tests/ui/manual_unwrap_or_default.rs b/tests/ui/manual_unwrap_or_default.rs index 343fbc4879ce..ca87926763c9 100644 --- a/tests/ui/manual_unwrap_or_default.rs +++ b/tests/ui/manual_unwrap_or_default.rs @@ -138,7 +138,7 @@ fn issue_12928() { let y = if let Some(Y(a, ..)) = x { a } else { 0 }; } -// For symetry with `manual_unwrap_or` test +// For symmetry with `manual_unwrap_or` test fn allowed_manual_unwrap_or_zero() -> u32 { if let Some(x) = Some(42) { //~^ manual_unwrap_or_default diff --git a/tests/ui/map_identity.fixed b/tests/ui/map_identity.fixed index b82d3e6d9567..6c971ba63384 100644 --- a/tests/ui/map_identity.fixed +++ b/tests/ui/map_identity.fixed @@ -1,5 +1,5 @@ #![warn(clippy::map_identity)] -#![allow(clippy::needless_return)] +#![allow(clippy::needless_return, clippy::disallowed_names)] fn main() { let x: [u16; 3] = [1, 2, 3]; @@ -99,3 +99,65 @@ fn issue15198() { let _ = x.iter().copied(); //~^ map_identity } + +mod foo { + #[derive(Clone, Copy)] + pub struct Foo { + pub foo: u8, + pub bar: u8, + } + + #[derive(Clone, Copy)] + pub struct Foo2(pub u8, pub u8); +} +use foo::{Foo, Foo2}; + +struct Bar { + foo: u8, + bar: u8, +} + +struct Bar2(u8, u8); + +fn structs() { + let x = [Foo { foo: 0, bar: 0 }]; + + let _ = x.into_iter(); + //~^ map_identity + + // still lint when different paths are used for the same struct + let _ = x.into_iter(); + //~^ map_identity + + // don't lint: same fields but different structs + let _ = x.into_iter().map(|Foo { foo, bar }| Bar { foo, bar }); + + // still lint with redundant field names + #[allow(clippy::redundant_field_names)] + let _ = x.into_iter(); + //~^ map_identity + + // still lint with field order change + let _ = x.into_iter(); + //~^ map_identity + + // don't lint: switched field assignment + let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: bar, bar: foo }); +} + +fn tuple_structs() { + let x = [Foo2(0, 0)]; + + let _ = x.into_iter(); + //~^ map_identity + + // still lint when different paths are used for the same struct + let _ = x.into_iter(); + //~^ map_identity + + // don't lint: same fields but different structs + let _ = x.into_iter().map(|Foo2(foo, bar)| Bar2(foo, bar)); + + // don't lint: switched field assignment + let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(bar, foo)); +} diff --git a/tests/ui/map_identity.rs b/tests/ui/map_identity.rs index c295bf872701..59dcfcda3b6e 100644 --- a/tests/ui/map_identity.rs +++ b/tests/ui/map_identity.rs @@ -1,5 +1,5 @@ #![warn(clippy::map_identity)] -#![allow(clippy::needless_return)] +#![allow(clippy::needless_return, clippy::disallowed_names)] fn main() { let x: [u16; 3] = [1, 2, 3]; @@ -105,3 +105,65 @@ fn issue15198() { let _ = x.iter().copied().map(|[x, y]| [x, y]); //~^ map_identity } + +mod foo { + #[derive(Clone, Copy)] + pub struct Foo { + pub foo: u8, + pub bar: u8, + } + + #[derive(Clone, Copy)] + pub struct Foo2(pub u8, pub u8); +} +use foo::{Foo, Foo2}; + +struct Bar { + foo: u8, + bar: u8, +} + +struct Bar2(u8, u8); + +fn structs() { + let x = [Foo { foo: 0, bar: 0 }]; + + let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo, bar }); + //~^ map_identity + + // still lint when different paths are used for the same struct + let _ = x.into_iter().map(|Foo { foo, bar }| foo::Foo { foo, bar }); + //~^ map_identity + + // don't lint: same fields but different structs + let _ = x.into_iter().map(|Foo { foo, bar }| Bar { foo, bar }); + + // still lint with redundant field names + #[allow(clippy::redundant_field_names)] + let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: foo, bar: bar }); + //~^ map_identity + + // still lint with field order change + let _ = x.into_iter().map(|Foo { foo, bar }| Foo { bar, foo }); + //~^ map_identity + + // don't lint: switched field assignment + let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: bar, bar: foo }); +} + +fn tuple_structs() { + let x = [Foo2(0, 0)]; + + let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(foo, bar)); + //~^ map_identity + + // still lint when different paths are used for the same struct + let _ = x.into_iter().map(|Foo2(foo, bar)| foo::Foo2(foo, bar)); + //~^ map_identity + + // don't lint: same fields but different structs + let _ = x.into_iter().map(|Foo2(foo, bar)| Bar2(foo, bar)); + + // don't lint: switched field assignment + let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(bar, foo)); +} diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr index 9b624a0dc755..a50c0d6b87b5 100644 --- a/tests/ui/map_identity.stderr +++ b/tests/ui/map_identity.stderr @@ -93,5 +93,41 @@ error: unnecessary map of the identity function LL | let _ = x.iter().copied().map(|[x, y]| [x, y]); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` -error: aborting due to 14 previous errors +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:131:26 + | +LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo, bar }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:135:26 + | +LL | let _ = x.into_iter().map(|Foo { foo, bar }| foo::Foo { foo, bar }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:143:26 + | +LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: foo, bar: bar }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:147:26 + | +LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { bar, foo }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:157:26 + | +LL | let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(foo, bar)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:161:26 + | +LL | let _ = x.into_iter().map(|Foo2(foo, bar)| foo::Foo2(foo, bar)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: aborting due to 20 previous errors diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index e11dea352049..e29fb87dbc30 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -204,3 +204,65 @@ mod issue14991 { }], } } + +mod issue15018 { + fn used_later(a: i32, b: i32, c: i32) { + let x = 1; + { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + } + println!("x = {x}"); + } + + fn not_used_later(a: i32, b: i32, c: i32) { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z) + } + + #[allow(irrefutable_let_patterns)] + fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + let x = 1; + println!("x = {x}"); + } + + #[allow(irrefutable_let_patterns)] + fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + if let (x, y, z) = (a, b, c) { + println!("{} {} {}", x, y, z) + } + + { + let x: i32 = 1; + { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + } + if let (x, y, z) = (a, x, c) { + println!("{} {} {}", x, y, z) + } + } + + { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + let fn_ = |y| { + println!("{} {} {}", a, b, y); + }; + fn_(c); + } + } +} + +#[allow(clippy::short_circuit_statement)] +fn issue15269(a: usize, b: usize, c: usize) -> bool { + a < b + && b < c; + + a < b + && b < c +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index d498da30fc87..ede1ab32beb5 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -267,3 +267,79 @@ mod issue14991 { }], } } + +mod issue15018 { + fn used_later(a: i32, b: i32, c: i32) { + let x = 1; + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + println!("x = {x}"); + } + + fn not_used_later(a: i32, b: i32, c: i32) { + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + } + + #[allow(irrefutable_let_patterns)] + fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + let x = 1; + println!("x = {x}"); + } + + #[allow(irrefutable_let_patterns)] + fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + if let (x, y, z) = (a, b, c) { + println!("{} {} {}", x, y, z) + } + + { + let x: i32 = 1; + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + if let (x, y, z) = (a, x, c) { + println!("{} {} {}", x, y, z) + } + } + + { + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + let fn_ = |y| { + println!("{} {} {}", a, b, y); + }; + fn_(c); + } + } +} + +#[allow(clippy::short_circuit_statement)] +fn issue15269(a: usize, b: usize, c: usize) -> bool { + a < b + && match b { + //~^ match_single_binding + b => b < c, + }; + + a < b + && match (a, b) { + //~^ match_single_binding + (a, b) => b < c, + } +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index f274f80c81da..eea71777890e 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -411,5 +411,119 @@ LL ~ let _n = 1; LL + 42 | -error: aborting due to 29 previous errors +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:274:9 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_________^ + | +help: consider using a `let` statement + | +LL ~ { +LL + let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z); +LL + } + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:282:9 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_________^ + | +help: consider using a `let` statement + | +LL ~ let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z) + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:290:9 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_________^ + | +help: consider using a `let` statement + | +LL ~ let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z); + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:300:9 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_________^ + | +help: consider using a `let` statement + | +LL ~ let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z); + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:310:13 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_____________^ + | +help: consider using a `let` statement + | +LL ~ { +LL + let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z); +LL + } + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:320:13 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_____________^ + | +help: consider using a `let` statement + | +LL ~ let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z); + | + +error: this match could be replaced by its body itself + --> tests/ui/match_single_binding.rs:335:12 + | +LL | && match b { + | ____________^ +LL | | +LL | | b => b < c, +LL | | }; + | |_________^ help: consider using the match body instead: `b < c` + +error: this match could be replaced by its body itself + --> tests/ui/match_single_binding.rs:341:12 + | +LL | && match (a, b) { + | ____________^ +LL | | +LL | | (a, b) => b < c, +LL | | } + | |_________^ help: consider using the match body instead: `b < c` + +error: aborting due to 37 previous errors diff --git a/tests/ui/min_ident_chars.rs b/tests/ui/min_ident_chars.rs index f473ac848a8e..e2f82e2a182f 100644 --- a/tests/ui/min_ident_chars.rs +++ b/tests/ui/min_ident_chars.rs @@ -124,3 +124,52 @@ fn wrong_pythagoras(a: f32, b: f32) -> f32 { mod issue_11163 { struct Array([T; N]); } + +struct Issue13396; + +impl core::fmt::Display for Issue13396 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Issue13396") + } +} + +impl core::fmt::Debug for Issue13396 { + fn fmt(&self, g: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + //~^ min_ident_chars + write!(g, "Issue13396") + } +} + +fn issue13396() { + let a = |f: i8| f; + //~^ min_ident_chars + //~| min_ident_chars +} + +trait D { + //~^ min_ident_chars + fn f(g: i32); + //~^ min_ident_chars + //~| min_ident_chars + fn long(long: i32); + + fn g(arg: i8) { + //~^ min_ident_chars + fn c(d: u8) {} + //~^ min_ident_chars + //~| min_ident_chars + } +} + +impl D for Issue13396 { + fn f(g: i32) { + fn h() {} + //~^ min_ident_chars + fn inner(a: i32) {} + //~^ min_ident_chars + let a = |f: String| f; + //~^ min_ident_chars + //~| min_ident_chars + } + fn long(long: i32) {} +} diff --git a/tests/ui/min_ident_chars.stderr b/tests/ui/min_ident_chars.stderr index bd6c45cf648e..36bf89e79f00 100644 --- a/tests/ui/min_ident_chars.stderr +++ b/tests/ui/min_ident_chars.stderr @@ -193,5 +193,83 @@ error: this ident consists of a single char LL | fn wrong_pythagoras(a: f32, b: f32) -> f32 { | ^ -error: aborting due to 32 previous errors +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:137:19 + | +LL | fn fmt(&self, g: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:144:14 + | +LL | let a = |f: i8| f; + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:144:9 + | +LL | let a = |f: i8| f; + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:149:7 + | +LL | trait D { + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:151:10 + | +LL | fn f(g: i32); + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:151:8 + | +LL | fn f(g: i32); + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:156:8 + | +LL | fn g(arg: i8) { + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:158:12 + | +LL | fn c(d: u8) {} + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:158:14 + | +LL | fn c(d: u8) {} + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:166:12 + | +LL | fn h() {} + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:168:18 + | +LL | fn inner(a: i32) {} + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:170:18 + | +LL | let a = |f: String| f; + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:170:13 + | +LL | let a = |f: String| f; + | ^ + +error: aborting due to 45 previous errors diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 57d0f2b99480..fff6d2f34b8e 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,5 +1,4 @@ -#![allow(clippy::uninlined_format_args, clippy::useless_vec)] -#![allow(clippy::needless_if, clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::useless_vec, clippy::needless_if)] #![warn(clippy::needless_collect)] //@no-rustfix use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index c7bf1b14df80..24523c9f97b0 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:9:39 + --> tests/ui/needless_collect_indirect.rs:8:39 | LL | let indirect_iter = sample.iter().collect::>(); | ^^^^^^^ @@ -18,7 +18,7 @@ LL ~ sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:13:38 + --> tests/ui/needless_collect_indirect.rs:12:38 | LL | let indirect_len = sample.iter().collect::>(); | ^^^^^^^ @@ -35,7 +35,7 @@ LL ~ sample.iter().count(); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:17:40 + --> tests/ui/needless_collect_indirect.rs:16:40 | LL | let indirect_empty = sample.iter().collect::>(); | ^^^^^^^ @@ -52,7 +52,7 @@ LL ~ sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:21:43 + --> tests/ui/needless_collect_indirect.rs:20:43 | LL | let indirect_contains = sample.iter().collect::>(); | ^^^^^^^ @@ -69,7 +69,7 @@ LL ~ sample.iter().any(|x| x == &5); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:35:48 + --> tests/ui/needless_collect_indirect.rs:34:48 | LL | let non_copy_contains = sample.into_iter().collect::>(); | ^^^^^^^ @@ -86,7 +86,7 @@ LL ~ sample.into_iter().any(|x| x == a); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:66:51 + --> tests/ui/needless_collect_indirect.rs:65:51 | LL | let buffer: Vec<&str> = string.split('/').collect(); | ^^^^^^^ @@ -103,7 +103,7 @@ LL ~ string.split('/').count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:73:55 + --> tests/ui/needless_collect_indirect.rs:72:55 | LL | let indirect_len: VecDeque<_> = sample.iter().collect(); | ^^^^^^^ @@ -120,7 +120,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:80:57 + --> tests/ui/needless_collect_indirect.rs:79:57 | LL | let indirect_len: LinkedList<_> = sample.iter().collect(); | ^^^^^^^ @@ -137,7 +137,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:87:57 + --> tests/ui/needless_collect_indirect.rs:86:57 | LL | let indirect_len: BinaryHeap<_> = sample.iter().collect(); | ^^^^^^^ @@ -154,7 +154,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:149:59 + --> tests/ui/needless_collect_indirect.rs:148:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -172,7 +172,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == i); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:176:59 + --> tests/ui/needless_collect_indirect.rs:175:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -190,7 +190,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:207:63 + --> tests/ui/needless_collect_indirect.rs:206:63 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -208,7 +208,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:245:59 + --> tests/ui/needless_collect_indirect.rs:244:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -226,7 +226,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:272:26 + --> tests/ui/needless_collect_indirect.rs:271:26 | LL | let w = v.iter().collect::>(); | ^^^^^^^ @@ -244,7 +244,7 @@ LL ~ for _ in 0..v.iter().count() { | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:296:30 + --> tests/ui/needless_collect_indirect.rs:295:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ @@ -262,7 +262,7 @@ LL ~ while 1 == v.iter().count() { | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:320:30 + --> tests/ui/needless_collect_indirect.rs:319:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index 1eecc3dee3dc..cacce9a7d1cb 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -236,3 +236,21 @@ mod issue14404 { } } } + +fn dont_simplify_double_not_if_types_differ() { + struct S; + + impl std::ops::Not for S { + type Output = bool; + fn not(self) -> bool { + true + } + } + + // The lint must propose `if !!S`, not `if S`. + // FIXME: `bool_comparison` will propose to use `S == true` + // which is invalid. + if !S != true {} + //~^ nonminimal_bool + //~| bool_comparison +} diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr index ecb82a23da03..c20412974b20 100644 --- a/tests/ui/nonminimal_bool.stderr +++ b/tests/ui/nonminimal_bool.stderr @@ -179,7 +179,7 @@ error: inequality checks against true can be replaced by a negation --> tests/ui/nonminimal_bool.rs:186:8 | LL | if !b != true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!!b` + | ^^^^^^^^^^ help: try simplifying it as shown: `b` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:189:8 @@ -209,7 +209,7 @@ error: inequality checks against true can be replaced by a negation --> tests/ui/nonminimal_bool.rs:193:8 | LL | if true != !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!!b` + | ^^^^^^^^^^ help: try simplifying it as shown: `b` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:196:8 @@ -235,5 +235,17 @@ error: this boolean expression can be simplified LL | if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!matches!(ty, TyKind::Ref(_, _, _)) || is_mutable(&expr)` -error: aborting due to 31 previous errors +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool.rs:253:8 + | +LL | if !S != true {} + | ^^^^^^^^^^ help: try: `S == true` + +error: inequality checks against true can be replaced by a negation + --> tests/ui/nonminimal_bool.rs:253:8 + | +LL | if !S != true {} + | ^^^^^^^^^^ help: try simplifying it as shown: `!!S` + +error: aborting due to 33 previous errors diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index fe3ac9e8f92c..0f86de5646cd 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -302,3 +302,15 @@ mod issue11059 { if let Some(o) = o { o } else { &S } } } + +fn issue15379() { + let opt = Some(0usize); + let opt_raw_ptr = &opt as *const Option; + let _ = unsafe { (*opt_raw_ptr).map_or(1, |o| o) }; + //~^ option_if_let_else +} + +fn issue15002() { + let res: Result = Ok("_".to_string()); + let _ = res.map_or_else(|_| String::new(), |s| s.clone()); +} diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 5b7498bc8e23..7aabd778f87e 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -365,3 +365,19 @@ mod issue11059 { if let Some(o) = o { o } else { &S } } } + +fn issue15379() { + let opt = Some(0usize); + let opt_raw_ptr = &opt as *const Option; + let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; + //~^ option_if_let_else +} + +fn issue15002() { + let res: Result = Ok("_".to_string()); + let _ = match res { + //~^ option_if_let_else + Ok(s) => s.clone(), + Err(_) => String::new(), + }; +} diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 9eb41f81a539..2e2fe6f20492 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -334,5 +334,22 @@ error: use Option::map_or_else instead of an if let/else LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())` -error: aborting due to 25 previous errors +error: use Option::map_or instead of an if let/else + --> tests/ui/option_if_let_else.rs:372:22 + | +LL | let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*opt_raw_ptr).map_or(1, |o| o)` + +error: use Option::map_or_else instead of an if let/else + --> tests/ui/option_if_let_else.rs:378:13 + | +LL | let _ = match res { + | _____________^ +LL | | +LL | | Ok(s) => s.clone(), +LL | | Err(_) => String::new(), +LL | | }; + | |_____^ help: try: `res.map_or_else(|_| String::new(), |s| s.clone())` + +error: aborting due to 27 previous errors diff --git a/tests/ui/search_is_some.rs b/tests/ui/search_is_some.rs index 4143b8bfba58..802d27449abf 100644 --- a/tests/ui/search_is_some.rs +++ b/tests/ui/search_is_some.rs @@ -87,3 +87,18 @@ fn is_none() { let _ = (0..1).find(some_closure).is_none(); //~^ search_is_some } + +#[allow(clippy::match_like_matches_macro)] +fn issue15102() { + let values = [None, Some(3)]; + let has_even = values + //~^ search_is_some + .iter() + .find(|v| match v { + Some(x) if x % 2 == 0 => true, + _ => false, + }) + .is_some(); + + println!("{has_even}"); +} diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr index d9a43c8915e8..d5412f901110 100644 --- a/tests/ui/search_is_some.stderr +++ b/tests/ui/search_is_some.stderr @@ -90,5 +90,20 @@ error: called `is_none()` after searching an `Iterator` with `find` LL | let _ = (0..1).find(some_closure).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(some_closure)` -error: aborting due to 8 previous errors +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some.rs:94:20 + | +LL | let has_even = values + | ____________________^ +LL | | +LL | | .iter() +LL | | .find(|v| match v { +... | +LL | | }) +LL | | .is_some(); + | |__________________^ + | + = help: this is more succinctly expressed by calling `any()` + +error: aborting due to 9 previous errors diff --git a/tests/ui/search_is_some_fixable_some.fixed b/tests/ui/search_is_some_fixable_some.fixed index 42b39b33b575..c7a4422f3737 100644 --- a/tests/ui/search_is_some_fixable_some.fixed +++ b/tests/ui/search_is_some_fixable_some.fixed @@ -289,3 +289,10 @@ mod issue9120 { //~^ search_is_some } } + +fn issue15102() { + let values = [None, Some(3)]; + let has_even = values.iter().any(|v| matches!(&v, Some(x) if x % 2 == 0)); + //~^ search_is_some + println!("{has_even}"); +} diff --git a/tests/ui/search_is_some_fixable_some.rs b/tests/ui/search_is_some_fixable_some.rs index ca4f4d941cb2..d6b1c67c9718 100644 --- a/tests/ui/search_is_some_fixable_some.rs +++ b/tests/ui/search_is_some_fixable_some.rs @@ -297,3 +297,10 @@ mod issue9120 { //~^ search_is_some } } + +fn issue15102() { + let values = [None, Some(3)]; + let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some(); + //~^ search_is_some + println!("{has_even}"); +} diff --git a/tests/ui/search_is_some_fixable_some.stderr b/tests/ui/search_is_some_fixable_some.stderr index 8291f48d43c4..551a670d937f 100644 --- a/tests/ui/search_is_some_fixable_some.stderr +++ b/tests/ui/search_is_some_fixable_some.stderr @@ -289,5 +289,11 @@ error: called `is_some()` after searching an `Iterator` with `find` LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` -error: aborting due to 46 previous errors +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some.rs:303:34 + | +LL | let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| matches!(&v, Some(x) if x % 2 == 0))` + +error: aborting due to 47 previous errors diff --git a/tests/ui/string_to_string.rs b/tests/ui/string_to_string.rs deleted file mode 100644 index 7c5bd8a897ba..000000000000 --- a/tests/ui/string_to_string.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![warn(clippy::string_to_string)] -#![allow(clippy::redundant_clone, clippy::unnecessary_literal_unwrap)] - -fn main() { - let mut message = String::from("Hello"); - let mut v = message.to_string(); - //~^ string_to_string - - let variable1 = String::new(); - let v = &variable1; - let variable2 = Some(v); - let _ = variable2.map(|x| { - println!(); - x.to_string() - }); - //~^^ string_to_string - - let x = Some(String::new()); - let _ = x.unwrap_or_else(|| v.to_string()); - //~^ string_to_string -} diff --git a/tests/ui/string_to_string.stderr b/tests/ui/string_to_string.stderr deleted file mode 100644 index 99eea06f18eb..000000000000 --- a/tests/ui/string_to_string.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: `to_string()` called on a `String` - --> tests/ui/string_to_string.rs:6:17 - | -LL | let mut v = message.to_string(); - | ^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `.clone()` - = note: `-D clippy::string-to-string` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::string_to_string)]` - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string.rs:14:9 - | -LL | x.to_string() - | ^^^^^^^^^^^^^ - | - = help: consider using `.clone()` - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string.rs:19:33 - | -LL | let _ = x.unwrap_or_else(|| v.to_string()); - | ^^^^^^^^^^^^^ - | - = help: consider using `.clone()` - -error: aborting due to 3 previous errors - diff --git a/tests/ui/string_to_string_in_map.fixed b/tests/ui/string_to_string_in_map.fixed deleted file mode 100644 index efc085539f15..000000000000 --- a/tests/ui/string_to_string_in_map.fixed +++ /dev/null @@ -1,20 +0,0 @@ -#![deny(clippy::string_to_string)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::useless_vec, clippy::iter_cloned_collect)] - -fn main() { - let variable1 = String::new(); - let v = &variable1; - let variable2 = Some(v); - let _ = variable2.cloned(); - //~^ string_to_string - let _ = variable2.cloned(); - //~^ string_to_string - #[rustfmt::skip] - let _ = variable2.cloned(); - //~^ string_to_string - - let _ = vec![String::new()].iter().cloned().collect::>(); - //~^ string_to_string - let _ = vec![String::new()].iter().cloned().collect::>(); - //~^ string_to_string -} diff --git a/tests/ui/string_to_string_in_map.rs b/tests/ui/string_to_string_in_map.rs deleted file mode 100644 index 5bf1d7ba5a2e..000000000000 --- a/tests/ui/string_to_string_in_map.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![deny(clippy::string_to_string)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::useless_vec, clippy::iter_cloned_collect)] - -fn main() { - let variable1 = String::new(); - let v = &variable1; - let variable2 = Some(v); - let _ = variable2.map(String::to_string); - //~^ string_to_string - let _ = variable2.map(|x| x.to_string()); - //~^ string_to_string - #[rustfmt::skip] - let _ = variable2.map(|x| { x.to_string() }); - //~^ string_to_string - - let _ = vec![String::new()].iter().map(String::to_string).collect::>(); - //~^ string_to_string - let _ = vec![String::new()].iter().map(|x| x.to_string()).collect::>(); - //~^ string_to_string -} diff --git a/tests/ui/string_to_string_in_map.stderr b/tests/ui/string_to_string_in_map.stderr deleted file mode 100644 index 35aeed656eed..000000000000 --- a/tests/ui/string_to_string_in_map.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: `to_string()` called on a `String` - --> tests/ui/string_to_string_in_map.rs:8:23 - | -LL | let _ = variable2.map(String::to_string); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` - | -note: the lint level is defined here - --> tests/ui/string_to_string_in_map.rs:1:9 - | -LL | #![deny(clippy::string_to_string)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string_in_map.rs:10:23 - | -LL | let _ = variable2.map(|x| x.to_string()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string_in_map.rs:13:23 - | -LL | let _ = variable2.map(|x| { x.to_string() }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string_in_map.rs:16:40 - | -LL | let _ = vec![String::new()].iter().map(String::to_string).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string_in_map.rs:18:40 - | -LL | let _ = vec![String::new()].iter().map(|x| x.to_string()).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` - -error: aborting due to 5 previous errors - diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs index 28a3b551116d..072e7b27b0d0 100644 --- a/tests/ui/suspicious_else_formatting.rs +++ b/tests/ui/suspicious_else_formatting.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macro_suspicious_else_formatting.rs -#![warn(clippy::suspicious_else_formatting)] +#![warn(clippy::suspicious_else_formatting, clippy::possible_missing_else)] #![allow( clippy::if_same_then_else, clippy::let_unit_value, @@ -20,12 +20,12 @@ fn main() { // weird `else` formatting: if foo() { } { - //~^ suspicious_else_formatting + //~^ possible_missing_else } if foo() { } if foo() { - //~^ suspicious_else_formatting + //~^ possible_missing_else } let _ = { // if as the last expression @@ -33,7 +33,7 @@ fn main() { if foo() { } if foo() { - //~^ suspicious_else_formatting + //~^ possible_missing_else } else { } @@ -42,7 +42,7 @@ fn main() { let _ = { // if in the middle of a block if foo() { } if foo() { - //~^ suspicious_else_formatting + //~^ possible_missing_else } else { } diff --git a/tests/ui/suspicious_else_formatting.stderr b/tests/ui/suspicious_else_formatting.stderr index affd20b22d9c..04555c6edbd3 100644 --- a/tests/ui/suspicious_else_formatting.stderr +++ b/tests/ui/suspicious_else_formatting.stderr @@ -5,8 +5,8 @@ LL | } { | ^ | = note: to remove this lint, add the missing `else` or add a new line before the next block - = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::suspicious_else_formatting)]` + = note: `-D clippy::possible-missing-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::possible_missing_else)]` error: this looks like an `else if` but the `else` is missing --> tests/ui/suspicious_else_formatting.rs:27:6 @@ -41,6 +41,8 @@ LL | | { | |____^ | = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::suspicious_else_formatting)]` error: this is an `else if` but the formatting might hide it --> tests/ui/suspicious_else_formatting.rs:67:6 diff --git a/tests/ui/unnecessary_clippy_cfg.rs b/tests/ui/unnecessary_clippy_cfg.rs index e7e01248dfbe..65f67df79131 100644 --- a/tests/ui/unnecessary_clippy_cfg.rs +++ b/tests/ui/unnecessary_clippy_cfg.rs @@ -1,5 +1,6 @@ //@no-rustfix +#![allow(clippy::duplicated_attributes)] #![warn(clippy::unnecessary_clippy_cfg)] #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ unnecessary_clippy_cfg @@ -7,7 +8,6 @@ //~^ unnecessary_clippy_cfg #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ unnecessary_clippy_cfg -//~| duplicated_attributes #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ unnecessary_clippy_cfg @@ -17,7 +17,6 @@ //~^ unnecessary_clippy_cfg #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ unnecessary_clippy_cfg -//~| duplicated_attributes #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ unnecessary_clippy_cfg diff --git a/tests/ui/unnecessary_clippy_cfg.stderr b/tests/ui/unnecessary_clippy_cfg.stderr index f66c68949548..4f638d5c513c 100644 --- a/tests/ui/unnecessary_clippy_cfg.stderr +++ b/tests/ui/unnecessary_clippy_cfg.stderr @@ -1,5 +1,5 @@ error: no need to put clippy lints behind a `clippy` cfg - --> tests/ui/unnecessary_clippy_cfg.rs:4:1 + --> tests/ui/unnecessary_clippy_cfg.rs:5:1 | LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#![deny(clippy::non_minimal_cfg)]` @@ -8,7 +8,7 @@ LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] = help: to override `-D warnings` add `#[allow(clippy::unnecessary_clippy_cfg)]` error: no need to put clippy lints behind a `clippy` cfg - --> tests/ui/unnecessary_clippy_cfg.rs:6:37 + --> tests/ui/unnecessary_clippy_cfg.rs:7:37 | LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] = note: write instead: `#![deny(clippy::non_minimal_cfg)]` error: no need to put clippy lints behind a `clippy` cfg - --> tests/ui/unnecessary_clippy_cfg.rs:8:37 + --> tests/ui/unnecessary_clippy_cfg.rs:9:37 | LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,46 +52,10 @@ LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] = note: write instead: `#[deny(clippy::non_minimal_cfg)]` error: no need to put clippy lints behind a `clippy` cfg - --> tests/ui/unnecessary_clippy_cfg.rs:21:1 + --> tests/ui/unnecessary_clippy_cfg.rs:20:1 | LL | #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#[deny(clippy::non_minimal_cfg)]` -error: duplicated attribute - --> tests/ui/unnecessary_clippy_cfg.rs:8:26 - | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ - | -note: first defined here - --> tests/ui/unnecessary_clippy_cfg.rs:6:26 - | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ -help: remove this attribute - --> tests/ui/unnecessary_clippy_cfg.rs:8:26 - | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ - = note: `-D clippy::duplicated-attributes` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]` - -error: duplicated attribute - --> tests/ui/unnecessary_clippy_cfg.rs:18:25 - | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ - | -note: first defined here - --> tests/ui/unnecessary_clippy_cfg.rs:16:25 - | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ -help: remove this attribute - --> tests/ui/unnecessary_clippy_cfg.rs:18:25 - | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ - -error: aborting due to 10 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/unnecessary_map_or.fixed b/tests/ui/unnecessary_map_or.fixed index 3109c4af8e28..10552431d65d 100644 --- a/tests/ui/unnecessary_map_or.fixed +++ b/tests/ui/unnecessary_map_or.fixed @@ -131,6 +131,26 @@ fn issue14201(a: Option, b: Option, s: &String) -> bool { x && y } +fn issue14714() { + assert!(Some("test") == Some("test")); + //~^ unnecessary_map_or + + // even though we're in a macro context, we still need to parenthesise because of the `then` + assert!((Some("test") == Some("test")).then(|| 1).is_some()); + //~^ unnecessary_map_or + + // method lints don't fire on macros + macro_rules! m { + ($x:expr) => { + // should become !($x == Some(1)) + let _ = !$x.map_or(false, |v| v == 1); + // should become $x == Some(1) + let _ = $x.map_or(false, |v| v == 1); + }; + } + m!(Some(5)); +} + fn issue15180() { let s = std::sync::Mutex::new(Some("foo")); _ = s.lock().unwrap().is_some_and(|s| s == "foo"); diff --git a/tests/ui/unnecessary_map_or.rs b/tests/ui/unnecessary_map_or.rs index 52a55f9fc9e4..4b406ec2998b 100644 --- a/tests/ui/unnecessary_map_or.rs +++ b/tests/ui/unnecessary_map_or.rs @@ -135,6 +135,26 @@ fn issue14201(a: Option, b: Option, s: &String) -> bool { x && y } +fn issue14714() { + assert!(Some("test").map_or(false, |x| x == "test")); + //~^ unnecessary_map_or + + // even though we're in a macro context, we still need to parenthesise because of the `then` + assert!(Some("test").map_or(false, |x| x == "test").then(|| 1).is_some()); + //~^ unnecessary_map_or + + // method lints don't fire on macros + macro_rules! m { + ($x:expr) => { + // should become !($x == Some(1)) + let _ = !$x.map_or(false, |v| v == 1); + // should become $x == Some(1) + let _ = $x.map_or(false, |v| v == 1); + }; + } + m!(Some(5)); +} + fn issue15180() { let s = std::sync::Mutex::new(Some("foo")); _ = s.lock().unwrap().map_or(false, |s| s == "foo"); diff --git a/tests/ui/unnecessary_map_or.stderr b/tests/ui/unnecessary_map_or.stderr index 99e17e8b34ba..b8a22346c378 100644 --- a/tests/ui/unnecessary_map_or.stderr +++ b/tests/ui/unnecessary_map_or.stderr @@ -327,7 +327,31 @@ LL + let y = b.is_none_or(|b| b == *s); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:140:9 + --> tests/ui/unnecessary_map_or.rs:139:13 + | +LL | assert!(Some("test").map_or(false, |x| x == "test")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL - assert!(Some("test").map_or(false, |x| x == "test")); +LL + assert!(Some("test") == Some("test")); + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:143:13 + | +LL | assert!(Some("test").map_or(false, |x| x == "test").then(|| 1).is_some()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL - assert!(Some("test").map_or(false, |x| x == "test").then(|| 1).is_some()); +LL + assert!((Some("test") == Some("test")).then(|| 1).is_some()); + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:160:9 | LL | _ = s.lock().unwrap().map_or(false, |s| s == "foo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -339,7 +363,7 @@ LL + _ = s.lock().unwrap().is_some_and(|s| s == "foo"); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:144:9 + --> tests/ui/unnecessary_map_or.rs:164:9 | LL | _ = s.map_or(false, |s| s == "foo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -350,5 +374,5 @@ LL - _ = s.map_or(false, |s| s == "foo"); LL + _ = s.is_some_and(|s| s == "foo"); | -error: aborting due to 28 previous errors +error: aborting due to 30 previous errors diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 9a38d3746da8..a066554037fe 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -7,7 +7,7 @@ LL | vec.sort_by(|a, b| a.cmp(b)); = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_sort_by)]` -error: consider using `sort` +error: consider using `sort_unstable` --> tests/ui/unnecessary_sort_by.rs:14:5 | LL | vec.sort_unstable_by(|a, b| a.cmp(b)); @@ -19,7 +19,7 @@ error: consider using `sort_by_key` LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())` -error: consider using `sort_by_key` +error: consider using `sort_unstable_by_key` --> tests/ui/unnecessary_sort_by.rs:18:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); @@ -31,7 +31,7 @@ error: consider using `sort_by_key` LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs()))` -error: consider using `sort_by_key` +error: consider using `sort_unstable_by_key` --> tests/ui/unnecessary_sort_by.rs:24:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); @@ -43,7 +43,7 @@ error: consider using `sort_by_key` LL | vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())` -error: consider using `sort_by_key` +error: consider using `sort_unstable_by_key` --> tests/ui/unnecessary_sort_by.rs:37:5 | LL | vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); @@ -55,7 +55,7 @@ error: consider using `sort_by_key` LL | args.sort_by(|a, b| a.name().cmp(&b.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())` -error: consider using `sort_by_key` +error: consider using `sort_unstable_by_key` --> tests/ui/unnecessary_sort_by.rs:99:9 | LL | args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); @@ -67,7 +67,7 @@ error: consider using `sort_by_key` LL | args.sort_by(|a, b| b.name().cmp(&a.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| std::cmp::Reverse(b.name()))` -error: consider using `sort_by_key` +error: consider using `sort_unstable_by_key` --> tests/ui/unnecessary_sort_by.rs:104:9 | LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); diff --git a/triagebot.toml b/triagebot.toml index a62b6269a3bf..805baf2af6dd 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -54,7 +54,6 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB users_on_vacation = [ "matthiaskrgr", "Manishearth", - "samueltardieu", ] [assign.owners] diff --git a/util/gh-pages/index_template.html b/util/gh-pages/index_template.html index 5d65ea585df6..d34ff0a59732 100644 --- a/util/gh-pages/index_template.html +++ b/util/gh-pages/index_template.html @@ -14,7 +14,6 @@ Otherwise, have a great day =^.^= Clippy Lints {# #} - {# #} {# #} {# #} @@ -49,7 +48,7 @@ Otherwise, have a great day =^.^= {# #}
{# #} -

Clippy Lints

{# #} +

Clippy Lints Total number: {{+ count }}

{# #}