From b24b80c9cc72c6ede3392c87432327ed9d7d4f76 Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Sun, 14 Sep 2025 01:18:59 -0400 Subject: [PATCH 001/278] changed Location<'_> lifetime to 'static in Panic[Hook]Info --- library/core/src/panic/panic_info.rs | 6 +++--- library/std/src/panic.rs | 6 +++--- library/std/src/panicking.rs | 2 +- library/std/tests/panic.rs | 20 +++++++++++++++++++- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index 9d53567a26fd9..86e69d2d73379 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -13,7 +13,7 @@ use crate::panic::Location; #[derive(Debug)] pub struct PanicInfo<'a> { message: &'a fmt::Arguments<'a>, - location: &'a Location<'a>, + location: &'a Location<'static>, can_unwind: bool, force_no_backtrace: bool, } @@ -33,7 +33,7 @@ impl<'a> PanicInfo<'a> { #[inline] pub(crate) fn new( message: &'a fmt::Arguments<'a>, - location: &'a Location<'a>, + location: &'a Location<'static>, can_unwind: bool, force_no_backtrace: bool, ) -> Self { @@ -88,7 +88,7 @@ impl<'a> PanicInfo<'a> { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn location(&self) -> Option<&Location<'_>> { + pub fn location(&self) -> Option<&Location<'static>> { // NOTE: If this is changed to sometimes return None, // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. Some(&self.location) diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 5e8d2f8e78ec7..eb5155a1e5054 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -41,7 +41,7 @@ pub type PanicInfo<'a> = PanicHookInfo<'a>; #[derive(Debug)] pub struct PanicHookInfo<'a> { payload: &'a (dyn Any + Send), - location: &'a Location<'a>, + location: &'a Location<'static>, can_unwind: bool, force_no_backtrace: bool, } @@ -49,7 +49,7 @@ pub struct PanicHookInfo<'a> { impl<'a> PanicHookInfo<'a> { #[inline] pub(crate) fn new( - location: &'a Location<'a>, + location: &'a Location<'static>, payload: &'a (dyn Any + Send), can_unwind: bool, force_no_backtrace: bool, @@ -160,7 +160,7 @@ impl<'a> PanicHookInfo<'a> { #[must_use] #[inline] #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn location(&self) -> Option<&Location<'_>> { + pub fn location(&self) -> Option<&Location<'static>> { // NOTE: If this is changed to sometimes return None, // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. Some(&self.location) diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 8b7282c51d123..677144f4fff9c 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -795,7 +795,7 @@ fn payload_as_str(payload: &dyn Any) -> &str { #[optimize(size)] fn panic_with_hook( payload: &mut dyn PanicPayload, - location: &Location<'_>, + location: &Location<'static>, can_unwind: bool, force_no_backtrace: bool, ) -> ! { diff --git a/library/std/tests/panic.rs b/library/std/tests/panic.rs index f13b931dd222e..8f58ddd4a1609 100644 --- a/library/std/tests/panic.rs +++ b/library/std/tests/panic.rs @@ -1,7 +1,8 @@ #![allow(dead_code)] +use core::panic::PanicInfo; use std::cell::RefCell; -use std::panic::{AssertUnwindSafe, UnwindSafe}; +use std::panic::{AssertUnwindSafe, Location, PanicHookInfo, UnwindSafe}; use std::rc::Rc; use std::sync::{Arc, Mutex, RwLock}; @@ -54,3 +55,20 @@ fn panic_safety_traits() { assert::>>(); } } + +#[test] +fn panic_info_static_location<'x>() { + // Verify that the returned `Location<'_>`s generic lifetime is 'static when + // calling `PanicInfo::location`. Test failure is indicated by a compile + // failure, not a runtime panic. + let _: for<'a> fn(&'a PanicInfo<'x>) -> Option<&'a Location<'static>> = PanicInfo::location; +} + +#[test] +fn panic_hook_info_static_location<'x>() { + // Verify that the returned `Location<'_>`s generic lifetime is 'static when + // calling `PanicHookInfo::location`. Test failure is indicated by a compile + // failure, not a runtime panic. + let _: for<'a> fn(&'a PanicHookInfo<'x>) -> Option<&'a Location<'static>> = + PanicHookInfo::location; +} From e3b8df16fca8469bc3c9f190971cba744bc862f6 Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Tue, 16 Sep 2025 17:48:40 -0400 Subject: [PATCH 002/278] updated returned Location references to 'static --- library/core/src/panic/panic_info.rs | 8 ++++---- library/std/src/panic.rs | 8 ++++---- library/std/src/panicking.rs | 2 +- library/std/tests/panic.rs | 20 +------------------- 4 files changed, 10 insertions(+), 28 deletions(-) diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index 86e69d2d73379..ee22521cb014c 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -13,7 +13,7 @@ use crate::panic::Location; #[derive(Debug)] pub struct PanicInfo<'a> { message: &'a fmt::Arguments<'a>, - location: &'a Location<'static>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, } @@ -33,7 +33,7 @@ impl<'a> PanicInfo<'a> { #[inline] pub(crate) fn new( message: &'a fmt::Arguments<'a>, - location: &'a Location<'static>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, ) -> Self { @@ -88,10 +88,10 @@ impl<'a> PanicInfo<'a> { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn location(&self) -> Option<&Location<'static>> { + pub fn location(&self) -> Option<&'static Location<'static>> { // NOTE: If this is changed to sometimes return None, // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. - Some(&self.location) + Some(self.location) } /// Returns the payload associated with the panic. diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index eb5155a1e5054..13be7371c2329 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -41,7 +41,7 @@ pub type PanicInfo<'a> = PanicHookInfo<'a>; #[derive(Debug)] pub struct PanicHookInfo<'a> { payload: &'a (dyn Any + Send), - location: &'a Location<'static>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, } @@ -49,7 +49,7 @@ pub struct PanicHookInfo<'a> { impl<'a> PanicHookInfo<'a> { #[inline] pub(crate) fn new( - location: &'a Location<'static>, + location: &'static Location<'static>, payload: &'a (dyn Any + Send), can_unwind: bool, force_no_backtrace: bool, @@ -160,10 +160,10 @@ impl<'a> PanicHookInfo<'a> { #[must_use] #[inline] #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn location(&self) -> Option<&Location<'static>> { + pub fn location(&self) -> Option<&'static Location<'static>> { // NOTE: If this is changed to sometimes return None, // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. - Some(&self.location) + Some(self.location) } /// Returns whether the panic handler is allowed to unwind the stack from diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 677144f4fff9c..e489d0f9f807e 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -795,7 +795,7 @@ fn payload_as_str(payload: &dyn Any) -> &str { #[optimize(size)] fn panic_with_hook( payload: &mut dyn PanicPayload, - location: &Location<'static>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, ) -> ! { diff --git a/library/std/tests/panic.rs b/library/std/tests/panic.rs index 8f58ddd4a1609..f13b931dd222e 100644 --- a/library/std/tests/panic.rs +++ b/library/std/tests/panic.rs @@ -1,8 +1,7 @@ #![allow(dead_code)] -use core::panic::PanicInfo; use std::cell::RefCell; -use std::panic::{AssertUnwindSafe, Location, PanicHookInfo, UnwindSafe}; +use std::panic::{AssertUnwindSafe, UnwindSafe}; use std::rc::Rc; use std::sync::{Arc, Mutex, RwLock}; @@ -55,20 +54,3 @@ fn panic_safety_traits() { assert::>>(); } } - -#[test] -fn panic_info_static_location<'x>() { - // Verify that the returned `Location<'_>`s generic lifetime is 'static when - // calling `PanicInfo::location`. Test failure is indicated by a compile - // failure, not a runtime panic. - let _: for<'a> fn(&'a PanicInfo<'x>) -> Option<&'a Location<'static>> = PanicInfo::location; -} - -#[test] -fn panic_hook_info_static_location<'x>() { - // Verify that the returned `Location<'_>`s generic lifetime is 'static when - // calling `PanicHookInfo::location`. Test failure is indicated by a compile - // failure, not a runtime panic. - let _: for<'a> fn(&'a PanicHookInfo<'x>) -> Option<&'a Location<'static>> = - PanicHookInfo::location; -} From 8c92dc84abd3756a6027238ca09a79e7b68b91fa Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 25 Feb 2026 09:20:07 -0800 Subject: [PATCH 003/278] Expand `OptionFlatten`'s iterator methods If someone considers moving from `option.into_iter().flatten()` to `option.into_flat_iter()`, it may be important for performance that this iterator "specializes" methods the same way `Flatten` does, especially forwarding to underlying `fold`, etc. --- library/core/src/option.rs | 100 +++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 3dc53941977cb..154149ffcb877 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -584,6 +584,7 @@ use crate::clone::TrivialClone; use crate::iter::{self, FusedIterator, TrustedLen}; use crate::marker::Destruct; +use crate::num::NonZero; use crate::ops::{self, ControlFlow, Deref, DerefMut, Residual, Try}; use crate::panicking::{panic, panic_display}; use crate::pin::Pin; @@ -2651,18 +2652,111 @@ impl Iterator for OptionFlatten { type Item = A::Item; fn next(&mut self) -> Option { - self.iter.as_mut()?.next() + match &mut self.iter { + Some(iter) => iter.next(), + None => None, + } } fn size_hint(&self) -> (usize, Option) { - self.iter.as_ref().map(|i| i.size_hint()).unwrap_or((0, Some(0))) + match &self.iter { + Some(iter) => iter.size_hint(), + None => (0, Some(0)), + } + } + + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + match &mut self.iter { + Some(iter) => iter.advance_by(n), + None => NonZero::new(n).map_or(Ok(()), Err), + } + } + + fn nth(&mut self, n: usize) -> Option { + match &mut self.iter { + Some(iter) => iter.nth(n), + None => None, + } + } + + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + match self.iter { + Some(iter) => iter.fold(init, fold), + None => init, + } + } + + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + match &mut self.iter { + Some(iter) => iter.try_fold(init, fold), + None => try { init }, + } + } + + fn count(self) -> usize { + match self.iter { + Some(iter) => iter.count(), + None => 0, + } + } + + fn last(self) -> Option { + match self.iter { + Some(iter) => iter.last(), + None => None, + } } } #[unstable(feature = "option_into_flat_iter", issue = "148441")] impl DoubleEndedIterator for OptionFlatten { fn next_back(&mut self) -> Option { - self.iter.as_mut()?.next_back() + match &mut self.iter { + Some(iter) => iter.next_back(), + None => None, + } + } + + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + match &mut self.iter { + Some(iter) => iter.advance_back_by(n), + None => NonZero::new(n).map_or(Ok(()), Err), + } + } + + fn nth_back(&mut self, n: usize) -> Option { + match &mut self.iter { + Some(iter) => iter.nth_back(n), + None => None, + } + } + + fn rfold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + match self.iter { + Some(iter) => iter.rfold(init, fold), + None => init, + } + } + + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + match &mut self.iter { + Some(iter) => iter.try_rfold(init, fold), + None => try { init }, + } } } From 8ec8389fe42c0df711f3f297c9497c506fabb9cb Mon Sep 17 00:00:00 2001 From: laffed Date: Thu, 16 Apr 2026 14:01:01 -0500 Subject: [PATCH 004/278] fix(unnecessary_sort_by): use first closure param name in reverse sort suggestion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When suggesting `sort_by_key` for a reversed comparator like `|a, b| b.foo.cmp(&a.foo)`, the lint was picking `left_expr` (the receiver of `.cmp()`, which references `b`) and `r_pat` as the closure arg, producing `|b| Reverse(b.foo)`. Both sides consistently named `b`, but `b` is the second parameter — confusing and inconsistent with the forward-sort suggestion style. Fix by using `right_expr` (the `.cmp()` argument, which references `a`) as the key body and `l_pat` as the closure parameter, peeling the single `&` that `.cmp(&rhs)` introduces so the suggestion doesn't contain a spurious borrow. --- clippy_lints/src/methods/unnecessary_sort_by.rs | 10 +++++++++- tests/ui/unnecessary_sort_by.fixed | 8 ++++---- tests/ui/unnecessary_sort_by.stderr | 8 ++++---- tests/ui/unnecessary_sort_by_no_std.fixed | 4 ++-- tests/ui/unnecessary_sort_by_no_std.rs | 2 +- tests/ui/unnecessary_sort_by_no_std.stderr | 2 +- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index 3f81a6ecd2f8c..c8cfcaaa5aaee 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -257,7 +257,15 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) -> Option< if mirrored_exprs(left_expr, right_expr, &binding_map, BindingSource::Left) { (left_expr, l_pat.span, false) } else if mirrored_exprs(left_expr, right_expr, &binding_map, BindingSource::Right) { - (left_expr, r_pat.span, true) + // Use the right-hand expr (the `a` side) as the key body, peeling any `&` + // introduced by the `.cmp(&rhs)` call so the suggestion doesn't contain a + // spurious borrow. + let right_body = if let ExprKind::AddrOf(_, _, inner) = right_expr.kind { + inner + } else { + right_expr + }; + (right_body, l_pat.span, true) } else { return None; }; diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 317140eacc785..db31b339a08b0 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -19,9 +19,9 @@ fn unnecessary_sort_by() { //~^ unnecessary_sort_by // Reverse examples vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow - vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs())); + vec.sort_by_key(|a| std::cmp::Reverse((a + 5).abs())); //~^ unnecessary_sort_by - vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b))); + vec.sort_unstable_by_key(|a| std::cmp::Reverse(id(-a))); //~^ unnecessary_sort_by // Negative examples (shouldn't be changed) let c = &7; @@ -99,9 +99,9 @@ mod issue_6001 { args.sort_unstable_by_key(|a| a.name()); //~^ unnecessary_sort_by // Reverse - args.sort_by_key(|b| std::cmp::Reverse(b.name())); + args.sort_by_key(|a| std::cmp::Reverse(a.name())); //~^ unnecessary_sort_by - args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name())); + args.sort_unstable_by_key(|a| std::cmp::Reverse(a.name())); //~^ unnecessary_sort_by } } diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 56d4831eb70aa..868f7895fc069 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -57,7 +57,7 @@ LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); help: try | LL - vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); -LL + vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs())); +LL + vec.sort_by_key(|a| std::cmp::Reverse((a + 5).abs())); | error: consider using `sort_unstable_by_key` @@ -69,7 +69,7 @@ LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); help: try | LL - vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); -LL + vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b))); +LL + vec.sort_unstable_by_key(|a| std::cmp::Reverse(id(-a))); | error: consider using `sort_by_key` @@ -129,7 +129,7 @@ LL | args.sort_by(|a, b| b.name().cmp(&a.name())); help: try | LL - args.sort_by(|a, b| b.name().cmp(&a.name())); -LL + args.sort_by_key(|b| std::cmp::Reverse(b.name())); +LL + args.sort_by_key(|a| std::cmp::Reverse(a.name())); | error: consider using `sort_unstable_by_key` @@ -141,7 +141,7 @@ LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); help: try | LL - args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); -LL + args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name())); +LL + args.sort_unstable_by_key(|a| std::cmp::Reverse(a.name())); | error: consider using `sort_by_key` diff --git a/tests/ui/unnecessary_sort_by_no_std.fixed b/tests/ui/unnecessary_sort_by_no_std.fixed index 40e6aecd1b6d0..6fd740d87757a 100644 --- a/tests/ui/unnecessary_sort_by_no_std.fixed +++ b/tests/ui/unnecessary_sort_by_no_std.fixed @@ -15,8 +15,8 @@ fn issue_11524() -> Vec { fn issue_11524_2() -> Vec { let mut vec = vec![1, 2, 3]; - // Should lint and suggest `vec.sort_by_key(|b| core::cmp::Reverse(b + 1));` - vec.sort_by_key(|b| core::cmp::Reverse(b + 1)); + // Should lint and suggest `vec.sort_by_key(|a| core::cmp::Reverse(a + 1));` + vec.sort_by_key(|a| core::cmp::Reverse(a + 1)); //~^ unnecessary_sort_by vec } diff --git a/tests/ui/unnecessary_sort_by_no_std.rs b/tests/ui/unnecessary_sort_by_no_std.rs index 184c90d959eb5..266553e7ace6b 100644 --- a/tests/ui/unnecessary_sort_by_no_std.rs +++ b/tests/ui/unnecessary_sort_by_no_std.rs @@ -15,7 +15,7 @@ fn issue_11524() -> Vec { fn issue_11524_2() -> Vec { let mut vec = vec![1, 2, 3]; - // Should lint and suggest `vec.sort_by_key(|b| core::cmp::Reverse(b + 1));` + // Should lint and suggest `vec.sort_by_key(|a| core::cmp::Reverse(a + 1));` vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); //~^ unnecessary_sort_by vec diff --git a/tests/ui/unnecessary_sort_by_no_std.stderr b/tests/ui/unnecessary_sort_by_no_std.stderr index b4dd6a6dbdc52..26a8ad38fcd32 100644 --- a/tests/ui/unnecessary_sort_by_no_std.stderr +++ b/tests/ui/unnecessary_sort_by_no_std.stderr @@ -21,7 +21,7 @@ LL | vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); help: try | LL - vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); -LL + vec.sort_by_key(|b| core::cmp::Reverse(b + 1)); +LL + vec.sort_by_key(|a| core::cmp::Reverse(a + 1)); | error: aborting due to 2 previous errors From f6a921c1bd028fa3c463f5bd1ce4b069286c7d7c Mon Sep 17 00:00:00 2001 From: Gri-ffin Date: Tue, 28 Apr 2026 20:46:35 +0100 Subject: [PATCH 005/278] fix: [manual_slice_fill] detect for in loops over &mut [T; n] slices --- clippy_lints/src/loops/manual_slice_fill.rs | 19 ++++++++++ tests/ui/manual_slice_fill.fixed | 32 ++++++++++++++++ tests/ui/manual_slice_fill.rs | 41 +++++++++++++++++++++ tests/ui/manual_slice_fill.stderr | 29 ++++++++++++++- 4 files changed, 120 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops/manual_slice_fill.rs b/clippy_lints/src/loops/manual_slice_fill.rs index ffc6f7186922a..ceaf681088399 100644 --- a/clippy_lints/src/loops/manual_slice_fill.rs +++ b/clippy_lints/src/loops/manual_slice_fill.rs @@ -13,6 +13,7 @@ use rustc_hir::QPath::Resolved; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, Pat}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_span::{Spanned, sym}; use super::MANUAL_SLICE_FILL; @@ -84,6 +85,24 @@ pub(super) fn check<'tcx>( { sugg(cx, body, expr, recv_path.span, assignval.span); } + // `for slot in s { *slot = value; }` where `s` is already `&mut [T; N]` + else if let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind + && let ExprKind::Unary(UnOp::Deref, slice_iter) = assignee.kind + && let ExprKind::Path(Resolved(_, slice_path)) = slice_iter.kind + && let Res::Local(local) = slice_path.res + && local == pat.hir_id + && !assignval.span.from_expansion() + && switch_to_eager_eval(cx, assignval) + && !is_local_used(cx, assignval, local) + && let arg_ty = cx.typeck_results().expr_ty(arg) + && let ty::Ref(_, inner_ty, rustc_ast::Mutability::Mut) = arg_ty.kind() + && is_slice_like(cx, *inner_ty) + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, *inner_ty, clone_trait, &[]) + && msrv.meets(cx, msrvs::SLICE_FILL) + { + sugg(cx, body, expr, arg.span, assignval.span); + } } fn sugg<'tcx>( diff --git a/tests/ui/manual_slice_fill.fixed b/tests/ui/manual_slice_fill.fixed index d07d1d60e2c16..15e8e57655701 100644 --- a/tests/ui/manual_slice_fill.fixed +++ b/tests/ui/manual_slice_fill.fixed @@ -34,6 +34,38 @@ fn should_lint() { some_slice.fill(0); } +fn should_lint_direct_mutref_array(s: &mut [u8; 1]) { + s.fill(0); +} + +fn should_lint_direct_mutref_array_non_zero(s: &mut [u8; 4]) { + s.fill(42); +} + +fn should_lint_direct_mutref_array_variable(s: &mut [i32; 3]) { + let x = 7; + s.fill(x); +} + +fn should_not_lint_direct_mutref_array_fn(s: &mut [usize; 2]) { + for slot in s { + *slot = num(); + } +} + +fn should_not_lint_direct_mutref_array_iter_used(s: &mut [u8; 3]) { + for slot in s { + *slot = !*slot; + } +} + +fn should_not_lint_direct_mutref_array_extra_stmt(s: &mut [u8; 2]) { + for slot in s { + *slot = 0; + println!("foo"); + } +} + fn should_not_lint() { let mut some_slice = [1, 2, 3, 4, 5]; diff --git a/tests/ui/manual_slice_fill.rs b/tests/ui/manual_slice_fill.rs index c74ab2225c0a4..ce5f9f0e8afac 100644 --- a/tests/ui/manual_slice_fill.rs +++ b/tests/ui/manual_slice_fill.rs @@ -47,6 +47,47 @@ fn should_lint() { } } +fn should_lint_direct_mutref_array(s: &mut [u8; 1]) { + for slot in s { + //~^ manual_slice_fill + *slot = 0; + } +} + +fn should_lint_direct_mutref_array_non_zero(s: &mut [u8; 4]) { + for slot in s { + //~^ manual_slice_fill + *slot = 42; + } +} + +fn should_lint_direct_mutref_array_variable(s: &mut [i32; 3]) { + let x = 7; + for slot in s { + //~^ manual_slice_fill + *slot = x; + } +} + +fn should_not_lint_direct_mutref_array_fn(s: &mut [usize; 2]) { + for slot in s { + *slot = num(); + } +} + +fn should_not_lint_direct_mutref_array_iter_used(s: &mut [u8; 3]) { + for slot in s { + *slot = !*slot; + } +} + +fn should_not_lint_direct_mutref_array_extra_stmt(s: &mut [u8; 2]) { + for slot in s { + *slot = 0; + println!("foo"); + } +} + fn should_not_lint() { let mut some_slice = [1, 2, 3, 4, 5]; diff --git a/tests/ui/manual_slice_fill.stderr b/tests/ui/manual_slice_fill.stderr index 38e43d5b4e06e..cd1f36f4291f8 100644 --- a/tests/ui/manual_slice_fill.stderr +++ b/tests/ui/manual_slice_fill.stderr @@ -38,5 +38,32 @@ LL | | // foo LL | | } | |_____^ help: try: `some_slice.fill(0);` -error: aborting due to 4 previous errors +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:51:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = 0; +LL | | } + | |_____^ help: try: `s.fill(0);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:58:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = 42; +LL | | } + | |_____^ help: try: `s.fill(42);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:66:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = x; +LL | | } + | |_____^ help: try: `s.fill(x);` + +error: aborting due to 7 previous errors From a259d0a96fc7cdd57b3b8b5090eb46a2a0cc9ec7 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 28 Apr 2026 14:05:24 -0700 Subject: [PATCH 006/278] absolute_paths.rs: change item in "What it does" example The choice of `std::env::current_dir` made me originally think this was about file and directory paths - use an item where "path" couldn't mean something else. `std::f64::consts::PI` was chosen to match the example. --- clippy_lints/src/absolute_paths.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/absolute_paths.rs b/clippy_lints/src/absolute_paths.rs index fd515939dfb8e..cf2059eecc079 100644 --- a/clippy_lints/src/absolute_paths.rs +++ b/clippy_lints/src/absolute_paths.rs @@ -12,7 +12,7 @@ use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does - /// Checks for usage of items through absolute paths, like `std::env::current_dir`. + /// Checks for usage of items through absolute paths, like `std::f64::consts::PI`. /// /// ### Why restrict this? /// Many codebases have their own style when it comes to importing, but one that is seldom used From 9fc79b23006308b1568bb8743623189f7e0764e8 Mon Sep 17 00:00:00 2001 From: cyphercodes Date: Wed, 29 Apr 2026 02:05:32 +0300 Subject: [PATCH 007/278] Avoid map_unwrap_or fix when default is adjusted --- clippy_lints/src/methods/map_unwrap_or.rs | 9 +++++---- tests/ui/map_unwrap_or.rs | 7 +++++++ tests/ui/map_unwrap_or.stderr | 8 +++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index ac2f991804865..a12687c63736c 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -35,6 +35,7 @@ pub(super) fn check<'tcx>( }; let unwrap_arg_ty = cx.typeck_results().expr_ty(unwrap_arg); + let unwrap_arg_ty_adjusted = cx.typeck_results().expr_ty_adjusted(unwrap_arg); if !is_copy(cx, unwrap_arg_ty) { // Replacing `.map().unwrap_or()` with `.map_or(, )` can sometimes lead to // borrowck errors, see #10579 for one such instance. @@ -128,10 +129,10 @@ pub(super) fn check<'tcx>( (SuggestedKind::AndThen, _) => "and_then", (SuggestedKind::IsVariantAnd, sym::Result) => "is_ok_and", (SuggestedKind::IsVariantAnd, sym::Option) => "is_some_and", - (SuggestedKind::Other, _) - if unwrap_arg_ty.peel_refs().is_array() - && cx.typeck_results().expr_ty_adjusted(unwrap_arg).peel_refs().is_slice() => - { + (SuggestedKind::Other, _) if unwrap_arg_ty != unwrap_arg_ty_adjusted => { + // If the `unwrap_or` argument needs an adjustment, moving it into `map_or`'s + // first argument can make type inference pick the unadjusted type and reject + // the closure return type. Keep the lint, but don't emit a rustfix. return; }, _ => "map_or", diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 37470a50cfbef..c63e4bd2285a0 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -161,3 +161,10 @@ fn issue15752() { x.map(|y| y.0).unwrap_or(&[]); //~^ map_unwrap_or } + +fn issue16901() { + let raw = String::from("scope:value"); + let after_scope = raw.split_once(':').map(|(_, v)| v).unwrap_or(&raw); + //~^ map_unwrap_or + let _: &str = after_scope; +} diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index a90da4a97e0c8..cd62c17848583 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -238,5 +238,11 @@ error: called `map().unwrap_or()` on an `Option` value LL | x.map(|y| y.0).unwrap_or(&[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: called `map().unwrap_or()` on an `Option` value + --> tests/ui/map_unwrap_or.rs:167:23 + | +LL | let after_scope = raw.split_once(':').map(|(_, v)| v).unwrap_or(&raw); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors From fbb403696e9b5c25e545bdfdf3cbb4e13e076cab Mon Sep 17 00:00:00 2001 From: rommeld <138243859+rommeld@users.noreply.github.com> Date: Sat, 1 Nov 2025 12:00:47 +0100 Subject: [PATCH 008/278] Add new `chunks_exact_to_as_chunks` lint This lint suggests using `as_chunks` or `as_chunks_mut` instead of `chunks_exact` or `chunks_exact_mut` when called with a constant size. changelog: new lint: [`chunks_exact_to_as_chunks`] --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + .../src/methods/chunks_exact_to_as_chunks.rs | 108 ++++++++++++++++++ clippy_lints/src/methods/mod.rs | 31 +++++ clippy_utils/src/msrvs.rs | 2 +- clippy_utils/src/sym.rs | 2 + tests/ui/chunks_exact_to_as_chunks.rs | 34 ++++++ tests/ui/chunks_exact_to_as_chunks.stderr | 56 +++++++++ 8 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/methods/chunks_exact_to_as_chunks.rs create mode 100644 tests/ui/chunks_exact_to_as_chunks.rs create mode 100644 tests/ui/chunks_exact_to_as_chunks.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 24b91932567a5..01a9b87943b77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6600,6 +6600,7 @@ Released 2018-09-13 [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp [`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions +[`chunks_exact_to_as_chunks`]: https://rust-lang.github.io/rust-clippy/master/index.html#chunks_exact_to_as_chunks [`clear_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#clear_with_drain [`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 79ed199147f1d..468c015e6f99e 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -364,6 +364,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO, crate::methods::CHARS_LAST_CMP_INFO, crate::methods::CHARS_NEXT_CMP_INFO, + crate::methods::CHUNKS_EXACT_TO_AS_CHUNKS_INFO, crate::methods::CLEAR_WITH_DRAIN_INFO, crate::methods::CLONE_ON_COPY_INFO, crate::methods::CLONE_ON_REF_PTR_INFO, diff --git a/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs b/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs new file mode 100644 index 0000000000000..af50cf455ed96 --- /dev/null +++ b/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs @@ -0,0 +1,108 @@ +use super::CHUNKS_EXACT_TO_AS_CHUNKS; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::source::snippet_with_context; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::{get_expr_use_site, sym}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Node, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::{DesugaringKind, ExpnKind, Span, Symbol}; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, + expr: &'tcx Expr<'tcx>, + call_span: Span, + method_name: Symbol, + msrv: Msrv, +) { + let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); + if !matches!(recv_ty.kind(), ty::Ref(_, inner, _) if inner.is_slice()) { + return; + } + + if is_const_evaluatable(cx, arg) { + if !msrv.meets(cx, msrvs::AS_CHUNKS) { + return; + } + + let suggestion_method = if method_name == sym::chunks_exact_mut { + "as_chunks_mut" + } else { + "as_chunks" + }; + + let mut applicability = Applicability::MachineApplicable; + let arg_str = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut applicability).0; + + let as_chunks = format_args!("{suggestion_method}::<{arg_str}>()"); + + span_lint_and_then( + cx, + CHUNKS_EXACT_TO_AS_CHUNKS, + call_span, + format!("using `{method_name}` with a constant chunk size"), + |diag| { + let use_ctxt = get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr); + + if use_ctxt.is_ty_unified { + diag.span_help(call_span, format!("consider using `{as_chunks}` instead")); + return; + } + + if let Node::Expr(use_expr) = use_ctxt.node { + match use_expr.kind { + ExprKind::Call(_, [recv]) | ExprKind::MethodCall(_, recv, [], _) + if recv.hir_id == use_ctxt.child_id + && matches!( + use_expr.span.ctxt().outer_expn_data().kind, + ExpnKind::Desugaring(DesugaringKind::ForLoop), + ) => + { + // Suggest `.0` + diag.span_suggestion( + call_span, + "consider using `as_chunks` instead", + format!("{as_chunks}.0"), + applicability, + ); + return; + }, + ExprKind::MethodCall(_, recv, ..) + if recv.hir_id == use_ctxt.child_id + && cx + .ty_based_def(use_expr) + .assoc_fn_parent(cx) + .is_diag_item(cx, sym::Iterator) => + { + // Suggest `.0.iter()` + diag.span_suggestion( + call_span, + "consider using `as_chunks` instead", + format!("{as_chunks}.0.iter()"), + applicability, + ); + return; + }, + _ => {}, + } + } + + // Fallback suggestion + diag.span_help(call_span, format!("consider using `{as_chunks}` instead")); + + if let Node::LetStmt(let_stmt) = use_ctxt.node + && let PatKind::Binding(_, _, ident, _) = let_stmt.pat.kind + { + diag.note(format!( + "you can access the chunks using `{ident}.0.iter()`, and the remainder using `{ident}.1`" + )); + } + }, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3dc5767438ae7..1dcb611953310 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -9,6 +9,7 @@ mod chars_last_cmp; mod chars_last_cmp_with_unwrap; mod chars_next_cmp; mod chars_next_cmp_with_unwrap; +mod chunks_exact_to_as_chunks; mod clear_with_drain; mod clone_on_copy; mod clone_on_ref_ptr; @@ -327,6 +328,32 @@ declare_clippy_lint! { "using `.chars().next()` to check if a string starts with a char" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `chunks_exact` or `chunks_exact_mut` with a constant chunk size. + /// + /// ### Why is this bad? + /// `as_chunks` provides better ergonomics and type safety by returning arrays instead of slices. + /// It was stabilized in Rust 1.88. + /// + /// ### Example + /// ```no_run + /// let slice = [1, 2, 3, 4, 5, 6]; + /// let mut it = slice.chunks_exact(2); + /// for chunk in it {} + /// ``` + /// Use instead: + /// ```no_run + /// let slice = [1, 2, 3, 4, 5, 6]; + /// let (chunks, remainder) = slice.as_chunks::<2>(); + /// for chunk in chunks {} + /// ``` + #[clippy::version = "1.93.0"] + pub CHUNKS_EXACT_TO_AS_CHUNKS, + style, + "using `chunks_exact` with constant when `as_chunks` is more ergonomic" +} + declare_clippy_lint! { /// ### What it does /// Checks for usage of `.drain(..)` for the sole purpose of clearing a container. @@ -4790,6 +4817,7 @@ impl_lint_pass!(Methods => [ CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, CHARS_LAST_CMP, CHARS_NEXT_CMP, + CHUNKS_EXACT_TO_AS_CHUNKS, CLEAR_WITH_DRAIN, CLONED_INSTEAD_OF_COPIED, CLONE_ON_COPY, @@ -5148,6 +5176,9 @@ impl Methods { _ => {}, } }, + (name @ (sym::chunks_exact | sym::chunks_exact_mut), [arg]) => { + chunks_exact_to_as_chunks::check(cx, recv, arg, expr, call_span, name, self.msrv); + }, (sym::and_then, [arg]) => { manual_option_zip::check(cx, expr, recv, arg, self.msrv); let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index a56e729c70bbb..b7cec1ddb6f15 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -25,7 +25,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,93,0 { VEC_DEQUE_POP_BACK_IF, VEC_DEQUE_POP_FRONT_IF } 1,91,0 { DURATION_FROM_MINUTES_HOURS } - 1,88,0 { LET_CHAINS } + 1,88,0 { LET_CHAINS, AS_CHUNKS } 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST } 1,86,0 { VEC_POP_IF } 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL, WAKER_NOOP } diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 4eaeafe127078..f58df7db65839 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -174,6 +174,8 @@ generate! { checked_sub, child_id, child_kill, + chunks_exact, + chunks_exact_mut, clamp, clippy_utils, clone_into, diff --git a/tests/ui/chunks_exact_to_as_chunks.rs b/tests/ui/chunks_exact_to_as_chunks.rs new file mode 100644 index 0000000000000..a1422437ccccb --- /dev/null +++ b/tests/ui/chunks_exact_to_as_chunks.rs @@ -0,0 +1,34 @@ +#![warn(clippy::chunks_exact_to_as_chunks)] +#![allow(unused)] + +fn main() { + let slice = [1, 2, 3, 4, 5, 6, 7, 8]; + + // Should trigger lint - literal constant + let mut it = slice.chunks_exact(4); + //~^ ERROR: using `chunks_exact` with a constant chunk size + for chunk in it {} + + // Should trigger lint - const value + const CHUNK_SIZE: usize = 4; + let mut it = slice.chunks_exact(CHUNK_SIZE); + //~^ ERROR: using `chunks_exact` with a constant chunk size + for chunk in it {} + + // Should NOT trigger - runtime value + let size = 4; + let mut it = slice.chunks_exact(size); + for chunk in it {} + + // Should trigger lint - with remainder + let mut it = slice.chunks_exact(3); + //~^ ERROR: using `chunks_exact` with a constant chunk size + for chunk in &mut it {} + for e in it.remainder() {} + + // Should trigger - mutable variant + let mut arr = [1, 2, 3, 4, 5, 6, 7, 8]; + let mut it = arr.chunks_exact_mut(4); + //~^ ERROR: using `chunks_exact_mut` with a constant chunk size + for chunk in it {} +} diff --git a/tests/ui/chunks_exact_to_as_chunks.stderr b/tests/ui/chunks_exact_to_as_chunks.stderr new file mode 100644 index 0000000000000..628752f9bae71 --- /dev/null +++ b/tests/ui/chunks_exact_to_as_chunks.stderr @@ -0,0 +1,56 @@ +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:8:24 + | +LL | let mut it = slice.chunks_exact(4); + | ^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::<4>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:8:24 + | +LL | let mut it = slice.chunks_exact(4); + | ^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + = note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]` + +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:14:24 + | +LL | let mut it = slice.chunks_exact(CHUNK_SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:14:24 + | +LL | let mut it = slice.chunks_exact(CHUNK_SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:24:24 + | +LL | let mut it = slice.chunks_exact(3); + | ^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::<3>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:24:24 + | +LL | let mut it = slice.chunks_exact(3); + | ^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: using `chunks_exact_mut` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:31:22 + | +LL | let mut it = arr.chunks_exact_mut(4); + | ^^^^^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks_mut::<4>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:31:22 + | +LL | let mut it = arr.chunks_exact_mut(4); + | ^^^^^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: aborting due to 4 previous errors + From 871036832a1c03b5054c8c98365d95afbb52758c Mon Sep 17 00:00:00 2001 From: rommeld Date: Mon, 12 Jan 2026 20:20:56 +0100 Subject: [PATCH 009/278] add skip lint when is_ty_unified is true. update test comments --- .../src/methods/chunks_exact_to_as_chunks.rs | 12 ++++-------- tests/ui/chunks_exact_to_as_chunks.rs | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs b/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs index af50cf455ed96..dc6b50e4f8132 100644 --- a/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs +++ b/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs @@ -30,6 +30,10 @@ pub(super) fn check<'tcx>( return; } + if expr_use_ctxt(cx, expr).is_ty_unified { + return; + } + let suggestion_method = if method_name == sym::chunks_exact_mut { "as_chunks_mut" } else { @@ -49,11 +53,6 @@ pub(super) fn check<'tcx>( |diag| { let use_ctxt = get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr); - if use_ctxt.is_ty_unified { - diag.span_help(call_span, format!("consider using `{as_chunks}` instead")); - return; - } - if let Node::Expr(use_expr) = use_ctxt.node { match use_expr.kind { ExprKind::Call(_, [recv]) | ExprKind::MethodCall(_, recv, [], _) @@ -63,7 +62,6 @@ pub(super) fn check<'tcx>( ExpnKind::Desugaring(DesugaringKind::ForLoop), ) => { - // Suggest `.0` diag.span_suggestion( call_span, "consider using `as_chunks` instead", @@ -79,7 +77,6 @@ pub(super) fn check<'tcx>( .assoc_fn_parent(cx) .is_diag_item(cx, sym::Iterator) => { - // Suggest `.0.iter()` diag.span_suggestion( call_span, "consider using `as_chunks` instead", @@ -92,7 +89,6 @@ pub(super) fn check<'tcx>( } } - // Fallback suggestion diag.span_help(call_span, format!("consider using `{as_chunks}` instead")); if let Node::LetStmt(let_stmt) = use_ctxt.node diff --git a/tests/ui/chunks_exact_to_as_chunks.rs b/tests/ui/chunks_exact_to_as_chunks.rs index a1422437ccccb..0d5f4d06043d1 100644 --- a/tests/ui/chunks_exact_to_as_chunks.rs +++ b/tests/ui/chunks_exact_to_as_chunks.rs @@ -6,13 +6,13 @@ fn main() { // Should trigger lint - literal constant let mut it = slice.chunks_exact(4); - //~^ ERROR: using `chunks_exact` with a constant chunk size + //~^ chunks_exact_to_as_chunks for chunk in it {} // Should trigger lint - const value const CHUNK_SIZE: usize = 4; let mut it = slice.chunks_exact(CHUNK_SIZE); - //~^ ERROR: using `chunks_exact` with a constant chunk size + //~^ chunks_exact_to_as_chunks for chunk in it {} // Should NOT trigger - runtime value @@ -22,13 +22,22 @@ fn main() { // Should trigger lint - with remainder let mut it = slice.chunks_exact(3); - //~^ ERROR: using `chunks_exact` with a constant chunk size + //~^ chunks_exact_to_as_chunks for chunk in &mut it {} for e in it.remainder() {} // Should trigger - mutable variant let mut arr = [1, 2, 3, 4, 5, 6, 7, 8]; let mut it = arr.chunks_exact_mut(4); - //~^ ERROR: using `chunks_exact_mut` with a constant chunk size + //~^ chunks_exact_to_as_chunks for chunk in it {} + + // Should NOT trigger - type must unify with another branch + let condition = true; + let y = 3; + let _ = if condition { + slice.chunks_exact(5) + } else { + slice.chunks_exact(y) + }; } From 74ad9b63fd0ad1c7455a5fb15b55f06d16ced546 Mon Sep 17 00:00:00 2001 From: mtotbagi Date: Wed, 29 Apr 2026 13:57:53 +0200 Subject: [PATCH 010/278] remove multiple calls to expr_use_ctxt --- clippy_lints/src/methods/chunks_exact_to_as_chunks.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs b/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs index dc6b50e4f8132..235df80e46985 100644 --- a/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs +++ b/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs @@ -30,7 +30,9 @@ pub(super) fn check<'tcx>( return; } - if expr_use_ctxt(cx, expr).is_ty_unified { + let use_ctxt = get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr); + + if use_ctxt.is_ty_unified { return; } @@ -51,8 +53,6 @@ pub(super) fn check<'tcx>( call_span, format!("using `{method_name}` with a constant chunk size"), |diag| { - let use_ctxt = get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr); - if let Node::Expr(use_expr) = use_ctxt.node { match use_expr.kind { ExprKind::Call(_, [recv]) | ExprKind::MethodCall(_, recv, [], _) From f0577192e3f874f18a68a6bc4d1d1febd1a72b3b Mon Sep 17 00:00:00 2001 From: ChrisJr404 Date: Tue, 12 May 2026 09:38:00 -0400 Subject: [PATCH 011/278] let_underscore_future: skip when type is annotated An explicit type annotation on the LHS (`let _: T = expr`) signals an intentional discard, mirroring the rationale for `let_underscore_untyped`. Without this gate, typed bindings such as `let _: Pin>> = foo();` trigger the lint despite the author explicitly opting in. changelog: [`let_underscore_future`]: no longer triggers when the binding has an explicit type annotation Signed-off-by: ChrisJr404 --- clippy_lints/src/let_underscore.rs | 3 ++- tests/ui/let_underscore_future.rs | 8 ++++++++ tests/ui/let_underscore_future.stderr | 6 +++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 984574c221fb6..3f8bd2223876f 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -162,7 +162,8 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { ); }, ); - } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() + } else if local.ty.is_none() + && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] diff --git a/tests/ui/let_underscore_future.rs b/tests/ui/let_underscore_future.rs index eb1a985a91172..17c73876302dd 100644 --- a/tests/ui/let_underscore_future.rs +++ b/tests/ui/let_underscore_future.rs @@ -1,4 +1,5 @@ use std::future::Future; +use std::pin::Pin; async fn some_async_fn() {} @@ -10,6 +11,10 @@ fn custom() -> impl Future { fn do_something_to_future(future: &mut impl Future) {} +fn boxed() -> Pin>> { + Box::pin(async {}) +} + fn main() { let _ = some_async_fn(); //~^ let_underscore_future @@ -21,4 +26,7 @@ fn main() { do_something_to_future(&mut future); let _ = future; //~^ let_underscore_future + + // Typed bindings are an intentional discard, see also `let_underscore_untyped`. + let _: Pin>> = boxed(); } diff --git a/tests/ui/let_underscore_future.stderr b/tests/ui/let_underscore_future.stderr index baa489551d4a3..12cbfff133e9c 100644 --- a/tests/ui/let_underscore_future.stderr +++ b/tests/ui/let_underscore_future.stderr @@ -1,5 +1,5 @@ error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:14:5 + --> tests/ui/let_underscore_future.rs:19:5 | LL | let _ = some_async_fn(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = some_async_fn(); = help: to override `-D warnings` add `#[allow(clippy::let_underscore_future)]` error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:17:5 + --> tests/ui/let_underscore_future.rs:22:5 | LL | let _ = custom(); | ^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = custom(); = help: consider awaiting the future or dropping explicitly with `std::mem::drop` error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:22:5 + --> tests/ui/let_underscore_future.rs:27:5 | LL | let _ = future; | ^^^^^^^^^^^^^^^ From c0c218589ef2be54f8776e9d761708d91914664c Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Mon, 25 May 2026 14:48:11 +0800 Subject: [PATCH 012/278] Stabilize LoongArch CRC intrinsics --- .../crates/core_arch/src/loongarch64/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs index e8bf098a33327..bc2d941c1446c 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs @@ -62,56 +62,56 @@ unsafe extern "unadjusted" { /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_b_w(a: i8, b: i32) -> i32 { unsafe { __crc_w_b_w(a as i32, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_h_w(a: i16, b: i32) -> i32 { unsafe { __crc_w_h_w(a as i32, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_w_w(a: i32, b: i32) -> i32 { unsafe { __crc_w_w_w(a, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_d_w(a: i64, b: i32) -> i32 { unsafe { __crc_w_d_w(a, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_b_w(a: i8, b: i32) -> i32 { unsafe { __crcc_w_b_w(a as i32, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_h_w(a: i16, b: i32) -> i32 { unsafe { __crcc_w_h_w(a as i32, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_w_w(a: i32, b: i32) -> i32 { unsafe { __crcc_w_w_w(a, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_d_w(a: i64, b: i32) -> i32 { unsafe { __crcc_w_d_w(a, b) } } From 09d22acd113e640280242e76e267689291ea3aeb Mon Sep 17 00:00:00 2001 From: sayantn Date: Sun, 31 May 2026 01:21:36 +0530 Subject: [PATCH 013/278] Correct some wrong uses of LLVM intrinsics --- .../core_arch/src/aarch64/neon/generated.rs | 2 +- .../crates/core_arch/src/aarch64/prefetch.rs | 2 +- .../core_arch/src/aarch64/sve/generated.rs | 386 ++++++++++-------- .../stdarch/crates/core_arch/src/nvptx/mod.rs | 6 +- .../crates/core_arch/src/wasm32/memory.rs | 6 +- .../stdarch/crates/core_arch/src/x86/sse.rs | 2 +- .../spec/neon/aarch64.spec.yml | 4 +- .../stdarch-gen-arm/spec/sve/aarch64.spec.yml | 59 ++- 8 files changed, 264 insertions(+), 203 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs index 8d701d9b88056..1b5b17e538bde 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs @@ -11903,7 +11903,7 @@ pub unsafe fn vluti4q_lane_s8(a: int8x16_t, b: uint8x8_t) -> in unsafe extern "unadjusted" { #[cfg_attr( any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.vluti4q.lane.v8i8" + link_name = "llvm.aarch64.neon.vluti4q.lane.v16i8" )] fn _vluti4q_lane_s8(a: int8x16_t, b: uint8x8_t, n: i32) -> int8x16_t; } diff --git a/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs b/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs index 4dcbc9549f115..88e5c0987f34d 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs @@ -2,7 +2,7 @@ use stdarch_test::assert_instr; unsafe extern "unadjusted" { - #[link_name = "llvm.prefetch"] + #[link_name = "llvm.prefetch.p0"] fn prefetch(p: *const i8, rw: i32, loc: i32, ty: i32); } diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs index 6c6a2476a43fd..6c5d5692a16ac 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs @@ -1843,7 +1843,7 @@ pub fn svadrd_u64base_u64index(bases: svuint64_t, indices: svuint64_t) -> svuint #[cfg_attr(test, assert_instr(and))] pub fn svand_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.and.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.and.z.nxv16i1")] fn _svand_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svand_b_z(pg, op1, op2) } @@ -2935,7 +2935,7 @@ pub fn svasrd_n_s64_z(pg: svbool_t, op1: svint64_t) -> svint64_ #[cfg_attr(test, assert_instr(bic))] pub fn svbic_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.bic.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.bic.z.nxv16i1")] fn _svbic_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svbic_b_z(pg, op1, op2) } @@ -4559,7 +4559,7 @@ pub fn svcmla_lane_f32( unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", - link_name = "llvm.aarch64.sve.fcmla.lane.x.nxv4f32" + link_name = "llvm.aarch64.sve.fcmla.lane.nxv4f32" )] fn _svcmla_lane_f32( op1: svfloat32_t, @@ -7657,7 +7657,10 @@ pub fn svcvt_f64_f32_z(pg: svbool_t, op: svfloat32_t) -> svfloat64_t { #[cfg_attr(test, assert_instr(scvtf))] pub fn svcvt_f32_s32_m(inactive: svfloat32_t, pg: svbool_t, op: svint32_t) -> svfloat32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f32i32")] + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.scvtf.nxv4f32.nxv4i32" + )] fn _svcvt_f32_s32_m(inactive: svfloat32_t, pg: svbool4_t, op: svint32_t) -> svfloat32_t; } unsafe { _svcvt_f32_s32_m(inactive, pg.sve_into(), op) } @@ -7681,66 +7684,137 @@ pub fn svcvt_f32_s32_z(pg: svbool_t, op: svint32_t) -> svfloat32_t { svcvt_f32_s32_m(svdup_n_f32(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_m)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(ucvtf))] +pub fn svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool_t, op: svuint32_t) -> svfloat32_t { + unsafe extern "unadjusted" { + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.ucvtf.nxv4f32.nxv4i32" + )] + fn _svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool4_t, op: svint32_t) -> svfloat32_t; + } + unsafe { _svcvt_f32_u32_m(inactive, pg.sve_into(), op.as_signed()) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_x)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(ucvtf))] +pub fn svcvt_f32_u32_x(pg: svbool_t, op: svuint32_t) -> svfloat32_t { + unsafe { svcvt_f32_u32_m(transmute_unchecked(op), pg, op) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_z)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(ucvtf))] +pub fn svcvt_f32_u32_z(pg: svbool_t, op: svuint32_t) -> svfloat32_t { + svcvt_f32_u32_m(svdup_n_f32(0.0), pg, op) +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool_t, op: svint64_t) -> svfloat32_t { +pub fn svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool_t, op: svint64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f32i64")] - fn _svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool2_t, op: svint64_t) -> svfloat32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.scvtf.nxv2f64.nxv2i64" + )] + fn _svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; } - unsafe { _svcvt_f32_s64_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_f64_s64_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f32_s64_x(pg: svbool_t, op: svint64_t) -> svfloat32_t { - unsafe { svcvt_f32_s64_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_f64_s64_x(pg: svbool_t, op: svint64_t) -> svfloat64_t { + unsafe { svcvt_f64_s64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f32_s64_z(pg: svbool_t, op: svint64_t) -> svfloat32_t { - svcvt_f32_s64_m(svdup_n_f32(0.0), pg, op) +pub fn svcvt_f64_s64_z(pg: svbool_t, op: svint64_t) -> svfloat64_t { + svcvt_f64_s64_m(svdup_n_f64(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool_t, op: svuint32_t) -> svfloat32_t { +pub fn svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool_t, op: svuint64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.ucvtf.f32i32")] - fn _svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool4_t, op: svint32_t) -> svfloat32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.ucvtf.nxv2f64.nxv2i64" + )] + fn _svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; } - unsafe { _svcvt_f32_u32_m(inactive, pg.sve_into(), op.as_signed()) } + unsafe { _svcvt_f64_u64_m(inactive, pg.sve_into(), op.as_signed()) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f32_u32_x(pg: svbool_t, op: svuint32_t) -> svfloat32_t { - unsafe { svcvt_f32_u32_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_f64_u64_x(pg: svbool_t, op: svuint64_t) -> svfloat64_t { + unsafe { svcvt_f64_u64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f32_u32_z(pg: svbool_t, op: svuint32_t) -> svfloat32_t { - svcvt_f32_u32_m(svdup_n_f32(0.0), pg, op) +pub fn svcvt_f64_u64_z(pg: svbool_t, op: svuint64_t) -> svfloat64_t { + svcvt_f64_u64_m(svdup_n_f64(0.0), pg, op) +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_m)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(scvtf))] +pub fn svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool_t, op: svint64_t) -> svfloat32_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f32i64")] + fn _svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool2_t, op: svint64_t) -> svfloat32_t; + } + unsafe { _svcvt_f32_s64_m(inactive, pg.sve_into(), op) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_x)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(scvtf))] +pub fn svcvt_f32_s64_x(pg: svbool_t, op: svint64_t) -> svfloat32_t { + unsafe { svcvt_f32_s64_m(transmute_unchecked(op), pg, op) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_z)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(scvtf))] +pub fn svcvt_f32_s64_z(pg: svbool_t, op: svint64_t) -> svfloat32_t { + svcvt_f32_s64_m(svdup_n_f32(0.0), pg, op) } #[doc = "Floating-point convert"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u64]_m)"] @@ -7805,37 +7879,6 @@ pub fn svcvt_f64_s32_z(pg: svbool_t, op: svint32_t) -> svfloat64_t { svcvt_f64_s32_m(svdup_n_f64(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_m)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool_t, op: svint64_t) -> svfloat64_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f64i64")] - fn _svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; - } - unsafe { _svcvt_f64_s64_m(inactive, pg.sve_into(), op) } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_x)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f64_s64_x(pg: svbool_t, op: svint64_t) -> svfloat64_t { - unsafe { svcvt_f64_s64_m(transmute_unchecked(op), pg, op) } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_z)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f64_s64_z(pg: svbool_t, op: svint64_t) -> svfloat64_t { - svcvt_f64_s64_m(svdup_n_f64(0.0), pg, op) -} -#[doc = "Floating-point convert"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u32]_m)"] #[inline] #[target_feature(enable = "sve")] @@ -7867,190 +7910,202 @@ pub fn svcvt_f64_u32_z(pg: svbool_t, op: svuint32_t) -> svfloat64_t { svcvt_f64_u32_m(svdup_n_f64(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool_t, op: svuint64_t) -> svfloat64_t { +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s32_f32_m(inactive: svint32_t, pg: svbool_t, op: svfloat32_t) -> svint32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.ucvtf.f64i64")] - fn _svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzs.nxv4i32.nxv4f32" + )] + fn _svcvt_s32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; } - unsafe { _svcvt_f64_u64_m(inactive, pg.sve_into(), op.as_signed()) } + unsafe { _svcvt_s32_f32_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f64_u64_x(pg: svbool_t, op: svuint64_t) -> svfloat64_t { - unsafe { svcvt_f64_u64_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s32_f32_x(pg: svbool_t, op: svfloat32_t) -> svint32_t { + unsafe { svcvt_s32_f32_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f64_u64_z(pg: svbool_t, op: svuint64_t) -> svfloat64_t { - svcvt_f64_u64_m(svdup_n_f64(0.0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s32_f32_z(pg: svbool_t, op: svfloat32_t) -> svint32_t { + svcvt_s32_f32_m(svdup_n_s32(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f32_m(inactive: svint32_t, pg: svbool_t, op: svfloat32_t) -> svint32_t { +pub fn svcvt_s64_f64_m(inactive: svint64_t, pg: svbool_t, op: svfloat64_t) -> svint64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i32f32")] - fn _svcvt_s32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzs.nxv2i64.nxv2f64" + )] + fn _svcvt_s64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; } - unsafe { _svcvt_s32_f32_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_s64_f64_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f32_x(pg: svbool_t, op: svfloat32_t) -> svint32_t { - unsafe { svcvt_s32_f32_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_s64_f64_x(pg: svbool_t, op: svfloat64_t) -> svint64_t { + unsafe { svcvt_s64_f64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f32_z(pg: svbool_t, op: svfloat32_t) -> svint32_t { - svcvt_s32_f32_m(svdup_n_s32(0), pg, op) +pub fn svcvt_s64_f64_z(pg: svbool_t, op: svfloat64_t) -> svint64_t { + svcvt_s64_f64_m(svdup_n_s64(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f64_m(inactive: svint32_t, pg: svbool_t, op: svfloat64_t) -> svint32_t { +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u32_f32_m(inactive: svuint32_t, pg: svbool_t, op: svfloat32_t) -> svuint32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i32f64")] - fn _svcvt_s32_f64_m(inactive: svint32_t, pg: svbool2_t, op: svfloat64_t) -> svint32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzu.nxv4i32.nxv4f32" + )] + fn _svcvt_u32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; } - unsafe { _svcvt_s32_f64_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_u32_f32_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f64_x(pg: svbool_t, op: svfloat64_t) -> svint32_t { - unsafe { svcvt_s32_f64_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u32_f32_x(pg: svbool_t, op: svfloat32_t) -> svuint32_t { + unsafe { svcvt_u32_f32_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f64_z(pg: svbool_t, op: svfloat64_t) -> svint32_t { - svcvt_s32_f64_m(svdup_n_s32(0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u32_f32_z(pg: svbool_t, op: svfloat32_t) -> svuint32_t { + svcvt_u32_f32_m(svdup_n_u32(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f32_m(inactive: svint64_t, pg: svbool_t, op: svfloat32_t) -> svint64_t { +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u64_f64_m(inactive: svuint64_t, pg: svbool_t, op: svfloat64_t) -> svuint64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i64f32")] - fn _svcvt_s64_f32_m(inactive: svint64_t, pg: svbool2_t, op: svfloat32_t) -> svint64_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzu.nxv2i64.nxv2f64" + )] + fn _svcvt_u64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; } - unsafe { _svcvt_s64_f32_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_u64_f64_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f32_x(pg: svbool_t, op: svfloat32_t) -> svint64_t { - unsafe { svcvt_s64_f32_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u64_f64_x(pg: svbool_t, op: svfloat64_t) -> svuint64_t { + unsafe { svcvt_u64_f64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f32_z(pg: svbool_t, op: svfloat32_t) -> svint64_t { - svcvt_s64_f32_m(svdup_n_s64(0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u64_f64_z(pg: svbool_t, op: svfloat64_t) -> svuint64_t { + svcvt_u64_f64_m(svdup_n_u64(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f64_m(inactive: svint64_t, pg: svbool_t, op: svfloat64_t) -> svint64_t { +pub fn svcvt_s32_f64_m(inactive: svint32_t, pg: svbool_t, op: svfloat64_t) -> svint32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i64f64")] - fn _svcvt_s64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i32f64")] + fn _svcvt_s32_f64_m(inactive: svint32_t, pg: svbool2_t, op: svfloat64_t) -> svint32_t; } - unsafe { _svcvt_s64_f64_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_s32_f64_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f64_x(pg: svbool_t, op: svfloat64_t) -> svint64_t { - unsafe { svcvt_s64_f64_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_s32_f64_x(pg: svbool_t, op: svfloat64_t) -> svint32_t { + unsafe { svcvt_s32_f64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f64_z(pg: svbool_t, op: svfloat64_t) -> svint64_t { - svcvt_s64_f64_m(svdup_n_s64(0), pg, op) +pub fn svcvt_s32_f64_z(pg: svbool_t, op: svfloat64_t) -> svint32_t { + svcvt_s32_f64_m(svdup_n_s32(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u32_f32_m(inactive: svuint32_t, pg: svbool_t, op: svfloat32_t) -> svuint32_t { +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s64_f32_m(inactive: svint64_t, pg: svbool_t, op: svfloat32_t) -> svint64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzu.i32f32")] - fn _svcvt_u32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i64f32")] + fn _svcvt_s64_f32_m(inactive: svint64_t, pg: svbool2_t, op: svfloat32_t) -> svint64_t; } - unsafe { _svcvt_u32_f32_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } + unsafe { _svcvt_s64_f32_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u32_f32_x(pg: svbool_t, op: svfloat32_t) -> svuint32_t { - unsafe { svcvt_u32_f32_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s64_f32_x(pg: svbool_t, op: svfloat32_t) -> svint64_t { + unsafe { svcvt_s64_f32_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u32_f32_z(pg: svbool_t, op: svfloat32_t) -> svuint32_t { - svcvt_u32_f32_m(svdup_n_u32(0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s64_f32_z(pg: svbool_t, op: svfloat32_t) -> svint64_t { + svcvt_s64_f32_m(svdup_n_s64(0), pg, op) } #[doc = "Floating-point convert"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f64]_m)"] @@ -8114,37 +8169,6 @@ pub fn svcvt_u64_f32_x(pg: svbool_t, op: svfloat32_t) -> svuint64_t { pub fn svcvt_u64_f32_z(pg: svbool_t, op: svfloat32_t) -> svuint64_t { svcvt_u64_f32_m(svdup_n_u64(0), pg, op) } -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_m)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u64_f64_m(inactive: svuint64_t, pg: svbool_t, op: svfloat64_t) -> svuint64_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzu.i64f64")] - fn _svcvt_u64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; - } - unsafe { _svcvt_u64_f64_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_x)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u64_f64_x(pg: svbool_t, op: svfloat64_t) -> svuint64_t { - unsafe { svcvt_u64_f64_m(transmute_unchecked(op), pg, op) } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_z)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u64_f64_z(pg: svbool_t, op: svfloat64_t) -> svuint64_t { - svcvt_u64_f64_m(svdup_n_u64(0), pg, op) -} #[doc = "Divide"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svdiv[_f32]_m)"] #[inline] @@ -10040,7 +10064,7 @@ pub fn svdupq_n_u8( #[cfg_attr(test, assert_instr(eor))] pub fn sveor_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.eor.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.eor.z.nxv16i1")] fn _sveor_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _sveor_b_z(pg, op1, op2) } @@ -10591,7 +10615,7 @@ pub fn svexpa_f32(op: svuint32_t) -> svfloat32_t { unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", - link_name = "llvm.aarch64.sve.fexpa.x.nxv4f32 " + link_name = "llvm.aarch64.sve.fexpa.x.nxv4f32" )] fn _svexpa_f32(op: svint32_t) -> svfloat32_t; } @@ -10607,7 +10631,7 @@ pub fn svexpa_f64(op: svuint64_t) -> svfloat64_t { unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", - link_name = "llvm.aarch64.sve.fexpa.x.nxv2f64 " + link_name = "llvm.aarch64.sve.fexpa.x.nxv2f64" )] fn _svexpa_f64(op: svint64_t) -> svfloat64_t; } @@ -27371,7 +27395,10 @@ pub fn svmls_lane_f64( #[cfg_attr(test, assert_instr(fmmla))] pub fn svmmla_f32(op1: svfloat32_t, op2: svfloat32_t, op3: svfloat32_t) -> svfloat32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fmmla.nxv4f32")] + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fmmla.nxv4f32.nxv4f32" + )] fn _svmmla_f32(op1: svfloat32_t, op2: svfloat32_t, op3: svfloat32_t) -> svfloat32_t; } unsafe { _svmmla_f32(op1, op2, op3) } @@ -27384,7 +27411,10 @@ pub fn svmmla_f32(op1: svfloat32_t, op2: svfloat32_t, op3: svfloat32_t) -> svflo #[cfg_attr(test, assert_instr(fmmla))] pub fn svmmla_f64(op1: svfloat64_t, op2: svfloat64_t, op3: svfloat64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fmmla.nxv2f64")] + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fmmla.nxv2f64.nxv2f64" + )] fn _svmmla_f64(op1: svfloat64_t, op2: svfloat64_t, op3: svfloat64_t) -> svfloat64_t; } unsafe { _svmmla_f64(op1, op2, op3) } @@ -30260,7 +30290,7 @@ pub fn svnot_u64_z(pg: svbool_t, op: svuint64_t) -> svuint64_t { #[cfg_attr(test, assert_instr(orn))] pub fn svorn_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orn.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orn.z.nxv16i1")] fn _svorn_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svorn_b_z(pg, op1, op2) } @@ -30273,7 +30303,7 @@ pub fn svorn_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(orr))] pub fn svorr_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orr.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orr.z.nxv16i1")] fn _svorr_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svorr_b_z(pg, op1, op2) } @@ -34340,10 +34370,7 @@ pub fn svrecps_f64(op1: svfloat64_t, op2: svfloat64_t) -> svfloat64_t { #[cfg_attr(test, assert_instr(frecpx))] pub fn svrecpx_f32_m(inactive: svfloat32_t, pg: svbool_t, op: svfloat32_t) -> svfloat32_t { unsafe extern "unadjusted" { - #[cfg_attr( - target_arch = "aarch64", - link_name = "llvm.aarch64.sve.frecpx.x.nxv4f32" - )] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.frecpx.nxv4f32")] fn _svrecpx_f32_m(inactive: svfloat32_t, pg: svbool4_t, op: svfloat32_t) -> svfloat32_t; } unsafe { _svrecpx_f32_m(inactive, pg.sve_into(), op) } @@ -34374,10 +34401,7 @@ pub fn svrecpx_f32_z(pg: svbool_t, op: svfloat32_t) -> svfloat32_t { #[cfg_attr(test, assert_instr(frecpx))] pub fn svrecpx_f64_m(inactive: svfloat64_t, pg: svbool_t, op: svfloat64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr( - target_arch = "aarch64", - link_name = "llvm.aarch64.sve.frecpx.x.nxv2f64" - )] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.frecpx.nxv2f64")] fn _svrecpx_f64_m(inactive: svfloat64_t, pg: svbool2_t, op: svfloat64_t) -> svfloat64_t; } unsafe { _svrecpx_f64_m(inactive, pg.sve_into(), op) } diff --git a/library/stdarch/crates/core_arch/src/nvptx/mod.rs b/library/stdarch/crates/core_arch/src/nvptx/mod.rs index b63a5d01a7a22..84d8810320a15 100644 --- a/library/stdarch/crates/core_arch/src/nvptx/mod.rs +++ b/library/stdarch/crates/core_arch/src/nvptx/mod.rs @@ -20,8 +20,8 @@ pub use packed::*; #[allow(improper_ctypes)] unsafe extern "C" { - #[link_name = "llvm.nvvm.barrier0"] - fn syncthreads() -> (); + #[link_name = "llvm.nvvm.barrier.cta.sync.aligned.all"] + fn syncthreads(a: u32) -> (); #[link_name = "llvm.nvvm.read.ptx.sreg.ntid.x"] fn block_dim_x() -> u32; #[link_name = "llvm.nvvm.read.ptx.sreg.ntid.y"] @@ -54,7 +54,7 @@ unsafe extern "C" { #[inline] #[unstable(feature = "stdarch_nvptx", issue = "111199")] pub unsafe fn _syncthreads() -> () { - syncthreads() + syncthreads(0) } /// x-th thread-block dimension. diff --git a/library/stdarch/crates/core_arch/src/wasm32/memory.rs b/library/stdarch/crates/core_arch/src/wasm32/memory.rs index 90e9075e5136b..9d7b7287ede1b 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/memory.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/memory.rs @@ -2,9 +2,11 @@ use stdarch_test::assert_instr; unsafe extern "unadjusted" { - #[link_name = "llvm.wasm.memory.grow"] + #[cfg_attr(target_pointer_width = "32", link_name = "llvm.wasm.memory.grow.i32")] + #[cfg_attr(target_pointer_width = "64", link_name = "llvm.wasm.memory.grow.i64")] fn llvm_memory_grow(mem: u32, pages: usize) -> usize; - #[link_name = "llvm.wasm.memory.size"] + #[cfg_attr(target_pointer_width = "32", link_name = "llvm.wasm.memory.size.i32")] + #[cfg_attr(target_pointer_width = "64", link_name = "llvm.wasm.memory.size.i64")] fn llvm_memory_size(mem: u32) -> usize; } diff --git a/library/stdarch/crates/core_arch/src/x86/sse.rs b/library/stdarch/crates/core_arch/src/x86/sse.rs index 867387290119b..afaaccfbf27f4 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse.rs @@ -2040,7 +2040,7 @@ unsafe extern "C" { fn stmxcsr(p: *mut i8); #[link_name = "llvm.x86.sse.ldmxcsr"] fn ldmxcsr(p: *const i8); - #[link_name = "llvm.prefetch"] + #[link_name = "llvm.prefetch.p0"] fn prefetch(p: *const i8, rw: i32, loc: i32, ty: i32); #[link_name = "llvm.x86.sse.cmp.ss"] fn cmpss(a: __m128, b: __m128, imm8: i8) -> __m128; diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml index 03fea5e0a4af3..e5ce77ed8b33f 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml @@ -13951,7 +13951,7 @@ intrinsics: - 'b: {neon_type[1]}' - 'n: i32' links: - - link: "llvm.aarch64.neon.vluti4q.lane.{neon_type[1]}" + - link: "llvm.aarch64.neon.vluti4q.lane.{neon_type[0]}" arch: aarch64,arm64ec - FnCall: ['_vluti4{neon_type[0].lane_nox}', [a, b, LANE]] @@ -14002,7 +14002,7 @@ intrinsics: - 'b: {neon_type[1]}' - 'n: i32' links: - - link: "llvm.aarch64.neon.vluti4q.laneq.{neon_type[1]}" + - link: "llvm.aarch64.neon.vluti4q.laneq.{neon_type[0]}" arch: aarch64,arm64ec - FnCall: ['_vluti4{neon_type[0].laneq_nox}', [a, b, LANE]] diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml index 29dd3a095d7e6..1f65732412241 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml @@ -115,7 +115,7 @@ intrinsics: assert_instr: [[fcmla, "IMM_INDEX = 0, IMM_ROTATION = 90"]] compose: - LLVMLink: - name: fcmla.lane.x.{sve_type} + name: fcmla.lane.{sve_type} arguments: - "op1: {sve_type}" - "op2: {sve_type}" @@ -4179,7 +4179,8 @@ intrinsics: ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] return_type: "{sve_type[0]}" types: - - [[f32, f64], [i32, u32, i64, u64]] + - [f32, [i64, u64]] + - [f64, [i32, u32]] zeroing_method: { drop: inactive } substitutions: convert_from: { match_kind: "{type[1]}", default: s, unsigned: u } @@ -4187,6 +4188,23 @@ intrinsics: compose: - LLVMLink: name: "{convert_from}cvtf.{type[0]}{type[1]}" + + - name: svcvt_{type[0]}[_{type[1]}]{_mxz} + attr: [*sve-unstable] + doc: Floating-point convert + arguments: + ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] + return_type: "{sve_type[0]}" + types: + - [f32, [i32, u32]] + - [f64, [i64, u64]] + zeroing_method: { drop: inactive } + substitutions: + convert_from: { match_kind: "{type[1]}", default: s, unsigned: u } + assert_instr: ["{convert_from}cvtf"] + compose: + - LLVMLink: + name: "{convert_from}cvtf.{sve_type[0]}.{sve_type[1]}" - name: svcvt_{type[0]}[_{type[1]}]{_mxz} attr: [*sve-unstable] @@ -4195,13 +4213,30 @@ intrinsics: ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] return_type: "{sve_type[0]}" types: - - [[i32, u32, i64, u64], [f32, f64]] + - [[i32, u32], f64] + - [[i64, u64], f32] zeroing_method: { drop: inactive } substitutions: convert_to: { match_kind: "{type[0]}", default: s, unsigned: u } assert_instr: ["fcvtz{convert_to}"] compose: - LLVMLink: { name: "fcvtz{convert_to}.{type[0]}{type[1]}" } + + - name: svcvt_{type[0]}[_{type[1]}]{_mxz} + attr: [*sve-unstable] + doc: Floating-point convert + arguments: + ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] + return_type: "{sve_type[0]}" + types: + - [[i32, u32], f32] + - [[i64, u64], f64] + zeroing_method: { drop: inactive } + substitutions: + convert_to: { match_kind: "{type[0]}", default: s, unsigned: u } + assert_instr: ["fcvtz{convert_to}"] + compose: + - LLVMLink: { name: "fcvtz{convert_to}.{sve_type[0]}.{sve_type[1]}" } - name: svcvt_{type[0]}[_{type[1]}]{_mxz} attr: [*sve-unstable] @@ -4356,7 +4391,7 @@ intrinsics: return_type: svbool_t assert_instr: [and] compose: - - LLVMLink: { name: "and.z.nvx16i1" } + - LLVMLink: { name: "and.z.nxv16i1" } - name: svmov[_b]_z attr: [*sve-unstable] @@ -4386,7 +4421,7 @@ intrinsics: return_type: svbool_t assert_instr: [bic] compose: - - LLVMLink: { name: "bic.z.nvx16i1" } + - LLVMLink: { name: "bic.z.nxv16i1" } - name: sveor[{_n}_{type}]{_mxz} attr: [*sve-unstable] @@ -4417,7 +4452,7 @@ intrinsics: return_type: svbool_t assert_instr: [eor] compose: - - LLVMLink: { name: "eor.z.nvx16i1" } + - LLVMLink: { name: "eor.z.nxv16i1" } - name: svnot[_{type}]{_mxz} attr: [*sve-unstable] @@ -4497,7 +4532,7 @@ intrinsics: return_type: svbool_t assert_instr: [orr] compose: - - LLVMLink: { name: "orr.z.nvx16i1" } + - LLVMLink: { name: "orr.z.nxv16i1" } - name: svorn[_b]_z attr: [*sve-unstable] @@ -4506,7 +4541,7 @@ intrinsics: return_type: svbool_t assert_instr: [orn] compose: - - LLVMLink: { name: "orn.z.nvx16i1" } + - LLVMLink: { name: "orn.z.nxv16i1" } - name: svlsl[{_n}_{type[0]}]{_mxz} attr: [*sve-unstable] @@ -4749,7 +4784,7 @@ intrinsics: assert_instr: [frecpx] zeroing_method: { drop: inactive } compose: - - LLVMLink: { name: "frecpx.x.{sve_type}" } + - LLVMLink: { name: "frecpx.{sve_type}" } - name: svrsqrte[_{type}] attr: [*sve-unstable] @@ -5115,7 +5150,7 @@ intrinsics: types: [[f32, u32], [f64, u64]] assert_instr: [fexpa] compose: - - LLVMLink: { name: "fexpa.x.{sve_type[0]} " } + - LLVMLink: { name: "fexpa.x.{sve_type[0]}" } - name: svscale[{_n}_{type[0]}]{_mxz} attr: [*sve-unstable] @@ -5139,7 +5174,7 @@ intrinsics: types: [f32] assert_instr: [fmmla] compose: - - LLVMLink: { name: "fmmla.{sve_type}" } + - LLVMLink: { name: "fmmla.{sve_type}.{sve_type}" } - name: svmmla[_{type}] attr: [*sve-unstable] @@ -5150,7 +5185,7 @@ intrinsics: types: [f64] assert_instr: [fmmla] compose: - - LLVMLink: { name: "fmmla.{sve_type}" } + - LLVMLink: { name: "fmmla.{sve_type}.{sve_type}" } - name: svmmla[_{type[0]}] attr: [*sve-unstable] From 2dd54ff3227d07f6e975e9cbfe3b39a76abebfd2 Mon Sep 17 00:00:00 2001 From: sayantn Date: Sun, 31 May 2026 12:36:47 +0530 Subject: [PATCH 014/278] Use `unadjusted` ABI everywhere --- library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs | 4 ++-- library/stdarch/crates/core_arch/src/mips/msa.rs | 2 +- library/stdarch/crates/core_arch/src/nvptx/mod.rs | 2 +- library/stdarch/crates/core_arch/src/nvptx/packed.rs | 2 +- library/stdarch/crates/core_arch/src/powerpc/altivec.rs | 2 +- library/stdarch/crates/core_arch/src/powerpc/vsx.rs | 2 +- library/stdarch/crates/core_arch/src/powerpc64/vsx.rs | 2 +- library/stdarch/crates/core_arch/src/x86/aes.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx2.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx512bf16.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx512bw.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx512cd.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx512dq.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx512f.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx512ifma.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avx512vnni.rs | 2 +- .../stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs | 2 +- library/stdarch/crates/core_arch/src/x86/avxneconvert.rs | 2 +- library/stdarch/crates/core_arch/src/x86/bmi1.rs | 2 +- library/stdarch/crates/core_arch/src/x86/bmi2.rs | 2 +- library/stdarch/crates/core_arch/src/x86/fxsr.rs | 2 +- library/stdarch/crates/core_arch/src/x86/gfni.rs | 2 +- library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs | 2 +- library/stdarch/crates/core_arch/src/x86/rtm.rs | 2 +- library/stdarch/crates/core_arch/src/x86/sha.rs | 2 +- library/stdarch/crates/core_arch/src/x86/sse.rs | 2 +- library/stdarch/crates/core_arch/src/x86/sse2.rs | 2 +- library/stdarch/crates/core_arch/src/x86/sse3.rs | 2 +- library/stdarch/crates/core_arch/src/x86/sse41.rs | 2 +- library/stdarch/crates/core_arch/src/x86/sse42.rs | 2 +- library/stdarch/crates/core_arch/src/x86/sse4a.rs | 2 +- library/stdarch/crates/core_arch/src/x86/ssse3.rs | 2 +- library/stdarch/crates/core_arch/src/x86/tbm.rs | 2 +- library/stdarch/crates/core_arch/src/x86/vaes.rs | 2 +- library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs | 2 +- library/stdarch/crates/core_arch/src/x86/xsave.rs | 2 +- library/stdarch/crates/core_arch/src/x86_64/amx.rs | 2 +- library/stdarch/crates/core_arch/src/x86_64/avx512f.rs | 2 +- library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs | 2 +- library/stdarch/crates/core_arch/src/x86_64/bmi.rs | 2 +- library/stdarch/crates/core_arch/src/x86_64/bmi2.rs | 2 +- library/stdarch/crates/core_arch/src/x86_64/fxsr.rs | 2 +- library/stdarch/crates/core_arch/src/x86_64/sse.rs | 2 +- library/stdarch/crates/core_arch/src/x86_64/sse2.rs | 2 +- library/stdarch/crates/core_arch/src/x86_64/sse42.rs | 2 +- library/stdarch/crates/core_arch/src/x86_64/tbm.rs | 2 +- library/stdarch/crates/core_arch/src/x86_64/xsave.rs | 2 +- 51 files changed, 52 insertions(+), 52 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs index a3f70ab61c40f..c6d5d57aca869 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs @@ -130,7 +130,7 @@ macro_rules! impl_internal_sve_predicate { #[target_feature(enable = "sve")] unsafe fn sve_into(self) -> svbool_t { #[allow(improper_ctypes)] - unsafe extern "C" { + unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", link_name = concat!("llvm.aarch64.sve.convert.to.svbool.nxv", $elt, "i1") @@ -147,7 +147,7 @@ macro_rules! impl_internal_sve_predicate { #[target_feature(enable = "sve")] unsafe fn sve_into(self) -> $name { #[allow(improper_ctypes)] - unsafe extern "C" { + unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", link_name = concat!("llvm.aarch64.sve.convert.from.svbool.nxv", $elt, "i1") diff --git a/library/stdarch/crates/core_arch/src/mips/msa.rs b/library/stdarch/crates/core_arch/src/mips/msa.rs index bc601baef9e2a..dab64319ccc3b 100644 --- a/library/stdarch/crates/core_arch/src/mips/msa.rs +++ b/library/stdarch/crates/core_arch/src/mips/msa.rs @@ -45,7 +45,7 @@ types! { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.mips.add.a.b"] fn msa_add_a_b(a: v16i8, b: v16i8) -> v16i8; #[link_name = "llvm.mips.add.a.h"] diff --git a/library/stdarch/crates/core_arch/src/nvptx/mod.rs b/library/stdarch/crates/core_arch/src/nvptx/mod.rs index 84d8810320a15..d22f3a25bf70e 100644 --- a/library/stdarch/crates/core_arch/src/nvptx/mod.rs +++ b/library/stdarch/crates/core_arch/src/nvptx/mod.rs @@ -19,7 +19,7 @@ mod packed; pub use packed::*; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.nvvm.barrier.cta.sync.aligned.all"] fn syncthreads(a: u32) -> (); #[link_name = "llvm.nvvm.read.ptx.sreg.ntid.x"] diff --git a/library/stdarch/crates/core_arch/src/nvptx/packed.rs b/library/stdarch/crates/core_arch/src/nvptx/packed.rs index 1c7e81268fc99..7e6c14bc993d9 100644 --- a/library/stdarch/crates/core_arch/src/nvptx/packed.rs +++ b/library/stdarch/crates/core_arch/src/nvptx/packed.rs @@ -7,7 +7,7 @@ use crate::intrinsics::simd::*; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.minimum.v2f16"] fn llvm_f16x2_minimum(a: f16x2, b: f16x2) -> f16x2; #[link_name = "llvm.maximum.v2f16"] diff --git a/library/stdarch/crates/core_arch/src/powerpc/altivec.rs b/library/stdarch/crates/core_arch/src/powerpc/altivec.rs index 78ec39f91ff33..ccfed88a93c2a 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/altivec.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/altivec.rs @@ -96,7 +96,7 @@ impl From for m32x4 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.ppc.altivec.lvx"] fn lvx(p: *const i8) -> vector_unsigned_int; diff --git a/library/stdarch/crates/core_arch/src/powerpc/vsx.rs b/library/stdarch/crates/core_arch/src/powerpc/vsx.rs index 4a7b561a20c55..60cb2ad44cd4e 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/vsx.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/vsx.rs @@ -52,7 +52,7 @@ impl From for m64x2 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.ppc.altivec.vperm"] fn vperm( a: vector_signed_int, diff --git a/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs b/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs index 7b42be8653c55..9032e7795980f 100644 --- a/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs +++ b/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs @@ -17,7 +17,7 @@ use stdarch_test::assert_instr; use crate::mem::transmute; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.ppc.vsx.lxvl"] fn lxvl(a: *const u8, l: usize) -> vector_signed_int; diff --git a/library/stdarch/crates/core_arch/src/x86/aes.rs b/library/stdarch/crates/core_arch/src/x86/aes.rs index d07ab4dc2a01e..ecb09a6a9099f 100644 --- a/library/stdarch/crates/core_arch/src/x86/aes.rs +++ b/library/stdarch/crates/core_arch/src/x86/aes.rs @@ -13,7 +13,7 @@ use crate::core_arch::x86::__m128i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.aesni.aesdec"] fn aesdec(a: __m128i, round_key: __m128i) -> __m128i; #[link_name = "llvm.x86.aesni.aesdeclast"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx.rs b/library/stdarch/crates/core_arch/src/x86/avx.rs index ef434205b52a0..f5ddff6457aea 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx.rs @@ -3292,7 +3292,7 @@ pub const fn _mm256_cvtss_f32(a: __m256) -> f32 { // LLVM intrinsics used in the above functions #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx.round.pd.256"] fn roundpd256(a: __m256d, b: i32) -> __m256d; #[link_name = "llvm.x86.avx.round.ps.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx2.rs b/library/stdarch/crates/core_arch/src/x86/avx2.rs index b49ad9522a412..6925ba8e27028 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx2.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx2.rs @@ -3906,7 +3906,7 @@ pub const fn _mm256_extract_epi16(a: __m256i) -> i32 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx2.pmadd.wd"] fn pmaddwd(a: i16x16, b: i16x16) -> i32x8; #[link_name = "llvm.x86.avx2.pmadd.ub.sw"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs b/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs index 8d944f5ba817b..230a4f3754cad 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs @@ -9,7 +9,7 @@ use crate::intrinsics::simd::*; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512bf16.cvtne2ps2bf16.128"] fn cvtne2ps2bf16(a: f32x4, b: f32x4) -> i16x8; #[link_name = "llvm.x86.avx512bf16.cvtne2ps2bf16.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs b/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs index dd211854afbb4..344130fb6e720 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs @@ -27,7 +27,7 @@ use crate::mem::transmute; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpshufbitqmb.512"] fn bitshuffle_512(data: i8x64, indices: i8x64) -> __mmask64; #[link_name = "llvm.x86.avx512.vpshufbitqmb.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512bw.rs b/library/stdarch/crates/core_arch/src/x86/avx512bw.rs index 659d6c3be88e7..50d57b4964489 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512bw.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512bw.rs @@ -12764,7 +12764,7 @@ pub unsafe fn _mm_mask_cvtusepi16_storeu_epi8(mem_addr: *mut i8, k: __mmask8, a: } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.pmul.hr.sw.512"] fn vpmulhrsw(a: i16x32, b: i16x32) -> i16x32; diff --git a/library/stdarch/crates/core_arch/src/x86/avx512cd.rs b/library/stdarch/crates/core_arch/src/x86/avx512cd.rs index 4082433e70759..378bc3617ed63 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512cd.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512cd.rs @@ -563,7 +563,7 @@ pub const fn _mm_maskz_lzcnt_epi64(k: __mmask8, a: __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.conflict.d.512"] fn vpconflictd(a: i32x16) -> i32x16; #[link_name = "llvm.x86.avx512.conflict.d.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512dq.rs b/library/stdarch/crates/core_arch/src/x86/avx512dq.rs index 0b322c8b83c7b..5fe40b7541549 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512dq.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512dq.rs @@ -7235,7 +7235,7 @@ pub fn _mm_mask_fpclass_ss_mask(k1: __mmask8, a: __m128) -> __m } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.sitofp.round.v2f64.v2i64"] fn vcvtqq2pd_128(a: i64x2, rounding: i32) -> f64x2; #[link_name = "llvm.x86.avx512.sitofp.round.v4f64.v4i64"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512f.rs b/library/stdarch/crates/core_arch/src/x86/avx512f.rs index 66ea63b674f12..225e447cea441 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512f.rs @@ -44215,7 +44215,7 @@ pub const _MM_PERM_DDDC: _MM_PERM_ENUM = 0xFE; pub const _MM_PERM_DDDD: _MM_PERM_ENUM = 0xFF; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.sqrt.ps.512"] fn vsqrtps(a: f32x16, rounding: i32) -> f32x16; #[link_name = "llvm.x86.avx512.sqrt.pd.512"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs b/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs index 5ce28565d1085..262215e62c43d 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs @@ -347,7 +347,7 @@ pub fn _mm_maskz_madd52lo_epu64(k: __mmask8, a: __m128i, b: __m128i, c: __m128i) } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpmadd52l.uq.128"] fn vpmadd52luq_128(z: __m128i, x: __m128i, y: __m128i) -> __m128i; #[link_name = "llvm.x86.avx512.vpmadd52h.uq.128"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs b/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs index d9ad14ef00ddb..141a8c8aec2bc 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs @@ -453,7 +453,7 @@ pub fn _mm_maskz_multishift_epi64_epi8(k: __mmask16, a: __m128i, b: __m128i) -> } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpermi2var.qi.512"] fn vpermi2b(a: i8x64, idx: i8x64, b: i8x64) -> i8x64; #[link_name = "llvm.x86.avx512.vpermi2var.qi.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs b/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs index 78a50b90c8614..0a4accc2f70fb 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs @@ -2383,7 +2383,7 @@ pub const fn _mm_maskz_shrdi_epi16( } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.mask.compress.store.w.512"] fn vcompressstorew(mem: *mut i8, data: i16x32, mask: u32); #[link_name = "llvm.x86.avx512.mask.compress.store.w.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs b/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs index 8cd8764f24868..b9084aec59344 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs @@ -873,7 +873,7 @@ pub fn _mm256_dpwuuds_epi32(src: __m256i, a: __m256i, b: __m256i) -> __m256i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpdpwssd.512"] fn vpdpwssd(src: i32x16, a: i16x32, b: i16x32) -> i32x16; #[link_name = "llvm.x86.avx512.vpdpwssd.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs b/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs index 4dd7412e9e330..30fcfce63ccf4 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs @@ -110,7 +110,7 @@ pub unsafe fn _mm512_2intersect_epi64( } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vp2intersect.d.128"] fn vp2intersectd_128(a: i32x4, b: i32x4) -> (u8, u8); #[link_name = "llvm.x86.avx512.vp2intersect.q.128"] diff --git a/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs b/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs index 861213eb4257f..b17d3de48b595 100644 --- a/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs +++ b/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs @@ -176,7 +176,7 @@ pub fn _mm256_cvtneps_avx_pbh(a: __m256) -> __m128bh { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.vbcstnebf162ps128"] fn bcstnebf162ps_128(a: *const bf16) -> __m128; #[link_name = "llvm.x86.vbcstnebf162ps256"] diff --git a/library/stdarch/crates/core_arch/src/x86/bmi1.rs b/library/stdarch/crates/core_arch/src/x86/bmi1.rs index 432051abd1cfa..21da03667f849 100644 --- a/library/stdarch/crates/core_arch/src/x86/bmi1.rs +++ b/library/stdarch/crates/core_arch/src/x86/bmi1.rs @@ -131,7 +131,7 @@ pub const fn _mm_tzcnt_32(x: u32) -> i32 { x.trailing_zeros() as i32 } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bextr.32"] fn x86_bmi_bextr_32(x: u32, y: u32) -> u32; } diff --git a/library/stdarch/crates/core_arch/src/x86/bmi2.rs b/library/stdarch/crates/core_arch/src/x86/bmi2.rs index 5320640d96873..dae4133d63bec 100644 --- a/library/stdarch/crates/core_arch/src/x86/bmi2.rs +++ b/library/stdarch/crates/core_arch/src/x86/bmi2.rs @@ -67,7 +67,7 @@ pub fn _pext_u32(a: u32, mask: u32) -> u32 { unsafe { x86_bmi2_pext_32(a, mask) } } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bzhi.32"] fn x86_bmi2_bzhi_32(x: u32, y: u32) -> u32; #[link_name = "llvm.x86.bmi.pdep.32"] diff --git a/library/stdarch/crates/core_arch/src/x86/fxsr.rs b/library/stdarch/crates/core_arch/src/x86/fxsr.rs index 08619efe7c9ef..74a95e192a3af 100644 --- a/library/stdarch/crates/core_arch/src/x86/fxsr.rs +++ b/library/stdarch/crates/core_arch/src/x86/fxsr.rs @@ -4,7 +4,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.fxsave"] fn fxsave(p: *mut u8); #[link_name = "llvm.x86.fxrstor"] diff --git a/library/stdarch/crates/core_arch/src/x86/gfni.rs b/library/stdarch/crates/core_arch/src/x86/gfni.rs index e9ee27a7b823b..8cebd04a4b836 100644 --- a/library/stdarch/crates/core_arch/src/x86/gfni.rs +++ b/library/stdarch/crates/core_arch/src/x86/gfni.rs @@ -23,7 +23,7 @@ use crate::mem::transmute; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.vgf2p8affineinvqb.512"] fn vgf2p8affineinvqb_512(x: i8x64, a: i8x64, imm8: u8) -> i8x64; #[link_name = "llvm.x86.vgf2p8affineinvqb.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs b/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs index 0f2769257f958..454785c5e0c89 100644 --- a/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs +++ b/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs @@ -11,7 +11,7 @@ use crate::core_arch::x86::__m128i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.pclmulqdq"] fn pclmulqdq(a: __m128i, round_key: __m128i, imm8: u8) -> __m128i; } diff --git a/library/stdarch/crates/core_arch/src/x86/rtm.rs b/library/stdarch/crates/core_arch/src/x86/rtm.rs index c88bd6592d784..f09b95d76bdb3 100644 --- a/library/stdarch/crates/core_arch/src/x86/rtm.rs +++ b/library/stdarch/crates/core_arch/src/x86/rtm.rs @@ -16,7 +16,7 @@ #[cfg(test)] use stdarch_test::assert_instr; -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.xbegin"] fn x86_xbegin() -> i32; #[link_name = "llvm.x86.xend"] diff --git a/library/stdarch/crates/core_arch/src/x86/sha.rs b/library/stdarch/crates/core_arch/src/x86/sha.rs index f8a3295d19589..f7d8c3c0b2408 100644 --- a/library/stdarch/crates/core_arch/src/x86/sha.rs +++ b/library/stdarch/crates/core_arch/src/x86/sha.rs @@ -1,7 +1,7 @@ use crate::core_arch::{simd::*, x86::*}; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sha1msg1"] fn sha1msg1(a: i32x4, b: i32x4) -> i32x4; #[link_name = "llvm.x86.sha1msg2"] diff --git a/library/stdarch/crates/core_arch/src/x86/sse.rs b/library/stdarch/crates/core_arch/src/x86/sse.rs index afaaccfbf27f4..e31175d48cf94 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse.rs @@ -1987,7 +1987,7 @@ pub const fn _MM_TRANSPOSE4_PS( } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse.rcp.ss"] fn rcpss(a: __m128) -> __m128; #[link_name = "llvm.x86.sse.rcp.ps"] diff --git a/library/stdarch/crates/core_arch/src/x86/sse2.rs b/library/stdarch/crates/core_arch/src/x86/sse2.rs index 1f97f3c69d0e3..983d7ff23d39f 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse2.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse2.rs @@ -3236,7 +3236,7 @@ pub const fn _mm_unpacklo_pd(a: __m128d, b: __m128d) -> __m128d { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse2.pause"] fn pause(); #[link_name = "llvm.x86.sse2.clflush"] diff --git a/library/stdarch/crates/core_arch/src/x86/sse3.rs b/library/stdarch/crates/core_arch/src/x86/sse3.rs index e4c75702544d9..2f6fa35cb616a 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse3.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse3.rs @@ -178,7 +178,7 @@ pub const fn _mm_moveldup_ps(a: __m128) -> __m128 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse3.ldu.dq"] fn lddqu(mem_addr: *const i8) -> i8x16; } diff --git a/library/stdarch/crates/core_arch/src/x86/sse41.rs b/library/stdarch/crates/core_arch/src/x86/sse41.rs index 4ebf7d3bd39a8..d981166ff08c9 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse41.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse41.rs @@ -1181,7 +1181,7 @@ pub unsafe fn _mm_stream_load_si128(mem_addr: *const __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse41.insertps"] fn insertps(a: __m128, b: __m128, imm8: u8) -> __m128; #[link_name = "llvm.x86.sse41.dppd"] diff --git a/library/stdarch/crates/core_arch/src/x86/sse42.rs b/library/stdarch/crates/core_arch/src/x86/sse42.rs index 55e22592637f1..0534fe9379a18 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse42.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse42.rs @@ -569,7 +569,7 @@ pub const fn _mm_cmpgt_epi64(a: __m128i, b: __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { // SSE 4.2 string and text comparison ops #[link_name = "llvm.x86.sse42.pcmpestrm128"] fn pcmpestrm128(a: i8x16, la: i32, b: i8x16, lb: i32, imm8: i8) -> u8x16; diff --git a/library/stdarch/crates/core_arch/src/x86/sse4a.rs b/library/stdarch/crates/core_arch/src/x86/sse4a.rs index f36b879a030e3..14e9b56dcb2ed 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse4a.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse4a.rs @@ -6,7 +6,7 @@ use crate::core_arch::{simd::*, x86::*}; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse4a.extrq"] fn extrq(x: i64x2, y: i8x16) -> i64x2; #[link_name = "llvm.x86.sse4a.extrqi"] diff --git a/library/stdarch/crates/core_arch/src/x86/ssse3.rs b/library/stdarch/crates/core_arch/src/x86/ssse3.rs index 1d7a97944a37b..b0152c81596bd 100644 --- a/library/stdarch/crates/core_arch/src/x86/ssse3.rs +++ b/library/stdarch/crates/core_arch/src/x86/ssse3.rs @@ -345,7 +345,7 @@ pub fn _mm_sign_epi32(a: __m128i, b: __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.ssse3.pshuf.b.128"] fn pshufb128(a: u8x16, b: u8x16) -> u8x16; diff --git a/library/stdarch/crates/core_arch/src/x86/tbm.rs b/library/stdarch/crates/core_arch/src/x86/tbm.rs index 0ba4572dcd029..2a44579408364 100644 --- a/library/stdarch/crates/core_arch/src/x86/tbm.rs +++ b/library/stdarch/crates/core_arch/src/x86/tbm.rs @@ -13,7 +13,7 @@ #[cfg(test)] use stdarch_test::assert_instr; -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.tbm.bextri.u32"] fn bextri_u32(a: u32, control: u32) -> u32; } diff --git a/library/stdarch/crates/core_arch/src/x86/vaes.rs b/library/stdarch/crates/core_arch/src/x86/vaes.rs index 864b1d56d1057..72dd387f900d5 100644 --- a/library/stdarch/crates/core_arch/src/x86/vaes.rs +++ b/library/stdarch/crates/core_arch/src/x86/vaes.rs @@ -14,7 +14,7 @@ use crate::core_arch::x86::__m512i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.aesni.aesenc.256"] fn aesenc_256(a: __m256i, round_key: __m256i) -> __m256i; #[link_name = "llvm.x86.aesni.aesenclast.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs b/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs index ad44e59f3ada1..4c39ae6ee7aa3 100644 --- a/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs +++ b/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs @@ -12,7 +12,7 @@ use crate::core_arch::x86::__m512i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.pclmulqdq.256"] fn pclmulqdq_256(a: __m256i, round_key: __m256i, imm8: u8) -> __m256i; #[link_name = "llvm.x86.pclmulqdq.512"] diff --git a/library/stdarch/crates/core_arch/src/x86/xsave.rs b/library/stdarch/crates/core_arch/src/x86/xsave.rs index e22d3580ff463..395e2c64be3a0 100644 --- a/library/stdarch/crates/core_arch/src/x86/xsave.rs +++ b/library/stdarch/crates/core_arch/src/x86/xsave.rs @@ -5,7 +5,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.xsave"] fn xsave(p: *mut u8, hi: u32, lo: u32); #[link_name = "llvm.x86.xrstor"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/amx.rs b/library/stdarch/crates/core_arch/src/x86_64/amx.rs index 08585d2067c15..2e46e50f9ff9b 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/amx.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/amx.rs @@ -595,7 +595,7 @@ pub unsafe fn _tile_movrowi() -> __m512i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.ldtilecfg"] fn ldtilecfg(mem_addr: *const u8); #[link_name = "llvm.x86.sttilecfg"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs index 0fd9b09363d4b..e26bfc5c1ca4f 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs @@ -527,7 +527,7 @@ pub fn _mm_cvtt_roundss_u64(a: __m128) -> u64 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vcvtss2si64"] fn vcvtss2si64(a: f32x4, rounding: i32) -> i64; #[link_name = "llvm.x86.avx512.vcvtss2usi64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs index 2a511328bb382..5baf1c9036e19 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs @@ -211,7 +211,7 @@ pub fn _mm_cvtt_roundsh_u64(a: __m128h) -> u64 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512fp16.vcvtsi642sh"] fn vcvtsi642sh(a: __m128h, b: i64, rounding: i32) -> __m128h; #[link_name = "llvm.x86.avx512fp16.vcvtusi642sh"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/bmi.rs b/library/stdarch/crates/core_arch/src/x86_64/bmi.rs index 8d2b22089ac10..f082953f9df90 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/bmi.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/bmi.rs @@ -122,7 +122,7 @@ pub const fn _mm_tzcnt_64(x: u64) -> i64 { x.trailing_zeros() as i64 } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bextr.64"] fn x86_bmi_bextr_64(x: u64, y: u64) -> u64; } diff --git a/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs b/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs index 6151eee8bdbb5..81ab9e05add96 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs @@ -69,7 +69,7 @@ pub fn _pext_u64(a: u64, mask: u64) -> u64 { unsafe { x86_bmi2_pext_64(a, mask) } } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bzhi.64"] fn x86_bmi2_bzhi_64(x: u64, y: u64) -> u64; #[link_name = "llvm.x86.bmi.pdep.64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs b/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs index 28bf1951167a5..f50dacb4bee4f 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs @@ -4,7 +4,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.fxsave64"] fn fxsave64(p: *mut u8); #[link_name = "llvm.x86.fxrstor64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse.rs b/library/stdarch/crates/core_arch/src/x86_64/sse.rs index 81e1070b55691..521652c126914 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse.rs @@ -6,7 +6,7 @@ use crate::core_arch::x86::*; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse.cvtss2si64"] fn cvtss2si64(a: __m128) -> i64; #[link_name = "llvm.x86.sse.cvttss2si64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse2.rs b/library/stdarch/crates/core_arch/src/x86_64/sse2.rs index 08dabf053d428..c4768cedbfa6b 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse2.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse2.rs @@ -6,7 +6,7 @@ use crate::core_arch::x86::*; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse2.cvtsd2si64"] fn cvtsd2si64(a: __m128d) -> i64; #[link_name = "llvm.x86.sse2.cvttsd2si64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse42.rs b/library/stdarch/crates/core_arch/src/x86_64/sse42.rs index cd32c149aff5b..307e6167aabae 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse42.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse42.rs @@ -4,7 +4,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse42.crc32.64.64"] fn crc32_64_64(crc: u64, v: u64) -> u64; } diff --git a/library/stdarch/crates/core_arch/src/x86_64/tbm.rs b/library/stdarch/crates/core_arch/src/x86_64/tbm.rs index fe12538b07a06..b4ec50a66fa0b 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/tbm.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/tbm.rs @@ -13,7 +13,7 @@ #[cfg(test)] use stdarch_test::assert_instr; -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.tbm.bextri.u64"] fn bextri_u64(a: u64, control: u64) -> u64; } diff --git a/library/stdarch/crates/core_arch/src/x86_64/xsave.rs b/library/stdarch/crates/core_arch/src/x86_64/xsave.rs index 30a7123315e5f..c307d40073433 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/xsave.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/xsave.rs @@ -6,7 +6,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.xsave64"] fn xsave64(p: *mut u8, hi: u32, lo: u32); #[link_name = "llvm.x86.xrstor64"] From fe9d6fe6f2e0db1dde6bdce709e6c863fc43a095 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 3 Jun 2026 22:03:14 +0200 Subject: [PATCH 015/278] test that even ZST references prevent deallocation via child pointers --- .../src/borrow_tracker/tree_borrows/tree.rs | 4 +- .../deallocate_against_protector1.rs | 6 ++- ...eallocate_against_protector1.stack.stderr} | 8 ++-- .../deallocate_against_protector1.tree.stderr | 40 +++++++++++++++++++ .../deallocate_against_protector2.rs | 23 +++++++++++ ...deallocate_against_protector2.stack.stderr | 27 +++++++++++++ .../deallocate_against_protector2.tree.stderr | 29 ++++++++++++++ 7 files changed, 131 insertions(+), 6 deletions(-) rename src/tools/miri/tests/fail/{stacked_borrows => both_borrows}/deallocate_against_protector1.rs (50%) rename src/tools/miri/tests/fail/{stacked_borrows/deallocate_against_protector1.stderr => both_borrows/deallocate_against_protector1.stack.stderr} (76%) create mode 100644 src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr create mode 100644 src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs create mode 100644 src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr create mode 100644 src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 296de803d5828..b58a7cfeba92e 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -574,7 +574,9 @@ impl<'tcx> Tree { // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`). // Related to https://github.com/rust-lang/rust/issues/55005. && !perm.permission.is_cell() - // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579. + // Only trigger UB if the accessed bit is set, i.e. if the protector + // is actually protecting this offset. See #4579. Note that this + // takes into account the access we just did above! && perm.accessed { Err(TbError { diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs similarity index 50% rename from src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs rename to src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs index a34df7c7fe3ae..211d74caadd8a 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs @@ -1,4 +1,8 @@ -//@error-in-other-file: /deallocating while item \[Unique for .*\] is strongly protected/ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +//@[stack]error-in-other-file: /deallocating while item \[Unique for .*\] is strongly protected/ +//@[tree]error-in-other-file: /deallocation through .* is forbidden/ fn inner(x: &mut i32, f: fn(&mut i32)) { // `f` may mutate, but it may not deallocate! diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr similarity index 76% rename from src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr rename to src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr index 9f0df14ee4ddd..b8a763a5fc3f4 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr @@ -14,13 +14,13 @@ LL | self.1.deallocate(From::from(ptr.cast()), layout); 2: std::mem::drop::> at RUSTLIB/core/src/mem/mod.rs:LL:CC 3: main::{closure#0} - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC - 4: <{closure@tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 4: <{closure@tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim at RUSTLIB/core/src/ops/function.rs:LL:CC 5: inner - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC 6: main - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr new file mode 100644 index 0000000000000..3e97a15333ac6 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr @@ -0,0 +1,40 @@ +error: Undefined Behavior: deallocation through at ALLOC[0x0] is forbidden + --> RUSTLIB/alloc/src/boxed.rs:LL:CC + | +LL | self.1.deallocate(From::from(ptr.cast()), layout); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the allocation of the accessed tag also contains the strongly protected tag + = help: the strongly protected tag disallows deallocations +help: the accessed tag was created here + --> tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + | +LL | drop(unsafe { Box::from_raw(raw) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the strongly protected tag was created here, in the initial state Reserved + --> tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + | +LL | inner(Box::leak(Box::new(0)), |x| { + | ^ + = note: stack backtrace: + 0: as std::ops::Drop>::drop + at RUSTLIB/alloc/src/boxed.rs:LL:CC + 1: std::ptr::drop_glue::> - shim(Some(std::boxed::Box)) + at RUSTLIB/core/src/ptr/mod.rs:LL:CC + 2: std::mem::drop::> + at RUSTLIB/core/src/mem/mod.rs:LL:CC + 3: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 4: <{closure@tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 5: inner + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 6: main + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + +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/both_borrows/deallocate_against_protector2.rs b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs new file mode 100644 index 0000000000000..4cac0cdd03f8b --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs @@ -0,0 +1,23 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +// Ensure that even a ZST prevents the reference from being used for deallocation. +// The `nofree` attributes we add in LLVM IR rely on this. + +use std::alloc::Layout; + +fn inner(x: &mut (), f: fn(&mut ())) { + // `f` may mutate, but it may not deallocate! + f(x) +} + +fn main() { + let ptr = Box::leak(Box::new(0i32)) as *mut i32; + inner(unsafe { &mut *(ptr as *mut ()) }, |x| unsafe { + let raw = x as *mut _ as *mut i32; + // Avoid ever creating a `Box`, we don't want any implicit accesses. + std::alloc::dealloc(raw.cast(), Layout::new::()); + //~[tree]^ERROR: /deallocation through .* is forbidden/ + //~[stack]|ERROR: tag does not exist in the borrow stack for this location + }); +} diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr new file mode 100644 index 0000000000000..20993ef5072b1 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr @@ -0,0 +1,27 @@ +error: Undefined Behavior: attempting deallocation using at ALLOC, but that tag does not exist in the borrow stack for this location + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | std::alloc::dealloc(raw.cast(), Layout::new::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: would have been created here, but this is a zero-size retag ([0x0..0x0]) so the tag in question does not exist anywhere + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | let raw = x as *mut _ as *mut i32; + | ^ + = note: stack backtrace: + 0: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 1: <{closure@tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC} as std::ops::FnOnce<(&mut (),)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 2: inner + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 3: main + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + +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/both_borrows/deallocate_against_protector2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr new file mode 100644 index 0000000000000..7fae0b6a07773 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr @@ -0,0 +1,29 @@ +error: Undefined Behavior: deallocation through at ALLOC[0x0] is forbidden + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | std::alloc::dealloc(raw.cast(), Layout::new::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the allocation of the accessed tag also contains the strongly protected tag + = help: the strongly protected tag disallows deallocations +help: the strongly protected tag was created here, in the initial state Reserved + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | inner(unsafe { &mut *(ptr as *mut ()) }, |x| unsafe { + | ^ + = note: stack backtrace: + 0: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 1: <{closure@tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC} as std::ops::FnOnce<(&mut (),)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 2: inner + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 3: main + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From a44451731d6c3fe201fd386456eb6893c440d9af Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 8 Jun 2026 05:38:35 +0000 Subject: [PATCH 016/278] Prepare for merging from rust-lang/rust This updates the rust-version file to 029c9e18dd1f4668e1d42bb187c1c263dfe20093. --- library/stdarch/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/stdarch/rust-version b/library/stdarch/rust-version index 59e9e5a0e6ee9..387bd8edd2196 100644 --- a/library/stdarch/rust-version +++ b/library/stdarch/rust-version @@ -1 +1 @@ -045b17737dab5fcc28e4cbee0cfe2ce4ed363b32 +029c9e18dd1f4668e1d42bb187c1c263dfe20093 From 029b8d2e4e68243acbf1cd0b27b8d36860761e90 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 21 May 2026 19:06:48 +0200 Subject: [PATCH 017/278] Attributes cleanup in tests [12/20] --- tests/ui/methods.rs | 22 +------- tests/ui/methods.stderr | 6 +-- tests/ui/methods_fixable.fixed | 2 +- tests/ui/methods_fixable.rs | 2 +- tests/ui/min_ident_chars.rs | 4 +- tests/ui/min_max.rs | 3 +- tests/ui/min_max.stderr | 29 +++++------ tests/ui/min_rust_version_attr.rs | 1 - tests/ui/min_rust_version_attr.stderr | 12 ++--- tests/ui/mismatching_type_param_order.rs | 1 - tests/ui/mismatching_type_param_order.stderr | 20 ++++---- tests/ui/misnamed_getters.fixed | 3 +- tests/ui/misnamed_getters.rs | 3 +- tests/ui/misnamed_getters.stderr | 36 ++++++------- tests/ui/misnamed_getters_2021.fixed | 2 - tests/ui/misnamed_getters_2021.rs | 2 - tests/ui/misnamed_getters_2021.stderr | 2 +- tests/ui/misrefactored_assign_op.1.fixed | 3 +- tests/ui/misrefactored_assign_op.2.fixed | 3 +- tests/ui/misrefactored_assign_op.rs | 3 +- tests/ui/misrefactored_assign_op.stderr | 18 +++---- .../missing_asserts_for_indexing_unfixable.rs | 1 - ...sing_asserts_for_indexing_unfixable.stderr | 20 ++++---- .../missing_const_for_fn/could_be_const.fixed | 2 +- .../ui/missing_const_for_fn/could_be_const.rs | 2 +- tests/ui/missing_fields_in_debug.rs | 1 - tests/ui/missing_fields_in_debug.stderr | 16 +++--- tests/ui/missing_inline.rs | 2 +- tests/ui/missing_panics_doc.rs | 2 +- tests/ui/missing_spin_loop.fixed | 3 +- tests/ui/missing_spin_loop.rs | 3 +- tests/ui/missing_spin_loop.stderr | 12 ++--- tests/ui/missing_trait_methods.rs | 2 +- tests/ui/missing_transmute_annotations.fixed | 2 +- tests/ui/missing_transmute_annotations.rs | 2 +- tests/ui/mistyped_literal_suffix.fixed | 6 +-- tests/ui/mistyped_literal_suffix.rs | 6 +-- tests/ui/mistyped_literal_suffix.stderr | 35 ++++++------- tests/ui/module_name_repetitions.rs | 1 - tests/ui/module_name_repetitions.stderr | 12 ++--- tests/ui/modulo_arithmetic_float.rs | 2 +- tests/ui/modulo_arithmetic_integral.rs | 2 +- tests/ui/modulo_arithmetic_integral_const.rs | 7 +-- .../modulo_arithmetic_integral_const.stderr | 34 ++++++------- tests/ui/modulo_one.rs | 3 +- tests/ui/modulo_one.stderr | 12 ++--- .../ui/msrv_attributes_without_early_lints.rs | 1 - tests/ui/multiple_unsafe_ops_per_block.rs | 6 +-- tests/ui/must_use_candidates.fixed | 6 --- tests/ui/must_use_candidates.rs | 6 --- tests/ui/must_use_candidates.stderr | 14 +++--- tests/ui/must_use_unit.fixed | 2 +- tests/ui/must_use_unit.rs | 2 +- tests/ui/mut_from_ref.rs | 9 ++-- tests/ui/mut_from_ref.stderr | 32 ++++++------ tests/ui/mut_mut.fixed | 8 --- tests/ui/mut_mut.rs | 8 --- tests/ui/mut_mut.stderr | 14 +++--- tests/ui/mut_mut_unfixable.rs | 1 - tests/ui/mut_mut_unfixable.stderr | 12 ++--- tests/ui/mut_mutex_lock.fixed | 1 - tests/ui/mut_mutex_lock.rs | 1 - tests/ui/mut_mutex_lock.stderr | 4 +- tests/ui/mut_range_bound.rs | 2 +- tests/ui/mutex_atomic.fixed | 5 +- tests/ui/mutex_atomic.rs | 5 +- tests/ui/mutex_atomic.stderr | 30 +++++------ tests/ui/needless_arbitrary_self_type.fixed | 2 +- tests/ui/needless_arbitrary_self_type.rs | 2 +- tests/ui/needless_bitwise_bool.fixed | 2 +- tests/ui/needless_bitwise_bool.rs | 2 +- tests/ui/needless_bool/fixable.fixed | 14 +----- tests/ui/needless_bool/fixable.rs | 14 +----- tests/ui/needless_bool/fixable.stderr | 50 +++++++++---------- tests/ui/needless_bool/simple.rs | 9 +--- tests/ui/needless_bool/simple.stderr | 8 +-- tests/ui/needless_bool_assign.fixed | 1 - tests/ui/needless_bool_assign.rs | 1 - tests/ui/needless_bool_assign.stderr | 14 +++--- 79 files changed, 278 insertions(+), 377 deletions(-) diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index f73ec563dceb1..b45b7dcd56a43 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -1,25 +1,7 @@ //@aux-build:option_helpers.rs -#![allow( - clippy::disallowed_names, - clippy::default_trait_access, - clippy::let_underscore_untyped, - clippy::missing_docs_in_private_items, - clippy::missing_safety_doc, - clippy::non_ascii_literal, - clippy::new_without_default, - clippy::needless_pass_by_value, - clippy::needless_lifetimes, - clippy::elidable_lifetime_names, - clippy::print_stdout, - clippy::must_use_candidate, - clippy::use_self, - clippy::useless_format, - clippy::wrong_self_convention, - clippy::unused_async, - clippy::unused_self, - clippy::useless_vec -)] +#![warn(clippy::filter_next, clippy::new_ret_no_self)] +#![expect(clippy::disallowed_names, clippy::useless_vec)] #[macro_use] extern crate option_helpers; diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 45efea4ee01cd..a637818cee48c 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> tests/ui/methods.rs:102:5 + --> tests/ui/methods.rs:84:5 | LL | / fn new() -> i32 { LL | | @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::new_ret_no_self)]` error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods.rs:124:13 + --> tests/ui/methods.rs:106:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -25,7 +25,7 @@ LL | | ).next(); = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods.rs:143:13 + --> tests/ui/methods.rs:125:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ diff --git a/tests/ui/methods_fixable.fixed b/tests/ui/methods_fixable.fixed index 287d8d881ec28..17a751eb79457 100644 --- a/tests/ui/methods_fixable.fixed +++ b/tests/ui/methods_fixable.fixed @@ -1,5 +1,5 @@ #![warn(clippy::filter_next)] -#![allow(clippy::useless_vec)] +#![expect(clippy::useless_vec)] /// Checks implementation of `FILTER_NEXT` lint. fn main() { diff --git a/tests/ui/methods_fixable.rs b/tests/ui/methods_fixable.rs index 11ce1b63560db..b2520acc36385 100644 --- a/tests/ui/methods_fixable.rs +++ b/tests/ui/methods_fixable.rs @@ -1,5 +1,5 @@ #![warn(clippy::filter_next)] -#![allow(clippy::useless_vec)] +#![expect(clippy::useless_vec)] /// Checks implementation of `FILTER_NEXT` lint. fn main() { diff --git a/tests/ui/min_ident_chars.rs b/tests/ui/min_ident_chars.rs index e2f82e2a182f6..298798be7a1e6 100644 --- a/tests/ui/min_ident_chars.rs +++ b/tests/ui/min_ident_chars.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs -#![allow(irrefutable_let_patterns, nonstandard_style, unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::min_ident_chars)] +#![expect(irrefutable_let_patterns, nonstandard_style)] +#![allow(clippy::struct_field_names)] extern crate proc_macros; use proc_macros::{external, with_span}; diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index ee19d3ff71421..cc97e496514bc 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -1,4 +1,5 @@ -#![allow(clippy::manual_clamp)] +#![warn(clippy::min_max)] +#![expect(clippy::manual_clamp)] use std::cmp::{max as my_max, max, min as my_min, min}; diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index 87510a465a08b..84b4d37545529 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -1,79 +1,80 @@ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:21:5 + --> tests/ui/min_max.rs:22:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ | - = note: `#[deny(clippy::min_max)]` on by default + = note: `-D clippy::min-max` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::min_max)]` error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:24:5 + --> tests/ui/min_max.rs:25:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:27:5 + --> tests/ui/min_max.rs:28:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:30:5 + --> tests/ui/min_max.rs:31:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:33:5 + --> tests/ui/min_max.rs:34:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:44:5 + --> tests/ui/min_max.rs:45:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:47:5 + --> tests/ui/min_max.rs:48:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:53:5 + --> tests/ui/min_max.rs:54:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:56:5 + --> tests/ui/min_max.rs:57:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:59:5 + --> tests/ui/min_max.rs:60:5 | LL | f.max(3f32).min(1f32); | ^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:66:5 + --> tests/ui/min_max.rs:67:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:71:5 + --> tests/ui/min_max.rs:72:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:74:5 + --> tests/ui/min_max.rs:75:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 4627bef28a29b..c624774a8a9cf 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -1,4 +1,3 @@ -#![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] fn main() {} diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 809b1cfe73bf0..842bd5c238618 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,5 +1,5 @@ error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:13:19 + --> tests/ui/min_rust_version_attr.rs:12:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let log2_10 = 3.321928094887362; = note: `#[deny(clippy::approx_constant)]` on by default error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:19:19 + --> tests/ui/min_rust_version_attr.rs:18:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:30:19 + --> tests/ui/min_rust_version_attr.rs:29:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:41:19 + --> tests/ui/min_rust_version_attr.rs:40:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:52:19 + --> tests/ui/min_rust_version_attr.rs:51:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:60:27 + --> tests/ui/min_rust_version_attr.rs:59:27 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/mismatching_type_param_order.rs b/tests/ui/mismatching_type_param_order.rs index 11ff865b583bf..49d7031441066 100644 --- a/tests/ui/mismatching_type_param_order.rs +++ b/tests/ui/mismatching_type_param_order.rs @@ -1,5 +1,4 @@ #![warn(clippy::mismatching_type_param_order)] -#![allow(clippy::disallowed_names, clippy::needless_lifetimes)] fn main() { struct Foo { diff --git a/tests/ui/mismatching_type_param_order.stderr b/tests/ui/mismatching_type_param_order.stderr index 214d9d734d1bb..a1b5ca7ec26c7 100644 --- a/tests/ui/mismatching_type_param_order.stderr +++ b/tests/ui/mismatching_type_param_order.stderr @@ -1,5 +1,5 @@ error: `Foo` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:11:20 + --> tests/ui/mismatching_type_param_order.rs:10:20 | LL | impl Foo {} | ^ @@ -9,7 +9,7 @@ LL | impl Foo {} = help: to override `-D warnings` add `#[allow(clippy::mismatching_type_param_order)]` error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:11:23 + --> tests/ui/mismatching_type_param_order.rs:10:23 | LL | impl Foo {} | ^ @@ -17,7 +17,7 @@ LL | impl Foo {} = help: try `B`, or a name that does not conflict with `Foo`'s generic params error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:16:23 + --> tests/ui/mismatching_type_param_order.rs:15:23 | LL | impl Foo {} | ^ @@ -25,7 +25,7 @@ LL | impl Foo {} = help: try `B`, or a name that does not conflict with `Foo`'s generic params error: `FooLifetime` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:28:44 + --> tests/ui/mismatching_type_param_order.rs:27:44 | LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} | ^ @@ -33,7 +33,7 @@ LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} = help: try `A`, or a name that does not conflict with `FooLifetime`'s generic params error: `FooLifetime` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:28:47 + --> tests/ui/mismatching_type_param_order.rs:27:47 | LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} | ^ @@ -41,7 +41,7 @@ LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} = help: try `B`, or a name that does not conflict with `FooLifetime`'s generic params error: `FooEnum` has a similarly named generic type parameter `C` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:27 + --> tests/ui/mismatching_type_param_order.rs:45:27 | LL | impl FooEnum {} | ^ @@ -49,7 +49,7 @@ LL | impl FooEnum {} = help: try `A`, or a name that does not conflict with `FooEnum`'s generic params error: `FooEnum` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:30 + --> tests/ui/mismatching_type_param_order.rs:45:30 | LL | impl FooEnum {} | ^ @@ -57,7 +57,7 @@ LL | impl FooEnum {} = help: try `B`, or a name that does not conflict with `FooEnum`'s generic params error: `FooEnum` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:33 + --> tests/ui/mismatching_type_param_order.rs:45:33 | LL | impl FooEnum {} | ^ @@ -65,7 +65,7 @@ LL | impl FooEnum {} = help: try `C`, or a name that does not conflict with `FooEnum`'s generic params error: `FooUnion` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:60:31 + --> tests/ui/mismatching_type_param_order.rs:59:31 | LL | impl FooUnion where A: Copy {} | ^ @@ -73,7 +73,7 @@ LL | impl FooUnion where A: Copy {} = help: try `A`, or a name that does not conflict with `FooUnion`'s generic params error: `FooUnion` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:60:34 + --> tests/ui/mismatching_type_param_order.rs:59:34 | LL | impl FooUnion where A: Copy {} | ^ diff --git a/tests/ui/misnamed_getters.fixed b/tests/ui/misnamed_getters.fixed index bc123d1a40ba2..479d5f91492d5 100644 --- a/tests/ui/misnamed_getters.fixed +++ b/tests/ui/misnamed_getters.fixed @@ -1,6 +1,5 @@ -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] +#![expect(clippy::struct_field_names)] struct A { a: u8, diff --git a/tests/ui/misnamed_getters.rs b/tests/ui/misnamed_getters.rs index 6590101157c3f..985c273c59ed7 100644 --- a/tests/ui/misnamed_getters.rs +++ b/tests/ui/misnamed_getters.rs @@ -1,6 +1,5 @@ -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] +#![expect(clippy::struct_field_names)] struct A { a: u8, diff --git a/tests/ui/misnamed_getters.stderr b/tests/ui/misnamed_getters.stderr index aaf21cecb9255..37f57d7f79e55 100644 --- a/tests/ui/misnamed_getters.stderr +++ b/tests/ui/misnamed_getters.stderr @@ -1,5 +1,5 @@ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:12:5 + --> tests/ui/misnamed_getters.rs:11:5 | LL | / fn a(&self) -> &u8 { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::misnamed_getters)]` error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:17:5 + --> tests/ui/misnamed_getters.rs:16:5 | LL | / fn a_mut(&mut self) -> &mut u8 { LL | | @@ -24,7 +24,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:23:5 + --> tests/ui/misnamed_getters.rs:22:5 | LL | / fn b(self) -> u8 { LL | | @@ -35,7 +35,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:29:5 + --> tests/ui/misnamed_getters.rs:28:5 | LL | / fn b_mut(&mut self) -> &mut u8 { LL | | @@ -46,7 +46,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:35:5 + --> tests/ui/misnamed_getters.rs:34:5 | LL | / fn c(&self) -> &u8 { LL | | @@ -57,7 +57,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:41:5 + --> tests/ui/misnamed_getters.rs:40:5 | LL | / fn c_mut(&mut self) -> &mut u8 { LL | | @@ -68,7 +68,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:54:5 + --> tests/ui/misnamed_getters.rs:53:5 | LL | / unsafe fn a(&self) -> &u8 { LL | | @@ -79,7 +79,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:59:5 + --> tests/ui/misnamed_getters.rs:58:5 | LL | / unsafe fn a_mut(&mut self) -> &mut u8 { LL | | @@ -90,7 +90,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:65:5 + --> tests/ui/misnamed_getters.rs:64:5 | LL | / unsafe fn b(self) -> u8 { LL | | @@ -101,7 +101,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:71:5 + --> tests/ui/misnamed_getters.rs:70:5 | LL | / unsafe fn b_mut(&mut self) -> &mut u8 { LL | | @@ -112,7 +112,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:85:5 + --> tests/ui/misnamed_getters.rs:84:5 | LL | / unsafe fn a_unchecked(&self) -> &u8 { LL | | @@ -123,7 +123,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:90:5 + --> tests/ui/misnamed_getters.rs:89:5 | LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { LL | | @@ -134,7 +134,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:96:5 + --> tests/ui/misnamed_getters.rs:95:5 | LL | / unsafe fn b_unchecked(self) -> u8 { LL | | @@ -145,7 +145,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:102:5 + --> tests/ui/misnamed_getters.rs:101:5 | LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { LL | | @@ -156,7 +156,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:136:5 + --> tests/ui/misnamed_getters.rs:135:5 | LL | / fn a(&self) -> &u8 { LL | | @@ -167,7 +167,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:141:5 + --> tests/ui/misnamed_getters.rs:140:5 | LL | / fn a_mut(&mut self) -> &mut u8 { LL | | @@ -178,7 +178,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:147:5 + --> tests/ui/misnamed_getters.rs:146:5 | LL | / fn d(&self) -> &u8 { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:152:5 + --> tests/ui/misnamed_getters.rs:151:5 | LL | / fn d_mut(&mut self) -> &mut u8 { LL | | diff --git a/tests/ui/misnamed_getters_2021.fixed b/tests/ui/misnamed_getters_2021.fixed index 7112719a9f284..a923da1962fbe 100644 --- a/tests/ui/misnamed_getters_2021.fixed +++ b/tests/ui/misnamed_getters_2021.fixed @@ -1,6 +1,4 @@ //@edition: 2021 -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] // Edition 2021 specific check, where `unsafe` blocks are not required diff --git a/tests/ui/misnamed_getters_2021.rs b/tests/ui/misnamed_getters_2021.rs index 19b5d086041f4..bdb011939da64 100644 --- a/tests/ui/misnamed_getters_2021.rs +++ b/tests/ui/misnamed_getters_2021.rs @@ -1,6 +1,4 @@ //@edition: 2021 -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] // Edition 2021 specific check, where `unsafe` blocks are not required diff --git a/tests/ui/misnamed_getters_2021.stderr b/tests/ui/misnamed_getters_2021.stderr index 5495e2e3733f0..9771d2d8b119f 100644 --- a/tests/ui/misnamed_getters_2021.stderr +++ b/tests/ui/misnamed_getters_2021.stderr @@ -1,5 +1,5 @@ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters_2021.rs:15:5 + --> tests/ui/misnamed_getters_2021.rs:13:5 | LL | / unsafe fn a(&self) -> &u8 { LL | | diff --git a/tests/ui/misrefactored_assign_op.1.fixed b/tests/ui/misrefactored_assign_op.1.fixed index 882ff6bf8944a..c5628f2486b0a 100644 --- a/tests/ui/misrefactored_assign_op.1.fixed +++ b/tests/ui/misrefactored_assign_op.1.fixed @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/tests/ui/misrefactored_assign_op.2.fixed b/tests/ui/misrefactored_assign_op.2.fixed index de3a0f1710d24..02c4383f45ab7 100644 --- a/tests/ui/misrefactored_assign_op.2.fixed +++ b/tests/ui/misrefactored_assign_op.2.fixed @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/tests/ui/misrefactored_assign_op.rs b/tests/ui/misrefactored_assign_op.rs index 62d83d1619c1e..dcb284574ce43 100644 --- a/tests/ui/misrefactored_assign_op.rs +++ b/tests/ui/misrefactored_assign_op.rs @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/tests/ui/misrefactored_assign_op.stderr b/tests/ui/misrefactored_assign_op.stderr index 63f3a3e28f12c..50547a91dedc1 100644 --- a/tests/ui/misrefactored_assign_op.stderr +++ b/tests/ui/misrefactored_assign_op.stderr @@ -1,5 +1,5 @@ error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:6:5 + --> tests/ui/misrefactored_assign_op.rs:7:5 | LL | a += a + 1; | ^^^^^^^^^^ @@ -18,7 +18,7 @@ LL + a = a + a + 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:9:5 + --> tests/ui/misrefactored_assign_op.rs:10:5 | LL | a += 1 + a; | ^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + a = a + 1 + a; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:12:5 + --> tests/ui/misrefactored_assign_op.rs:13:5 | LL | a -= a - 1; | ^^^^^^^^^^ @@ -52,7 +52,7 @@ LL + a = a - (a - 1); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:15:5 + --> tests/ui/misrefactored_assign_op.rs:16:5 | LL | a *= a * 99; | ^^^^^^^^^^^ @@ -69,7 +69,7 @@ LL + a = a * a * 99; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:18:5 + --> tests/ui/misrefactored_assign_op.rs:19:5 | LL | a *= 42 * a; | ^^^^^^^^^^^ @@ -86,7 +86,7 @@ LL + a = a * 42 * a; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:21:5 + --> tests/ui/misrefactored_assign_op.rs:22:5 | LL | a /= a / 2; | ^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + a = a / (a / 2); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:24:5 + --> tests/ui/misrefactored_assign_op.rs:25:5 | LL | a %= a % 5; | ^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + a = a % (a % 5); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:27:5 + --> tests/ui/misrefactored_assign_op.rs:28:5 | LL | a &= a & 1; | ^^^^^^^^^^ @@ -137,7 +137,7 @@ LL + a = a & a & 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:30:5 + --> tests/ui/misrefactored_assign_op.rs:31:5 | LL | a *= a * a; | ^^^^^^^^^^ diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.rs b/tests/ui/missing_asserts_for_indexing_unfixable.rs index eb98969efa47f..b8cdb71bd30a9 100644 --- a/tests/ui/missing_asserts_for_indexing_unfixable.rs +++ b/tests/ui/missing_asserts_for_indexing_unfixable.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::missing_asserts_for_indexing)] fn sum(v: &[u8]) -> u8 { diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/tests/ui/missing_asserts_for_indexing_unfixable.stderr index 2929646494a41..fe929aa36dbad 100644 --- a/tests/ui/missing_asserts_for_indexing_unfixable.stderr +++ b/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -1,5 +1,5 @@ error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:5 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:4:5 | LL | v[0] + v[1] + v[2] + v[3] + v[4] | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ @@ -10,7 +10,7 @@ LL | v[0] + v[1] + v[2] + v[3] + v[4] = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:10:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:9:13 | LL | let _ = v[0]; | ^^^^ @@ -21,7 +21,7 @@ LL | let _ = v[1..4]; = help: consider asserting the length before indexing: `assert!(v.len() > 3);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:17:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:16:13 | LL | let a = v[0]; | ^^^^ @@ -34,7 +34,7 @@ LL | let c = v[2]; = help: consider asserting the length before indexing: `assert!(v.len() > 2);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:25:13 | LL | let _ = v1[0] + v1[12]; | ^^^^^ ^^^^^^ @@ -42,7 +42,7 @@ LL | let _ = v1[0] + v1[12]; = help: consider asserting the length before indexing: `assert!(v1.len() > 12);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:27:13 | LL | let _ = v2[5] + v2[15]; | ^^^^^ ^^^^^^ @@ -50,7 +50,7 @@ LL | let _ = v2[5] + v2[15]; = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:34:13 | LL | let _ = v2[5] + v2[15]; | ^^^^^ ^^^^^^ @@ -58,7 +58,7 @@ LL | let _ = v2[5] + v2[15]; = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:44:13 | LL | let _ = f.v[0] + f.v[1]; | ^^^^^^ ^^^^^^ @@ -66,7 +66,7 @@ LL | let _ = f.v[0] + f.v[1]; = help: consider asserting the length before indexing: `assert!(f.v.len() > 1);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:58:13 | LL | let _ = x[0] + x[1]; | ^^^^ ^^^^ @@ -74,7 +74,7 @@ LL | let _ = x[0] + x[1]; = help: consider asserting the length before indexing: `assert!(x.len() > 1);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:76:13 | LL | let _ = v1[1] + v1[2]; | ^^^^^ ^^^^^ @@ -82,7 +82,7 @@ LL | let _ = v1[1] + v1[2]; = help: consider asserting the length before indexing: `assert!(v1.len() > 2);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:84:13 | LL | let _ = v1[0] + v1[1] + v1[2]; | ^^^^^ ^^^^^ ^^^^^ diff --git a/tests/ui/missing_const_for_fn/could_be_const.fixed b/tests/ui/missing_const_for_fn/could_be_const.fixed index 46a7f79ac16ce..bc2d5b9d1c7b4 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -1,5 +1,5 @@ #![warn(clippy::missing_const_for_fn)] -#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] +#![expect(clippy::let_and_return)] #![feature(const_trait_impl)] use std::mem::transmute; diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 78e1939a85973..26baebfc535e2 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,5 +1,5 @@ #![warn(clippy::missing_const_for_fn)] -#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] +#![expect(clippy::let_and_return)] #![feature(const_trait_impl)] use std::mem::transmute; diff --git a/tests/ui/missing_fields_in_debug.rs b/tests/ui/missing_fields_in_debug.rs index 14803b5485a11..e52630408f039 100644 --- a/tests/ui/missing_fields_in_debug.rs +++ b/tests/ui/missing_fields_in_debug.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::missing_fields_in_debug)] use std::fmt; diff --git a/tests/ui/missing_fields_in_debug.stderr b/tests/ui/missing_fields_in_debug.stderr index 75b551e1f5c7e..4ea837e47836d 100644 --- a/tests/ui/missing_fields_in_debug.stderr +++ b/tests/ui/missing_fields_in_debug.stderr @@ -1,5 +1,5 @@ error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:14:1 + --> tests/ui/missing_fields_in_debug.rs:13:1 | LL | / impl fmt::Debug for NamedStruct1Ignored { ... | @@ -7,7 +7,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:11:5 + --> tests/ui/missing_fields_in_debug.rs:10:5 | LL | hidden: u32, | ^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | hidden: u32, = help: to override `-D warnings` add `#[allow(clippy::missing_fields_in_debug)]` error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:34:1 + --> tests/ui/missing_fields_in_debug.rs:33:1 | LL | / impl fmt::Debug for NamedStructMultipleIgnored { ... | @@ -25,17 +25,17 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:28:5 + --> tests/ui/missing_fields_in_debug.rs:27:5 | LL | hidden: u32, | ^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:29:5 + --> tests/ui/missing_fields_in_debug.rs:28:5 | LL | hidden2: String, | ^^^^^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:31:5 + --> tests/ui/missing_fields_in_debug.rs:30:5 | LL | hidden4: ((((u8), u16), u32), u64), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -43,7 +43,7 @@ LL | hidden4: ((((u8), u16), u32), u64), = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:97:1 + --> tests/ui/missing_fields_in_debug.rs:96:1 | LL | / impl fmt::Debug for MultiExprDebugImpl { LL | | @@ -54,7 +54,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:93:5 + --> tests/ui/missing_fields_in_debug.rs:92:5 | LL | b: String, | ^^^^^^^^^ diff --git a/tests/ui/missing_inline.rs b/tests/ui/missing_inline.rs index 8e937d609512a..0adaeaa9cab8a 100644 --- a/tests/ui/missing_inline.rs +++ b/tests/ui/missing_inline.rs @@ -2,7 +2,7 @@ #![crate_type = "dylib"] // When denying at the crate level, be sure to not get random warnings from the // injected intrinsics by the compiler. -#![allow(dead_code, non_snake_case)] +#![expect(non_snake_case)] type Typedef = String; pub type PubTypedef = String; diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs index d016e099e303b..63e84ffaab634 100644 --- a/tests/ui/missing_panics_doc.rs +++ b/tests/ui/missing_panics_doc.rs @@ -1,6 +1,6 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_panics_doc)] -#![allow(clippy::option_map_unit_fn, clippy::unnecessary_literal_unwrap)] +#![expect(clippy::option_map_unit_fn, clippy::unnecessary_literal_unwrap)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/missing_spin_loop.fixed b/tests/ui/missing_spin_loop.fixed index 03fbf8ccc8a37..7876a0c47d5e7 100644 --- a/tests/ui/missing_spin_loop.fixed +++ b/tests/ui/missing_spin_loop.fixed @@ -1,6 +1,5 @@ #![warn(clippy::missing_spin_loop)] -#![allow(clippy::bool_comparison)] -#![allow(unused_braces)] +#![expect(clippy::bool_comparison)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/tests/ui/missing_spin_loop.rs b/tests/ui/missing_spin_loop.rs index bf18590b9408e..06bfe8eeaab80 100644 --- a/tests/ui/missing_spin_loop.rs +++ b/tests/ui/missing_spin_loop.rs @@ -1,6 +1,5 @@ #![warn(clippy::missing_spin_loop)] -#![allow(clippy::bool_comparison)] -#![allow(unused_braces)] +#![expect(clippy::bool_comparison)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/tests/ui/missing_spin_loop.stderr b/tests/ui/missing_spin_loop.stderr index 98c32d0423469..d4a93bb2d75b7 100644 --- a/tests/ui/missing_spin_loop.stderr +++ b/tests/ui/missing_spin_loop.stderr @@ -1,5 +1,5 @@ error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:10:37 + --> tests/ui/missing_spin_loop.rs:9:37 | LL | while b.load(Ordering::Acquire) {} | ^^ help: try: `{ std::hint::spin_loop() }` @@ -8,31 +8,31 @@ LL | while b.load(Ordering::Acquire) {} = help: to override `-D warnings` add `#[allow(clippy::missing_spin_loop)]` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:13:37 + --> tests/ui/missing_spin_loop.rs:12:37 | LL | while !b.load(Ordering::SeqCst) {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:16:46 + --> tests/ui/missing_spin_loop.rs:15:46 | LL | while b.load(Ordering::Acquire) == false {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:19:49 + --> tests/ui/missing_spin_loop.rs:18:49 | LL | while { true == b.load(Ordering::Acquire) } {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:22:93 + --> tests/ui/missing_spin_loop.rs:21:93 | LL | while b.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) != Ok(true) {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:25:94 + --> tests/ui/missing_spin_loop.rs:24:94 | LL | while Ok(false) != b.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {} | ^^ help: try: `{ std::hint::spin_loop() }` diff --git a/tests/ui/missing_trait_methods.rs b/tests/ui/missing_trait_methods.rs index 67070a2999510..90349a0de101f 100644 --- a/tests/ui/missing_trait_methods.rs +++ b/tests/ui/missing_trait_methods.rs @@ -1,5 +1,5 @@ -#![allow(unused, clippy::needless_lifetimes)] #![warn(clippy::missing_trait_methods)] +#![expect(clippy::needless_lifetimes)] trait A { fn provided() {} diff --git a/tests/ui/missing_transmute_annotations.fixed b/tests/ui/missing_transmute_annotations.fixed index 58faeaee09d46..2602a2fda5135 100644 --- a/tests/ui/missing_transmute_annotations.fixed +++ b/tests/ui/missing_transmute_annotations.fixed @@ -1,7 +1,7 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_transmute_annotations)] -#![allow(clippy::let_with_type_underscore)] +#![expect(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/missing_transmute_annotations.rs b/tests/ui/missing_transmute_annotations.rs index c9a4c5fa83b2b..fe7b7238d33fe 100644 --- a/tests/ui/missing_transmute_annotations.rs +++ b/tests/ui/missing_transmute_annotations.rs @@ -1,7 +1,7 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_transmute_annotations)] -#![allow(clippy::let_with_type_underscore)] +#![expect(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index a0190acc5d4b3..3a7d714375a77 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -1,10 +1,8 @@ //@aux-build: proc_macros.rs -#![allow( - dead_code, - unused_variables, +#![warn(clippy::mistyped_literal_suffixes)] +#![expect( overflowing_literals, - clippy::excessive_precision, clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings )] diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 12a26204e7557..02e76a468d210 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -1,10 +1,8 @@ //@aux-build: proc_macros.rs -#![allow( - dead_code, - unused_variables, +#![warn(clippy::mistyped_literal_suffixes)] +#![expect( overflowing_literals, - clippy::excessive_precision, clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings )] diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index 9c6b2fab21c31..a986bc0e69ec0 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -1,97 +1,98 @@ error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:16:18 + --> tests/ui/mistyped_literal_suffix.rs:14:18 | LL | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` | - = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default + = note: `-D clippy::mistyped-literal-suffixes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mistyped_literal_suffixes)]` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:18:18 + --> tests/ui/mistyped_literal_suffix.rs:16:18 | LL | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:20:18 + --> tests/ui/mistyped_literal_suffix.rs:18:18 | LL | let fail16 = 7_8; // | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:23:18 + --> tests/ui/mistyped_literal_suffix.rs:21:18 | LL | let fail17 = 23_16; // | ^^^^^ help: did you mean to write: `23_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:28:18 + --> tests/ui/mistyped_literal_suffix.rs:26:18 | LL | let fail20 = 2__8; // | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:31:18 + --> tests/ui/mistyped_literal_suffix.rs:29:18 | LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:36:18 + --> tests/ui/mistyped_literal_suffix.rs:34:18 | LL | let fail25 = 1E2_32; | ^^^^^^ help: did you mean to write: `1E2_f32` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:38:18 + --> tests/ui/mistyped_literal_suffix.rs:36:18 | LL | let fail26 = 43E7_64; | ^^^^^^^ help: did you mean to write: `43E7_f64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:40:18 + --> tests/ui/mistyped_literal_suffix.rs:38:18 | LL | let fail27 = 243E17_32; | ^^^^^^^^^ help: did you mean to write: `243E17_f32` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:42:18 + --> tests/ui/mistyped_literal_suffix.rs:40:18 | LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:47:18 + --> tests/ui/mistyped_literal_suffix.rs:45:18 | LL | let fail30 = 127_8; // should be i8 | ^^^^^ help: did you mean to write: `127_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:50:18 + --> tests/ui/mistyped_literal_suffix.rs:48:18 | LL | let fail31 = 240_8; // should be u8 | ^^^^^ help: did you mean to write: `240_u8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:54:18 + --> tests/ui/mistyped_literal_suffix.rs:52:18 | LL | let fail33 = 0x1234_16; | ^^^^^^^^^ help: did you mean to write: `0x1234_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:56:18 + --> tests/ui/mistyped_literal_suffix.rs:54:18 | LL | let fail34 = 0xABCD_16; | ^^^^^^^^^ help: did you mean to write: `0xABCD_u16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:59:18 + --> tests/ui/mistyped_literal_suffix.rs:57:18 | LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:67:13 + --> tests/ui/mistyped_literal_suffix.rs:65:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` diff --git a/tests/ui/module_name_repetitions.rs b/tests/ui/module_name_repetitions.rs index 5d16858bf85ec..8fdce71d05528 100644 --- a/tests/ui/module_name_repetitions.rs +++ b/tests/ui/module_name_repetitions.rs @@ -1,7 +1,6 @@ //@compile-flags: --test #![warn(clippy::module_name_repetitions)] -#![allow(dead_code)] pub mod foo { pub fn foo() {} diff --git a/tests/ui/module_name_repetitions.stderr b/tests/ui/module_name_repetitions.stderr index 9000c44a3902e..e4c3daacdbead 100644 --- a/tests/ui/module_name_repetitions.stderr +++ b/tests/ui/module_name_repetitions.stderr @@ -1,5 +1,5 @@ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:8:12 + --> tests/ui/module_name_repetitions.rs:7:12 | LL | pub fn foo_bar() {} | ^^^^^^^ @@ -8,31 +8,31 @@ LL | pub fn foo_bar() {} = help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]` error: item name ends with its containing module's name - --> tests/ui/module_name_repetitions.rs:11:12 + --> tests/ui/module_name_repetitions.rs:10:12 | LL | pub fn bar_foo() {} | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:14:16 + --> tests/ui/module_name_repetitions.rs:13:16 | LL | pub struct FooCake; | ^^^^^^^ error: item name ends with its containing module's name - --> tests/ui/module_name_repetitions.rs:17:14 + --> tests/ui/module_name_repetitions.rs:16:14 | LL | pub enum CakeFoo {} | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:20:16 + --> tests/ui/module_name_repetitions.rs:19:16 | LL | pub struct Foo7Bar; | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:33:20 + --> tests/ui/module_name_repetitions.rs:32:20 | LL | pub use error::FooError; | ^^^^^^^^ diff --git a/tests/ui/modulo_arithmetic_float.rs b/tests/ui/modulo_arithmetic_float.rs index 0e08174f686eb..46d554616f8df 100644 --- a/tests/ui/modulo_arithmetic_float.rs +++ b/tests/ui/modulo_arithmetic_float.rs @@ -1,7 +1,7 @@ #![feature(f128)] #![feature(f16)] #![warn(clippy::modulo_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] +#![expect(clippy::no_effect)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/tests/ui/modulo_arithmetic_integral.rs b/tests/ui/modulo_arithmetic_integral.rs index d7cd910e43cb7..583637dbc2855 100644 --- a/tests/ui/modulo_arithmetic_integral.rs +++ b/tests/ui/modulo_arithmetic_integral.rs @@ -1,5 +1,5 @@ #![warn(clippy::modulo_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] +#![expect(clippy::no_effect)] fn main() { // Lint on signed integral numbers diff --git a/tests/ui/modulo_arithmetic_integral_const.rs b/tests/ui/modulo_arithmetic_integral_const.rs index 9f5fb697df0ea..160bc86629247 100644 --- a/tests/ui/modulo_arithmetic_integral_const.rs +++ b/tests/ui/modulo_arithmetic_integral_const.rs @@ -1,10 +1,5 @@ #![warn(clippy::modulo_arithmetic)] -#![allow( - clippy::no_effect, - clippy::unnecessary_operation, - clippy::modulo_one, - clippy::identity_op -)] +#![expect(clippy::identity_op, clippy::modulo_one, clippy::no_effect)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/tests/ui/modulo_arithmetic_integral_const.stderr b/tests/ui/modulo_arithmetic_integral_const.stderr index 12edee8cf4255..e09673213f79a 100644 --- a/tests/ui/modulo_arithmetic_integral_const.stderr +++ b/tests/ui/modulo_arithmetic_integral_const.stderr @@ -1,5 +1,5 @@ error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:11:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:6:5 | LL | -1 % 2; | ^^^^^^ @@ -10,7 +10,7 @@ LL | -1 % 2; = help: to override `-D warnings` add `#[allow(clippy::modulo_arithmetic)]` error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:14:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:9:5 | LL | 1 % -2; | ^^^^^^ @@ -19,7 +19,7 @@ LL | 1 % -2; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 3` - --> tests/ui/modulo_arithmetic_integral_const.rs:17:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:12:5 | LL | (1 - 2) % (1 + 2); | ^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | (1 - 2) % (1 + 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `3 % -1` - --> tests/ui/modulo_arithmetic_integral_const.rs:20:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:15:5 | LL | (1 + 2) % (1 - 2); | ^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | (1 + 2) % (1 - 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-35 % 300000` - --> tests/ui/modulo_arithmetic_integral_const.rs:23:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:18:5 | LL | 35 * (7 - 4 * 2) % (-500 * -600); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -46,7 +46,7 @@ LL | 35 * (7 - 4 * 2) % (-500 * -600); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:26:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:21:5 | LL | -1i8 % 2i8; | ^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | -1i8 % 2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:29:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:24:5 | LL | 1i8 % -2i8; | ^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | 1i8 % -2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:32:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:27:5 | LL | -1i16 % 2i16; | ^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | -1i16 % 2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:35:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:30:5 | LL | 1i16 % -2i16; | ^^^^^^^^^^^^ @@ -82,7 +82,7 @@ LL | 1i16 % -2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:38:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:33:5 | LL | -1i32 % 2i32; | ^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | -1i32 % 2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:41:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:36:5 | LL | 1i32 % -2i32; | ^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | 1i32 % -2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:44:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:39:5 | LL | -1i64 % 2i64; | ^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | -1i64 % 2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:47:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:42:5 | LL | 1i64 % -2i64; | ^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | 1i64 % -2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:50:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:45:5 | LL | -1i128 % 2i128; | ^^^^^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | -1i128 % 2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:53:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:48:5 | LL | 1i128 % -2i128; | ^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | 1i128 % -2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:56:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:51:5 | LL | -1isize % 2isize; | ^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | -1isize % 2isize; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:59:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:54:5 | LL | 1isize % -2isize; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/modulo_one.rs b/tests/ui/modulo_one.rs index 63ae61059cb86..730c37d0e368e 100644 --- a/tests/ui/modulo_one.rs +++ b/tests/ui/modulo_one.rs @@ -1,6 +1,5 @@ #![warn(clippy::modulo_one)] -#![allow(unconditional_panic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::identity_op)] +#![expect(clippy::identity_op, clippy::no_effect)] static STATIC_ONE: usize = 2 - 1; static STATIC_NEG_ONE: i64 = 1 - 2; diff --git a/tests/ui/modulo_one.stderr b/tests/ui/modulo_one.stderr index 1c9c79d1c6214..f841ac18bbc3d 100644 --- a/tests/ui/modulo_one.stderr +++ b/tests/ui/modulo_one.stderr @@ -1,5 +1,5 @@ error: any number modulo 1 will be 0 - --> tests/ui/modulo_one.rs:9:5 + --> tests/ui/modulo_one.rs:8:5 | LL | 10 % 1; | ^^^^^^ @@ -8,31 +8,31 @@ LL | 10 % 1; = help: to override `-D warnings` add `#[allow(clippy::modulo_one)]` error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:12:5 + --> tests/ui/modulo_one.rs:11:5 | LL | 10 % -1; | ^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:17:5 + --> tests/ui/modulo_one.rs:16:5 | LL | i32::MIN % (-1); | ^^^^^^^^^^^^^^^ error: any number modulo 1 will be 0 - --> tests/ui/modulo_one.rs:24:5 + --> tests/ui/modulo_one.rs:23:5 | LL | 2 % ONE; | ^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:29:5 + --> tests/ui/modulo_one.rs:28:5 | LL | 2 % NEG_ONE; | ^^^^^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:35:5 + --> tests/ui/modulo_one.rs:34:5 | LL | INT_MIN % NEG_ONE; | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/msrv_attributes_without_early_lints.rs b/tests/ui/msrv_attributes_without_early_lints.rs index dcef1a485fceb..fe83023aaeb79 100644 --- a/tests/ui/msrv_attributes_without_early_lints.rs +++ b/tests/ui/msrv_attributes_without_early_lints.rs @@ -1,6 +1,5 @@ //@check-pass -#![allow(clippy::all, clippy::pedantic, clippy::restriction, clippy::nursery)] #![forbid(clippy::ptr_as_ptr)] /// MSRV checking in late passes skips checking the parent nodes if no early pass sees a diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 0ff881472cbb9..5d51c47eeed91 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,11 +1,11 @@ //@needs-asm-support //@aux-build:proc_macros.rs +#![warn(clippy::multiple_unsafe_ops_per_block)] #![expect( dropping_copy_types, - clippy::unnecessary_operation, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::unnecessary_operation )] -#![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; use proc_macros::external; diff --git a/tests/ui/must_use_candidates.fixed b/tests/ui/must_use_candidates.fixed index 17569884658b1..226fafccddced 100644 --- a/tests/ui/must_use_candidates.fixed +++ b/tests/ui/must_use_candidates.fixed @@ -1,10 +1,4 @@ #![feature(never_type)] -#![allow( - unused_mut, - clippy::redundant_allocation, - clippy::needless_pass_by_ref_mut, - static_mut_refs -)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::Arc; diff --git a/tests/ui/must_use_candidates.rs b/tests/ui/must_use_candidates.rs index 8b898533d01b7..f9b6c2986504c 100644 --- a/tests/ui/must_use_candidates.rs +++ b/tests/ui/must_use_candidates.rs @@ -1,10 +1,4 @@ #![feature(never_type)] -#![allow( - unused_mut, - clippy::redundant_allocation, - clippy::needless_pass_by_ref_mut, - static_mut_refs -)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::Arc; diff --git a/tests/ui/must_use_candidates.stderr b/tests/ui/must_use_candidates.stderr index 38c7334322503..24fda87cd16aa 100644 --- a/tests/ui/must_use_candidates.stderr +++ b/tests/ui/must_use_candidates.stderr @@ -1,5 +1,5 @@ error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:16:8 + --> tests/ui/must_use_candidates.rs:10:8 | LL | pub fn pure(i: u8) -> u8 { | ^^^^ @@ -13,7 +13,7 @@ LL | pub fn pure(i: u8) -> u8 { | error: this method could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:22:12 + --> tests/ui/must_use_candidates.rs:16:12 | LL | pub fn inherent_pure(&self) -> u8 { | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL ~ pub fn inherent_pure(&self) -> u8 { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:54:8 + --> tests/ui/must_use_candidates.rs:48:8 | LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { | ^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:67:8 + --> tests/ui/must_use_candidates.rs:61:8 | LL | pub fn rcd(_x: Rc) -> bool { | ^^^ @@ -49,7 +49,7 @@ LL | pub fn rcd(_x: Rc) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:76:8 + --> tests/ui/must_use_candidates.rs:70:8 | LL | pub fn arcd(_x: Arc) -> bool { | ^^^^ @@ -61,7 +61,7 @@ LL | pub fn arcd(_x: Arc) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:108:8 + --> tests/ui/must_use_candidates.rs:102:8 | LL | pub fn result_uninhabited() -> Result { | ^^^^^^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ LL | pub fn result_uninhabited() -> Result { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:113:8 + --> tests/ui/must_use_candidates.rs:107:8 | LL | pub fn controlflow_uninhabited() -> std::ops::ControlFlow { | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/must_use_unit.fixed b/tests/ui/must_use_unit.fixed index b9871991b16f0..5c1993f6575a2 100644 --- a/tests/ui/must_use_unit.fixed +++ b/tests/ui/must_use_unit.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::must_use_unit)] -#![allow(clippy::unused_unit)] +#![expect(clippy::unused_unit)] extern crate proc_macros; use proc_macros::external; diff --git a/tests/ui/must_use_unit.rs b/tests/ui/must_use_unit.rs index a5865681c8a8e..86b4380a477aa 100644 --- a/tests/ui/must_use_unit.rs +++ b/tests/ui/must_use_unit.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::must_use_unit)] -#![allow(clippy::unused_unit)] +#![expect(clippy::unused_unit)] extern crate proc_macros; use proc_macros::external; diff --git a/tests/ui/mut_from_ref.rs b/tests/ui/mut_from_ref.rs index 1b0b351518cbb..f91e2992406b7 100644 --- a/tests/ui/mut_from_ref.rs +++ b/tests/ui/mut_from_ref.rs @@ -1,11 +1,10 @@ -#![allow( - unused, +#![warn(clippy::mut_from_ref)] +#![expect( + clippy::boxed_local, clippy::needless_lifetimes, clippy::needless_pass_by_ref_mut, - clippy::redundant_allocation, - clippy::boxed_local + clippy::redundant_allocation )] -#![warn(clippy::mut_from_ref)] struct Foo; diff --git a/tests/ui/mut_from_ref.stderr b/tests/ui/mut_from_ref.stderr index 0974268734653..92165eb9ae735 100644 --- a/tests/ui/mut_from_ref.stderr +++ b/tests/ui/mut_from_ref.stderr @@ -1,11 +1,11 @@ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:13:39 + --> tests/ui/mut_from_ref.rs:12:39 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:13:29 + --> tests/ui/mut_from_ref.rs:12:29 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^ @@ -13,85 +13,85 @@ LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { = help: to override `-D warnings` add `#[allow(clippy::mut_from_ref)]` error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:21:25 + --> tests/ui/mut_from_ref.rs:20:25 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:21:16 + --> tests/ui/mut_from_ref.rs:20:16 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:31:21 + --> tests/ui/mut_from_ref.rs:30:21 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:31:12 + --> tests/ui/mut_from_ref.rs:30:12 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:37:50 + --> tests/ui/mut_from_ref.rs:36:50 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:37:25 + --> tests/ui/mut_from_ref.rs:36:25 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:43:67 + --> tests/ui/mut_from_ref.rs:42:67 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:43:27 + --> tests/ui/mut_from_ref.rs:42:27 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:49:46 + --> tests/ui/mut_from_ref.rs:48:46 | LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:49:24 + --> tests/ui/mut_from_ref.rs:48:24 | LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:55:37 + --> tests/ui/mut_from_ref.rs:54:37 | LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:55:24 + --> tests/ui/mut_from_ref.rs:54:24 | LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { | ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:85:35 + --> tests/ui/mut_from_ref.rs:84:35 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:85:26 + --> tests/ui/mut_from_ref.rs:84:26 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^ diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed index f9a7f5dcb5a15..c34f605e3da6e 100644 --- a/tests/ui/mut_mut.fixed +++ b/tests/ui/mut_mut.fixed @@ -1,13 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::mut_mut)] -#![allow(unused)] -#![allow( - clippy::no_effect, - clippy::uninlined_format_args, - clippy::unnecessary_operation, - clippy::needless_pass_by_ref_mut -)] extern crate proc_macros; use proc_macros::{external, inline_macros}; @@ -26,7 +19,6 @@ macro_rules! mut_ptr { }; } -#[allow(unused_mut, unused_variables)] #[inline_macros] fn main() { let mut x = &mut 1u32; diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index bbec48011a17e..d17394dfe3f69 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -1,13 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::mut_mut)] -#![allow(unused)] -#![allow( - clippy::no_effect, - clippy::uninlined_format_args, - clippy::unnecessary_operation, - clippy::needless_pass_by_ref_mut -)] extern crate proc_macros; use proc_macros::{external, inline_macros}; @@ -26,7 +19,6 @@ macro_rules! mut_ptr { }; } -#[allow(unused_mut, unused_variables)] #[inline_macros] fn main() { let mut x = &mut &mut 1u32; diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 85e9c5649b89a..0a7a923e0f48f 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -1,5 +1,5 @@ error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:15:11 + --> tests/ui/mut_mut.rs:8:11 | LL | fn fun(x: &mut &mut u32) { | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` @@ -8,37 +8,37 @@ LL | fn fun(x: &mut &mut u32) { = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:32:17 + --> tests/ui/mut_mut.rs:24:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` error: this expression mutably borrows a mutable reference - --> tests/ui/mut_mut.rs:35:21 + --> tests/ui/mut_mut.rs:27:21 | LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:40:32 + --> tests/ui/mut_mut.rs:32:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:40:16 + --> tests/ui/mut_mut.rs:32:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:46:37 + --> tests/ui/mut_mut.rs:38:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:46:16 + --> tests/ui/mut_mut.rs:38:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut u32` diff --git a/tests/ui/mut_mut_unfixable.rs b/tests/ui/mut_mut_unfixable.rs index 271cb7b968898..0c6444fd9fa35 100644 --- a/tests/ui/mut_mut_unfixable.rs +++ b/tests/ui/mut_mut_unfixable.rs @@ -1,7 +1,6 @@ //@no-rustfix #![warn(clippy::mut_mut)] -#![allow(unused)] #![expect(clippy::no_effect)] //! removing the extra `&mut`s will break the derefs diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr index cf66eb2ed1eca..7e7fb801ce1e7 100644 --- a/tests/ui/mut_mut_unfixable.stderr +++ b/tests/ui/mut_mut_unfixable.stderr @@ -1,5 +1,5 @@ error: a type of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:9:11 + --> tests/ui/mut_mut_unfixable.rs:8:11 | LL | fn fun(x: &mut &mut u32) -> bool { | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` @@ -8,31 +8,31 @@ LL | fn fun(x: &mut &mut u32) -> bool { = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:15:17 + --> tests/ui/mut_mut_unfixable.rs:14:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` error: this expression mutably borrows a mutable reference - --> tests/ui/mut_mut_unfixable.rs:18:21 + --> tests/ui/mut_mut_unfixable.rs:17:21 | LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:24:17 + --> tests/ui/mut_mut_unfixable.rs:23:17 | LL | let y = &mut &mut 2; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:30:17 + --> tests/ui/mut_mut_unfixable.rs:29:17 | LL | let y = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:39:17 + --> tests/ui/mut_mut_unfixable.rs:38:17 | LL | let y = &mut &mut x; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut x` diff --git a/tests/ui/mut_mutex_lock.fixed b/tests/ui/mut_mutex_lock.fixed index 5790ee8c1b022..44587b89ec836 100644 --- a/tests/ui/mut_mutex_lock.fixed +++ b/tests/ui/mut_mutex_lock.fixed @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_mut)] #![warn(clippy::mut_mutex_lock)] use std::sync::{Arc, Mutex}; diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs index 7286afac823b7..eef4f26c09595 100644 --- a/tests/ui/mut_mutex_lock.rs +++ b/tests/ui/mut_mutex_lock.rs @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_mut)] #![warn(clippy::mut_mutex_lock)] use std::sync::{Arc, Mutex}; diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr index 67543c73c6eef..587405449523c 100644 --- a/tests/ui/mut_mutex_lock.stderr +++ b/tests/ui/mut_mutex_lock.stderr @@ -1,5 +1,5 @@ error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference - --> tests/ui/mut_mutex_lock.rs:10:33 + --> tests/ui/mut_mutex_lock.rs:9:33 | LL | let mut value = value_mutex.lock().unwrap(); | ^^^^ help: change this to: `get_mut` @@ -8,7 +8,7 @@ LL | let mut value = value_mutex.lock().unwrap(); = help: to override `-D warnings` add `#[allow(clippy::mut_mutex_lock)]` error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference - --> tests/ui/mut_mutex_lock.rs:16:43 + --> tests/ui/mut_mutex_lock.rs:15:43 | LL | let mut value = mut_ref_mut_ref_mutex.lock().unwrap(); | ^^^^ help: change this to: `get_mut` diff --git a/tests/ui/mut_range_bound.rs b/tests/ui/mut_range_bound.rs index 107a6229b86d8..4e1192a86c2c1 100644 --- a/tests/ui/mut_range_bound.rs +++ b/tests/ui/mut_range_bound.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +#![warn(clippy::mut_range_bound)] fn main() {} diff --git a/tests/ui/mutex_atomic.fixed b/tests/ui/mutex_atomic.fixed index dc05d8a2c61fe..711aba8da37b6 100644 --- a/tests/ui/mutex_atomic.fixed +++ b/tests/ui/mutex_atomic.fixed @@ -1,6 +1,5 @@ -#![warn(clippy::mutex_integer)] -#![warn(clippy::mutex_atomic)] -#![allow(clippy::borrow_as_ptr)] +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] +#![expect(clippy::borrow_as_ptr)] use std::sync::Mutex; diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index 33745f8fc5e13..f0ca75458d100 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -1,6 +1,5 @@ -#![warn(clippy::mutex_integer)] -#![warn(clippy::mutex_atomic)] -#![allow(clippy::borrow_as_ptr)] +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] +#![expect(clippy::borrow_as_ptr)] use std::sync::Mutex; diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index c1391e0111c8d..85c1829f44121 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:8:13 + --> tests/ui/mutex_atomic.rs:7:13 | LL | let _ = Mutex::new(true); | ^^^^^^^^^^^^^^^^ @@ -14,7 +14,7 @@ LL + let _ = std::sync::atomic::AtomicBool::new(true); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:11:13 + --> tests/ui/mutex_atomic.rs:10:13 | LL | let _ = Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL + let _ = std::sync::atomic::AtomicUsize::new(5usize); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:14:13 + --> tests/ui/mutex_atomic.rs:13:13 | LL | let _ = Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL + let _ = std::sync::atomic::AtomicIsize::new(9isize); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:21:13 + --> tests/ui/mutex_atomic.rs:20:13 | LL | let _ = Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL + let _ = std::sync::atomic::AtomicPtr::new(&mut x as *mut u32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:24:13 + --> tests/ui/mutex_atomic.rs:23:13 | LL | let _ = Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL + let _ = std::sync::atomic::AtomicU32::new(0u32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:27:13 + --> tests/ui/mutex_atomic.rs:26:13 | LL | let _ = Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL + let _ = std::sync::atomic::AtomicI32::new(0i32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:31:13 + --> tests/ui/mutex_atomic.rs:30:13 | LL | let _ = Mutex::new(0u8); | ^^^^^^^^^^^^^^^ @@ -94,7 +94,7 @@ LL + let _ = std::sync::atomic::AtomicU8::new(0u8); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:34:13 + --> tests/ui/mutex_atomic.rs:33:13 | LL | let _ = Mutex::new(0i16); | ^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL + let _ = std::sync::atomic::AtomicI16::new(0i16); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:37:25 + --> tests/ui/mutex_atomic.rs:36:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + let _x = std::sync::atomic::AtomicI8::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:41:13 + --> tests/ui/mutex_atomic.rs:40:13 | LL | let _ = Mutex::new(X); | ^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + let _ = std::sync::atomic::AtomicI64::new(X); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:53:30 + --> tests/ui/mutex_atomic.rs:52:30 | LL | static MTX: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -146,7 +146,7 @@ LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32 | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:56:15 + --> tests/ui/mutex_atomic.rs:55:15 | LL | let mtx = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -159,7 +159,7 @@ LL + let mtx = std::sync::atomic::AtomicI32::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:60:22 + --> tests/ui/mutex_atomic.rs:59:22 | LL | let reassigned = mtx; | ^^^ @@ -168,7 +168,7 @@ LL | let reassigned = mtx; = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:65:35 + --> tests/ui/mutex_atomic.rs:64:35 | LL | let (funky_mtx): Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:76:13 + --> tests/ui/mutex_atomic.rs:75:13 | LL | let _ = Mutex::new(test_expr!(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/needless_arbitrary_self_type.fixed b/tests/ui/needless_arbitrary_self_type.fixed index adb9096c9f242..0eef094895dd8 100644 --- a/tests/ui/needless_arbitrary_self_type.fixed +++ b/tests/ui/needless_arbitrary_self_type.fixed @@ -1,5 +1,5 @@ #![warn(clippy::needless_arbitrary_self_type)] -#![allow(unused_mut, clippy::needless_lifetimes)] +#![expect(clippy::needless_lifetimes)] pub enum ValType { A, diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs index 550546ed24fd7..0ea16ce89946b 100644 --- a/tests/ui/needless_arbitrary_self_type.rs +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_arbitrary_self_type)] -#![allow(unused_mut, clippy::needless_lifetimes)] +#![expect(clippy::needless_lifetimes)] pub enum ValType { A, diff --git a/tests/ui/needless_bitwise_bool.fixed b/tests/ui/needless_bitwise_bool.fixed index eedeecd8a8cbc..a5c34939a5c9d 100644 --- a/tests/ui/needless_bitwise_bool.fixed +++ b/tests/ui/needless_bitwise_bool.fixed @@ -1,5 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] -#![allow(clippy::const_is_empty)] +#![expect(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/tests/ui/needless_bitwise_bool.rs b/tests/ui/needless_bitwise_bool.rs index 5c0a4ccf266c3..ca03d888f3650 100644 --- a/tests/ui/needless_bitwise_bool.rs +++ b/tests/ui/needless_bitwise_bool.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] -#![allow(clippy::const_is_empty)] +#![expect(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/tests/ui/needless_bool/fixable.fixed b/tests/ui/needless_bool/fixable.fixed index 0af4f87bec677..c3eed4e3fd717 100644 --- a/tests/ui/needless_bool/fixable.fixed +++ b/tests/ui/needless_bool/fixable.fixed @@ -1,15 +1,6 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::equatable_if_let, - clippy::needless_ifs, - clippy::needless_return, - clippy::self_named_constructors, - clippy::struct_field_names -)] +#![allow(clippy::no_effect)] +#![expect(clippy::needless_return)] use std::cell::Cell; @@ -21,7 +12,6 @@ macro_rules! bool_comparison_trigger { $($i: (Cell, bool, bool)),+ } - #[allow(dead_code)] impl Trigger { pub fn trigger(&self, key: &str) -> bool { $( diff --git a/tests/ui/needless_bool/fixable.rs b/tests/ui/needless_bool/fixable.rs index 2e8719bd041e0..4571628be20ea 100644 --- a/tests/ui/needless_bool/fixable.rs +++ b/tests/ui/needless_bool/fixable.rs @@ -1,15 +1,6 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::equatable_if_let, - clippy::needless_ifs, - clippy::needless_return, - clippy::self_named_constructors, - clippy::struct_field_names -)] +#![allow(clippy::no_effect)] +#![expect(clippy::needless_return)] use std::cell::Cell; @@ -21,7 +12,6 @@ macro_rules! bool_comparison_trigger { $($i: (Cell, bool, bool)),+ } - #[allow(dead_code)] impl Trigger { pub fn trigger(&self, key: &str) -> bool { $( diff --git a/tests/ui/needless_bool/fixable.stderr b/tests/ui/needless_bool/fixable.stderr index e15260242f4dd..f8cad52c2bbf2 100644 --- a/tests/ui/needless_bool/fixable.stderr +++ b/tests/ui/needless_bool/fixable.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:41:5 + --> tests/ui/needless_bool/fixable.rs:31:5 | LL | / if x { LL | | true @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::needless_bool)]` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:47:5 + --> tests/ui/needless_bool/fixable.rs:37:5 | LL | / if x { LL | | false @@ -22,7 +22,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:53:5 + --> tests/ui/needless_bool/fixable.rs:43:5 | LL | / if x && y { LL | | false @@ -32,7 +32,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!(x && y)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:62:5 + --> tests/ui/needless_bool/fixable.rs:52:5 | LL | / if a == b { LL | | false @@ -42,7 +42,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a != b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:68:5 + --> tests/ui/needless_bool/fixable.rs:58:5 | LL | / if a != b { LL | | false @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a == b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:74:5 + --> tests/ui/needless_bool/fixable.rs:64:5 | LL | / if a < b { LL | | false @@ -62,7 +62,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a >= b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:80:5 + --> tests/ui/needless_bool/fixable.rs:70:5 | LL | / if a <= b { LL | | false @@ -72,7 +72,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a > b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:86:5 + --> tests/ui/needless_bool/fixable.rs:76:5 | LL | / if a > b { LL | | false @@ -82,7 +82,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a <= b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:92:5 + --> tests/ui/needless_bool/fixable.rs:82:5 | LL | / if a >= b { LL | | false @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a < b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:121:5 + --> tests/ui/needless_bool/fixable.rs:111:5 | LL | / if x { LL | | return true; @@ -102,7 +102,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:130:5 + --> tests/ui/needless_bool/fixable.rs:120:5 | LL | / if x { LL | | return false; @@ -112,7 +112,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:139:5 + --> tests/ui/needless_bool/fixable.rs:129:5 | LL | / if x && y { LL | | return true; @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x && y` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:148:5 + --> tests/ui/needless_bool/fixable.rs:138:5 | LL | / if x && y { LL | | return false; @@ -132,7 +132,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !(x && y)` error: equality checks against true are unnecessary - --> tests/ui/needless_bool/fixable.rs:157:8 + --> tests/ui/needless_bool/fixable.rs:147:8 | LL | if x == true {}; | ^^^^^^^^^ help: try: `x` @@ -141,25 +141,25 @@ LL | if x == true {}; = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` error: equality checks against false can be replaced by a negation - --> tests/ui/needless_bool/fixable.rs:162:8 + --> tests/ui/needless_bool/fixable.rs:152:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try: `!x` error: equality checks against true are unnecessary - --> tests/ui/needless_bool/fixable.rs:173:8 + --> tests/ui/needless_bool/fixable.rs:163:8 | LL | if x == true {}; | ^^^^^^^^^ help: try: `x` error: equality checks against false can be replaced by a negation - --> tests/ui/needless_bool/fixable.rs:175:8 + --> tests/ui/needless_bool/fixable.rs:165:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try: `!x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:185:12 + --> tests/ui/needless_bool/fixable.rs:175:12 | LL | } else if returns_bool() { | ____________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: you can reduce it to: `{ !returns_bool() }` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:199:5 + --> tests/ui/needless_bool/fixable.rs:189:5 | LL | / if unsafe { no(4) } & 1 != 0 { LL | | true @@ -180,37 +180,37 @@ LL | | }; | |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:205:30 + --> tests/ui/needless_bool/fixable.rs:195:30 | LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:209:9 + --> tests/ui/needless_bool/fixable.rs:199:9 | LL | if unsafe { no(4) } & 1 != 0 { true } else { false } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:221:14 + --> tests/ui/needless_bool/fixable.rs:211:14 | LL | let _x = if a && b { true } else { false }.then(|| todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:223:14 + --> tests/ui/needless_bool/fixable.rs:213:14 | LL | let _x = if a && b { true } else { false } as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:227:14 + --> tests/ui/needless_bool/fixable.rs:217:14 | LL | let _x = if a { true } else { false }.then(|| todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `a` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:239:5 + --> tests/ui/needless_bool/fixable.rs:229:5 | LL | / if test_expr!(x) { LL | | true diff --git a/tests/ui/needless_bool/simple.rs b/tests/ui/needless_bool/simple.rs index 40ffeae6c56fa..c5d2ef51fade5 100644 --- a/tests/ui/needless_bool/simple.rs +++ b/tests/ui/needless_bool/simple.rs @@ -1,12 +1,5 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::needless_return, - clippy::branches_sharing_code -)] +#![expect(clippy::if_same_then_else, clippy::needless_return)] fn main() { let x = true; diff --git a/tests/ui/needless_bool/simple.stderr b/tests/ui/needless_bool/simple.stderr index 077e9b5df47cf..07711b9234cdd 100644 --- a/tests/ui/needless_bool/simple.stderr +++ b/tests/ui/needless_bool/simple.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression will always return true - --> tests/ui/needless_bool/simple.rs:14:5 + --> tests/ui/needless_bool/simple.rs:7:5 | LL | / if x { LL | | true @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::needless_bool)]` error: this if-then-else expression will always return false - --> tests/ui/needless_bool/simple.rs:20:5 + --> tests/ui/needless_bool/simple.rs:13:5 | LL | / if x { LL | | false @@ -22,7 +22,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return true - --> tests/ui/needless_bool/simple.rs:36:5 + --> tests/ui/needless_bool/simple.rs:29:5 | LL | / if x { LL | | return true; @@ -32,7 +32,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return false - --> tests/ui/needless_bool/simple.rs:45:5 + --> tests/ui/needless_bool/simple.rs:38:5 | LL | / if x { LL | | return false; diff --git a/tests/ui/needless_bool_assign.fixed b/tests/ui/needless_bool_assign.fixed index 8fd5720381404..3bd592b471e4b 100644 --- a/tests/ui/needless_bool_assign.fixed +++ b/tests/ui/needless_bool_assign.fixed @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::needless_bool_assign)] fn random() -> bool { diff --git a/tests/ui/needless_bool_assign.rs b/tests/ui/needless_bool_assign.rs index 4721ab433b327..1279176cb20a2 100644 --- a/tests/ui/needless_bool_assign.rs +++ b/tests/ui/needless_bool_assign.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::needless_bool_assign)] fn random() -> bool { diff --git a/tests/ui/needless_bool_assign.stderr b/tests/ui/needless_bool_assign.stderr index 34ff782f34a35..3dfea7cd1af99 100644 --- a/tests/ui/needless_bool_assign.stderr +++ b/tests/ui/needless_bool_assign.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:13:5 + --> tests/ui/needless_bool_assign.rs:12:5 | LL | / if random() && random() { LL | | a.field = true; @@ -12,7 +12,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::needless_bool_assign)]` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:19:5 + --> tests/ui/needless_bool_assign.rs:18:5 | LL | / if random() && random() { LL | | a.field = false; @@ -22,7 +22,7 @@ LL | | } | |_____^ help: you can reduce it to: `a.field = !(random() && random());` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:34:5 + --> tests/ui/needless_bool_assign.rs:33:5 | LL | / if random() { LL | | a.field = true; @@ -32,7 +32,7 @@ LL | | } | |_____^ help: you can reduce it to: `random(); a.field = true;` error: this `if` has identical blocks - --> tests/ui/needless_bool_assign.rs:34:17 + --> tests/ui/needless_bool_assign.rs:33:17 | LL | if random() { | _________________^ @@ -41,7 +41,7 @@ LL | | } else { | |_____^ | note: same as this - --> tests/ui/needless_bool_assign.rs:36:12 + --> tests/ui/needless_bool_assign.rs:35:12 | LL | } else { | ____________^ @@ -52,7 +52,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::if_same_then_else)]` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:54:12 + --> tests/ui/needless_bool_assign.rs:53:12 | LL | } else if x || y { | ____________^ @@ -63,7 +63,7 @@ LL | | } | |_____^ help: you can reduce it to: `{ z = x || y; }` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:77:5 + --> tests/ui/needless_bool_assign.rs:76:5 | LL | / if invoke!(must_keep, x, y) { LL | | dot_0!(skip) = false; From 1709e202159338953dbd0134d589e55bd2a76152 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 9 Jun 2026 12:42:11 +0100 Subject: [PATCH 018/278] intrinsic-test: remove now-unnecessary feat attr --- library/stdarch/crates/intrinsic-test/src/arm/config.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/config.rs b/library/stdarch/crates/intrinsic-test/src/arm/config.rs index 87b8ebfa183e7..7c26143622e71 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/config.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/config.rs @@ -9,7 +9,6 @@ pub const PLATFORM_RUST_CFGS: &str = r#" #![cfg_attr(target_arch = "arm", feature(stdarch_arm_neon_intrinsics))] #![cfg_attr(target_arch = "arm", feature(stdarch_aarch32_crc32))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fcma))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_dotprod))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_i8mm))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_sm4))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] From 6c8aae7088feb3ab530ab9694481fa367fa4226a Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Tue, 9 Jun 2026 22:22:55 -0400 Subject: [PATCH 019/278] refactor TypeRelativePath::AssocItem to use AliasTerm, remove alias_ty_kind_from_def_id and new_from_def_id --- clippy_utils/src/ty/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 056eb818c1ac3..a944e91db0c0e 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -1050,11 +1050,13 @@ pub fn make_projection<'tcx>( #[cfg(debug_assertions)] assert_generic_args_match(tcx, assoc_item.def_id, args); - Some(AliasTy::new_from_args( - tcx, - ty::AliasTyKind::new_from_def_id(tcx, assoc_item.def_id), - args, - )) + let kind = if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(assoc_item.def_id)) { + ty::AliasTyKind::Inherent { def_id: assoc_item.def_id } + } else { + ty::AliasTyKind::Projection { def_id: assoc_item.def_id } + }; + + Some(AliasTy::new_from_args(tcx, kind, args)) } helper( tcx, From c60910b5eb230a741b3dc636f2f4be53caf0629d Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Tue, 9 Jun 2026 22:40:38 -0400 Subject: [PATCH 020/278] Remove unused is_type_const from hir::TraitItemKind --- clippy_lints/src/non_copy_const.rs | 4 ++-- clippy_lints/src/types/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index fabc1f5c74ee0..22d7d83d730d9 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -757,7 +757,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Const(_, ct_rhs_opt, _) = item.kind + if let TraitItemKind::Const(_, ct_rhs_opt) = item.kind && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip() && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { IsFreeze::No => true, @@ -958,7 +958,7 @@ fn get_const_hir_value<'tcx>( { match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) { Node::ImplItem(item) if let ImplItemKind::Const(.., ct_rhs) = item.kind => (did, ct_rhs), - Node::TraitItem(item) if let TraitItemKind::Const(_, Some(ct_rhs), _) = item.kind => (did, ct_rhs), + Node::TraitItem(item) if let TraitItemKind::Const(_, Some(ct_rhs)) = item.kind => (did, ct_rhs), _ => return None, } }, diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 17f1adf6960f0..f6c6e3848a4c4 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -514,7 +514,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { }; match item.kind { - TraitItemKind::Const(ty, _, _) | TraitItemKind::Type(_, Some(ty)) => { + TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => { self.check_ty(cx, ty, context); }, TraitItemKind::Fn(ref sig, trait_method) => { From 2ae17355e34cb079ab4ef3b9054dbf50d79db293 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 20 May 2026 15:03:26 +0200 Subject: [PATCH 021/278] New `by_ref_peekable_peek` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + .../src/methods/by_ref_peekable_peek.rs | 84 +++++++++++++++ clippy_lints/src/methods/mod.rs | 38 +++++++ tests/ui/by_ref_peekable_peek.1.fixed | 55 ++++++++++ tests/ui/by_ref_peekable_peek.2.fixed | 55 ++++++++++ tests/ui/by_ref_peekable_peek.3.fixed | 55 ++++++++++ tests/ui/by_ref_peekable_peek.rs | 55 ++++++++++ tests/ui/by_ref_peekable_peek.stderr | 101 ++++++++++++++++++ 9 files changed, 445 insertions(+) create mode 100644 clippy_lints/src/methods/by_ref_peekable_peek.rs create mode 100644 tests/ui/by_ref_peekable_peek.1.fixed create mode 100644 tests/ui/by_ref_peekable_peek.2.fixed create mode 100644 tests/ui/by_ref_peekable_peek.3.fixed create mode 100644 tests/ui/by_ref_peekable_peek.rs create mode 100644 tests/ui/by_ref_peekable_peek.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index f53143e564b6c..5de08fbbdf03a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6576,6 +6576,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`by_ref_peekable_peek`]: https://rust-lang.github.io/rust-clippy/master/index.html#by_ref_peekable_peek [`byte_char_slices`]: https://rust-lang.github.io/rust-clippy/master/index.html#byte_char_slices [`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 4218d1fdc2901..6c73e2501dabe 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -360,6 +360,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO, crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO, crate::methods::BIND_INSTEAD_OF_MAP_INFO, + crate::methods::BY_REF_PEEKABLE_PEEK_INFO, crate::methods::BYTES_COUNT_TO_LEN_INFO, crate::methods::BYTES_NTH_INFO, crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO, diff --git a/clippy_lints/src/methods/by_ref_peekable_peek.rs b/clippy_lints/src/methods/by_ref_peekable_peek.rs new file mode 100644 index 0000000000000..1faaf2c9c8e43 --- /dev/null +++ b/clippy_lints/src/methods/by_ref_peekable_peek.rs @@ -0,0 +1,84 @@ +use crate::clippy_utils::res::MaybeTypeckRes; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath as _}; +use clippy_utils::sugg::Sugg; +use clippy_utils::sym; +use clippy_utils::ty::implements_trait; +use rustc_ast::BindingMode; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LetStmt, Node, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; + +use super::BY_REF_PEEKABLE_PEEK; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { + if let ExprKind::MethodCall(maybe_peekable, peekable_recv, [], _) = recv.kind + && maybe_peekable.ident.name == sym::peekable + && !peekable_recv.span.from_expansion() + && let ExprKind::MethodCall(maybe_by_ref, by_ref_recv, [], _) = peekable_recv.kind + && maybe_by_ref.ident.name == sym::by_ref + && !by_ref_recv.span.from_expansion() + && [peekable_recv, recv] + .into_iter() + .all(|e| cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Iterator)) + { + span_lint_and_then( + cx, + BY_REF_PEEKABLE_PEEK, + expr.span, + "calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output", + |diag| { + let span = by_ref_recv.span.shrink_to_hi().with_hi(expr.span.hi()); + if let ty::Ref(_, iter_ty, _) = cx.typeck_results().expr_ty_adjusted(by_ref_recv).kind() + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, *iter_ty, clone_trait, &[]) + { + diag.span_suggestion_verbose( + span, + "to peek the first item without advancing the underlying iterator, use", + ".clone().next().as_ref()", + Applicability::MaybeIncorrect, + ); + } + diag.span_suggestion_verbose( + span, + "to advance the underlying iterator, use", + ".next().as_ref()", + Applicability::MaybeIncorrect, + ); + // If the iterator is a local variable, initialized through a simple binding with an inferred + // initialization expression, suggest making the initialization expression peekable. + if let Some(iter_local_id) = by_ref_recv.res_local_id() + && let Node::LetStmt(LetStmt { + pat: let_pat, + ty: None, + init: Some(init_expr), + els: None, + span: let_stmt_span, + .. + }) = cx.tcx.parent_hir_node(iter_local_id) + && let PatKind::Binding(BindingMode::MUT, _, _, None) = let_pat.kind + && !let_stmt_span.from_expansion() + // Changing the type of the iterator may prevent the code from compiling + && let mut app = Applicability::MaybeIncorrect + && let sugg = + Sugg::hir_with_context(cx, init_expr, let_stmt_span.ctxt(), "_", &mut app).maybe_paren() + { + diag.multipart_suggestion( + "to make the iterator peekable, use", + vec![ + (init_expr.span.source_callsite(), format!("{sugg}.peekable()")), + (recv.span.with_lo(by_ref_recv.span.hi()), String::new()), + ], + app, + ); + } else { + diag.help( + "you might want to transform the iterator itself using `.peekable()` without using `.by_ref()`", + ); + } + }, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a8a2fc55c9019..6b271a4a6371a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,4 +1,5 @@ mod bind_instead_of_map; +mod by_ref_peekable_peek; mod bytecount; mod bytes_count_to_len; mod bytes_nth; @@ -201,6 +202,39 @@ declare_clippy_lint! { "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `Iterator::by_ref().peekable().peek()`. + /// + /// ### Why is this bad? + /// While it might look like this will allow peeking on the first + /// element of an iterator without consuming it and without consuming + /// the iterator itself, it will in practice consume the first element. + /// + /// The implementation of `Peekable::peek()` produces the first element + /// of the underlying iterator, and stores it internally so that it can + /// be later produced. As a consequence, it advances the underlying + /// iterator, whose `.next()` method will now produce its second element. + /// + /// ### Example + /// ```no_run + /// let mut iter = [1, 2, 3].into_iter(); + /// let x = iter.by_ref().peekable().peek(); // 1 + /// let y = iter.by_ref().peekable().peek(); // 2 + /// ``` + /// If this does what you intended, use the following instead, which is + /// shorter and clearer: + /// ```no_run + /// let mut iter = [1, 2, 3].into_iter(); + /// let x = iter.next().as_ref(); // 1 + /// let y = iter.next().as_ref(); // 2 + /// ``` + #[clippy::version = "1.98.0"] + pub BY_REF_PEEKABLE_PEEK, + suspicious, + "Using `.by_ref().peekable().peek()` on an iterator" +} + declare_clippy_lint! { /// ### What it does /// It checks for `str::bytes().count()` and suggests replacing it with @@ -4837,6 +4871,7 @@ impl_lint_pass!(Methods => [ BIND_INSTEAD_OF_MAP, BYTES_COUNT_TO_LEN, BYTES_NTH, + BY_REF_PEEKABLE_PEEK, CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, CHARS_LAST_CMP, CHARS_NEXT_CMP, @@ -5606,6 +5641,9 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } }, + (sym::peek, []) => { + by_ref_peekable_peek::check(cx, expr, recv); + }, (sym::push, [arg]) => { path_buf_push_overwrite::check(cx, expr, arg); }, diff --git a/tests/ui/by_ref_peekable_peek.1.fixed b/tests/ui/by_ref_peekable_peek.1.fixed new file mode 100644 index 0000000000000..e40b745245568 --- /dev/null +++ b/tests/ui/by_ref_peekable_peek.1.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.clone().next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.clone().next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.clone().next().as_ref(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/tests/ui/by_ref_peekable_peek.2.fixed b/tests/ui/by_ref_peekable_peek.2.fixed new file mode 100644 index 0000000000000..b9b7cdeaf5922 --- /dev/null +++ b/tests/ui/by_ref_peekable_peek.2.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.next().as_ref(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/tests/ui/by_ref_peekable_peek.3.fixed b/tests/ui/by_ref_peekable_peek.3.fixed new file mode 100644 index 0000000000000..67256fc86887c --- /dev/null +++ b/tests/ui/by_ref_peekable_peek.3.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter().peekable(); + let _: Option<&i32> = i.peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!().peekable(); + let _: Option<&i32> = i.peek(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter().peekable(); + let _: Option<&i32> = iter.peek(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/tests/ui/by_ref_peekable_peek.rs b/tests/ui/by_ref_peekable_peek.rs new file mode 100644 index 0000000000000..d8ccb8b74fb64 --- /dev/null +++ b/tests/ui/by_ref_peekable_peek.rs @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/tests/ui/by_ref_peekable_peek.stderr b/tests/ui/by_ref_peekable_peek.stderr new file mode 100644 index 0000000000000..5a284775e04f3 --- /dev/null +++ b/tests/ui/by_ref_peekable_peek.stderr @@ -0,0 +1,101 @@ +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:19:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you might want to transform the iterator itself using `.peekable()` without using `.by_ref()` + = note: `-D clippy::by-ref-peekable-peek` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::by_ref_peekable_peek)]` +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:25:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut i = a.into_iter().peekable(); +LL ~ let _: Option<&i32> = i.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:36:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut i = mac!().peekable(); +LL ~ let _: Option<&i32> = i.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:42:27 + | +LL | let _: Option<&i32> = iter.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = iter.by_ref().peekable().peek(); +LL + let _: Option<&i32> = iter.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = iter.by_ref().peekable().peek(); +LL + let _: Option<&i32> = iter.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut iter = [1, 2, 3].into_iter().peekable(); +LL ~ let _: Option<&i32> = iter.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:46:27 + | +LL | let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you might want to transform the iterator itself using `.peekable()` without using `.by_ref()` +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); +LL + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); +LL + let _: Option<&i32> = (&mut iter).next().as_ref(); + | + +error: aborting due to 5 previous errors + From 9b80946bc0c0d962e3a7e66d100d29c2155ae7de Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Thu, 11 Jun 2026 19:55:05 +0800 Subject: [PATCH 022/278] loongarch: Remove explicit zero-extension from CRC[C].W.{B,H}.W The CRC[C].W.{B,H}.W only consume the low 8/16 bits of the input operand. The previous unsigned cast was a workaround for Miri's software implementation. Miri now masks the inputs to match hardware semantics, and LLVM will learn the demanded-bits property of CRC intrinsics, so the explicit zero-extension is no longer required. --- library/stdarch/crates/core_arch/src/loongarch64/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs index 7e9f45f5e7b04..05a2f9a39ff0e 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs @@ -64,14 +64,14 @@ unsafe extern "unadjusted" { #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn crc_w_b_w(a: i8, b: i32) -> i32 { - unsafe { __crc_w_b_w(a.cast_unsigned() as i32, b) } + unsafe { __crc_w_b_w(a as i32, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn crc_w_h_w(a: i16, b: i32) -> i32 { - unsafe { __crc_w_h_w(a.cast_unsigned() as i32, b) } + unsafe { __crc_w_h_w(a as i32, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) @@ -92,14 +92,14 @@ pub fn crc_w_d_w(a: i64, b: i32) -> i32 { #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn crcc_w_b_w(a: i8, b: i32) -> i32 { - unsafe { __crcc_w_b_w(a.cast_unsigned() as i32, b) } + unsafe { __crcc_w_b_w(a as i32, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] pub fn crcc_w_h_w(a: i16, b: i32) -> i32 { - unsafe { __crcc_w_h_w(a.cast_unsigned() as i32, b) } + unsafe { __crcc_w_h_w(a as i32, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) From 85daf6e7d0ea3a811f5caffc3b8bd12129b365c8 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 11 Jun 2026 18:14:56 +0200 Subject: [PATCH 023/278] Merge commit '5f29bd0df789f92cd494061f9223dbb5d96e8e16' into clippy-subtree-update --- CHANGELOG.md | 100 ++- CONTRIBUTING.md | 34 +- Cargo.toml | 2 +- book/src/configuration.md | 2 +- book/src/development/adding_lints.md | 15 +- book/src/development/defining_lints.md | 11 +- book/src/lint_configuration.md | 28 +- book/src/lints.md | 7 +- build.rs | 2 - clippy_config/src/conf.rs | 216 ++++- clippy_config/src/types.rs | 9 + clippy_dev/src/new_lint.rs | 27 +- clippy_dummy/Cargo.toml | 4 +- clippy_lints/src/attrs/inline_always.rs | 9 +- clippy_lints/src/attrs/mod.rs | 32 +- clippy_lints/src/attrs/utils.rs | 47 +- clippy_lints/src/combined_early_pass.rs | 92 ++ clippy_lints/src/combined_late_pass.rs | 106 +++ clippy_lints/src/declared_lints.rs | 4 +- clippy_lints/src/default_numeric_fallback.rs | 6 +- clippy_lints/src/deprecated_lints.rs | 2 + clippy_lints/src/dereference.rs | 55 +- clippy_lints/src/disallowed_methods.rs | 125 ++- clippy_lints/src/disallowed_types.rs | 167 +++- clippy_lints/src/doc/mod.rs | 48 +- clippy_lints/src/empty_line_after.rs | 128 +-- clippy_lints/src/escape.rs | 113 ++- clippy_lints/src/excessive_bools.rs | 48 +- clippy_lints/src/functions/must_use.rs | 60 +- clippy_lints/src/functions/result.rs | 34 +- clippy_lints/src/inline_trait_bounds.rs | 33 +- clippy_lints/src/large_const_arrays.rs | 36 +- clippy_lints/src/large_futures.rs | 4 +- clippy_lints/src/lib.rs | 828 +++++++++--------- clippy_lints/src/lifetimes.rs | 50 +- .../src/loops/explicit_counter_loop.rs | 6 +- clippy_lints/src/loops/for_unbounded_range.rs | 42 + clippy_lints/src/loops/infinite_loop.rs | 32 +- clippy_lints/src/loops/mod.rs | 32 + clippy_lints/src/loops/needless_range_loop.rs | 4 +- clippy_lints/src/loops/never_loop.rs | 90 +- clippy_lints/src/loops/utils.rs | 15 +- clippy_lints/src/manual_non_exhaustive.rs | 22 +- clippy_lints/src/manual_option_as_slice.rs | 2 +- clippy_lints/src/manual_pop_if.rs | 2 +- clippy_lints/src/matches/collapsible_match.rs | 36 +- .../matches/infallible_destructuring_match.rs | 28 +- .../src/matches/redundant_pattern_match.rs | 161 ++-- .../matches/significant_drop_in_scrutinee.rs | 3 +- clippy_lints/src/mem_replace.rs | 27 +- .../methods/from_iter_instead_of_collect.rs | 81 -- .../src/methods/iter_cloned_collect.rs | 38 +- clippy_lints/src/methods/iter_next_slice.rs | 31 +- .../src/methods/manual_is_variant_and.rs | 32 +- clippy_lints/src/methods/mod.rs | 68 +- .../src/methods/range_zip_with_len.rs | 4 +- .../operators/manual_isolate_lowest_one.rs | 167 ++++ clippy_lints/src/operators/mod.rs | 27 + clippy_lints/src/operators/modulo_one.rs | 23 +- clippy_lints/src/ranges.rs | 18 +- clippy_lints/src/redundant_closure_call.rs | 120 +-- .../src/significant_drop_tightening.rs | 3 +- clippy_lints/src/single_range_in_vec_init.rs | 150 ++-- .../src/transmute/transmuting_null.rs | 8 +- clippy_lints/src/types/borrowed_box.rs | 85 +- clippy_lints/src/unused_async.rs | 35 +- clippy_lints/src/utils/author.rs | 4 +- clippy_lints/src/utils/dump_hir.rs | 4 +- clippy_lints/src/with_capacity_zero.rs | 78 ++ clippy_utils/README.md | 2 +- clippy_utils/src/attrs.rs | 84 +- clippy_utils/src/consts.rs | 25 + clippy_utils/src/disallowed_profiles.rs | 180 ++++ clippy_utils/src/lib.rs | 24 +- clippy_utils/src/macros.rs | 28 + clippy_utils/src/msrvs.rs | 1 + clippy_utils/src/qualify_min_const_fn.rs | 10 +- clippy_utils/src/sym.rs | 4 +- rust-toolchain.toml | 2 +- src/driver.rs | 81 +- tests/integration.rs | 12 +- .../disallowed_profiles_methods/clippy.toml | 13 + .../disallowed_profiles_methods/main.rs | 78 ++ .../disallowed_profiles_methods/main.stderr | 72 ++ .../disallowed_profiles_types/clippy.toml | 13 + .../ui-toml/disallowed_profiles_types/main.rs | 61 ++ .../disallowed_profiles_types/main.stderr | 78 ++ ...ub_underscore_fields.all_pub_fields.stderr | 14 +- .../pub_underscore_fields.exported.stderr | 2 +- .../pub_underscore_fields.rs | 1 - tests/ui-toml/ref_option/ref_option.all.fixed | 2 +- .../ref_option/ref_option.private.fixed | 2 +- tests/ui-toml/ref_option/ref_option.rs | 2 +- .../renamed_function_params.default.stderr | 12 +- .../renamed_function_params.extend.stderr | 8 +- .../renamed_function_params.rs | 1 - tests/ui-toml/semicolon_block/both.fixed | 10 +- tests/ui-toml/semicolon_block/both.rs | 10 +- tests/ui-toml/semicolon_block/both.stderr | 8 +- .../semicolon_inside_block.fixed | 7 +- .../semicolon_block/semicolon_inside_block.rs | 7 +- .../semicolon_inside_block.stderr | 2 +- .../semicolon_outside_block.fixed | 7 +- .../semicolon_outside_block.rs | 7 +- .../semicolon_outside_block.stderr | 6 +- tests/ui-toml/suppress_lint_in_const/test.rs | 14 +- .../suppress_lint_in_const/test.stderr | 12 +- .../conf_french_disallowed_name.rs | 2 - .../conf_french_disallowed_name.stderr | 14 +- .../conf_disallowed_methods.rs | 3 +- .../conf_disallowed_methods.stderr | 34 +- ...conf_inconsistent_struct_constructor.fixed | 4 +- .../conf_inconsistent_struct_constructor.rs | 4 +- ...onf_inconsistent_struct_constructor.stderr | 10 +- .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui-toml/unwrap_used/unwrap_used.fixed | 10 +- tests/ui-toml/unwrap_used/unwrap_used.rs | 10 +- tests/ui-toml/unwrap_used/unwrap_used.stderr | 56 +- .../unwrap_used_allowed.rs | 1 - .../unwrap_used_allowed.stderr | 4 +- tests/ui-toml/useless_vec/useless_vec.fixed | 2 +- tests/ui-toml/useless_vec/useless_vec.rs | 2 +- tests/ui/absurd-extreme-comparisons.rs | 8 +- tests/ui/absurd-extreme-comparisons.stderr | 36 +- tests/ui/allow_attributes.fixed | 1 - tests/ui/allow_attributes.rs | 1 - tests/ui/allow_attributes.stderr | 8 +- tests/ui/almost_complete_range.fixed | 5 +- tests/ui/almost_complete_range.rs | 5 +- tests/ui/almost_complete_range.stderr | 54 +- tests/ui/arc_with_non_send_sync.rs | 1 - tests/ui/arc_with_non_send_sync.stderr | 6 +- tests/ui/arithmetic_side_effects.rs | 6 +- tests/ui/arithmetic_side_effects.stderr | 262 +++--- tests/ui/as_conversions.rs | 2 +- tests/ui/as_ptr_cast_mut.fixed | 3 +- tests/ui/as_ptr_cast_mut.rs | 3 +- tests/ui/as_ptr_cast_mut.stderr | 2 +- tests/ui/as_ptr_cast_mut_unfixable.rs | 1 - tests/ui/as_ptr_cast_mut_unfixable.stderr | 2 +- tests/ui/assertions_on_constants.rs | 3 +- tests/ui/assertions_on_constants.stderr | 34 +- tests/ui/assertions_on_result_states.fixed | 2 +- tests/ui/assertions_on_result_states.rs | 2 +- tests/ui/assign_ops.fixed | 2 +- tests/ui/assign_ops.rs | 2 +- tests/ui/assigning_clones.fixed | 6 +- tests/ui/assigning_clones.rs | 6 +- tests/ui/assigning_clones.stderr | 60 +- tests/ui/attrs.rs | 4 +- tests/ui/author/blocks.rs | 2 +- tests/ui/author/if.rs | 1 + tests/ui/author/issue_3849.rs | 4 +- tests/ui/author/loop.rs | 6 +- tests/ui/author/macro_in_closure.rs | 2 +- tests/ui/author/macro_in_loop.rs | 2 +- tests/ui/author/matches.rs | 2 +- tests/ui/author/struct.rs | 7 +- tests/ui/auxiliary/extern_fake_libc.rs | 3 +- tests/ui/auxiliary/macro_rules.rs | 2 - tests/ui/auxiliary/option_helpers.rs | 2 +- tests/ui/auxiliary/proc_macro_attr.rs | 2 +- tests/ui/auxiliary/proc_macro_derive.rs | 8 +- tests/ui/auxiliary/proc_macros.rs | 2 +- tests/ui/await_holding_lock.rs | 2 +- tests/ui/bind_instead_of_map.fixed | 2 +- tests/ui/bind_instead_of_map.rs | 2 +- tests/ui/bind_instead_of_map_multipart.fixed | 2 +- tests/ui/bind_instead_of_map_multipart.rs | 2 +- tests/ui/blocks_in_conditions.fixed | 11 +- tests/ui/blocks_in_conditions.rs | 9 +- tests/ui/blocks_in_conditions.stderr | 20 +- tests/ui/blocks_in_conditions_2021.fixed | 2 +- tests/ui/blocks_in_conditions_2021.rs | 2 +- tests/ui/bool_assert_comparison.fixed | 3 +- tests/ui/bool_assert_comparison.rs | 3 +- tests/ui/bool_assert_comparison.stderr | 78 +- tests/ui/bool_comparison.fixed | 3 +- tests/ui/bool_comparison.rs | 3 +- tests/ui/bool_comparison.stderr | 46 +- tests/ui/bool_to_int_with_if.fixed | 1 - tests/ui/bool_to_int_with_if.rs | 1 - tests/ui/bool_to_int_with_if.stderr | 22 +- tests/ui/borrow_and_ref_as_ptr.fixed | 3 +- tests/ui/borrow_and_ref_as_ptr.rs | 3 +- tests/ui/borrow_and_ref_as_ptr.stderr | 4 +- tests/ui/borrow_as_ptr.fixed | 2 +- tests/ui/borrow_as_ptr.rs | 2 +- tests/ui/borrow_as_ptr_raw_ref.fixed | 2 +- tests/ui/borrow_as_ptr_raw_ref.rs | 2 +- tests/ui/borrow_box.stderr | 68 -- tests/ui/borrow_deref_ref.fixed | 2 - tests/ui/borrow_deref_ref.rs | 2 - tests/ui/borrow_deref_ref.stderr | 18 +- tests/ui/borrow_deref_ref_unfixable.rs | 1 - tests/ui/borrow_deref_ref_unfixable.stderr | 2 +- tests/ui/borrow_interior_mutable_const.rs | 7 +- tests/ui/borrow_interior_mutable_const.stderr | 58 +- .../{borrow_box.fixed => borrowed_box.fixed} | 17 +- tests/ui/{borrow_box.rs => borrowed_box.rs} | 17 +- tests/ui/borrowed_box.stderr | 68 ++ tests/ui/box_collection.rs | 7 +- tests/ui/box_collection.stderr | 18 +- tests/ui/box_default.fixed | 2 +- tests/ui/box_default.rs | 2 +- tests/ui/boxed_local.rs | 8 +- .../branches_sharing_code/false_positives.rs | 3 +- .../branches_sharing_code/shared_at_bottom.rs | 7 +- .../shared_at_bottom.stderr | 38 +- .../ui/branches_sharing_code/shared_at_top.rs | 2 - .../shared_at_top.stderr | 18 +- .../shared_at_top_and_bottom.rs | 1 - .../shared_at_top_and_bottom.stderr | 24 +- .../branches_sharing_code/valid_if_blocks.rs | 7 +- .../valid_if_blocks.stderr | 20 +- tests/ui/builtin_type_shadow.rs | 2 +- tests/ui/byte_char_slices.fixed | 2 +- tests/ui/byte_char_slices.rs | 2 +- tests/ui/bytecount.rs | 2 +- tests/ui/bytes_nth.fixed | 2 - tests/ui/bytes_nth.rs | 2 - tests/ui/bytes_nth.stderr | 6 +- tests/ui/cast.rs | 14 +- tests/ui/cast_abs_to_unsigned.fixed | 1 - tests/ui/cast_abs_to_unsigned.rs | 1 - tests/ui/cast_abs_to_unsigned.stderr | 36 +- tests/ui/cast_alignment.rs | 7 +- tests/ui/cast_alignment.stderr | 8 +- tests/ui/cast_enum_constructor.rs | 2 +- tests/ui/cast_lossless_bool.fixed | 1 - tests/ui/cast_lossless_bool.rs | 1 - tests/ui/cast_lossless_bool.stderr | 30 +- tests/ui/cast_lossless_float.fixed | 1 - tests/ui/cast_lossless_float.rs | 1 - tests/ui/cast_lossless_float.stderr | 26 +- tests/ui/cast_lossless_integer.fixed | 2 +- tests/ui/cast_lossless_integer.rs | 2 +- tests/ui/cast_nan_to_int.rs | 2 +- tests/ui/cast_size.rs | 8 +- tests/ui/cast_slice_different_sizes.rs | 2 +- tests/ui/cfg_attr_cargo_clippy.fixed | 2 +- tests/ui/cfg_attr_cargo_clippy.rs | 2 +- tests/ui/cfg_attr_rustfmt.fixed | 2 +- tests/ui/cfg_attr_rustfmt.rs | 2 +- tests/ui/cfg_not_test.rs | 1 - tests/ui/cfg_not_test.stderr | 10 +- tests/ui/checked_conversions.fixed | 8 +- tests/ui/checked_conversions.rs | 8 +- tests/ui/checked_conversions.stderr | 36 +- .../ui/checked_unwrap/simple_conditionals.rs | 6 +- tests/ui/clear_with_drain.fixed | 1 - tests/ui/clear_with_drain.rs | 1 - tests/ui/clear_with_drain.stderr | 42 +- tests/ui/clone_on_copy.fixed | 7 +- tests/ui/clone_on_copy.rs | 7 +- tests/ui/clone_on_copy.stderr | 20 +- tests/ui/clone_on_copy_impl.rs | 2 +- tests/ui/cloned_instead_of_copied.fixed | 3 +- tests/ui/cloned_instead_of_copied.rs | 3 +- tests/ui/cloned_instead_of_copied.stderr | 16 +- tests/ui/cloned_ref_to_slice_refs.fixed | 1 - tests/ui/cloned_ref_to_slice_refs.rs | 1 - tests/ui/cloned_ref_to_slice_refs.stderr | 22 +- tests/ui/cmp_null.fixed | 1 - tests/ui/cmp_null.rs | 1 - tests/ui/cmp_null.stderr | 14 +- .../ui/cmp_owned/asymmetric_partial_eq.fixed | 7 +- tests/ui/cmp_owned/asymmetric_partial_eq.rs | 7 +- .../ui/cmp_owned/asymmetric_partial_eq.stderr | 12 +- tests/ui/cmp_owned/without_suggestion.rs | 3 - tests/ui/cmp_owned/without_suggestion.stderr | 6 +- tests/ui/cognitive_complexity.rs | 2 +- tests/ui/cognitive_complexity_attr_used.rs | 3 +- .../ui/cognitive_complexity_attr_used.stderr | 2 +- tests/ui/collapsible_else_if.fixed | 1 - tests/ui/collapsible_else_if.rs | 1 - tests/ui/collapsible_else_if.stderr | 24 +- tests/ui/collapsible_if.fixed | 10 +- tests/ui/collapsible_if.rs | 10 +- tests/ui/collapsible_if.stderr | 24 +- tests/ui/collapsible_match.rs | 7 +- tests/ui/collapsible_match.stderr | 68 +- tests/ui/collapsible_match2.rs | 7 +- tests/ui/collapsible_match2.stderr | 20 +- tests/ui/collapsible_match_fixable.fixed | 34 +- tests/ui/collapsible_match_fixable.rs | 34 +- tests/ui/collapsible_match_fixable.stderr | 50 +- tests/ui/collapsible_str_replace.fixed | 1 - tests/ui/collapsible_str_replace.rs | 1 - tests/ui/collapsible_str_replace.stderr | 28 +- tests/ui/collection_is_never_read.rs | 2 +- tests/ui/comparison_chain.rs | 1 - tests/ui/comparison_chain.stderr | 12 +- tests/ui/comparison_to_empty.fixed | 3 +- tests/ui/comparison_to_empty.rs | 3 +- tests/ui/comparison_to_empty.stderr | 26 +- tests/ui/const_comparisons.rs | 3 +- tests/ui/const_comparisons.stderr | 62 +- tests/ui/const_is_empty.rs | 2 +- tests/ui/copy_iterator.rs | 2 +- .../elidable_lifetime_names_impl_trait.fixed | 1 - .../elidable_lifetime_names_impl_trait.rs | 1 - .../elidable_lifetime_names_impl_trait.stderr | 4 +- tests/ui/crashes/ice-11230.fixed | 3 +- tests/ui/crashes/ice-11230.rs | 3 +- tests/ui/crashes/ice-11230.stderr | 2 +- tests/ui/crashes/ice-11939.rs | 2 +- tests/ui/crashes/ice-12585.rs | 2 +- tests/ui/crashes/ice-12616.fixed | 2 +- tests/ui/crashes/ice-12616.rs | 2 +- tests/ui/crashes/ice-13544-reduced.rs | 2 +- tests/ui/crashes/ice-14325.rs | 2 +- tests/ui/crashes/ice-1782.rs | 4 +- tests/ui/crashes/ice-2499.rs | 2 +- tests/ui/crashes/ice-2594.rs | 2 - tests/ui/crashes/ice-2760.rs | 7 +- tests/ui/crashes/ice-2865.rs | 2 +- tests/ui/crashes/ice-3969.rs | 1 - tests/ui/crashes/ice-3969.stderr | 10 +- tests/ui/crashes/ice-4579.rs | 2 +- tests/ui/crashes/ice-4760.rs | 2 - tests/ui/crashes/ice-4775.rs | 2 - tests/ui/crashes/ice-4968.rs | 2 +- tests/ui/crashes/ice-5389.rs | 2 +- tests/ui/crashes/ice-5497.rs | 2 +- tests/ui/crashes/ice-5579.rs | 2 +- tests/ui/crashes/ice-5944.rs | 2 +- tests/ui/crashes/ice-6179.rs | 2 +- tests/ui/crashes/ice-6254.rs | 2 +- tests/ui/crashes/ice-6256.rs | 2 +- tests/ui/crashes/ice-6840.rs | 1 - tests/ui/crashes/ice-7169.fixed | 2 - tests/ui/crashes/ice-7169.rs | 2 - tests/ui/crashes/ice-7169.stderr | 11 +- tests/ui/crashes/ice-7231.rs | 2 +- tests/ui/crashes/ice-7272.rs | 2 +- tests/ui/crashes/ice-7340.rs | 2 +- tests/ui/crashes/ice-7410.rs | 4 +- tests/ui/crashes/ice-7868.rs | 1 - tests/ui/crashes/ice-7934.rs | 1 - tests/ui/crashes/ice-9238.rs | 2 +- tests/ui/crashes/ice-9405.rs | 1 - tests/ui/crashes/ice-9405.stderr | 2 +- tests/ui/crashes/ice-rust-107877.rs | 2 - tests/ui/crashes/issue-825.rs | 2 +- tests/ui/crashes/issues_loop_mut_cond.rs | 2 - tests/ui/crashes/mgca-16691.rs | 2 +- tests/ui/crashes/regressions.rs | 2 - tests/ui/crashes/trivial_bounds.rs | 2 +- tests/ui/create_dir.fixed | 1 - tests/ui/create_dir.rs | 1 - tests/ui/create_dir.stderr | 10 +- tests/ui/dbg_macro/dbg_macro.fixed | 7 +- tests/ui/dbg_macro/dbg_macro.rs | 7 +- tests/ui/dbg_macro/dbg_macro.stderr | 40 +- tests/ui/debug_assert_with_mut_call.rs | 2 +- tests/ui/decimal_bitwise_operands.rs | 6 +- tests/ui/declare_interior_mutable_const.rs | 2 +- tests/ui/def_id_nocore.rs | 2 +- .../ui/default_constructed_unit_structs.fixed | 1 - tests/ui/default_constructed_unit_structs.rs | 1 - .../default_constructed_unit_structs.stderr | 14 +- tests/ui/default_instead_of_iter_empty.fixed | 1 - tests/ui/default_instead_of_iter_empty.rs | 1 - tests/ui/default_instead_of_iter_empty.stderr | 6 +- ...default_instead_of_iter_empty_no_std.fixed | 1 - .../default_instead_of_iter_empty_no_std.rs | 1 - ...efault_instead_of_iter_empty_no_std.stderr | 4 +- tests/ui/default_numeric_fallback_f64.fixed | 12 +- tests/ui/default_numeric_fallback_f64.rs | 12 +- tests/ui/default_numeric_fallback_f64.stderr | 46 +- tests/ui/default_numeric_fallback_i32.fixed | 10 +- tests/ui/default_numeric_fallback_i32.rs | 10 +- tests/ui/default_numeric_fallback_i32.stderr | 56 +- tests/ui/default_trait_access.fixed | 1 - tests/ui/default_trait_access.rs | 1 - tests/ui/default_trait_access.stderr | 16 +- tests/ui/default_union_representation.rs | 2 +- tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 36 +- tests/ui/deref_addrof.fixed | 8 +- tests/ui/deref_addrof.rs | 8 +- tests/ui/deref_addrof.stderr | 36 +- tests/ui/deref_by_slicing.fixed | 1 - tests/ui/deref_by_slicing.rs | 1 - tests/ui/deref_by_slicing.stderr | 20 +- tests/ui/derivable_impls.fixed | 1 - tests/ui/derivable_impls.rs | 1 - tests/ui/derivable_impls.stderr | 24 +- tests/ui/derivable_impls_derive_const.fixed | 1 - tests/ui/derivable_impls_derive_const.rs | 1 - tests/ui/derivable_impls_derive_const.stderr | 4 +- tests/ui/derive.rs | 12 +- tests/ui/derive.stderr | 12 +- tests/ui/derive_ord_xor_partial_ord.rs | 3 +- tests/ui/derive_ord_xor_partial_ord.stderr | 16 +- tests/ui/derive_partial_eq_without_eq.fixed | 1 - tests/ui/derive_partial_eq_without_eq.rs | 1 - tests/ui/derive_partial_eq_without_eq.stderr | 26 +- tests/ui/derived_hash_with_manual_eq.rs | 2 - tests/ui/derived_hash_with_manual_eq.stderr | 8 +- tests/ui/disallowed_names.rs | 10 +- tests/ui/disallowed_names.stderr | 28 +- tests/ui/disallowed_script_idents.rs | 1 - tests/ui/disallowed_script_idents.stderr | 4 +- tests/ui/diverging_sub_expression.rs | 7 +- tests/ui/diverging_sub_expression.stderr | 4 +- tests/ui/doc/doc-fixable.fixed | 3 +- tests/ui/doc/doc-fixable.rs | 3 +- tests/ui/doc/doc-fixable.stderr | 44 +- .../doc_comment_double_space_linebreaks.fixed | 2 +- .../doc_comment_double_space_linebreaks.rs | 2 +- tests/ui/doc/doc_lazy_list.fixed | 2 +- tests/ui/doc/doc_lazy_list.rs | 2 +- tests/ui/doc/doc_markdown-issue_13097.fixed | 2 +- tests/ui/doc/doc_markdown-issue_13097.rs | 2 +- tests/ui/doc/unbalanced_ticks.rs | 1 - tests/ui/doc/unbalanced_ticks.stderr | 22 +- tests/ui/doc_errors.rs | 2 +- tests/ui/doc_suspicious_footnotes.fixed | 2 +- tests/ui/doc_suspicious_footnotes.rs | 2 +- tests/ui/doc_unsafe.rs | 1 - tests/ui/doc_unsafe.stderr | 26 +- tests/ui/double_comparison.fixed | 2 - tests/ui/double_comparison.rs | 2 - tests/ui/double_comparison.stderr | 24 +- tests/ui/double_must_use.fixed | 65 ++ tests/ui/double_must_use.rs | 2 +- tests/ui/double_must_use.stderr | 24 +- tests/ui/double_must_use_unfixable.rs | 17 + tests/ui/double_must_use_unfixable.stderr | 30 + tests/ui/drain_collect.fixed | 1 - tests/ui/drain_collect.rs | 1 - tests/ui/drain_collect.stderr | 20 +- tests/ui/duration_subsec.fixed | 2 +- tests/ui/duration_subsec.rs | 2 +- tests/ui/eager_transmute.fixed | 2 +- tests/ui/eager_transmute.rs | 2 +- tests/ui/elidable_lifetime_names.fixed | 3 +- tests/ui/elidable_lifetime_names.rs | 3 +- tests/ui/elidable_lifetime_names.stderr | 48 +- tests/ui/else_if_without_else.rs | 2 +- tests/ui/empty_docs.rs | 3 +- tests/ui/empty_docs.stderr | 18 +- tests/ui/empty_drop.fixed | 1 - tests/ui/empty_drop.rs | 1 - tests/ui/empty_drop.stderr | 4 +- .../empty_enum_variants_with_brackets.fixed | 1 - tests/ui/empty_enum_variants_with_brackets.rs | 1 - .../empty_enum_variants_with_brackets.stderr | 22 +- .../ui/empty_line_after/doc_comments.1.fixed | 3 +- .../ui/empty_line_after/doc_comments.2.fixed | 3 +- tests/ui/empty_line_after/doc_comments.rs | 3 +- tests/ui/empty_line_after/doc_comments.stderr | 24 +- .../empty_line_after/outer_attribute.1.fixed | 3 +- .../empty_line_after/outer_attribute.2.fixed | 3 +- tests/ui/empty_line_after/outer_attribute.rs | 3 +- .../empty_line_after/outer_attribute.stderr | 20 +- tests/ui/empty_structs_with_brackets.fixed | 1 - tests/ui/empty_structs_with_brackets.rs | 1 - tests/ui/empty_structs_with_brackets.stderr | 6 +- tests/ui/endian_bytes.rs | 3 +- tests/ui/endian_bytes.stderr | 172 ++-- tests/ui/entry.fixed | 1 - tests/ui/entry.rs | 1 - tests/ui/entry.stderr | 24 +- tests/ui/entry_btree.fixed | 1 - tests/ui/entry_btree.rs | 1 - tests/ui/entry_btree.stderr | 2 +- tests/ui/entry_unfixable.rs | 1 - tests/ui/entry_unfixable.stderr | 6 +- tests/ui/entry_with_else.fixed | 1 - tests/ui/entry_with_else.rs | 1 - tests/ui/entry_with_else.stderr | 14 +- tests/ui/enum_clike_unportable_variant.rs | 2 +- tests/ui/enum_glob_use.fixed | 5 +- tests/ui/enum_glob_use.rs | 5 +- tests/ui/enum_glob_use.stderr | 6 +- tests/ui/enum_variants.rs | 2 +- tests/ui/eprint_with_newline.fixed | 2 +- tests/ui/eprint_with_newline.rs | 2 +- tests/ui/eq_op.rs | 2 +- tests/ui/eq_op_macros.rs | 2 +- tests/ui/equatable_if_let.fixed | 2 +- tests/ui/equatable_if_let.rs | 2 +- tests/ui/equatable_if_let_const_cmp.fixed | 1 - tests/ui/equatable_if_let_const_cmp.rs | 1 - tests/ui/equatable_if_let_const_cmp.stderr | 4 +- tests/ui/err_expect.fixed | 2 +- tests/ui/err_expect.rs | 2 +- tests/ui/error_impl_error.rs | 1 - tests/ui/error_impl_error.stderr | 14 +- tests/ui/eta.fixed | 12 +- tests/ui/eta.rs | 12 +- tests/ui/eta.stderr | 88 +- tests/ui/excessive_precision.fixed | 9 +- tests/ui/excessive_precision.rs | 9 +- tests/ui/excessive_precision.stderr | 44 +- tests/ui/exhaustive_items.fixed | 3 +- tests/ui/exhaustive_items.rs | 3 +- tests/ui/exhaustive_items.stderr | 20 +- tests/ui/expect.rs | 2 +- tests/ui/expect_fun_call.fixed | 6 +- tests/ui/expect_fun_call.rs | 6 +- tests/ui/expect_fun_call.stderr | 36 +- tests/ui/expect_tool_lint_rfc_2383.rs | 1 - tests/ui/expect_tool_lint_rfc_2383.stderr | 14 +- tests/ui/explicit_auto_deref.fixed | 15 +- tests/ui/explicit_auto_deref.rs | 15 +- tests/ui/explicit_auto_deref.stderr | 92 +- tests/ui/explicit_counter_loop.rs | 23 +- tests/ui/explicit_counter_loop.stderr | 8 +- tests/ui/explicit_deref_methods.fixed | 12 +- tests/ui/explicit_deref_methods.rs | 12 +- tests/ui/explicit_deref_methods.stderr | 26 +- tests/ui/explicit_into_iter_loop.fixed | 1 - tests/ui/explicit_into_iter_loop.rs | 1 - tests/ui/explicit_into_iter_loop.stderr | 14 +- tests/ui/explicit_iter_loop.fixed | 12 +- tests/ui/explicit_iter_loop.rs | 12 +- tests/ui/explicit_iter_loop.stderr | 45 +- tests/ui/explicit_write.fixed | 1 - tests/ui/explicit_write.rs | 1 - tests/ui/explicit_write.stderr | 26 +- tests/ui/extend_with_drain.fixed | 1 - tests/ui/extend_with_drain.rs | 1 - tests/ui/extend_with_drain.stderr | 8 +- tests/ui/extra_unused_lifetimes.rs | 41 +- tests/ui/extra_unused_lifetimes.stderr | 46 +- tests/ui/extra_unused_type_parameters.fixed | 2 +- tests/ui/extra_unused_type_parameters.rs | 2 +- tests/ui/field_reassign_with_default.rs | 2 +- tests/ui/filetype_is_file.rs | 1 - tests/ui/filetype_is_file.stderr | 6 +- tests/ui/filter_map_bool_then.fixed | 7 +- tests/ui/filter_map_bool_then.rs | 7 +- tests/ui/filter_map_bool_then.stderr | 22 +- tests/ui/filter_map_bool_then_unfixable.rs | 2 +- tests/ui/filter_map_identity.fixed | 2 +- tests/ui/filter_map_identity.rs | 2 +- tests/ui/find_map.rs | 35 - tests/ui/flat_map_identity.fixed | 2 +- tests/ui/flat_map_identity.rs | 2 +- tests/ui/flat_map_option.fixed | 1 - tests/ui/flat_map_option.rs | 1 - tests/ui/flat_map_option.stderr | 4 +- tests/ui/float_arithmetic.rs | 12 +- tests/ui/float_arithmetic.stderr | 34 +- tests/ui/float_cmp.rs | 7 +- tests/ui/float_cmp.stderr | 12 +- tests/ui/float_cmp_const.rs | 4 +- tests/ui/floating_point_arithmetic_nostd.rs | 3 +- tests/ui/floating_point_exp.fixed | 2 +- tests/ui/floating_point_exp.rs | 2 +- tests/ui/floating_point_log.fixed | 4 +- tests/ui/floating_point_log.rs | 4 +- tests/ui/floating_point_logbase.fixed | 2 +- tests/ui/floating_point_logbase.rs | 2 +- tests/ui/floating_point_powf.fixed | 4 +- tests/ui/floating_point_powf.rs | 4 +- tests/ui/floating_point_powi.fixed | 2 +- tests/ui/floating_point_powi.rs | 2 +- tests/ui/fn_params_excessive_bools.rs | 2 +- tests/ui/fn_to_numeric_cast.rs | 2 +- tests/ui/fn_to_numeric_cast_any.rs | 2 +- tests/ui/for_kv_map.fixed | 2 +- tests/ui/for_kv_map.rs | 2 +- tests/ui/for_unbounded_range.fixed | 35 + tests/ui/for_unbounded_range.rs | 35 + tests/ui/for_unbounded_range.stderr | 32 + tests/ui/format.fixed | 12 +- tests/ui/format.rs | 12 +- tests/ui/format.stderr | 38 +- tests/ui/format_args.fixed | 12 +- tests/ui/format_args.rs | 12 +- tests/ui/format_args.stderr | 52 +- tests/ui/format_collect.rs | 1 - tests/ui/format_collect.stderr | 18 +- ....rs => format_in_format_args_unfixable.rs} | 5 +- ...=> format_in_format_args_unfixable.stderr} | 50 +- tests/ui/formatting.rs | 4 +- tests/ui/formatting.stderr | 12 +- tests/ui/four_forward_slashes.fixed | 3 - tests/ui/four_forward_slashes.rs | 3 - tests/ui/four_forward_slashes.stderr | 12 +- tests/ui/from_iter_instead_of_collect.fixed | 118 --- tests/ui/from_iter_instead_of_collect.rs | 118 --- tests/ui/from_iter_instead_of_collect.stderr | 149 ---- tests/ui/from_over_into.fixed | 2 - tests/ui/from_over_into.rs | 2 - tests/ui/from_over_into.stderr | 16 +- tests/ui/from_raw_with_void_ptr.rs | 2 +- tests/ui/functions.rs | 2 +- tests/ui/functions_maxlines.rs | 2 +- tests/ui/get_first.fixed | 2 +- tests/ui/get_first.rs | 2 +- tests/ui/get_last_with_len.fixed | 2 +- tests/ui/get_last_with_len.rs | 2 +- tests/ui/get_unwrap.fixed | 11 +- tests/ui/get_unwrap.rs | 11 +- tests/ui/get_unwrap.stderr | 62 +- tests/ui/identity_op.fixed | 15 +- tests/ui/identity_op.rs | 15 +- tests/ui/identity_op.stderr | 140 +-- tests/ui/if_let_mutex.rs | 2 +- tests/ui/if_same_then_else.rs | 8 +- tests/ui/if_same_then_else.stderr | 92 +- tests/ui/if_same_then_else2.rs | 12 +- tests/ui/if_same_then_else2.stderr | 24 +- tests/ui/if_then_some_else_none.fixed | 7 +- tests/ui/if_then_some_else_none.rs | 7 +- tests/ui/if_then_some_else_none.stderr | 28 +- tests/ui/if_then_some_else_none_unfixable.rs | 2 +- tests/ui/ifs_same_cond.rs | 6 +- tests/ui/ifs_same_cond.stderr | 10 +- tests/ui/ignored_unit_patterns.fixed | 9 +- tests/ui/ignored_unit_patterns.rs | 9 +- tests/ui/ignored_unit_patterns.stderr | 18 +- tests/ui/impl.rs | 2 +- tests/ui/impl_trait_in_params.rs | 1 - tests/ui/impl_trait_in_params.stderr | 8 +- tests/ui/implicit_clone.fixed | 2 +- tests/ui/implicit_clone.rs | 2 +- tests/ui/implicit_return.fixed | 2 +- tests/ui/implicit_return.rs | 2 +- tests/ui/implicit_saturating_add.fixed | 1 - tests/ui/implicit_saturating_add.rs | 1 - tests/ui/implicit_saturating_add.stderr | 48 +- tests/ui/implicit_saturating_sub.fixed | 2 +- tests/ui/implicit_saturating_sub.rs | 2 +- tests/ui/implied_bounds_in_impls.fixed | 1 - tests/ui/implied_bounds_in_impls.rs | 1 - tests/ui/implied_bounds_in_impls.stderr | 46 +- tests/ui/incompatible_msrv.rs | 4 +- .../ui/inconsistent_struct_constructor.fixed | 5 +- tests/ui/inconsistent_struct_constructor.rs | 5 +- .../ui/inconsistent_struct_constructor.stderr | 4 +- .../if_let_slice_binding.fixed | 4 +- .../if_let_slice_binding.rs | 4 +- .../if_let_slice_binding.stderr | 7 +- tests/ui/indexing_slicing_index.rs | 12 +- tests/ui/indexing_slicing_index.stderr | 35 +- tests/ui/indexing_slicing_slice.rs | 14 +- tests/ui/indexing_slicing_slice.stderr | 45 +- tests/ui/infallible_destructuring_match.fixed | 3 +- tests/ui/infallible_destructuring_match.rs | 3 +- .../ui/infallible_destructuring_match.stderr | 24 +- tests/ui/infinite_iter.rs | 2 - tests/ui/infinite_iter.stderr | 32 +- tests/ui/infinite_loops.rs | 2 +- tests/ui/inherent_to_string.rs | 2 +- tests/ui/inline_fn_without_body.fixed | 1 - tests/ui/inline_fn_without_body.rs | 1 - tests/ui/inline_fn_without_body.stderr | 6 +- tests/ui/inline_trait_bounds.fixed | 36 + tests/ui/inline_trait_bounds.rs | 36 + tests/ui/inline_trait_bounds.stderr | 14 +- tests/ui/integer_division_remainder_used.rs | 3 +- .../ui/integer_division_remainder_used.stderr | 18 +- tests/ui/into_iter_on_ref.fixed | 2 +- tests/ui/into_iter_on_ref.rs | 2 +- tests/ui/invalid_upcast_comparisons.rs | 8 +- tests/ui/invalid_upcast_comparisons.stderr | 56 +- tests/ui/ip_constant.fixed | 3 +- tests/ui/ip_constant.rs | 3 +- tests/ui/ip_constant.stderr | 42 +- tests/ui/issue-111399.rs | 2 +- tests/ui/issue_2356.fixed | 3 +- tests/ui/issue_2356.rs | 3 +- tests/ui/issue_2356.stderr | 9 +- tests/ui/issue_4266.rs | 2 - tests/ui/issue_4266.stderr | 6 +- tests/ui/items_after_statement.rs | 2 +- .../imported_module.rs | 1 - tests/ui/iter_cloned_collect.fixed | 3 +- tests/ui/iter_cloned_collect.rs | 3 +- tests/ui/iter_cloned_collect.stderr | 75 +- tests/ui/iter_count.fixed | 11 +- tests/ui/iter_count.rs | 11 +- tests/ui/iter_count.stderr | 50 +- tests/ui/iter_filter_is_ok.fixed | 8 +- tests/ui/iter_filter_is_ok.rs | 8 +- tests/ui/iter_filter_is_ok.stderr | 24 +- tests/ui/iter_filter_is_some.fixed | 10 +- tests/ui/iter_filter_is_some.rs | 10 +- tests/ui/iter_filter_is_some.stderr | 20 +- tests/ui/iter_kv_map.fixed | 9 +- tests/ui/iter_kv_map.rs | 9 +- tests/ui/iter_kv_map.stderr | 102 +-- tests/ui/iter_next_loop.rs | 2 +- tests/ui/iter_next_slice.fixed | 11 +- tests/ui/iter_next_slice.rs | 11 +- tests/ui/iter_next_slice.stderr | 22 +- tests/ui/iter_nth.fixed | 2 +- tests/ui/iter_nth.rs | 2 +- tests/ui/iter_on_empty_collections.fixed | 3 +- tests/ui/iter_on_empty_collections.rs | 3 +- tests/ui/iter_on_single_items.fixed | 3 +- tests/ui/iter_on_single_items.rs | 3 +- tests/ui/iter_out_of_bounds.rs | 4 +- tests/ui/iter_out_of_bounds.stderr | 7 +- tests/ui/iter_overeager_cloned.fixed | 11 +- tests/ui/iter_overeager_cloned.rs | 11 +- tests/ui/iter_overeager_cloned.stderr | 42 +- tests/ui/iter_skip_next.fixed | 7 +- tests/ui/iter_skip_next.rs | 7 +- tests/ui/iter_skip_next.stderr | 14 +- tests/ui/iter_skip_next_unfixable.rs | 1 - tests/ui/iter_skip_next_unfixable.stderr | 12 +- tests/ui/iter_skip_zero.fixed | 2 +- tests/ui/iter_skip_zero.rs | 2 +- tests/ui/iter_with_drain.fixed | 6 +- tests/ui/iter_with_drain.rs | 6 +- tests/ui/iter_with_drain.stderr | 12 +- tests/ui/iter_without_into_iter.rs | 1 - tests/ui/iter_without_into_iter.stderr | 16 +- tests/ui/iterator_step_by_zero.rs | 2 +- tests/ui/join_absolute_paths.1.fixed | 2 +- tests/ui/join_absolute_paths.2.fixed | 2 +- tests/ui/join_absolute_paths.rs | 2 +- tests/ui/large_const_arrays.fixed | 15 +- tests/ui/large_const_arrays.rs | 15 +- tests/ui/large_const_arrays.stderr | 50 +- tests/ui/large_enum_variant.r32bit.stderr | 48 +- tests/ui/large_enum_variant.r64bit.stderr | 52 +- tests/ui/large_enum_variant.rs | 2 - tests/ui/large_futures.fixed | 7 +- tests/ui/large_futures.rs | 7 +- tests/ui/large_futures.stderr | 16 +- tests/ui/large_stack_arrays.rs | 2 +- tests/ui/large_stack_frames.rs | 1 - tests/ui/large_stack_frames.stderr | 8 +- tests/ui/legacy_numeric_constants.fixed | 83 +- tests/ui/legacy_numeric_constants.rs | 83 +- tests/ui/legacy_numeric_constants.stderr | 44 +- .../ui/legacy_numeric_constants_unfixable.rs | 1 - .../legacy_numeric_constants_unfixable.stderr | 18 +- tests/ui/len_without_is_empty.rs | 3 +- tests/ui/len_without_is_empty.stderr | 40 +- tests/ui/len_without_is_empty_expect.rs | 2 +- tests/ui/len_zero.fixed | 9 +- tests/ui/len_zero.rs | 9 +- tests/ui/len_zero.stderr | 54 +- tests/ui/len_zero_ranges.fixed | 1 - tests/ui/len_zero_ranges.rs | 1 - tests/ui/len_zero_ranges.stderr | 4 +- tests/ui/let_and_return.edition2021.fixed | 1 - tests/ui/let_and_return.edition2021.stderr | 22 +- tests/ui/let_and_return.edition2024.fixed | 1 - tests/ui/let_and_return.edition2024.stderr | 32 +- tests/ui/let_and_return.rs | 1 - tests/ui/let_if_seq.rs | 9 +- tests/ui/let_if_seq.stderr | 12 +- tests/ui/let_underscore_must_use.rs | 2 +- tests/ui/let_underscore_untyped.rs | 1 - tests/ui/let_underscore_untyped.stderr | 20 +- tests/ui/let_unit.fixed | 7 +- tests/ui/let_unit.rs | 7 +- tests/ui/let_unit.stderr | 18 +- tests/ui/let_with_type_underscore.fixed | 3 +- tests/ui/let_with_type_underscore.rs | 3 +- tests/ui/let_with_type_underscore.stderr | 18 +- tests/ui/lines_filter_map_ok.fixed | 2 +- tests/ui/lines_filter_map_ok.rs | 2 +- tests/ui/linkedlist.rs | 2 +- .../ui/literal_string_with_formatting_arg.rs | 2 +- tests/ui/literals.rs | 12 +- tests/ui/literals.stderr | 44 +- tests/ui/lossy_float_literal.fixed | 2 +- tests/ui/lossy_float_literal.rs | 2 +- tests/ui/macro_use_imports.fixed | 3 +- tests/ui/macro_use_imports.rs | 3 +- tests/ui/macro_use_imports.stderr | 8 +- tests/ui/macro_use_imports_expect.rs | 3 +- tests/ui/manual_arithmetic_check-2.rs | 1 - tests/ui/manual_arithmetic_check-2.stderr | 36 +- tests/ui/manual_arithmetic_check.fixed | 2 +- tests/ui/manual_arithmetic_check.rs | 2 +- tests/ui/manual_assert.edition2018.fixed | 3 +- tests/ui/manual_assert.edition2018.stderr | 22 +- tests/ui/manual_assert.edition2021.fixed | 3 +- tests/ui/manual_assert.edition2021.stderr | 22 +- tests/ui/manual_assert.rs | 3 +- tests/ui/manual_assert_eq.fixed | 3 +- tests/ui/manual_assert_eq.rs | 3 +- tests/ui/manual_assert_eq.stderr | 14 +- tests/ui/manual_async_fn.fixed | 2 +- tests/ui/manual_async_fn.rs | 2 +- tests/ui/manual_bits.fixed | 10 +- tests/ui/manual_bits.rs | 10 +- tests/ui/manual_bits.stderr | 58 +- tests/ui/manual_clamp.fixed | 9 +- tests/ui/manual_clamp.rs | 9 +- tests/ui/manual_clamp.stderr | 70 +- tests/ui/manual_contains.fixed | 2 +- tests/ui/manual_contains.rs | 2 +- tests/ui/manual_filter.fixed | 8 +- tests/ui/manual_filter.rs | 8 +- tests/ui/manual_filter.stderr | 40 +- tests/ui/manual_filter_map.fixed | 6 +- tests/ui/manual_filter_map.rs | 6 +- tests/ui/manual_filter_map.stderr | 76 +- tests/ui/manual_find.rs | 1 - tests/ui/manual_find.stderr | 4 +- tests/ui/manual_find_fixable.fixed | 3 +- tests/ui/manual_find_fixable.rs | 3 +- tests/ui/manual_find_fixable.stderr | 28 +- tests/ui/manual_find_map.fixed | 6 +- tests/ui/manual_find_map.rs | 6 +- tests/ui/manual_find_map.stderr | 78 +- tests/ui/manual_flatten.fixed | 2 +- tests/ui/manual_flatten.rs | 2 +- tests/ui/manual_float_methods.1.fixed | 67 ++ tests/ui/manual_float_methods.2.fixed | 67 ++ tests/ui/manual_float_methods.3.fixed | 67 ++ tests/ui/manual_float_methods.rs | 5 +- tests/ui/manual_float_methods.stderr | 10 +- tests/ui/manual_ignore_case_cmp.fixed | 2 +- tests/ui/manual_ignore_case_cmp.rs | 2 +- tests/ui/manual_ilog2.fixed | 2 +- tests/ui/manual_ilog2.rs | 2 +- tests/ui/manual_inspect.fixed | 2 +- tests/ui/manual_inspect.rs | 2 +- tests/ui/manual_instant_elapsed.fixed | 4 - tests/ui/manual_instant_elapsed.rs | 4 - tests/ui/manual_instant_elapsed.stderr | 8 +- tests/ui/manual_is_ascii_check.fixed | 1 - tests/ui/manual_is_ascii_check.rs | 1 - tests/ui/manual_is_ascii_check.stderr | 62 +- tests/ui/manual_is_power_of_two.fixed | 2 +- tests/ui/manual_is_power_of_two.rs | 2 +- tests/ui/manual_is_variant_and.fixed | 42 + tests/ui/manual_is_variant_and.rs | 42 + tests/ui/manual_is_variant_and.stderr | 14 +- tests/ui/manual_isolate_lowest_one.fixed | 92 ++ tests/ui/manual_isolate_lowest_one.rs | 92 ++ tests/ui/manual_isolate_lowest_one.stderr | 59 ++ tests/ui/manual_let_else.fixed | 433 +++++++++ tests/ui/manual_let_else.rs | 69 +- tests/ui/manual_let_else.stderr | 165 +--- tests/ui/manual_let_else_match.fixed | 10 +- tests/ui/manual_let_else_match.rs | 10 +- tests/ui/manual_let_else_match.stderr | 36 +- tests/ui/manual_let_else_question_mark.fixed | 8 - tests/ui/manual_let_else_question_mark.rs | 8 - tests/ui/manual_let_else_question_mark.stderr | 14 +- tests/ui/manual_let_else_unfixable.rs | 75 ++ tests/ui/manual_let_else_unfixable.stderr | 97 ++ tests/ui/manual_main_separator_str.fixed | 1 - tests/ui/manual_main_separator_str.rs | 1 - tests/ui/manual_main_separator_str.stderr | 8 +- tests/ui/manual_map_option.fixed | 12 +- tests/ui/manual_map_option.rs | 12 +- tests/ui/manual_map_option.stderr | 42 +- tests/ui/manual_map_option_2.fixed | 2 +- tests/ui/manual_map_option_2.rs | 2 +- .../ui/manual_memcpy/with_loop_counters.fixed | 7 +- tests/ui/manual_memcpy/with_loop_counters.rs | 7 +- .../manual_memcpy/with_loop_counters.stderr | 22 +- .../manual_memcpy/without_loop_counters.fixed | 10 +- .../ui/manual_memcpy/without_loop_counters.rs | 10 +- .../without_loop_counters.stderr | 36 +- tests/ui/manual_next_back.fixed | 1 - tests/ui/manual_next_back.rs | 1 - tests/ui/manual_next_back.stderr | 4 +- tests/ui/manual_non_exhaustive_enum.rs | 3 +- tests/ui/manual_non_exhaustive_enum.stderr | 8 +- tests/ui/manual_non_exhaustive_struct.rs | 3 +- tests/ui/manual_non_exhaustive_struct.stderr | 24 +- tests/ui/manual_ok_or.fixed | 6 +- tests/ui/manual_ok_or.rs | 6 +- tests/ui/manual_ok_or.stderr | 8 +- tests/ui/manual_option_as_slice.fixed | 2 +- tests/ui/manual_option_as_slice.rs | 2 +- tests/ui/manual_option_zip.fixed | 2 +- tests/ui/manual_option_zip.rs | 2 +- tests/ui/manual_pop_if.fixed | 2 +- tests/ui/manual_pop_if.rs | 2 +- tests/ui/manual_pop_if_unfixable.rs | 2 +- tests/ui/manual_range_patterns.fixed | 2 - tests/ui/manual_range_patterns.rs | 2 - tests/ui/manual_range_patterns.stderr | 38 +- tests/ui/manual_rem_euclid.fixed | 2 +- tests/ui/manual_rem_euclid.rs | 2 +- tests/ui/manual_retain.fixed | 2 +- tests/ui/manual_retain.rs | 2 +- tests/ui/manual_rotate.fixed | 1 - tests/ui/manual_rotate.rs | 1 - tests/ui/manual_rotate.stderr | 34 +- tests/ui/manual_saturating_arithmetic.fixed | 3 +- tests/ui/manual_saturating_arithmetic.rs | 3 +- tests/ui/manual_saturating_arithmetic.stderr | 50 +- tests/ui/manual_slice_fill.fixed | 2 +- tests/ui/manual_slice_fill.rs | 2 +- tests/ui/manual_slice_size_calculation.fixed | 1 - tests/ui/manual_slice_size_calculation.rs | 1 - tests/ui/manual_slice_size_calculation.stderr | 22 +- tests/ui/manual_split_once.fixed | 3 +- tests/ui/manual_split_once.rs | 3 +- tests/ui/manual_split_once.stderr | 38 +- tests/ui/manual_str_repeat.fixed | 2 +- tests/ui/manual_str_repeat.rs | 2 +- tests/ui/manual_string_new.fixed | 2 +- tests/ui/manual_string_new.rs | 2 +- tests/ui/manual_strip_fixable.fixed | 1 - tests/ui/manual_strip_fixable.rs | 1 - tests/ui/manual_strip_fixable.stderr | 8 +- tests/ui/manual_take_nocore.rs | 2 +- tests/ui/manual_try_fold.rs | 3 +- tests/ui/manual_unwrap_or.fixed | 9 +- tests/ui/manual_unwrap_or.rs | 9 +- tests/ui/manual_unwrap_or.stderr | 40 +- tests/ui/manual_while_let_some.fixed | 1 - tests/ui/manual_while_let_some.rs | 1 - tests/ui/manual_while_let_some.stderr | 14 +- tests/ui/many_single_char_names.rs | 2 +- tests/ui/map_clone.fixed | 10 +- tests/ui/map_clone.rs | 10 +- tests/ui/map_clone.stderr | 30 +- tests/ui/map_err.rs | 2 +- tests/ui/map_flatten.fixed | 2 +- tests/ui/map_flatten.rs | 2 +- tests/ui/map_flatten_fixable.fixed | 9 +- tests/ui/map_flatten_fixable.rs | 9 +- tests/ui/map_flatten_fixable.stderr | 18 +- tests/ui/map_identity.fixed | 3 +- tests/ui/map_identity.rs | 3 +- tests/ui/map_identity.stderr | 18 +- tests/ui/map_unit_fn.rs | 1 - tests/ui/map_unwrap_or.rs | 2 +- tests/ui/map_unwrap_or_fixable.fixed | 5 - tests/ui/map_unwrap_or_fixable.rs | 5 - tests/ui/map_unwrap_or_fixable.stderr | 22 +- ...map_with_unused_argument_over_ranges.fixed | 7 +- .../map_with_unused_argument_over_ranges.rs | 7 +- ...ap_with_unused_argument_over_ranges.stderr | 40 +- tests/ui/match_as_ref.fixed | 1 - tests/ui/match_as_ref.rs | 1 - tests/ui/match_as_ref.stderr | 14 +- tests/ui/match_bool.fixed | 4 +- tests/ui/match_bool.rs | 4 +- tests/ui/match_bool.stderr | 7 +- tests/ui/match_like_matches_macro.fixed | 9 +- tests/ui/match_like_matches_macro.rs | 9 +- tests/ui/match_like_matches_macro.stderr | 59 +- tests/ui/match_overlapping_arm.rs | 4 +- tests/ui/match_ref_pats.fixed | 9 +- tests/ui/match_ref_pats.rs | 9 +- tests/ui/match_ref_pats.stderr | 29 +- tests/ui/match_result_ok.fixed | 8 +- tests/ui/match_result_ok.rs | 8 +- tests/ui/match_result_ok.stderr | 6 +- tests/ui/match_same_arms2.fixed | 9 +- tests/ui/match_same_arms2.rs | 9 +- tests/ui/match_same_arms2.stderr | 32 +- tests/ui/match_single_binding.fixed | 9 +- tests/ui/match_single_binding.rs | 9 +- tests/ui/match_single_binding.stderr | 92 +- tests/ui/match_single_binding2.fixed | 2 - tests/ui/match_single_binding2.rs | 2 - tests/ui/match_single_binding2.stderr | 8 +- tests/ui/match_str_case_mismatch.fixed | 1 - tests/ui/match_str_case_mismatch.rs | 1 - tests/ui/match_str_case_mismatch.stderr | 14 +- tests/ui/match_wild_err_arm.rs | 2 +- .../match_wildcard_for_single_variants.fixed | 1 - .../ui/match_wildcard_for_single_variants.rs | 1 - .../match_wildcard_for_single_variants.stderr | 20 +- tests/ui/mem_replace.stderr | 191 ---- tests/ui/mem_replace_macro.rs | 13 - tests/ui/mem_replace_macro.stderr | 12 - tests/ui/mem_replace_no_std.fixed | 84 -- tests/ui/mem_replace_no_std.rs | 84 -- tests/ui/mem_replace_no_std.stderr | 50 -- tests/ui/mem_replace_option_with_none.fixed | 42 + tests/ui/mem_replace_option_with_none.rs | 42 + tests/ui/mem_replace_option_with_none.stderr | 35 + .../mem_replace_option_with_none_no_std.fixed | 13 + .../ui/mem_replace_option_with_none_no_std.rs | 13 + ...mem_replace_option_with_none_no_std.stderr | 17 + tests/ui/mem_replace_option_with_some.fixed | 25 + tests/ui/mem_replace_option_with_some.rs | 25 + tests/ui/mem_replace_option_with_some.stderr | 23 + .../mem_replace_option_with_some_no_std.fixed | 26 + .../ui/mem_replace_option_with_some_no_std.rs | 26 + ...mem_replace_option_with_some_no_std.stderr | 23 + ...e.fixed => mem_replace_with_default.fixed} | 78 +- ...replace.rs => mem_replace_with_default.rs} | 78 +- tests/ui/mem_replace_with_default.stderr | 145 +++ .../ui/mem_replace_with_default_no_std.fixed | 25 + tests/ui/mem_replace_with_default_no_std.rs | 25 + .../ui/mem_replace_with_default_no_std.stderr | 25 + tests/ui/mem_replace_with_uninit.fixed | 50 ++ ...l_uninit.rs => mem_replace_with_uninit.rs} | 21 +- ....stderr => mem_replace_with_uninit.stderr} | 22 +- tests/ui/mem_replace_with_uninit_unfixable.rs | 22 + .../mem_replace_with_uninit_unfixable.stderr | 12 + .../missing_const_for_fn/could_be_const.fixed | 6 + .../ui/missing_const_for_fn/could_be_const.rs | 6 + .../could_be_const.stderr | 17 +- ...needless_borrow_false_positive_16200.fixed | 38 + .../needless_borrow_false_positive_16200.rs | 38 + ...eedless_borrow_false_positive_16200.stderr | 29 + tests/ui/never_loop.rs | 103 +++ tests/ui/never_loop.stderr | 139 ++- tests/ui/range_plus_minus_one.fixed | 4 +- tests/ui/range_plus_minus_one.rs | 2 - tests/ui/range_plus_minus_one.stderr | 28 +- tests/ui/redundant_closure_call_early.rs | 58 +- tests/ui/redundant_closure_call_early.stderr | 4 +- tests/ui/redundant_closure_call_fixable.fixed | 40 +- tests/ui/redundant_closure_call_fixable.rs | 40 +- .../ui/redundant_closure_call_fixable.stderr | 64 +- tests/ui/redundant_closure_call_late.rs | 6 +- tests/ui/redundant_closure_call_late.stderr | 6 +- ...dundant_pattern_matching_drop_order.stderr | 208 ++++- ...undant_pattern_matching_if_let_true.stderr | 55 +- .../redundant_pattern_matching_ipaddr.stderr | 223 ++++- .../redundant_pattern_matching_option.stderr | 385 ++++++-- .../ui/redundant_pattern_matching_poll.stderr | 219 ++++- .../redundant_pattern_matching_result.stderr | 343 ++++++-- tests/ui/renamed_builtin_attr.fixed | 2 - tests/ui/renamed_builtin_attr.rs | 2 - tests/ui/renamed_builtin_attr.stderr | 2 +- tests/ui/result_large_err.rs | 14 + tests/ui/result_large_err.stderr | 18 +- tests/ui/result_unit_error.rs | 12 + tests/ui/result_unit_error.stderr | 10 +- tests/ui/single_range_in_vec_init.1.fixed | 7 + tests/ui/single_range_in_vec_init.2.fixed | 7 + tests/ui/single_range_in_vec_init.rs | 7 + tests/ui/single_range_in_vec_init.stderr | 26 +- .../ui/single_range_in_vec_init_unfixable.rs | 23 + .../single_range_in_vec_init_unfixable.stderr | 50 +- tests/ui/ty_fn_sig.rs | 2 +- tests/ui/unknown_attribute.rs | 2 - tests/ui/unknown_attribute.stderr | 2 +- tests/ui/unused_async_trait_impl.fixed | 33 + tests/ui/unused_async_trait_impl.rs | 33 + tests/ui/unused_async_trait_impl.stderr | 24 +- tests/ui/with_capacity_zero.fixed | 50 ++ tests/ui/with_capacity_zero.rs | 50 ++ tests/ui/with_capacity_zero.stderr | 112 +++ triagebot.toml | 1 + 1044 files changed, 11957 insertions(+), 7828 deletions(-) create mode 100644 clippy_lints/src/combined_early_pass.rs create mode 100644 clippy_lints/src/combined_late_pass.rs create mode 100644 clippy_lints/src/loops/for_unbounded_range.rs delete mode 100644 clippy_lints/src/methods/from_iter_instead_of_collect.rs create mode 100644 clippy_lints/src/operators/manual_isolate_lowest_one.rs create mode 100644 clippy_lints/src/with_capacity_zero.rs create mode 100644 clippy_utils/src/disallowed_profiles.rs create mode 100644 tests/ui-toml/disallowed_profiles_methods/clippy.toml create mode 100644 tests/ui-toml/disallowed_profiles_methods/main.rs create mode 100644 tests/ui-toml/disallowed_profiles_methods/main.stderr create mode 100644 tests/ui-toml/disallowed_profiles_types/clippy.toml create mode 100644 tests/ui-toml/disallowed_profiles_types/main.rs create mode 100644 tests/ui-toml/disallowed_profiles_types/main.stderr delete mode 100644 tests/ui/borrow_box.stderr rename tests/ui/{borrow_box.fixed => borrowed_box.fixed} (87%) rename tests/ui/{borrow_box.rs => borrowed_box.rs} (87%) create mode 100644 tests/ui/borrowed_box.stderr create mode 100644 tests/ui/double_must_use.fixed create mode 100644 tests/ui/double_must_use_unfixable.rs create mode 100644 tests/ui/double_must_use_unfixable.stderr delete mode 100644 tests/ui/find_map.rs create mode 100644 tests/ui/for_unbounded_range.fixed create mode 100644 tests/ui/for_unbounded_range.rs create mode 100644 tests/ui/for_unbounded_range.stderr rename tests/ui/{format_args_unfixable.rs => format_in_format_args_unfixable.rs} (96%) rename tests/ui/{format_args_unfixable.stderr => format_in_format_args_unfixable.stderr} (87%) delete mode 100644 tests/ui/from_iter_instead_of_collect.fixed delete mode 100644 tests/ui/from_iter_instead_of_collect.rs delete mode 100644 tests/ui/from_iter_instead_of_collect.stderr create mode 100644 tests/ui/manual_float_methods.1.fixed create mode 100644 tests/ui/manual_float_methods.2.fixed create mode 100644 tests/ui/manual_float_methods.3.fixed create mode 100644 tests/ui/manual_isolate_lowest_one.fixed create mode 100644 tests/ui/manual_isolate_lowest_one.rs create mode 100644 tests/ui/manual_isolate_lowest_one.stderr create mode 100644 tests/ui/manual_let_else.fixed create mode 100644 tests/ui/manual_let_else_unfixable.rs create mode 100644 tests/ui/manual_let_else_unfixable.stderr delete mode 100644 tests/ui/mem_replace.stderr delete mode 100644 tests/ui/mem_replace_macro.rs delete mode 100644 tests/ui/mem_replace_macro.stderr delete mode 100644 tests/ui/mem_replace_no_std.fixed delete mode 100644 tests/ui/mem_replace_no_std.rs delete mode 100644 tests/ui/mem_replace_no_std.stderr create mode 100644 tests/ui/mem_replace_option_with_none.fixed create mode 100644 tests/ui/mem_replace_option_with_none.rs create mode 100644 tests/ui/mem_replace_option_with_none.stderr create mode 100644 tests/ui/mem_replace_option_with_none_no_std.fixed create mode 100644 tests/ui/mem_replace_option_with_none_no_std.rs create mode 100644 tests/ui/mem_replace_option_with_none_no_std.stderr create mode 100644 tests/ui/mem_replace_option_with_some.fixed create mode 100644 tests/ui/mem_replace_option_with_some.rs create mode 100644 tests/ui/mem_replace_option_with_some.stderr create mode 100644 tests/ui/mem_replace_option_with_some_no_std.fixed create mode 100644 tests/ui/mem_replace_option_with_some_no_std.rs create mode 100644 tests/ui/mem_replace_option_with_some_no_std.stderr rename tests/ui/{mem_replace.fixed => mem_replace_with_default.fixed} (65%) rename tests/ui/{mem_replace.rs => mem_replace_with_default.rs} (66%) create mode 100644 tests/ui/mem_replace_with_default.stderr create mode 100644 tests/ui/mem_replace_with_default_no_std.fixed create mode 100644 tests/ui/mem_replace_with_default_no_std.rs create mode 100644 tests/ui/mem_replace_with_default_no_std.stderr create mode 100644 tests/ui/mem_replace_with_uninit.fixed rename tests/ui/{repl_uninit.rs => mem_replace_with_uninit.rs} (78%) rename tests/ui/{repl_uninit.stderr => mem_replace_with_uninit.stderr} (55%) create mode 100644 tests/ui/mem_replace_with_uninit_unfixable.rs create mode 100644 tests/ui/mem_replace_with_uninit_unfixable.stderr create mode 100644 tests/ui/needless_borrow_false_positive_16200.fixed create mode 100644 tests/ui/needless_borrow_false_positive_16200.rs create mode 100644 tests/ui/needless_borrow_false_positive_16200.stderr create mode 100644 tests/ui/with_capacity_zero.fixed create mode 100644 tests/ui/with_capacity_zero.rs create mode 100644 tests/ui/with_capacity_zero.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 682e0c7af6d0c..337c900aaf0c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,101 @@ document. ## Unreleased / Beta / In Rust Nightly -[df995e...master](https://github.com/rust-lang/rust-clippy/compare/df995e...master) +[88f787...master](https://github.com/rust-lang/rust-clippy/compare/88f787...master) + +## Rust 1.96 + +Current stable, released 2026-05-28 + +[View all 48 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2026-02-24T12%3A30%3A17Z..2026-04-03T17%3A32%3A48Z+base%3Amaster) + +### New Lints + +* Added [`manual_noop_waker`] to `complexity` + [#16687](https://github.com/rust-lang/rust-clippy/pull/16687) +* Added [`manual_option_zip`] to `complexity` + [#16600](https://github.com/rust-lang/rust-clippy/pull/16600) +* Added [`manual_pop_if`] to `complexity` + [#16582](https://github.com/rust-lang/rust-clippy/pull/16582) + +### Enhancements + +* [`explicit_counter_loop`] suggest `(init..).take(n)` when loop variable is unused and range is + `0..n` + [#16658](https://github.com/rust-lang/rust-clippy/pull/16658) +* [`iter_kv_map`] handle identity map for `map` and `flat_map` + [#16743](https://github.com/rust-lang/rust-clippy/pull/16743) +* [`manual_noop_waker`] add an MSRV check + [#16850](https://github.com/rust-lang/rust-clippy/pull/16850) +* [`manual_pop_if`] in case the popped value is used, just emit the lint with no suggestion + [#16683](https://github.com/rust-lang/rust-clippy/pull/16683) +* [`manual_pop_if`] also cover `.pop().unwrap_unchecked()` + [#16683](https://github.com/rust-lang/rust-clippy/pull/16683) +* [`manual_pop_if`] detect manual implementations of `BinaryHeap::pop_if()` + [#16734](https://github.com/rust-lang/rust-clippy/pull/16734) +* [`unnecessary_option_map_or_else`] function definitions are no longer traversed when checking if + an expression is the identity + [#15889](https://github.com/rust-lang/rust-clippy/pull/15889) +* [`unnecessary_result_map_or_else`] function definitions are no longer traversed when checking if + an expression is the identity + [#15889](https://github.com/rust-lang/rust-clippy/pull/15889) +* [`question_mark`] fix suggestion-caused error caused by semicolon inference relying only on + parent-node shape + [#16656](https://github.com/rust-lang/rust-clippy/pull/16656) +* Format-related lints now handle `core::panic!` + [#16597](https://github.com/rust-lang/rust-clippy/pull/16597) +* [`explicit_counter_loop`] fix FN when the initializer is not integral + [#16647](https://github.com/rust-lang/rust-clippy/pull/16647) +* [`suboptimal_flops`] fix FN on add and sub assign + [#16625](https://github.com/rust-lang/rust-clippy/pull/16625) +* [`infinite_loop`] fix wrong suggestion to add `-> !` when the loop is inside a conditional branch + [#16619](https://github.com/rust-lang/rust-clippy/pull/16619) +* [`unnecessary_cast`] preserve parentheses in presence of cascaded casts + [#16483](https://github.com/rust-lang/rust-clippy/pull/16483) +* [`cmp_owned`] fix wrong suggestions on `PathBuf` + [#16628](https://github.com/rust-lang/rust-clippy/pull/16628) +* [`redundant_closure`] fix wrong suggestions when local is dereferenced to callable + [#16648](https://github.com/rust-lang/rust-clippy/pull/16648) + +### False Positive Fixes + +* [`collapsible_if`] fix FP when the inner if contains cfg + [#16757](https://github.com/rust-lang/rust-clippy/pull/16757) +* [`collapsible_match`] fix FP when the pat binding is moved or mutated + [#16708](https://github.com/rust-lang/rust-clippy/pull/16708) +* [`collapsible_match`] fix a case where a suggested transformation changes runtime behavior + [#16878](https://github.com/rust-lang/rust-clippy/pull/16878) +* [`match_same_arms`] fix FP with associated consts + [#16701](https://github.com/rust-lang/rust-clippy/pull/16701) +* [`semicolon_inside_block`] fix FP in `try` blocks where moving `;` changes the block's return + type and causes type errors + [#16697](https://github.com/rust-lang/rust-clippy/pull/16697) +* [`unnecessary_safety_comment`] fix FP on code blocks inside inner docs + [#16559](https://github.com/rust-lang/rust-clippy/pull/16559) +* [`doc_paragraphs_missing_punctuation`] no longer lints punctuated paragraphs with a trailing + emoji + [#16514](https://github.com/rust-lang/rust-clippy/pull/16514) + +### ICE Fixes + +* [`match_same_arms`] fix ICE in `match_same_arms` + [#16685](https://github.com/rust-lang/rust-clippy/pull/16685) +* [`nonminimal_bool`] fix ICE in `swap_binop()` by using the proper `TypeckResults` + [#16659](https://github.com/rust-lang/rust-clippy/pull/16659) +* Fix ICE when using the `min_generic_const_args` incomplete feature + [#16692](https://github.com/rust-lang/rust-clippy/pull/16692) + +### Documentation Improvements + +* [`similar_names`] changed the lint docs to reflect its actual behavior + [#16300](https://github.com/rust-lang/rust-clippy/pull/16300) + +### Performance Improvements + +* [`repeat_vec_with_capacity`] optimized by 96.876% (784M -> 24M instructions) + [#16756](https://github.com/rust-lang/rust-clippy/pull/16756) +* [`manual_is_ascii_check`] optimized by 97.125% (822M -> 23M instructions) + [#16755](https://github.com/rust-lang/rust-clippy/pull/16755) ## Rust 1.95 @@ -6746,6 +6840,7 @@ Released 2018-09-13 [`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option [`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result [`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles +[`for_unbounded_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_unbounded_range [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref @@ -6906,6 +7001,7 @@ Released 2018-09-13 [`manual_is_multiple_of`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_multiple_of [`manual_is_power_of_two`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two [`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and +[`manual_isolate_lowest_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_isolate_lowest_one [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map @@ -7424,6 +7520,7 @@ Released 2018-09-13 [`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm [`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports [`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns +[`with_capacity_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#with_capacity_zero [`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal [`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline [`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string @@ -7511,6 +7608,7 @@ Released 2018-09-13 [`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit +[`profiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#profiles [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior [`recursive-self-in-type-definitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#recursive-self-in-type-definitions [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8aeba28494cb3..ccfbb0b88387e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -124,8 +124,11 @@ To have `rust-analyzer` also work in the `clippy_dev` and `lintcheck` crates, ad ## How Clippy works -[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers in the [`LintStore`]. -For example, the [`else_if_without_else`][else_if_without_else] lint is registered like this: +[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers them in the +[`LintStore`]. All early passes are folded into a single `CombinedEarlyLintPass`, and all late passes into a single +`CombinedLateLintPass`, each registered once with the store. A pass is added to one of these by listing it in the +`early_lint_methods!` or `late_lint_methods!` macro invocation. For example, the +[`else_if_without_else`][else_if_without_else] lint is added like this: ```rust // ./clippy_lints/src/lib.rs @@ -134,18 +137,21 @@ For example, the [`else_if_without_else`][else_if_without_else] lint is register pub mod else_if_without_else; // ... -pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { - // ... - store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse)); - // ... -} +rustc_lint::early_lint_methods!( + crate::combined_early_lint_pass, + [CombinedEarlyLintPass, (/* ... */), [ + // ... + ElseIfWithoutElse: else_if_without_else::ElseIfWithoutElse = else_if_without_else::ElseIfWithoutElse, + // ... + ]] +); ``` -The [`rustc_lint::LintStore`][`LintStore`] provides two methods to register lints: -[register_early_pass][reg_early_pass] and [register_late_pass][reg_late_pass]. Both take an object -that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in -every single lint. It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `cargo dev -update_lints`. When you are writing your own lint, you can use that script to save you some time. +Each entry has the form `Field: Type = constructor`, where the constructor builds the pass (passing `conf` when the pass +needs the user configuration). The combined passes implement [`EarlyLintPass`][early_lint_pass] and +[`LateLintPass`][late_lint_pass] respectively, so each listed pass must also implement the matching trait. It's worth +noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `cargo dev update_lints`. When you are +writing your own lint, you can use that script to save you some time. ```rust // ./clippy_lints/src/else_if_without_else.rs @@ -167,14 +173,12 @@ The difference between `EarlyLintPass` and `LateLintPass` is that the methods of AST information. The methods of the `LateLintPass` trait are executed after type checking and contain type information via the `LateContext` parameter. -That's why the `else_if_without_else` example uses the `register_early_pass` function. Because the +That's why the `else_if_without_else` example is listed in `early_lint_methods!`. Because the [actual lint logic][else_if_without_else] does not depend on any type information. [lint_crate_entry]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs [else_if_without_else]: https://github.com/rust-lang/rust-clippy/blob/4253aa7137cb7378acc96133c787e49a345c2b3c/clippy_lints/src/else_if_without_else.rs [`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html -[reg_early_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_early_pass -[reg_late_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_late_pass [early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html [late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html diff --git a/Cargo.toml b/Cargo.toml index b93a3f1cbe9c7..06c1840a3da26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ anstream = "0.6.18" [dev-dependencies] cargo_metadata = "0.23" -ui_test = "0.30.5" +ui_test = "0.30.7" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" diff --git a/book/src/configuration.md b/book/src/configuration.md index aff9222ea6066..cb2ac67361bef 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -89,7 +89,7 @@ cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... #### Lints Section in `Cargo.toml` Finally, lints can be allowed/denied using [the lints -section](https://doc.rust-lang.org/nightly/cargo/reference/manifest.html#the-lints-section)) in the `Cargo.toml` file: +section](https://doc.rust-lang.org/nightly/cargo/reference/manifest.html#the-lints-section) in the `Cargo.toml` file: To deny `clippy::enum_glob_use`, put the following in the `Cargo.toml`: diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index d9a5f04c3e1c0..474b8c6c474ef 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -282,21 +282,22 @@ When using `cargo dev new_lint`, the lint is automatically registered and nothing more has to be done. When declaring a new lint by hand and `cargo dev update_lints` is used, the lint -pass may have to be registered manually in the `register_lints` function in -`clippy_lints/src/lib.rs`: +pass may have to be registered manually by adding an entry to the +`early_lint_methods!` macro invocation in `clippy_lints/src/lib.rs`, at the +`// add early passes here` marker: ```rust,ignore -store.register_early_pass(|| Box::new(foo_functions::FooFunctions)); +FooFunctions: foo_functions::FooFunctions = foo_functions::FooFunctions, ``` -As one may expect, there is a corresponding `register_late_pass` method -available as well. Without a call to one of `register_early_pass` or -`register_late_pass`, the lint pass in question will not be run. +As one may expect, there is a corresponding `late_lint_methods!` macro available +as well. Without an entry in one of `early_lint_methods!` or `late_lint_methods!`, +the lint pass in question will not be run. One reason that `cargo dev update_lints` does not automate this step is that multiple lints can use the same lint pass, so registering the lint pass may already be done when adding a new lint. Another reason that this step is not -automated is that the order that the passes are registered determines the order +automated is that the order that the passes are listed determines the order the passes actually run, which in turn affects the order that any emitted lints are output in. diff --git a/book/src/development/defining_lints.md b/book/src/development/defining_lints.md index cb6d7b740dbd1..cc108ce70d3d7 100644 --- a/book/src/development/defining_lints.md +++ b/book/src/development/defining_lints.md @@ -184,18 +184,19 @@ However, sometimes we might want to declare a new lint by hand. In this case, we'd use `cargo dev update_lints` command afterwards. When a lint is manually declared, we might need to register the lint pass -manually in the `register_lints` function in `clippy_lints/src/lib.rs`: +manually by adding an entry to the `late_lint_methods!` macro invocation in +`clippy_lints/src/lib.rs`, at the `// add late passes here` marker: ```rust -store.register_late_pass(|_| Box::new(foo_functions::FooFunctions)); +FooFunctions: foo_functions::FooFunctions = foo_functions::FooFunctions, ``` As you might have guessed, where there's something late, there is something -early: in Clippy there is a `register_early_pass` method as well. More on early +early: in Clippy there is an `early_lint_methods!` macro as well. More on early vs. late passes in the [Lint Passes] chapter. -Without a call to one of `register_early_pass` or `register_late_pass`, the lint -pass in question will not be run. +Without an entry in one of `early_lint_methods!` or `late_lint_methods!`, the +lint pass in question will not be run. [all_lints]: https://rust-lang.github.io/rust-clippy/master/ diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 64d0bf9b62f69..e123d7ba5704d 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -609,7 +609,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", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "PowerShell", "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"]` +**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", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "PowerShell", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "SQLite", "MySQL", "PostgreSQL", "MariaDB", "MongoDB", "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:** @@ -780,7 +780,8 @@ be filtering for common types. ## `max-fn-params-bools` -The maximum number of bool parameters a function can have +The maximum number of bool parameters a function can have. +Use `0` to lint on any function with a bool parameter. **Default Value:** `3` @@ -925,6 +926,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one) * [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) * [`manual_is_power_of_two`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two) +* [`manual_isolate_lowest_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_isolate_lowest_one) * [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) * [`manual_midpoint`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint) * [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) @@ -984,6 +986,28 @@ The minimum size (in bytes) to consider a type for passing by reference instead * [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) +## `profiles` +Named profiles of disallowed items (unrelated to Cargo build profiles). + +#### Example + +```toml +[profiles.persistent] +disallowed-methods = [{ path = "std::env::temp_dir" }] +disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] + +[profiles.single_threaded] +disallowed-methods = [{ path = "std::thread::spawn" }] +``` + +**Default Value:** `{}` + +--- +**Affected lints:** +* [`disallowed_methods`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) +* [`disallowed_types`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) + + ## `pub-underscore-fields-behavior` Lint "public" fields in a struct that are prefixed with an underscore based on their exported visibility, or whether they are marked as "pub". diff --git a/book/src/lints.md b/book/src/lints.md index 55b382c855651..8943274a38e81 100644 --- a/book/src/lints.md +++ b/book/src/lints.md @@ -5,10 +5,11 @@ and idiomatic Rust code. A full list of all lints, that can be filtered by category, lint level or keywords, can be found in the [Clippy lint documentation]. -This chapter will give an overview of the different lint categories, which kind -of lints they offer and recommended actions when you should see a lint out of +This chapter provides details about the different lint categories, which kind +of lints they offer, and recommended actions when you should see a lint out of that category. For examples, see the [Clippy lint documentation] and filter by -category. +category. For an overview of these categories, see the +[introduction](index.md). The different lint groups were defined in the [Clippy 1.0 RFC]. diff --git a/build.rs b/build.rs index b79d09b0dd2d2..4a28465c77151 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,4 @@ fn main() { - // Forward the profile to the main compilation - println!("cargo:rustc-env=PROFILE={}", std::env::var("PROFILE").unwrap()); // Don't rebuild even if nothing changed println!("cargo:rerun-if-changed=build.rs"); rustc_tools_util::setup_version_info!(); diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 465e88a783ed8..47bc7b572c9d0 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -1,12 +1,13 @@ use crate::ClippyConfiguration; use crate::types::{ - DisallowedPath, DisallowedPathWithoutReplacement, InherentImplLintScope, MacroMatcher, MatchLintBehaviour, - PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, + DisallowedPath, DisallowedPathWithoutReplacement, DisallowedProfile, InherentImplLintScope, MacroMatcher, + MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; use itertools::Itertools; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; @@ -38,6 +39,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "PowerPC", "PowerShell", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", + "SQLite", "MySQL", "PostgreSQL", "MariaDB", "MongoDB", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", @@ -221,12 +223,74 @@ macro_rules! deserialize { }}; } +macro_rules! parse_conf_value { + ( + $map:expr, + $ty:ty, + $errors:expr, + $file:expr, + $field_span:expr, + profiles @[$($profiles:expr)?], + disallowed @[$($disallowed:expr)?] + ) => { + parse_conf_value_impl!( + $map, + $ty, + $errors, + $file, + $field_span, + ($($profiles)?), + ($($disallowed)?) + ) + }; +} + +macro_rules! parse_conf_value_impl { + ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ()) => {{ + let _ = &$field_span; + deserialize!($map, $ty, $errors, $file) + }}; + ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, ($profiles:expr), ()) => {{ + let raw_value = $map.next_value::()?; + let value_span = $field_span.clone(); + let toml::Value::Table(table) = raw_value else { + $errors.push(ConfError::spanned( + $file, + "expected table with named profiles", + None, + value_span.clone(), + )); + continue; + }; + + let map = parse_profiles(table, $file, value_span.clone(), &mut $errors); + + (map, value_span) + }}; + ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ($disallowed:expr)) => {{ + let _ = &$field_span; + deserialize!($map, $ty, $errors, $file, $disallowed) + }}; + ( + $map:expr, + $ty:ty, + $errors:expr, + $file:expr, + $field_span:expr, + ($profiles:expr), + ($disallowed:expr) + ) => { + compile_error!("field cannot specify both profiles and disallowed-paths attributes") + }; +} + macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? $(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])? + $(#[profiles = $profiles:expr])? $(#[lints($($for_lints:ident),* $(,)?)])? $name:ident: $ty:ty = $default:expr, )*) => { @@ -281,10 +345,20 @@ macro_rules! define_Conf { match field { $(Field::$name => { + let field_span = name.span(); // Is this a deprecated field, i.e., is `$dep` set? If so, push a warning. $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)? - let (value, value_span) = - deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?); + let (value, value_span) = parse_conf_value!( + map, + $ty, + errors, + self.0, + field_span, + // Disallowed-profile table parsing is special-cased to preserve spans for + // diagnostics in disallowed-path entries. + profiles @[$($profiles)?], + disallowed @[$($replacements_allowed)?] + ); // Was this field set previously? if $name.is_some() { errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); @@ -341,6 +415,121 @@ fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { ) } +fn parse_profiles( + table: toml::value::Table, + file: &SourceFile, + value_span: Range, + errors: &mut Vec, +) -> FxHashMap { + let mut profiles = FxHashMap::default(); + let config_span = span_from_toml_range(file, value_span.clone()); + + for (profile_name, profile_value) in table { + let toml::Value::Table(mut profile_table) = profile_value else { + errors.push(ConfError::spanned( + file, + format!("invalid profile `{profile_name}`: expected table"), + None, + value_span.clone(), + )); + continue; + }; + + let disallowed_methods = match profile_table + .remove("disallowed-methods") + .or_else(|| profile_table.remove("disallowed_methods")) + { + Some(value) => parse_profile_list( + file, + &profile_name, + "disallowed-methods", + value, + value_span.clone(), + config_span, + errors, + ), + None => Vec::new(), + }; + + let disallowed_types = match profile_table + .remove("disallowed-types") + .or_else(|| profile_table.remove("disallowed_types")) + { + Some(value) => parse_profile_list( + file, + &profile_name, + "disallowed-types", + value, + value_span.clone(), + config_span, + errors, + ), + None => Vec::new(), + }; + + if !profile_table.is_empty() { + let keys = profile_table.keys().map(String::as_str).collect::>().join(", "); + errors.push(ConfError::spanned( + file, + format!("profile `{profile_name}` has unknown keys: {keys}"), + None, + value_span.clone(), + )); + } + + profiles.insert( + profile_name, + DisallowedProfile { + disallowed_methods, + disallowed_types, + }, + ); + } + + profiles +} + +fn parse_profile_list( + file: &SourceFile, + profile_name: &str, + key_name: &str, + value: toml::Value, + value_span: Range, + config_span: Span, + errors: &mut Vec, +) -> Vec { + let toml::Value::Array(entries) = value else { + errors.push(ConfError::spanned( + file, + format!("profile `{profile_name}`: `{key_name}` must be an array"), + None, + value_span, + )); + return Vec::new(); + }; + + let mut disallowed = Vec::with_capacity(entries.len()); + for entry in entries { + match DisallowedPath::deserialize(entry.clone()) { + Ok(mut path) => { + path.set_span(config_span); + disallowed.push(path); + }, + Err(err) => errors.push(ConfError::spanned( + file, + format!( + "profile `{profile_name}`: {}", + err.to_string().replace('\n', " ").trim() + ), + None, + value_span.clone(), + )), + } + } + + disallowed +} + define_Conf! { /// Which crates to allow absolute paths from #[lints(absolute_paths)] @@ -713,7 +902,8 @@ define_Conf! { /// be filtering for common types. #[lints(manual_let_else)] matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes, - /// The maximum number of bool parameters a function can have + /// The maximum number of bool parameters a function can have. + /// Use `0` to lint on any function with a bool parameter. #[lints(fn_params_excessive_bools)] max_fn_params_bools: u64 = 3, /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes @@ -786,6 +976,7 @@ define_Conf! { manual_hash_one, manual_is_ascii_check, manual_is_power_of_two, + manual_isolate_lowest_one, manual_let_else, manual_midpoint, manual_non_exhaustive, @@ -838,6 +1029,21 @@ define_Conf! { /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. #[lints(large_types_passed_by_value)] pass_by_value_size_limit: u64 = 256, + /// Named profiles of disallowed items (unrelated to Cargo build profiles). + /// + /// #### Example + /// + /// ```toml + /// [profiles.persistent] + /// disallowed-methods = [{ path = "std::env::temp_dir" }] + /// disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] + /// + /// [profiles.single_threaded] + /// disallowed-methods = [{ path = "std::thread::spawn" }] + /// ``` + #[profiles = true] + #[lints(disallowed_methods, disallowed_types)] + profiles: FxHashMap = FxHashMap::default(), /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". #[lints(pub_underscore_fields)] diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index 8d9326a904b1e..5eaa44ddd51d7 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -57,6 +57,15 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath, + #[serde(default, alias = "disallowed_types")] + pub disallowed_types: Vec, +} + // `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just // above. `DisallowedPathEnum` is not meant to be used outside of this file. #[derive(Debug, Deserialize, Serialize)] diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index a5e2050a3865e..4e44cad472ae0 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -157,20 +157,29 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let path = "clippy_lints/src/lib.rs"; let mut lib_rs = fs::read_to_string(path).context("reading")?; - let (comment, ctor_arg) = if lint.pass == Pass::Late { - ("// add late passes here", "_") - } else { - ("// add early passes here", "") - }; - let comment_start = lib_rs.find(comment).expect("Couldn't find comment"); let module_name = lint.name; let camel_name = to_camel_case(lint.name); - let new_lint = if enable_msrv { - format!("Box::new(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf))),\n ") + let (comment, new_lint) = if lint.pass == Pass::Late { + // Late passes are folded into the statically-combined struct, so a new + // entry is just `Field: Type = constructor` (see `combined_late_pass`). + let new_lint = if enable_msrv { + format!("{camel_name}: {module_name}::{camel_name} = {module_name}::{camel_name}::new(conf),\n ") + } else { + format!("{camel_name}: {module_name}::{camel_name} = {module_name}::{camel_name},\n ") + }; + ("// add late passes here", new_lint) } else { - format!("Box::new(|{ctor_arg}| Box::new({module_name}::{camel_name})),\n ") + // Early passes are folded into the statically-combined struct, so a new + // entry is just `Field: Type = constructor` (see `combined_early_pass`). + let new_lint = if enable_msrv { + format!("{camel_name}: {module_name}::{camel_name} = {module_name}::{camel_name}::new(conf),\n ") + } else { + format!("{camel_name}: {module_name}::{camel_name} = {module_name}::{camel_name},\n ") + }; + ("// add early passes here", new_lint) }; + let comment_start = lib_rs.find(comment).expect("Couldn't find comment"); lib_rs.insert_str(comment_start, &new_lint); diff --git a/clippy_dummy/Cargo.toml b/clippy_dummy/Cargo.toml index 61bdd421c764e..5c7ed584309aa 100644 --- a/clippy_dummy/Cargo.toml +++ b/clippy_dummy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_dummy" # rename to clippy before publishing -version = "0.0.303" +version = "0.0.304" edition = "2024" readme = "crates-readme.md" description = "A bunch of helpful lints to avoid common pitfalls in Rust." @@ -13,4 +13,4 @@ keywords = ["clippy", "lint", "plugin"] categories = ["development-tools", "development-tools::cargo-plugins"] [build-dependencies] -term = "0.7" +term = "1" diff --git a/clippy_lints/src/attrs/inline_always.rs b/clippy_lints/src/attrs/inline_always.rs index c0b14c2a4b66e..7ba7f803d2229 100644 --- a/clippy_lints/src/attrs/inline_always.rs +++ b/clippy_lints/src/attrs/inline_always.rs @@ -1,17 +1,20 @@ use super::INLINE_ALWAYS; +use super::utils::is_relevant_expr; use clippy_utils::diagnostics::span_lint; use rustc_hir::attrs::InlineAttr; -use rustc_hir::{Attribute, find_attr}; +use rustc_hir::{Attribute, BodyId, find_attr}; use rustc_lint::LateContext; use rustc_span::Span; use rustc_span::symbol::Symbol; -pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) { +pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute], body: Option) { if span.from_expansion() { return; } - if let Some(span) = find_attr!(attrs, Inline(InlineAttr::Always, span) => *span) { + if let Some(span) = find_attr!(attrs, Inline(InlineAttr::Always, span) => *span) + && body.is_none_or(|body| is_relevant_expr(cx, cx.tcx.hir_body(body).value)) + { span_lint( cx, INLINE_ALWAYS, diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 372defbb4d7e2..78701b3368226 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -14,14 +14,15 @@ mod useless_attribute; mod utils; use clippy_config::Conf; +use clippy_utils::check_clippy_attr; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv, MsrvStack}; use rustc_ast::{self as ast, AttrArgs, AttrItemKind, AttrKind, Attribute, MetaItemInner, MetaItemKind}; -use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_hir::{ImplItem, ImplItemKind, Item, ItemKind, TraitFn, TraitItem, TraitItemKind}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::sym; -use utils::{is_lint_level, is_relevant_impl, is_relevant_item, is_relevant_trait}; +use utils::is_lint_level; declare_clippy_lint! { /// ### What it does @@ -512,23 +513,31 @@ impl Attributes { impl<'tcx> LateLintPass<'tcx> for Attributes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let attrs = cx.tcx.hir_attrs(item.hir_id()); - if let ItemKind::Fn { ident, .. } = item.kind - && is_relevant_item(cx, item) - { - inline_always::check(cx, item.span, ident.name, attrs); + if let ItemKind::Fn { ident, body, .. } = item.kind { + inline_always::check(cx, item.span, ident.name, attrs, Some(body)); } repr_attributes::check(cx, item.span, attrs, self.msrv); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { - if is_relevant_impl(cx, item) { - inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir_attrs(item.hir_id())); + if let ImplItemKind::Fn(_, body) = item.kind { + inline_always::check( + cx, + item.span, + item.ident.name, + cx.tcx.hir_attrs(item.hir_id()), + Some(body), + ); } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { - if is_relevant_trait(cx, item) { - inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir_attrs(item.hir_id())); + if let TraitItemKind::Fn(_, kind) = item.kind { + let body = match kind { + TraitFn::Required(_) => None, + TraitFn::Provided(body) => Some(body), + }; + inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir_attrs(item.hir_id()), body); } } } @@ -574,6 +583,7 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { + check_clippy_attr(cx.sess(), attr); if let Some(items) = &attr.meta_item_list() && let Some(name) = attr.name() { diff --git a/clippy_lints/src/attrs/utils.rs b/clippy_lints/src/attrs/utils.rs index 4822bdcb9bde2..9e56e019229ec 100644 --- a/clippy_lints/src/attrs/utils.rs +++ b/clippy_lints/src/attrs/utils.rs @@ -1,10 +1,7 @@ use clippy_utils::macros::{is_panic, macro_backtrace}; use rustc_ast::MetaItemInner; -use rustc_hir::{ - Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, -}; +use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, Level}; -use rustc_middle::ty; use rustc_span::sym; use rustc_span::symbol::Symbol; @@ -20,56 +17,26 @@ pub(super) fn is_lint_level(symbol: Symbol) -> bool { Level::from_symbol(symbol).is_some() } -pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { - if let ItemKind::Fn { body: eid, .. } = item.kind { - is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value) - } else { - false - } -} - -pub(super) fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool { - match item.kind { - ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value), - _ => false, - } -} - -pub(super) fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool { - match item.kind { - TraitItemKind::Fn(_, TraitFn::Required(_)) => true, - TraitItemKind::Fn(_, TraitFn::Provided(eid)) => { - is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value) - }, - _ => false, - } -} - -fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool { +fn is_relevant_block(cx: &LateContext<'_>, block: &Block<'_>) -> bool { block.stmts.first().map_or_else( - || { - block - .expr - .as_ref() - .is_some_and(|e| is_relevant_expr(cx, typeck_results, e)) - }, + || block.expr.as_ref().is_some_and(|e| is_relevant_expr(cx, e)), |stmt| match &stmt.kind { StmtKind::Let(_) => true, - StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, expr), StmtKind::Item(_) => false, }, ) } -fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool { +pub(super) fn is_relevant_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if macro_backtrace(expr.span).last().is_some_and(|macro_call| { is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable }) { return false; } match &expr.kind { - ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block), - ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e), + ExprKind::Block(block, _) => is_relevant_block(cx, block), + ExprKind::Ret(Some(e)) => is_relevant_expr(cx, e), ExprKind::Ret(None) | ExprKind::Break(_, None) => false, _ => true, } diff --git a/clippy_lints/src/combined_early_pass.rs b/clippy_lints/src/combined_early_pass.rs new file mode 100644 index 0000000000000..85de0b00a1b04 --- /dev/null +++ b/clippy_lints/src/combined_early_pass.rs @@ -0,0 +1,92 @@ +//! A statically-combined early lint pass. +//! +//! The early-pass analogue of [`combined_late_pass`]. Folds clippy's early +//! passes into one concrete struct, one field per pass, with a single +//! `EarlyLintPass` impl that forwards each `check_*` to every field. Same +//! static-dispatch / DCE win as the late version: because the field types are +//! concrete and the forwards are `#[inline(always)]`, a pass that doesn't +//! override a `check_*` contributes only the empty default body, which is +//! DCE'd away. So the per-node, per-pass indirect (vtable) call into an empty +//! method disappears entirely, and the passes that do override become direct, +//! inlined calls. No vtable, no per-node dynamic dispatch. +//! +//! Unlike the late combine there is no `active` gate. rustc drops fully-disabled +//! late passes via `lints_that_dont_need_to_run`, but the early pass runner has +//! no such filtering, so a plain forward is equivalent and loses nothing. +//! +//! [`combined_late_pass`]: crate::combined_late_pass + +/// Run one field's `check_*`. +/// +/// Fully qualified through [`rustc_lint::EarlyLintPass`] since some passes impl +/// both `EarlyLintPass` and `LateLintPass` with like-named methods, which would +/// be ambiguous on the concrete field type. +#[macro_export] +macro_rules! run_combined_early_lint_pass_field { + ($self:ident, $field:ident, $name:ident, ($($arg:expr),* $(,)?)) => { + rustc_lint::EarlyLintPass::$name(&mut $self.$field, $($arg),*); + }; +} + +/// Forward one `check_*` method to every field of the combined pass. +#[macro_export] +macro_rules! expand_combined_early_lint_pass_method { + ([$($field:ident),*], $self:ident, $name:ident, $args:tt) => ({ + $($crate::run_combined_early_lint_pass_field!($self, $field, $name, $args);)* + }) +} + +/// Generate the combined `EarlyLintPass` impl's `check_*` methods, one per method +/// in rustc's early-pass method list. +#[macro_export] +macro_rules! expand_combined_early_lint_pass_methods { + ($fields:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( + $(#[inline(always)] fn $name(&mut self, cx: &rustc_lint::EarlyContext<'_>, $($param: $arg),*) { + $crate::expand_combined_early_lint_pass_method!($fields, self, $name, (cx, $($param),*)); + })* + ) +} + +/// Declare the combined struct (one field per pass) plus its +/// `LintPass`/`EarlyLintPass` impls. The method list comes from +/// `rustc_lint::early_lint_methods!` so it can't drift from rustc's. +/// +/// Each entry is `Field: Type = constructor`; `new`'s params (`conf`, ...) come +/// from the caller so ctor exprs can name them without hygiene trouble. +#[macro_export] +macro_rules! combined_early_lint_pass { + ( + [$name:ident, ($($pname:ident: $pty:ty),* $(,)?), [$($field:ident: $fty:ty = $ctor:expr,)*]], + $methods:tt + ) => { + #[allow(non_snake_case)] + pub struct $name { + $($field: $fty,)* + } + + impl $name { + pub fn new($($pname: $pty,)*) -> Self { + Self { + $($field: $ctor,)* + } + } + } + + #[allow(rustc::lint_pass_impl_without_macro)] + impl rustc_lint::LintPass for $name { + fn name(&self) -> &'static str { + stringify!($name) + } + fn get_lints(&self) -> rustc_lint::LintVec { + // Reserve at least one slot per pass up front to skip the early reallocations. + let mut lints = Vec::with_capacity([$(stringify!($field)),*].len()); + $(lints.extend(self.$field.get_lints());)* + lints + } + } + + impl rustc_lint::EarlyLintPass for $name { + $crate::expand_combined_early_lint_pass_methods!([$($field),*], $methods); + } + }; +} diff --git a/clippy_lints/src/combined_late_pass.rs b/clippy_lints/src/combined_late_pass.rs new file mode 100644 index 0000000000000..135603fd15d08 --- /dev/null +++ b/clippy_lints/src/combined_late_pass.rs @@ -0,0 +1,106 @@ +//! A statically-combined late lint pass. +//! +//! Folds clippy's ~300 late passes into one concrete struct, one field per pass. +//! The single `LateLintPass` impl forwards each `check_*` to every field; with +//! concrete types and `#[inline(always)]`, unoverridden methods are DCE'd and the +//! rest become direct calls, so there is no vtable or per-node dynamic dispatch. +//! +//! Mirrors rustc's `declare_combined_late_lint_pass!`, but wraps each field in +//! [`Gated`] with a precomputed `active` flag (the same "lint still needs to run" +//! predicate `rustc_lint::late` uses). Disabled passes are skipped by a branch +//! rather than dropped from a `Vec`, keeping clippy's allow-by-default fast path. + +use rustc_lint::{LintPass, LintVec}; + +/// A pass paired with its precomputed "still needs to run" flag. +pub struct Gated

{ + pub(crate) active: bool, + pub(crate) pass: P, +} + +impl Gated

{ + #[inline] + pub fn new bool>(is_active: &F, pass: P) -> Self { + let active = is_active(&pass.get_lints()); + Gated { active, pass } + } +} + +/// Run one field's `check_*`, if that field is active. +/// +/// Fully qualified through [`rustc_lint::LateLintPass`] since some passes impl +/// both `EarlyLintPass` and `LateLintPass` with like-named methods, which would +/// be ambiguous on the concrete field type. The args arrive as one `tt` and are +/// re-parsed here so the per-field and per-argument repetitions never share a +/// nesting level. +#[macro_export] +macro_rules! run_combined_late_lint_pass_field { + ($self:ident, $field:ident, $name:ident, ($($arg:expr),* $(,)?)) => { + if $self.$field.active { + rustc_lint::LateLintPass::$name(&mut $self.$field.pass, $($arg),*); + } + }; +} + +/// Forward one `check_*` method to every field of the combined pass. +#[macro_export] +macro_rules! expand_combined_late_lint_pass_method { + ([$($field:ident),*], $self:ident, $name:ident, $args:tt) => ({ + $($crate::run_combined_late_lint_pass_field!($self, $field, $name, $args);)* + }) +} + +/// Generate the combined `LateLintPass` impl's `check_*` methods, one per method +/// in rustc's late-pass method list. +#[macro_export] +macro_rules! expand_combined_late_lint_pass_methods { + ($fields:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( + $(#[inline(always)] fn $name(&mut self, cx: &rustc_lint::LateContext<'tcx>, $($param: $arg),*) { + $crate::expand_combined_late_lint_pass_method!($fields, self, $name, (cx, $($param),*)); + })* + ) +} + +/// Declare the combined struct (one [`Gated`] field per pass) plus its +/// `LintPass`/`LateLintPass` impls. The method list comes from +/// `rustc_lint::late_lint_methods!` so it can't drift from rustc's. +/// +/// Each entry is `Field: Type = constructor`; `new`'s params (`tcx`, `conf`, ...) +/// come from the caller so ctor exprs can name them without hygiene trouble. +#[macro_export] +macro_rules! combined_late_lint_pass { + ( + [$name:ident, ($($pname:ident: $pty:ty),* $(,)?), [$($field:ident: $fty:ty = $ctor:expr,)*]], + $methods:tt + ) => { + #[allow(non_snake_case)] + pub struct $name<'tcx> { + $($field: $crate::combined_late_pass::Gated<$fty>,)* + } + + impl<'tcx> $name<'tcx> { + pub fn new bool>($($pname: $pty,)* is_active: &F) -> Self { + Self { + $($field: $crate::combined_late_pass::Gated::new(is_active, $ctor),)* + } + } + } + + #[allow(rustc::lint_pass_impl_without_macro)] + impl<'tcx> rustc_lint::LintPass for $name<'tcx> { + fn name(&self) -> &'static str { + stringify!($name) + } + fn get_lints(&self) -> rustc_lint::LintVec { + // Reserve at least one slot per pass up front to skip the early reallocations. + let mut lints = Vec::with_capacity([$(stringify!($field)),*].len()); + $(lints.extend(self.$field.pass.get_lints());)* + lints + } + } + + impl<'tcx> rustc_lint::LateLintPass<'tcx> for $name<'tcx> { + $crate::expand_combined_late_lint_pass_methods!([$($field),*], $methods); + } + }; +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index b5047c628d0f0..6daa70f390aaa 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -276,6 +276,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, crate::loops::EXPLICIT_ITER_LOOP_INFO, crate::loops::FOR_KV_MAP_INFO, + crate::loops::FOR_UNBOUNDED_RANGE_INFO, crate::loops::INFINITE_LOOP_INFO, crate::loops::ITER_NEXT_LOOP_INFO, crate::loops::MANUAL_FIND_INFO, @@ -385,7 +386,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::FLAT_MAP_IDENTITY_INFO, crate::methods::FLAT_MAP_OPTION_INFO, crate::methods::FORMAT_COLLECT_INFO, - crate::methods::FROM_ITER_INSTEAD_OF_COLLECT_INFO, crate::methods::GET_FIRST_INFO, crate::methods::GET_LAST_WITH_LEN_INFO, crate::methods::GET_UNWRAP_INFO, @@ -613,6 +613,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::operators::INVALID_UPCAST_COMPARISONS_INFO, crate::operators::MANUAL_DIV_CEIL_INFO, crate::operators::MANUAL_IS_MULTIPLE_OF_INFO, + crate::operators::MANUAL_ISOLATE_LOWEST_ONE_INFO, crate::operators::MANUAL_MIDPOINT_INFO, crate::operators::MISREFACTORED_ASSIGN_OP_INFO, crate::operators::MODULO_ARITHMETIC_INFO, @@ -807,6 +808,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::volatile_composites::VOLATILE_COMPOSITES_INFO, crate::wildcard_imports::ENUM_GLOB_USE_INFO, crate::wildcard_imports::WILDCARD_IMPORTS_INFO, + crate::with_capacity_zero::WITH_CAPACITY_ZERO_INFO, crate::write::PRINT_LITERAL_INFO, crate::write::PRINT_STDERR_INFO, crate::write::PRINT_STDOUT_INFO, diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 9ea70159eb9f1..649d8449e431b 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -56,7 +56,11 @@ impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { // Inline const supports type inference. let is_parent_const = matches!( cx.tcx.hir_body_const_context(cx.tcx.hir_body_owner_def_id(body.id())), - Some(ConstContext::Const { allow_const_fn_promotion: true } | ConstContext::Static(_)) + Some( + ConstContext::Const { + allow_const_fn_promotion: true + } | ConstContext::Static(_) + ) ); let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const); visitor.visit_body(body); diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index ff92cd0598393..f1d481909d675 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -17,6 +17,8 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), + #[clippy::version = "1.98.0"] + ("clippy::from_iter_instead_of_collect", "lint has proved problematic"), #[clippy::version = "1.88.0"] ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 7a5150da6593a..26a06992d1a6f 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -2,7 +2,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; +use clippy_utils::ty::{ + adjust_derefs_manually_drop, get_adt_inherent_method, implements_trait, is_manually_drop, peel_and_count_ty_refs, +}; use clippy_utils::{ DefinedTy, ExprUseNode, get_expr_use_site, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym, }; @@ -17,7 +19,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults, Unnormalized}; +use rustc_middle::ty::{self, AssocTag, Ty, TyCtxt, TypeVisitableExt, TypeckResults, Unnormalized}; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol, SyntaxContext}; use std::borrow::Cow; @@ -368,12 +370,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { }, ExprUseNode::Callee | ExprUseNode::FieldAccess(_) if !use_site.moved_before_use => true, ExprUseNode::MethodArg(hir_id, _, 0) if !use_site.moved_before_use => { - // Check for calls to trait methods where the trait is implemented - // on a reference. - // Two cases need to be handled: + // Check for calls to trait methods where auto-borrow will not resolve. + // Three cases need to be handled: // * `self` methods on `&T` will never have auto-borrow // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take // priority. + // * `&self` methods on `T` can have auto-borrow, but if there's another method with the + // same name, it may take priority. if let Some(fn_id) = typeck.type_dependent_def_id(hir_id) && let Some(trait_id) = cx.tcx.trait_of_assoc(fn_id) && let arg_ty = cx.tcx.erase_and_anonymize_regions(adjusted_ty) @@ -395,12 +398,42 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // Trait methods taking `self` arg_ty } - && impl_ty.is_ref() - && implements_trait( - cx, - impl_ty, - trait_id, - &args[..cx.tcx.generics_of(trait_id).own_params.len() - 1], + && let method_name = cx.tcx.item_name(fn_id) + && ( + // If this trait impl is implemented on `&T`, then auto-borrowing won't work + (impl_ty.is_ref() + && implements_trait( + cx, + impl_ty, + trait_id, + &args[..cx.tcx.generics_of(trait_id).own_params.len() - 1], + )) + // If there's an inherent method, or a method from another trait, + // with the same name that's also implemented on this same type, + // then removing the borrow might cause that method to be chosen + // instead of the current one. + || get_adt_inherent_method(cx, impl_ty, method_name).is_some() + || cx.tcx.in_scope_traits(hir_id).is_some_and(|traits| { + traits + .iter() + .filter(|trait_| { + cx.tcx + .non_blanket_impls_for_ty(trait_.def_id, impl_ty) + .next() + .is_some() + || !cx + .tcx + .trait_impls_of(trait_.def_id) + .blanket_impls() + .is_empty() + }) + .any(|trait_| { + cx.tcx + .associated_items(trait_.def_id) + .filter_by_name_unhygienic(method_name) + .any(|item| item.tag() == AssocTag::Fn && item.def_id != fn_id) + }) + }) ) { false diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index e2fd71b7d990f..58cc318d57ccd 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,13 +1,18 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; use clippy_utils::paths::PathNS; +use clippy_utils::sym; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::smallvec::SmallVec; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -55,6 +60,19 @@ declare_clippy_lint! { /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config. /// xs.push(123); // Vec::push is _not_ disallowed in the config. /// ``` + /// + /// Disallowed profiles allow scoping different disallow lists: + /// ```toml + /// [profiles.forward_pass] + /// disallowed-methods = [{ path = "crate::devices::Buffer::copy_to_host", reason = "Forward code must not touch host buffers" }] + /// ``` + /// + /// ```rust,ignore + /// #[clippy::disallowed_profile("forward_pass")] + /// fn evaluate() { + /// // Method calls in this function use the `forward_pass` profile. + /// } + /// ``` #[clippy::version = "1.49.0"] pub DISALLOWED_METHODS, style, @@ -64,12 +82,22 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); pub struct DisallowedMethods { - disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, + default: DefIdMap<(&'static str, &'static DisallowedPath)>, + /// Lookup per profile that declares a non-empty `disallowed_methods` list. Profiles + /// declared in `[profiles.*]` but without `disallowed_methods` entries are absent here. + profiles: FxHashMap>, + /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes + /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist + /// in config but only define entries for other lints (e.g. `disallowed_types`). + known_profiles: FxHashSet, + profile_cache: ProfileResolver, + warned_unknown_profiles: FxHashSet, } impl DisallowedMethods { + #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (disallowed, _) = create_disallowed_map( + let (default, _) = create_disallowed_map( tcx, &conf.disallowed_methods, PathNS::Value, @@ -82,7 +110,62 @@ impl DisallowedMethods { "function", false, ); - Self { disallowed } + + let mut profiles = FxHashMap::default(); + let mut known_profiles = FxHashSet::default(); + let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); + profile_entries.sort_by_key(|(a, _)| *a); + for (name, profile) in profile_entries { + let symbol = Symbol::intern(name.as_str()); + known_profiles.insert(symbol); + + let paths = profile.disallowed_methods.as_slice(); + if paths.is_empty() { + continue; + } + + let (map, _) = create_disallowed_map( + tcx, + paths, + PathNS::Value, + |def_kind| { + matches!( + def_kind, + DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn + ) + }, + "function", + false, + ); + profiles.insert(symbol, map); + } + + Self { + default, + profiles, + known_profiles, + profile_cache: ProfileResolver::default(), + warned_unknown_profiles: FxHashSet::default(), + } + } + + fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { + if self.warned_unknown_profiles.insert(entry.span) { + let attr_name = if entry.attr_name == sym::disallowed_profiles { + "clippy::disallowed_profiles" + } else { + "clippy::disallowed_profile" + }; + span_lint( + cx, + DISALLOWED_METHODS, + entry.span, + format!( + "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_methods`", + entry.name + ), + ); + } } } @@ -98,13 +181,43 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }, _ => return, }; - if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) { + let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); + // Copy entries out of the cache before iterating: `warn_unknown_profile` takes + // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. + let entries: SmallVec<[ProfileEntry; 2]> = self + .profile_cache + .active_profiles(cx, expr.hir_id) + .map(|selection| selection.iter().copied().collect()) + .unwrap_or_default(); + for entry in &entries { + if self.profiles.contains_key(&entry.name) { + active_profiles.push(entry.name); + } else if !self.known_profiles.contains(&entry.name) { + self.warn_unknown_profile(cx, entry); + } + } + + if let Some((profile, &(path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { + self.profiles + .get(symbol) + .and_then(|map| map.get(&id).map(|info| (*symbol, info))) + }) { + let diag_amendment = disallowed_path.diag_amendment(span); + span_lint_and_then( + cx, + DISALLOWED_METHODS, + span, + format!("use of a disallowed method `{path}` (profile: {profile})"), + |diag| diag_amendment(diag), + ); + } else if let Some(&(path, disallowed_path)) = self.default.get(&id) { + let diag_amendment = disallowed_path.diag_amendment(span); span_lint_and_then( cx, DISALLOWED_METHODS, span, format!("use of a disallowed method `{path}`"), - disallowed_path.diag_amendment(span), + |diag| diag_amendment(diag), ); } } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 2c520d053f439..37dd605617a7a 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,15 +1,18 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; use clippy_utils::paths::PathNS; -use rustc_data_structures::fx::FxHashMap; +use clippy_utils::sym; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::smallvec::SmallVec; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -51,6 +54,17 @@ declare_clippy_lint! { /// // A similar type that is allowed by the config /// use std::collections::HashMap; /// ``` + /// + /// Disallowed profiles can scope lists to specific modules: + /// ```toml + /// [profiles.forward_pass] + /// disallowed-types = [{ path = "crate::buffers::HostBuffer", reason = "Prefer device buffers in forward computations" }] + /// ``` + /// + /// ```rust,ignore + /// #[clippy::disallowed_profile("forward_pass")] + /// fn forward_step(buffer: crate::buffers::DeviceBuffer) { /* ... */ } + /// ``` #[clippy::version = "1.55.0"] pub DISALLOWED_TYPES, style, @@ -59,37 +73,127 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); -pub struct DisallowedTypes { +struct TypeLookup { def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>, prim_tys: FxHashMap, } +impl TypeLookup { + fn from_config(tcx: TyCtxt<'_>, paths: &'static [DisallowedPath]) -> Self { + let (def_ids, prim_tys) = create_disallowed_map(tcx, paths, PathNS::Type, def_kind_predicate, "type", true); + Self { def_ids, prim_tys } + } + + fn find(&self, res: &Res) -> Option<(&'static str, &'static DisallowedPath)> { + match res { + Res::Def(_, did) => self.def_ids.get(did).copied(), + Res::PrimTy(prim) => self.prim_tys.get(prim).copied(), + _ => None, + } + } +} + +pub struct DisallowedTypes { + default: TypeLookup, + /// Lookup per profile that declares a non-empty `disallowed_types` list. Profiles + /// declared in `[profiles.*]` but without `disallowed_types` entries are absent here. + profiles: FxHashMap, + /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes + /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist + /// in config but only define entries for other lints (e.g. `disallowed_methods`). + known_profiles: FxHashSet, + profile_cache: ProfileResolver, + warned_unknown_profiles: FxHashSet, +} + impl DisallowedTypes { + #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (def_ids, prim_tys) = create_disallowed_map( - tcx, - &conf.disallowed_types, - PathNS::Type, - def_kind_predicate, - "type", - true, - ); - Self { def_ids, prim_tys } + let default = TypeLookup::from_config(tcx, &conf.disallowed_types); + + let mut profiles = FxHashMap::default(); + let mut known_profiles = FxHashSet::default(); + let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); + profile_entries.sort_by_key(|(a, _)| *a); + for (name, profile) in profile_entries { + let symbol = Symbol::intern(name.as_str()); + known_profiles.insert(symbol); + + let paths = profile.disallowed_types.as_slice(); + if paths.is_empty() { + continue; + } + profiles.insert(symbol, TypeLookup::from_config(tcx, paths)); + } + + Self { + default, + profiles, + known_profiles, + profile_cache: ProfileResolver::default(), + warned_unknown_profiles: FxHashSet::default(), + } } - fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { - let (path, disallowed_path) = match res { - Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x, - Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x, - _ => return, - }; - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("use of a disallowed type `{path}`"), - disallowed_path.diag_amendment(span), - ); + fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { + if self.warned_unknown_profiles.insert(entry.span) { + let attr_name = if entry.attr_name == sym::disallowed_profiles { + "clippy::disallowed_profiles" + } else { + "clippy::disallowed_profile" + }; + span_lint( + cx, + DISALLOWED_TYPES, + entry.span, + format!( + "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_types`", + entry.name + ), + ); + } + } + + fn check_res_emit(&mut self, cx: &LateContext<'_>, hir_id: rustc_hir::HirId, res: &Res, span: Span) { + let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); + // Copy entries out of the cache before iterating: `warn_unknown_profile` takes + // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. + let entries: SmallVec<[ProfileEntry; 2]> = self + .profile_cache + .active_profiles(cx, hir_id) + .map(|selection| selection.iter().copied().collect()) + .unwrap_or_default(); + for entry in &entries { + if self.profiles.contains_key(&entry.name) { + active_profiles.push(entry.name); + } else if !self.known_profiles.contains(&entry.name) { + self.warn_unknown_profile(cx, entry); + } + } + + if let Some((profile, (path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { + self.profiles + .get(symbol) + .and_then(|lookup| lookup.find(res).map(|info| (*symbol, info))) + }) { + let diag_amendment = disallowed_path.diag_amendment(span); + span_lint_and_then( + cx, + DISALLOWED_TYPES, + span, + format!("use of a disallowed type `{path}` (profile: {profile})"), + |diag| diag_amendment(diag), + ); + } else if let Some((path, disallowed_path)) = self.default.find(res) { + let diag_amendment = disallowed_path.diag_amendment(span); + span_lint_and_then( + cx, + DISALLOWED_TYPES, + span, + format!("use of a disallowed type `{path}`"), + |diag| diag_amendment(diag), + ); + } } } @@ -111,17 +215,22 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind && let Some(res) = path.res.type_ns { - self.check_res_emit(cx, &res, item.span); + self.check_res_emit(cx, item.hir_id(), &res, item.span); } } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Path(path) = &ty.kind { - self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span); + self.check_res_emit(cx, ty.hir_id, &cx.qpath_res(path, ty.hir_id), ty.span); } } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { - self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span); + self.check_res_emit( + cx, + poly.trait_ref.hir_ref_id, + &poly.trait_ref.path.res, + poly.trait_ref.path.span, + ); } } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 1da0a010668bb..81da571bdd601 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -906,29 +906,35 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ return Some(DocHeaders::default()); } - check_for_code_clusters( - cx, - pulldown_cmark::Parser::new_with_broken_link_callback( + // Only emits the allow-by-default `DOC_LINK_CODE`; skip its extra markdown reparse when it's off. + if !clippy_utils::is_lint_allowed(cx, DOC_LINK_CODE, cx.last_node_with_lint_attrs) { + check_for_code_clusters( + cx, + pulldown_cmark::Parser::new_with_broken_link_callback( + &doc, + main_body_opts() - Options::ENABLE_SMART_PUNCTUATION, + Some(&mut fake_broken_link_callback), + ) + .into_offset_iter(), &doc, - main_body_opts() - Options::ENABLE_SMART_PUNCTUATION, - Some(&mut fake_broken_link_callback), - ) - .into_offset_iter(), - &doc, - Fragments { - doc: &doc, - fragments: &fragments, - }, - ); + Fragments { + doc: &doc, + fragments: &fragments, + }, + ); + } - doc_paragraphs_missing_punctuation::check( - cx, - &doc, - Fragments { - doc: &doc, - fragments: &fragments, - }, - ); + // Same for the allow-by-default `DOC_PARAGRAPHS_MISSING_PUNCTUATION`, which also reparses. + if !clippy_utils::is_lint_allowed(cx, DOC_PARAGRAPHS_MISSING_PUNCTUATION, cx.last_node_with_lint_attrs) { + doc_paragraphs_missing_punctuation::check( + cx, + &doc, + Fragments { + doc: &doc, + fragments: &fragments, + }, + ); + } // NOTE: check_doc uses it own cb function, // to avoid causing duplicated diagnostics for the broken link checker. diff --git a/clippy_lints/src/empty_line_after.rs b/clippy_lints/src/empty_line_after.rs index b7b84c173f418..9df1919867e2e 100644 --- a/clippy_lints/src/empty_line_after.rs +++ b/clippy_lints/src/empty_line_after.rs @@ -93,14 +93,48 @@ impl_lint_pass!(EmptyLineAfter => [ EMPTY_LINE_AFTER_OUTER_ATTR, ]); +/// The kind of the item a doc comment or attribute applies to. Used in lint messages and to +/// detect the first item of a module or crate. `Other` holds the description string from +/// `ItemKind::descr` or `assoc_item_descr`. +#[derive(Debug, Clone, Copy)] +enum ItemKindDescr { + Crate, + Module, + Other(&'static str), +} + +impl ItemKindDescr { + fn as_str(self) -> &'static str { + match self { + Self::Crate => "crate", + Self::Module => "module", + Self::Other(descr) => descr, + } + } +} + #[derive(Debug)] struct ItemInfo { - kind: &'static str, + kind: ItemKindDescr, name: Option, span: Span, mod_items: Option, } +impl ItemInfo { + fn new(kind: ItemKindDescr, ident: Option, span: Span, mod_items: Option) -> Self { + Self { + kind, + name: ident.map(|ident| ident.name), + span: match ident { + Some(ident) => span.with_hi(ident.span.hi()), + None => span.shrink_to_lo(), + }, + mod_items, + } + } +} + pub struct EmptyLineAfter { items: Vec, } @@ -339,8 +373,8 @@ impl EmptyLineAfter { diag.span_label( info.span, match kind { - StopKind::Attr => format!("the attribute applies to this {}", info.kind), - StopKind::Doc(_) => format!("the comment documents this {}", info.kind), + StopKind::Attr => format!("the attribute applies to this {}", info.kind.as_str()), + StopKind::Doc(_) => format!("the comment documents this {}", info.kind.as_str()), }, ); @@ -364,7 +398,7 @@ impl EmptyLineAfter { stop.comment_out(cx, &mut suggestions); } let name = match info.name { - Some(name) => format!("{} `{name}`", info.kind).into(), + Some(name) => format!("{} `{name}`", info.kind.as_str()).into(), None => Cow::from("the following item"), }; diag.multipart_suggestion( @@ -400,7 +434,7 @@ impl EmptyLineAfter { /// them to inner attributes/docs fn suggest_inner(&self, diag: &mut Diag<'_, ()>, kind: StopKind, gaps: &[Gap<'_>], id: NodeId) { if let Some(parent) = self.items.iter().rev().nth(1) - && (parent.kind == "module" || parent.kind == "crate") + && matches!(parent.kind, ItemKindDescr::Module | ItemKindDescr::Crate) && parent.mod_items == Some(id) && let suggestions = gaps .iter() @@ -409,10 +443,9 @@ impl EmptyLineAfter { .collect::>() && !suggestions.is_empty() { - let desc = if parent.kind == "module" { - "parent module" - } else { - parent.kind + let desc = match parent.kind { + ItemKindDescr::Module => "parent module", + _ => parent.kind.as_str(), }; diag.multipart_suggestion( match kind { @@ -425,31 +458,8 @@ impl EmptyLineAfter { } } - fn check_item_kind( - &mut self, - cx: &EarlyContext<'_>, - kind: &ItemKind, - ident: Option, - span: Span, - attrs: &[Attribute], - id: NodeId, - ) { - self.items.push(ItemInfo { - kind: kind.descr(), - name: ident.map(|ident| ident.name), - span: match ident { - Some(ident) => span.with_hi(ident.span.hi()), - None => span.shrink_to_lo(), - }, - mod_items: match kind { - ItemKind::Mod(_, _, ModKind::Loaded(items, _, _)) => items - .iter() - .filter(|i| !matches!(i.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_))) - .map(|i| i.id) - .next(), - _ => None, - }, - }); + fn check_item_kind(&mut self, cx: &EarlyContext<'_>, info: ItemInfo, attrs: &[Attribute], id: NodeId) { + self.items.push(info); let mut outer = attrs .iter() @@ -496,10 +506,21 @@ impl EmptyLineAfter { } } +fn assoc_item_descr(kind: &AssocItemKind) -> &'static str { + match kind { + AssocItemKind::Const(_) => "constant item", + AssocItemKind::Fn(_) => "function", + AssocItemKind::Type(_) => "type alias", + AssocItemKind::MacCall(_) => "item macro invocation", + AssocItemKind::Delegation(_) => "delegated function", + AssocItemKind::DelegationMac(_) => "delegation", + } +} + impl EarlyLintPass for EmptyLineAfter { fn check_crate(&mut self, _: &EarlyContext<'_>, krate: &Crate) { self.items.push(ItemInfo { - kind: "crate", + kind: ItemKindDescr::Crate, name: Some(kw::Crate), span: krate.spans.inner_span.with_hi(krate.spans.inner_span.lo()), mod_items: krate @@ -522,28 +543,31 @@ impl EarlyLintPass for EmptyLineAfter { } fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - self.check_item_kind( - cx, - &item.kind.clone().into(), - item.kind.ident(), - item.span, - &item.attrs, - item.id, - ); + let kind = ItemKindDescr::Other(assoc_item_descr(&item.kind)); + let info = ItemInfo::new(kind, item.kind.ident(), item.span, None); + self.check_item_kind(cx, info, &item.attrs, item.id); } fn check_trait_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - self.check_item_kind( - cx, - &item.kind.clone().into(), - item.kind.ident(), - item.span, - &item.attrs, - item.id, - ); + let kind = ItemKindDescr::Other(assoc_item_descr(&item.kind)); + let info = ItemInfo::new(kind, item.kind.ident(), item.span, None); + self.check_item_kind(cx, info, &item.attrs, item.id); } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - self.check_item_kind(cx, &item.kind, item.kind.ident(), item.span, &item.attrs, item.id); + let (kind, mod_items) = match &item.kind { + ItemKind::Mod(_, _, ModKind::Loaded(items, _, _)) => { + let first = items + .iter() + .filter(|i| !matches!(i.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_))) + .map(|i| i.id) + .next(); + (ItemKindDescr::Module, first) + }, + ItemKind::Mod(..) => (ItemKindDescr::Module, None), + _ => (ItemKindDescr::Other(item.kind.descr()), None), + }; + let info = ItemInfo::new(kind, item.kind.ident(), item.span, mod_items); + self.check_item_kind(cx, info, &item.attrs, item.id); } } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 2e88d78ce9095..e7df5fa020e34 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -2,12 +2,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir; use rustc_abi::ExternAbi; use rustc_hir::def::DefKind; -use rustc_hir::{Body, FnDecl, HirId, HirIdSet, Node, Pat, PatKind, intravisit}; +use rustc_hir::{Body, FnDecl, HirId, HirIdSet, PatKind, intravisit}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::impl_lint_pass; use rustc_span::Span; use rustc_span::def_id::LocalDefId; @@ -52,15 +52,16 @@ declare_clippy_lint! { impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]); -fn is_non_trait_box(ty: Ty<'_>) -> bool { - ty.boxed_ty().is_some_and(|boxed| !boxed.is_trait()) +/// Whether `ty` is a `Box` where `T` is not a trait object and is small enough to live on the +/// stack (large types are boxed to avoid stack overflows). +fn is_small_non_trait_box<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, too_large_for_stack: u64) -> bool { + ty.boxed_ty().is_some_and(|boxed| { + !boxed.is_trait() && cx.layout_of(boxed).map_or(0, |l| l.size.bytes()) <= too_large_for_stack + }) } -struct EscapeDelegate<'a, 'tcx> { - cx: &'a LateContext<'tcx>, +struct EscapeDelegate { set: HirIdSet, - trait_self_ty: Option>, - too_large_for_stack: u64, } impl<'tcx> LateLintPass<'tcx> for BoxedLocal { @@ -73,6 +74,11 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { _: Span, fn_def_id: LocalDefId, ) { + // Skip closures + if matches!(fn_kind, intravisit::FnKind::Closure) { + return; + } + if let Some(header) = fn_kind.header() && header.abi != ExternAbi::Rust { @@ -97,12 +103,42 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { _ => {}, } - let mut v = EscapeDelegate { - cx, - set: HirIdSet::default(), - trait_self_ty, - too_large_for_stack: self.too_large_for_stack, - }; + let typeck_results = cx.tcx.typeck_body(body.id()); + + // Seed the set with the `Box` parameters that could be unboxed. The `ExprUseVisitor` walk + // below then removes any that escape by being moved or borrowed. + let set: HirIdSet = body + .params + .iter() + .filter_map(|param| { + // Only simple bindings (`x: Box<_>`) bind a local that the walk can track and report. + if !matches!(param.pat.kind, PatKind::Binding(..)) { + return None; + } + + let ty = typeck_results.pat_ty(param.pat); + if !is_small_non_trait_box(cx, ty, self.too_large_for_stack) { + return None; + } + + // skip `self` parameters whose type contains `Self` (i.e.: `self: Box`), see #4804 + if let Some(trait_self_ty) = trait_self_ty + && cx.tcx.hir_name(param.pat.hir_id) == kw::SelfLower + && ty.contains(trait_self_ty) + { + return None; + } + + Some(param.pat.hir_id) + }) + .collect(); + + // Without any candidate parameter, the expensive `ExprUseVisitor` walk can never lint. + if set.is_empty() { + return; + } + + let mut v = EscapeDelegate { set }; ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v) .consume_body(body) @@ -120,20 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { } } -// TODO: Replace with Map::is_argument(..) when it's fixed -fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool { - match tcx.hir_node(id) { - Node::Pat(Pat { - kind: PatKind::Binding(..), - .. - }) => (), - _ => return false, - } - - matches!(tcx.parent_hir_node(id), Node::Param(_)) -} - -impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { +impl<'tcx> Delegate<'tcx> for EscapeDelegate { fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { if cmt.place.projections.is_empty() && let PlaceBase::Local(lid) = cmt.place.base @@ -154,39 +177,7 @@ impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { } } - fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { - if cmt.place.projections.is_empty() && is_argument(self.cx.tcx, cmt.hir_id) { - // Skip closure arguments - let parent_id = self.cx.tcx.parent_hir_id(cmt.hir_id); - if let Node::Expr(..) = self.cx.tcx.parent_hir_node(parent_id) { - return; - } - - // skip if there is a `self` parameter binding to a type - // that contains `Self` (i.e.: `self: Box`), see #4804 - if let Some(trait_self_ty) = self.trait_self_ty - && self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower - && cmt.place.ty().contains(trait_self_ty) - { - return; - } - - if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { - self.set.insert(cmt.hir_id); - } - } - } + fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } - -impl<'tcx> EscapeDelegate<'_, 'tcx> { - fn is_large_box(&self, ty: Ty<'tcx>) -> bool { - // Large types need to be boxed to avoid stack overflows. - if let Some(boxed_ty) = ty.boxed_ty() { - self.cx.layout_of(boxed_ty).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack - } else { - false - } - } -} diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 65d0ff9b543f8..b44497ded301e 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -11,35 +11,51 @@ use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does - /// Checks for excessive use of - /// bools in function definitions. + /// Checks for excessive use of bools in function declarations. /// /// ### Why is this bad? - /// Calls to such functions - /// are confusing and error prone, because it's - /// hard to remember argument order and you have - /// no type system support to back you up. Using - /// two-variant enums instead of bools often makes - /// API easier to use. + /// Boolean parameters obscure meaning at the call site. The reader has to + /// remember what each `true` or `false` means and which position each flag + /// belongs in, with no help from the type system. + /// + /// The best replacement depends on what role the bool plays: + /// + /// - Use a two-variant enum when one parameter selects a mode. + /// - Split one function into two named operations when the choices are + /// distinct actions. + /// - Group multiple related flags into a struct or options type when they + /// travel together. + /// - Move the setting onto `self` or an existing config/context parameter + /// when it is really part of the surrounding state. + /// + /// This optimizes for readability of the calling code, which future readers + /// encounter more often than the function declaration. + /// + /// The `max-fn-params-bools` configuration accepts `0`, which lints on any + /// function with a bool parameter. /// /// ### Example /// ```rust,ignore - /// fn f(is_round: bool, is_hot: bool) { ... } + /// fn render_document(show_hidden: bool, is_draft: bool) { ... } /// ``` /// /// Use instead: /// ```rust,ignore - /// enum Shape { - /// Round, - /// Spiky, + /// enum Visibility { + /// ShowHidden, + /// HideHidden, /// } /// - /// enum Temperature { - /// Hot, - /// IceCold, + /// enum DocumentKind { + /// Draft, + /// Final, /// } /// - /// fn f(shape: Shape, temperature: Temperature) { ... } + /// fn render_document(visibility: Visibility, kind: DocumentKind) { ... } + /// + /// // or split the operation when the choices are distinct + /// fn render_draft() { ... } + /// fn render_final() { ... } /// ``` #[clippy::version = "1.43.0"] pub FN_PARAMS_EXCESSIVE_BOOLS, diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 7c1b3ef225c6a..a3e982bb09430 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; use clippy_utils::attrs::is_proc_macro; -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_indent; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr_without_closures; @@ -145,31 +145,24 @@ fn check_needless_must_use( return; } if returns_unit(decl) { - if attrs.len() == 1 { - span_lint_and_then( - cx, - MUST_USE_UNIT, - fn_header_span, - "this unit-returning function has a `#[must_use]` attribute", - |diag| { + span_lint_and_then( + cx, + MUST_USE_UNIT, + fn_header_span, + "this unit-returning function has a `#[must_use]` attribute", + |diag| { + // When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see + // issue #12320. + // FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile, + // honestly looked incorrect, and is a little hard to support for a little bit now. Some day this + // could be re-added. + if attrs.len() == 1 { diag.span_suggestion(attr_span, "remove the attribute", "", Applicability::MachineApplicable); - }, - ); - } else { - // When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see - // issue #12320. - // FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile, - // honestly looked incorrect, and is a little hard to support for a little bit now. Some day this - // could be re-added. - span_lint_and_help( - cx, - MUST_USE_UNIT, - fn_header_span, - "this unit-returning function has a `#[must_use]` attribute", - Some(attr_span), - "remove `must_use`", - ); - } + } else { + diag.span_help(attr_span, "remove `must_use`"); + } + }, + ); } else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { // Ignore async functions unless Future::Output type is a must_use type if sig.header.is_async() { @@ -181,13 +174,24 @@ fn check_needless_must_use( } } - span_lint_and_help( + span_lint_and_then( cx, DOUBLE_MUST_USE, fn_header_span, "this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`", - None, - "either add some descriptive message or remove the attribute", + |diag| { + // When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see + // issue #12320. + // FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile, + // honestly looked incorrect, and is a little hard to support for a little bit now. Some day this + // could be re-added. + if attrs.len() == 1 { + diag.span_suggestion(attr_span, "remove the attribute", "", Applicability::MachineApplicable); + } else { + diag.span_help(attr_span, "remove `must_use`"); + } + diag.note("alternatively, you may add an explicit reason to the `must_use` attribute"); + }, ); } } diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 829f054d8854e..331c6e23bddd7 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -2,10 +2,12 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; use rustc_errors::Diag; use rustc_hir as hir; +use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_span::def_id::DefIdSet; use rustc_span::{Span, sym}; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::ty::{AdtVariantInfo, approx_ty_size}; @@ -23,17 +25,31 @@ fn result_err_ty<'tcx>( ) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> { if !item_span.in_external_macro(cx.sess().source_map()) && let hir::FnRetTy::Return(hir_ty) = decl.output - && let ty = cx - .tcx - .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(id).instantiate_identity().skip_norm_wip().output()) - && ty.is_diag_item(cx, sym::Result) - && let ty::Adt(_, args) = ty.kind() { - let err_ty = args.type_at(1); - Some((hir_ty, err_ty)) - } else { - None + let mut ty = cx + .tcx + .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(id).instantiate_identity().skip_norm_wip().output()); + + // for async functions, peel through `impl Future` to get `T` + if cx.tcx.ty_is_opaque_future(ty) + && let Some(future_output_ty) = cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .err_ctxt() + .get_impl_future_output_ty(ty) + { + ty = future_output_ty; + } + + if let ty::Adt(adt_def, args) = ty.kind() + && cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) + { + let err_ty = args.type_at(1); + return Some((hir_ty, err_ty)); + } } + None } pub(super) fn check_item<'tcx>( diff --git a/clippy_lints/src/inline_trait_bounds.rs b/clippy_lints/src/inline_trait_bounds.rs index 99716253c53e7..3ffa23dcd6046 100644 --- a/clippy_lints/src/inline_trait_bounds.rs +++ b/clippy_lints/src/inline_trait_bounds.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{HasSession, snippet}; -use rustc_ast::NodeId; +use clippy_utils::sym; use rustc_ast::ast::{Fn, FnRetTy, GenericParam, GenericParamKind}; use rustc_ast::visit::{FnCtxt, FnKind}; +use rustc_ast::{HasAttrs as _, NodeId}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::Span; declare_clippy_lint! { @@ -37,9 +38,30 @@ declare_clippy_lint! { "enforce that `where` bounds are used for all trait bounds" } -declare_lint_pass!(InlineTraitBounds => [INLINE_TRAIT_BOUNDS]); +impl_lint_pass!(InlineTraitBounds => [INLINE_TRAIT_BOUNDS]); + +#[derive(Default)] +pub struct InlineTraitBounds { + /// The top-level item onto which `#[automatically_derived]` has been encountered + automatically_derived_at: Option, +} impl EarlyLintPass for InlineTraitBounds { + fn check_item(&mut self, _: &EarlyContext<'_>, item: &rustc_ast::Item) { + if self.automatically_derived_at.is_none() + && item + .attrs() + .iter() + .any(|attr| attr.has_name(sym::automatically_derived)) + { + self.automatically_derived_at = Some(item.id); + } + } + + fn check_item_post(&mut self, _: &EarlyContext<'_>, item: &rustc_ast::Item) { + self.automatically_derived_at.take_if(|id| *id == item.id); + } + fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: NodeId) { let FnKind::Fn(ctxt, _vis, f) = kind else { return; @@ -50,6 +72,11 @@ impl EarlyLintPass for InlineTraitBounds { return; } + // Skip automatically derived functions + if self.automatically_derived_at.is_some() { + return; + } + if f.sig.span.in_external_macro(cx.sess().source_map()) { return; } diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 80b49ecc7a40b..9835c1decceee 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -3,9 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_middle::ty::Unnormalized; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, Ty, Unnormalized}; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Pos, Span}; @@ -45,6 +44,31 @@ impl LargeConstArrays { maximum_allowed_size: conf.array_size_threshold, } } + + /// Checks recursively checks whether `ty` has an array exceeding allowed size + fn check_impl<'a>(&self, cx: &LateContext<'a>, ty: Ty<'a>) -> bool { + match ty.kind() { + ty::Adt(adt_def, args) => adt_def + .all_fields() + .any(|f| self.check_impl(cx, f.ty(cx.tcx, args).skip_norm_wip())), + ty::Array(element_type, cst) => { + let normalized = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(*cst)) + .unwrap_or(*cst); + if let Some(element_count) = normalized.try_to_target_usize(cx.tcx) + && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) + && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) + { + true + } else { + false + } + }, + ty::Tuple(fields) => fields.iter().any(|f| self.check_impl(cx, f)), + _ => false, + } + } } impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { @@ -56,13 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { && generics.params.is_empty() && !generics.has_where_clause_predicates && !item.span.from_expansion() && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip() - && let ty::Array(element_type, cst) = ty.kind() - && let Some(element_count) = cx.tcx - .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(*cst)) - .unwrap_or(*cst) - .try_to_target_usize(cx.tcx) - && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) - && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) + && self.check_impl(cx, ty) { let hi_pos = ident.span.lo() - BytePos::from_usize(1); let sugg_span = Span::new( diff --git a/clippy_lints/src/large_futures.rs b/clippy_lints/src/large_futures.rs index 56ae1d04827e8..caa44d5ef1235 100644 --- a/clippy_lints/src/large_futures.rs +++ b/clippy_lints/src/large_futures.rs @@ -64,7 +64,9 @@ impl<'tcx> LateLintPass<'tcx> for LargeFuture { && let ty = cx.typeck_results().expr_ty(arg) && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() && implements_trait(cx, ty, future_trait_def_id, &[]) - && let Ok(layout) = cx.tcx.layout_of(cx.typing_env().with_codegen_normalized(cx.tcx).as_query_input(ty)) + && let Ok(layout) = cx + .tcx + .layout_of(cx.typing_env().with_codegen_normalized(cx.tcx).as_query_input(ty)) && let size = layout.layout.size() && size >= Size::from_bytes(self.future_size_threshold) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index af25b1f7f1aac..4a7659a9cc210 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -55,6 +55,9 @@ extern crate declare_clippy_lint; mod utils; +mod combined_early_pass; +mod combined_late_pass; + pub mod declared_lints; pub mod deprecated_lints; @@ -400,6 +403,7 @@ mod vec_init_then_push; mod visibility; mod volatile_composites; mod wildcard_imports; +mod with_capacity_zero; mod write; mod zero_div_zero; mod zero_repeat_side_effects; @@ -410,10 +414,9 @@ mod zombie_processes; use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync; -use rustc_lint::{EarlyLintPass, LateLintPass, Lint}; +use rustc_lint::Lint; use rustc_middle::ty::TyCtxt; -use utils::attr_collector::{AttrCollector, AttrStorage}; +use utils::attr_collector::AttrStorage; pub fn explain(name: &str) -> i32 { let target = format!("clippy::{}", name.to_ascii_uppercase()); @@ -440,7 +443,6 @@ pub fn explain(name: &str) -> i32 { /// Register all lints and lint groups with the rustc lint store /// /// Used in `./src/driver.rs`. -#[expect(clippy::too_many_lines)] pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Conf) { for (old_name, new_name) in deprecated_lints::RENAMED { store.register_renamed(old_name, new_name); @@ -457,421 +459,409 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co let format_args_storage = FormatArgsStorage::default(); let attr_storage = AttrStorage::default(); - let early_lints: [Box Box + sync::DynSend + sync::DynSync>; _] = [ - { - let format_args = format_args_storage.clone(); - Box::new(move || { - Box::new(utils::format_args_collector::FormatArgsCollector::new( - format_args.clone(), - )) - }) - }, - { - let attrs = attr_storage.clone(); - Box::new(move || Box::new(AttrCollector::new(attrs.clone()))) - }, - Box::new(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))), - Box::new(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)), - Box::new(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf))), - Box::new(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf))), - Box::new(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(conf))), - Box::new(|| Box::new(functions::EarlyFunctions)), - Box::new(move || Box::new(doc::Documentation::new(conf))), - Box::new(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)), - Box::new(|| Box::new(double_parens::DoubleParens)), - Box::new(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)), - Box::new(|| Box::new(else_if_without_else::ElseIfWithoutElse)), - Box::new(|| Box::new(int_plus_one::IntPlusOne)), - Box::new(|| Box::new(formatting::Formatting)), - Box::new(|| Box::new(misc_early::MiscEarlyLints)), - Box::new(|| Box::new(unused_unit::UnusedUnit)), - Box::new(|| Box::new(precedence::Precedence)), - Box::new(|| Box::new(redundant_else::RedundantElse)), - Box::new(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)), - Box::new(move || Box::new(literal_representation::LiteralDigitGrouping::new(conf))), - Box::new(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(conf))), - Box::new(|| Box::new(tabs_in_doc_comments::TabsInDocComments)), - Box::new(|| Box::::default()), - Box::new(|| Box::new(option_env_unwrap::OptionEnvUnwrap)), - Box::new(move || Box::new(non_expressive_names::NonExpressiveNames::new(conf))), - Box::new(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf))), - Box::new(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)), - Box::new(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)), - Box::new(move || Box::new(module_style::ModStyle::default())), - Box::new(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(conf))), - Box::new(|| Box::new(octal_escapes::OctalEscapes)), - Box::new(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)), - Box::new(|| Box::new(crate_in_macro_def::CrateInMacroDef)), - Box::new(|| Box::new(pub_use::PubUse)), - Box::new(move || Box::new(large_include_file::LargeIncludeFile::new(conf))), - Box::new(|| Box::::default()), - Box::new(|| Box::new(unused_rounding::UnusedRounding)), - Box::new(move || Box::new(almost_complete_range::AlmostCompleteRange::new(conf))), - Box::new(|| Box::new(multi_assignments::MultiAssignments)), - Box::new(|| Box::new(partial_pub_fields::PartialPubFields)), - Box::new(|| Box::new(let_with_type_underscore::UnderscoreTyped)), - Box::new(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))), - Box::new(|| Box::new(ref_patterns::RefPatterns)), - Box::new(|| Box::new(needless_else::NeedlessElse)), - Box::new(move || Box::new(raw_strings::RawStrings::new(conf))), - Box::new(|| Box::new(visibility::Visibility)), - Box::new(|| Box::new(multiple_bound_locations::MultipleBoundLocations)), - Box::new(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)), - Box::new(|| Box::new(cfg_not_test::CfgNotTest)), - Box::new(|| Box::new(empty_line_after::EmptyLineAfter::new())), - Box::new(|| Box::new(inline_trait_bounds::InlineTraitBounds)), + { + let format_args = format_args_storage.clone(); + let attrs = attr_storage.clone(); + store.early_passes.push(Box::new(move || { + Box::new(CombinedEarlyLintPass::new(conf, format_args.clone(), attrs.clone())) + })); + } + + store.late_passes.push(Box::new(move |tcx: TyCtxt<'_>| { + let dont_need = tcx.lints_that_dont_need_to_run(()); + let is_active = |lints: &rustc_lint::LintVec| { + lints.is_empty() + || !lints + .iter() + .all(|lint| dont_need.contains(&rustc_lint::LintId::of(lint))) + }; + Box::new(CombinedLateLintPass::new( + tcx, + conf, + format_args_storage.clone(), + attr_storage.clone(), + &is_active, + )) + })); +} + +// Fold every early pass into one statically-combined struct (see +// `combined_early_pass`); the method list comes from `early_lint_methods!`. +#[rustfmt::skip] +rustc_lint::early_lint_methods!( + crate::combined_early_lint_pass, + [CombinedEarlyLintPass, (conf: &'static Conf, format_args: FormatArgsStorage, attrs: AttrStorage), [ + FormatArgsCollector: utils::format_args_collector::FormatArgsCollector = utils::format_args_collector::FormatArgsCollector::new(format_args.clone()), + AttrCollector: utils::attr_collector::AttrCollector = utils::attr_collector::AttrCollector::new(attrs.clone()), + PostExpansionEarlyAttributes: attrs::PostExpansionEarlyAttributes = attrs::PostExpansionEarlyAttributes::new(conf), + UnnecessarySelfImports: unnecessary_self_imports::UnnecessarySelfImports = unnecessary_self_imports::UnnecessarySelfImports, + RedundantStaticLifetimes: redundant_static_lifetimes::RedundantStaticLifetimes = redundant_static_lifetimes::RedundantStaticLifetimes::new(conf), + RedundantFieldNames: redundant_field_names::RedundantFieldNames = redundant_field_names::RedundantFieldNames::new(conf), + UnnestedOrPatterns: unnested_or_patterns::UnnestedOrPatterns = unnested_or_patterns::UnnestedOrPatterns::new(conf), + EarlyFunctions: functions::EarlyFunctions = functions::EarlyFunctions, + Documentation: doc::Documentation = doc::Documentation::new(conf), + SuspiciousOperationGroupings: suspicious_operation_groupings::SuspiciousOperationGroupings = suspicious_operation_groupings::SuspiciousOperationGroupings, + DoubleParens: double_parens::DoubleParens = double_parens::DoubleParens, + UnsafeNameRemoval: unsafe_removed_from_name::UnsafeNameRemoval = unsafe_removed_from_name::UnsafeNameRemoval, + ElseIfWithoutElse: else_if_without_else::ElseIfWithoutElse = else_if_without_else::ElseIfWithoutElse, + IntPlusOne: int_plus_one::IntPlusOne = int_plus_one::IntPlusOne, + Formatting: formatting::Formatting = formatting::Formatting, + MiscEarlyLints: misc_early::MiscEarlyLints = misc_early::MiscEarlyLints, + UnusedUnit: unused_unit::UnusedUnit = unused_unit::UnusedUnit, + Precedence: precedence::Precedence = precedence::Precedence, + RedundantElse: redundant_else::RedundantElse = redundant_else::RedundantElse, + NeedlessArbitrarySelfType: needless_arbitrary_self_type::NeedlessArbitrarySelfType = needless_arbitrary_self_type::NeedlessArbitrarySelfType, + LiteralDigitGrouping: literal_representation::LiteralDigitGrouping = literal_representation::LiteralDigitGrouping::new(conf), + DecimalLiteralRepresentation: literal_representation::DecimalLiteralRepresentation = literal_representation::DecimalLiteralRepresentation::new(conf), + TabsInDocComments: tabs_in_doc_comments::TabsInDocComments = tabs_in_doc_comments::TabsInDocComments, + SingleComponentPathImports: single_component_path_imports::SingleComponentPathImports = single_component_path_imports::SingleComponentPathImports::default(), + OptionEnvUnwrap: option_env_unwrap::OptionEnvUnwrap = option_env_unwrap::OptionEnvUnwrap, + NonExpressiveNames: non_expressive_names::NonExpressiveNames = non_expressive_names::NonExpressiveNames::new(conf), + MacroBraces: nonstandard_macro_braces::MacroBraces = nonstandard_macro_braces::MacroBraces::new(conf), + InlineAsmX86AttSyntax: asm_syntax::InlineAsmX86AttSyntax = asm_syntax::InlineAsmX86AttSyntax, + InlineAsmX86IntelSyntax: asm_syntax::InlineAsmX86IntelSyntax = asm_syntax::InlineAsmX86IntelSyntax, + ModStyle: module_style::ModStyle = module_style::ModStyle::default(), + DisallowedScriptIdents: disallowed_script_idents::DisallowedScriptIdents = disallowed_script_idents::DisallowedScriptIdents::new(conf), + OctalEscapes: octal_escapes::OctalEscapes = octal_escapes::OctalEscapes, + SingleCharLifetimeNames: single_char_lifetime_names::SingleCharLifetimeNames = single_char_lifetime_names::SingleCharLifetimeNames, + CrateInMacroDef: crate_in_macro_def::CrateInMacroDef = crate_in_macro_def::CrateInMacroDef, + PubUse: pub_use::PubUse = pub_use::PubUse, + LargeIncludeFile: large_include_file::LargeIncludeFile = large_include_file::LargeIncludeFile::new(conf), + DuplicateMod: duplicate_mod::DuplicateMod = duplicate_mod::DuplicateMod::default(), + UnusedRounding: unused_rounding::UnusedRounding = unused_rounding::UnusedRounding, + AlmostCompleteRange: almost_complete_range::AlmostCompleteRange = almost_complete_range::AlmostCompleteRange::new(conf), + MultiAssignments: multi_assignments::MultiAssignments = multi_assignments::MultiAssignments, + PartialPubFields: partial_pub_fields::PartialPubFields = partial_pub_fields::PartialPubFields, + UnderscoreTyped: let_with_type_underscore::UnderscoreTyped = let_with_type_underscore::UnderscoreTyped, + ExcessiveNesting: excessive_nesting::ExcessiveNesting = excessive_nesting::ExcessiveNesting::new(conf), + RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns, + NeedlessElse: needless_else::NeedlessElse = needless_else::NeedlessElse, + RawStrings: raw_strings::RawStrings = raw_strings::RawStrings::new(conf), + Visibility: visibility::Visibility = visibility::Visibility, + MultipleBoundLocations: multiple_bound_locations::MultipleBoundLocations = multiple_bound_locations::MultipleBoundLocations, + FieldScopedVisibilityModifiers: field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers = field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers, + CfgNotTest: cfg_not_test::CfgNotTest = cfg_not_test::CfgNotTest, + EmptyLineAfter: empty_line_after::EmptyLineAfter = empty_line_after::EmptyLineAfter::new(), + InlineTraitBounds: inline_trait_bounds::InlineTraitBounds = inline_trait_bounds::InlineTraitBounds::default(), // add early passes here, used by `cargo dev new_lint` - ]; - store.early_passes.extend(early_lints); + ]] +); - #[expect(clippy::type_complexity)] - let late_lints: [Box< - dyn for<'tcx> Fn(TyCtxt<'tcx>) -> Box + 'tcx> + sync::DynSend + sync::DynSync, - >; _] = [ - Box::new(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))), - Box::new(|_| Box::new(utils::dump_hir::DumpHir)), - Box::new(|_| Box::new(utils::author::Author)), - Box::new(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf))), - Box::new(|_| Box::new(serde_api::SerdeApi)), - Box::new(move |_| Box::new(types::Types::new(conf))), - Box::new(move |_| Box::new(booleans::NonminimalBool::new(conf))), - Box::new(|_| Box::new(enum_clike::UnportableVariant)), - Box::new(move |_| Box::new(float_literal::FloatLiteral::new(conf))), - Box::new(|_| Box::new(ptr::Ptr)), - Box::new(|_| Box::new(needless_bool::NeedlessBool)), - Box::new(|_| Box::new(bool_comparison::BoolComparison)), - Box::new(|_| Box::new(needless_for_each::NeedlessForEach)), - Box::new(|_| Box::new(misc::LintPass)), - Box::new(|_| Box::new(eta_reduction::EtaReduction)), - Box::new(|_| Box::new(mut_mut::MutMut::default())), - Box::new(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)), - Box::new(|_| Box::>::default()), - Box::new(move |_| Box::new(len_zero::LenZero::new(conf))), - Box::new(|_| Box::new(len_without_is_empty::LenWithoutIsEmpty)), - Box::new(move |_| Box::new(attrs::Attributes::new(conf))), - Box::new(|_| Box::new(blocks_in_conditions::BlocksInConditions)), - Box::new(|_| Box::new(unicode::Unicode)), - Box::new(|_| Box::new(uninit_vec::UninitVec)), - Box::new(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)), - Box::new(|_| Box::new(strings::StringAdd)), - Box::new(|_| Box::new(implicit_return::ImplicitReturn)), - Box::new(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))), - Box::new(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)), - Box::new(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)), - Box::new(move |_| Box::new(approx_const::ApproxConstant::new(conf))), - Box::new(move |_| Box::new(matches::Matches::new(conf))), - Box::new(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf))), - Box::new(move |_| Box::new(manual_strip::ManualStrip::new(conf))), - Box::new(move |_| Box::new(checked_conversions::CheckedConversions::new(conf))), - Box::new(move |_| Box::new(mem_replace::MemReplace::new(conf))), - Box::new(move |_| Box::new(ranges::Ranges::new(conf))), - Box::new(move |_| Box::new(from_over_into::FromOverInto::new(conf))), - Box::new(move |_| Box::new(use_self::UseSelf::new(conf))), - Box::new(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(conf))), - Box::new(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)), - Box::new(move |_| Box::new(casts::Casts::new(conf))), - Box::new(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)), - Box::new(|_| Box::new(same_name_method::SameNameMethod)), - Box::new(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf))), - Box::new(|_| Box::::default()), - Box::new(move |_| { - Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new( - conf, - )) - }), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(methods::Methods::new(conf, format_args.clone()))) - }, - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(unit_types::UnitTypes::new(format_args.clone()))) - }, - Box::new(move |_| Box::new(loops::Loops::new(conf))), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(lifetimes::Lifetimes::new(conf))), - Box::new(|_| Box::new(entry::HashMapPass)), - Box::new(|_| Box::new(minmax::MinMaxPass)), - Box::new(|_| Box::new(zero_div_zero::ZeroDiv)), - Box::new(|_| Box::new(mutex_atomic::Mutex)), - Box::new(|_| Box::new(needless_update::NeedlessUpdate)), - Box::new(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef)), - Box::new(|_| Box::new(borrow_deref_ref::BorrowDerefRef)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(temporary_assignment::TemporaryAssignment)), - Box::new(move |_| Box::new(transmute::Transmute::new(conf))), - Box::new(move |_| Box::new(cognitive_complexity::CognitiveComplexity::new(conf))), - Box::new(move |_| Box::new(escape::BoxedLocal::new(conf))), - Box::new(move |_| Box::new(useless_vec::UselessVec::new(conf))), - Box::new(move |_| Box::new(panic_unimplemented::PanicUnimplemented::new(conf))), - Box::new(|_| Box::new(strings::StringLitAsBytes)), - Box::new(|_| Box::new(derive::Derive)), - Box::new(move |_| Box::new(derivable_impls::DerivableImpls::new(conf))), - Box::new(|_| Box::new(drop_forget_ref::DropForgetRef)), - Box::new(|_| Box::new(empty_enums::EmptyEnums)), - Box::new(|_| Box::::default()), - Box::new(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))), - Box::new(|_| Box::new(copy_iterator::CopyIterator)), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(format::UselessFormat::new(format_args.clone()))) - }, - Box::new(|_| Box::new(swap::Swap)), - Box::new(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks)), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(disallowed_names::DisallowedNames::new(conf))), - Box::new(move |tcx| Box::new(functions::Functions::new(tcx, conf))), - Box::new(move |_| Box::new(doc::Documentation::new(conf))), - Box::new(|_| Box::new(neg_multiply::NegMultiply)), - Box::new(|_| Box::new(let_if_seq::LetIfSeq)), - Box::new(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)), - Box::new(move |_| Box::new(missing_doc::MissingDoc::new(conf))), - Box::new(|_| Box::new(missing_inline::MissingInline)), - Box::new(move |_| Box::new(exhaustive_items::ExhaustiveItems)), - Box::new(|_| Box::new(unused_result_ok::UnusedResultOk)), - Box::new(|_| Box::new(match_result_ok::MatchResultOk)), - Box::new(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)), - Box::new(|_| Box::new(unused_io_amount::UnusedIoAmount)), - Box::new(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(conf))), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone()))) - }, - Box::new(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)), - Box::new(move |tcx| Box::new(pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf))), - Box::new(|_| Box::new(ref_option_ref::RefOptionRef)), - Box::new(|_| Box::new(infinite_iter::InfiniteIter)), - Box::new(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(implicit_hasher::ImplicitHasher)), - Box::new(|_| Box::new(fallible_impl_from::FallibleImplFrom)), - Box::new(move |_| Box::new(question_mark::QuestionMark::new(conf))), - Box::new(|_| Box::new(question_mark_used::QuestionMarkUsed)), - Box::new(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)), - Box::new(|_| Box::new(map_unit_fn::MapUnit)), - Box::new(move |_| Box::new(inherent_impl::MultipleInherentImpl::new(conf))), - Box::new(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)), - Box::new(move |_| Box::new(unwrap::Unwrap::new(conf))), - Box::new(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))), - Box::new(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))), - Box::new(|_| Box::new(redundant_clone::RedundantClone)), - Box::new(|_| Box::new(slow_vector_initialization::SlowVectorInit)), - Box::new(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))), - Box::new(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf))), - Box::new(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)), - Box::new(|_| Box::new(inherent_to_string::InherentToString)), - Box::new(move |_| Box::new(trait_bounds::TraitBounds::new(conf))), - Box::new(|_| Box::new(comparison_chain::ComparisonChain)), - Box::new(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf))), - Box::new(|_| Box::new(reference::DerefAddrOf)), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone()))) - }, - Box::new(|_| Box::new(redundant_closure_call::RedundantClosureCall)), - Box::new(|_| Box::new(unused_unit::UnusedUnit)), - Box::new(|_| Box::new(returns::Return)), - Box::new(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))), - Box::new(|_| Box::new(items_after_statements::ItemsAfterStatements)), - Box::new(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)), - Box::new(|_| Box::new(needless_continue::NeedlessContinue)), - Box::new(|_| Box::new(create_dir::CreateDir)), - Box::new(move |_| Box::new(item_name_repetitions::ItemNameRepetitions::new(conf))), - Box::new(move |_| Box::new(upper_case_acronyms::UpperCaseAcronyms::new(conf))), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(unused_self::UnusedSelf::new(conf))), - Box::new(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)), - Box::new(|_| Box::new(exit::Exit)), - Box::new(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf))), - Box::new(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))), - Box::new(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))), - Box::new(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)), - Box::new(|_| Box::new(as_conversions::AsConversions)), - Box::new(|_| Box::new(let_underscore::LetUnderscore)), - Box::new(move |_| Box::new(excessive_bools::ExcessiveBools::new(conf))), - Box::new(move |_| Box::new(wildcard_imports::WildcardImports::new(conf))), - Box::new(|_| Box::::default()), - Box::new(|_| Box::>::default()), - Box::new(|_| Box::new(option_if_let_else::OptionIfLetElse)), - Box::new(|_| Box::new(future_not_send::FutureNotSend)), - Box::new(move |_| Box::new(large_futures::LargeFuture::new(conf))), - Box::new(|_| Box::new(if_let_mutex::IfLetMutex)), - Box::new(|_| Box::new(if_not_else::IfNotElse)), - Box::new(|_| Box::new(equatable_if_let::PatternEquality)), - Box::new(|_| Box::new(manual_async_fn::ManualAsyncFn)), - Box::new(|_| Box::new(panic_in_result_fn::PanicInResultFn)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)), - Box::new(|_| Box::new(async_yields_async::AsyncYieldsAsync)), - { - let attrs = attr_storage.clone(); - Box::new(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone()))) - }, - Box::new(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf))), - Box::new(|_| Box::new(empty_drop::EmptyDrop)), - Box::new(|_| Box::new(strings::StrToString)), - Box::new(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(redundant_slicing::RedundantSlicing)), - Box::new(|_| Box::new(from_str_radix_10::FromStrRadix10)), - Box::new(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))), - Box::new(|_| Box::new(bool_assert_comparison::BoolAssertComparison)), - Box::new(|_| Box::::default()), - Box::new(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))), - Box::new(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))), - Box::new(move |_| Box::new(strlen_on_c_strings::StrlenOnCStrings::new(conf))), - Box::new(move |_| Box::new(self_named_constructors::SelfNamedConstructors)), - Box::new(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)), - Box::new(move |_| Box::new(manual_assert::ManualAssert)), - Box::new(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf))), - Box::new(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf))), - { - let format_args = format_args_storage.clone(); - Box::new(move |tcx| Box::new(format_args::FormatArgs::new(tcx, conf, format_args.clone()))) - }, - Box::new(|_| Box::new(trailing_empty_array::TrailingEmptyArray)), - Box::new(|_| Box::new(needless_late_init::NeedlessLateInit)), - Box::new(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)), - Box::new(|_| Box::new(init_numbered_fields::NumberedFields)), - Box::new(move |_| Box::new(manual_bits::ManualBits::new(conf))), - Box::new(|_| Box::new(default_union_representation::DefaultUnionRepresentation)), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(dbg_macro::DbgMacro::new(conf))), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(write::Write::new(conf, format_args.clone()))) - }, - Box::new(move |_| Box::new(cargo::Cargo::new(conf))), - Box::new(|_| Box::new(empty_with_brackets::EmptyWithBrackets::default())), - Box::new(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)), - { - let format_args = format_args_storage.clone(); - Box::new(move |_| Box::new(format_push_string::FormatPushString::new(format_args.clone()))) - }, - Box::new(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf))), - Box::new(|_| Box::new(strings::TrimSplitWhitespace)), - Box::new(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)), - Box::new(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)), - Box::new(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)), - Box::new(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)), - Box::new(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)), - Box::new(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(conf))), - Box::new(move |_| Box::new(manual_retain::ManualRetain::new(conf))), - Box::new(move |_| Box::new(manual_rotate::ManualRotate)), - Box::new(move |_| Box::new(operators::Operators::new(conf))), - Box::new(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))), - Box::new(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf))), - Box::new(|_| Box::new(partialeq_to_none::PartialeqToNone)), - Box::new(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))), - Box::new(move |_| Box::new(manual_clamp::ManualClamp::new(conf))), - Box::new(|_| Box::new(manual_string_new::ManualStringNew)), - Box::new(|_| Box::new(unused_peekable::UnusedPeekable)), - Box::new(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)), - Box::new(|_| Box::new(box_default::BoxDefault)), - Box::new(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)), - Box::new(|_| Box::new(missing_trait_methods::MissingTraitMethods)), - Box::new(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)), - Box::new(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)), - Box::new(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(conf))), - Box::new(move |_| Box::new(semicolon_block::SemicolonBlock::new(conf))), - Box::new(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)), - Box::new(|_| Box::new(size_of_ref::SizeOfRef)), - Box::new(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)), - Box::new(move |_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf))), - Box::new(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)), - Box::new(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)), - Box::new(|_| Box::new(missing_assert_message::MissingAssertMessage)), - Box::new(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)), - Box::new(|_| Box::new(redundant_async_block::RedundantAsyncBlock)), - Box::new(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))), - Box::new(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)), - Box::new(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))), - Box::new(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)), - Box::new(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))), - Box::new(|_| Box::new(items_after_test_module::ItemsAfterTestModule)), - Box::new(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)), - Box::new(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug)), - Box::new(|_| Box::new(endian_bytes::EndianBytes)), - Box::new(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations)), - Box::new(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync)), - Box::new(|_| Box::new(needless_ifs::NeedlessIfs)), - Box::new(move |_| Box::new(min_ident_chars::MinIdentChars::new(conf))), - Box::new(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))), - Box::new(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)), - Box::new(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))), - Box::new(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx))), - Box::new(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))), - Box::new(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))), - Box::new(|_| Box::new(manual_range_patterns::ManualRangePatterns)), - Box::new(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf))), - Box::new(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf))), - Box::new(|_| Box::new(four_forward_slashes::FourForwardSlashes)), - Box::new(|_| Box::new(error_impl_error::ErrorImplError)), - Box::new(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf))), - Box::new(|_| Box::new(redundant_locals::RedundantLocals)), - Box::new(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns)), - Box::new(|_| Box::::default()), - Box::new(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)), - Box::new(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)), - Box::new(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)), - Box::new(move |_| { - Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new( - conf, - )) - }), - Box::new(move |_| Box::new(manual_hash_one::ManualHashOne::new(conf))), - Box::new(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)), - Box::new(|_| Box::>::default()), - Box::new(|_| Box::new(iter_over_hash_type::IterOverHashType)), - Box::new(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes)), - Box::new(move |_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity::new(conf))), - Box::new(|_| Box::new(uninhabited_references::UninhabitedReferences)), - Box::new(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions)), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf))), - Box::new(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf))), - Box::new(move |tcx| Box::new(incompatible_msrv::IncompatibleMsrv::new(tcx, conf))), - Box::new(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)), - Box::new(move |_| Box::new(assigning_clones::AssigningClones::new(conf))), - Box::new(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)), - Box::new(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))), - Box::new(move |_| Box::new(string_patterns::StringPatterns::new(conf))), - Box::new(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)), - Box::new(|_| Box::new(zombie_processes::ZombieProcesses)), - Box::new(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)), - Box::new(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf))), - Box::new(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)), - Box::new(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)), - Box::new(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))), - Box::new(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)), - Box::new(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)), - Box::new(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))), - Box::new(|_| Box::new(useless_concat::UselessConcat)), - Box::new(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)), - Box::new(|_| Box::::default()), - Box::new(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))), - Box::new(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))), - Box::new(|_| Box::new(single_option_map::SingleOptionMap)), - Box::new(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)), - Box::new(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))), - Box::new(|_| Box::new(infallible_try_from::InfallibleTryFrom)), - Box::new(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)), - Box::new(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)), - Box::new(|_| Box::new(volatile_composites::VolatileComposites)), - Box::new(|_| Box::::default()), - Box::new(move |tcx| Box::new(disallowed_fields::DisallowedFields::new(tcx, conf))), - Box::new(move |_| Box::new(manual_ilog2::ManualIlog2::new(conf))), - Box::new(|_| Box::new(same_length_and_capacity::SameLengthAndCapacity)), - Box::new(move |tcx| Box::new(duration_suboptimal_units::DurationSuboptimalUnits::new(tcx, conf))), - Box::new(move |_| Box::new(manual_take::ManualTake::new(conf))), - Box::new(|_| Box::new(manual_checked_ops::ManualCheckedOps)), - Box::new(move |tcx| Box::new(manual_pop_if::ManualPopIf::new(tcx, conf))), - Box::new(move |_| Box::new(manual_noop_waker::ManualNoopWaker::new(conf))), - Box::new(|_| Box::new(byte_char_slices::ByteCharSlice)), - Box::new(|_| Box::new(manual_assert_eq::ManualAssertEq)), +// Fold every late pass into one statically-combined struct (see +// `combined_late_pass`); the method list comes from `late_lint_methods!`. +#[rustfmt::skip] +rustc_lint::late_lint_methods!( + crate::combined_late_lint_pass, + [CombinedLateLintPass, (tcx: TyCtxt<'tcx>, conf: &'static Conf, format_args: FormatArgsStorage, attrs: AttrStorage), [ + ArithmeticSideEffects: operators::arithmetic_side_effects::ArithmeticSideEffects = operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf), + DumpHir: utils::dump_hir::DumpHir = utils::dump_hir::DumpHir, + Author: utils::author::Author = utils::author::Author, + AwaitHolding: await_holding_invalid::AwaitHolding = await_holding_invalid::AwaitHolding::new(tcx, conf), + SerdeApi: serde_api::SerdeApi = serde_api::SerdeApi, + Types: types::Types = types::Types::new(conf), + NonminimalBool: booleans::NonminimalBool = booleans::NonminimalBool::new(conf), + UnportableVariant: enum_clike::UnportableVariant = enum_clike::UnportableVariant, + FloatLiteral: float_literal::FloatLiteral = float_literal::FloatLiteral::new(conf), + Ptr: ptr::Ptr = ptr::Ptr, + NeedlessBool: needless_bool::NeedlessBool = needless_bool::NeedlessBool, + BoolComparison: bool_comparison::BoolComparison = bool_comparison::BoolComparison, + NeedlessForEach: needless_for_each::NeedlessForEach = needless_for_each::NeedlessForEach, + LintPass: misc::LintPass = misc::LintPass, + EtaReduction: eta_reduction::EtaReduction = eta_reduction::EtaReduction, + MutMut: mut_mut::MutMut = mut_mut::MutMut::default(), + UnnecessaryMutPassed: unnecessary_mut_passed::UnnecessaryMutPassed = unnecessary_mut_passed::UnnecessaryMutPassed, + SignificantDropTightening: significant_drop_tightening::SignificantDropTightening<'tcx> = >::default(), + LenZero: len_zero::LenZero = len_zero::LenZero::new(conf), + LenWithoutIsEmpty: len_without_is_empty::LenWithoutIsEmpty = len_without_is_empty::LenWithoutIsEmpty, + Attributes: attrs::Attributes = attrs::Attributes::new(conf), + BlocksInConditions: blocks_in_conditions::BlocksInConditions = blocks_in_conditions::BlocksInConditions, + Unicode: unicode::Unicode = unicode::Unicode, + UninitVec: uninit_vec::UninitVec = uninit_vec::UninitVec, + UnitReturnExpectingOrd: unit_return_expecting_ord::UnitReturnExpectingOrd = unit_return_expecting_ord::UnitReturnExpectingOrd, + StringAdd: strings::StringAdd = strings::StringAdd, + ImplicitReturn: implicit_return::ImplicitReturn = implicit_return::ImplicitReturn, + ImplicitSaturatingSub: implicit_saturating_sub::ImplicitSaturatingSub = implicit_saturating_sub::ImplicitSaturatingSub::new(conf), + DefaultNumericFallback: default_numeric_fallback::DefaultNumericFallback = default_numeric_fallback::DefaultNumericFallback, + NonOctalUnixPermissions: non_octal_unix_permissions::NonOctalUnixPermissions = non_octal_unix_permissions::NonOctalUnixPermissions, + ApproxConstant: approx_const::ApproxConstant = approx_const::ApproxConstant::new(conf), + Matches: matches::Matches = matches::Matches::new(conf), + ManualNonExhaustive: manual_non_exhaustive::ManualNonExhaustive = manual_non_exhaustive::ManualNonExhaustive::new(conf), + ManualStrip: manual_strip::ManualStrip = manual_strip::ManualStrip::new(conf), + CheckedConversions: checked_conversions::CheckedConversions = checked_conversions::CheckedConversions::new(conf), + MemReplace: mem_replace::MemReplace = mem_replace::MemReplace::new(conf), + Ranges: ranges::Ranges = ranges::Ranges::new(conf), + FromOverInto: from_over_into::FromOverInto = from_over_into::FromOverInto::new(conf), + UseSelf: use_self::UseSelf = use_self::UseSelf::new(conf), + MissingConstForFn: missing_const_for_fn::MissingConstForFn = missing_const_for_fn::MissingConstForFn::new(conf), + NeedlessQuestionMark: needless_question_mark::NeedlessQuestionMark = needless_question_mark::NeedlessQuestionMark, + Casts: casts::Casts = casts::Casts::new(conf), + SizeOfInElementCount: size_of_in_element_count::SizeOfInElementCount = size_of_in_element_count::SizeOfInElementCount, + SameNameMethod: same_name_method::SameNameMethod = same_name_method::SameNameMethod, + IndexRefutableSlice: index_refutable_slice::IndexRefutableSlice = index_refutable_slice::IndexRefutableSlice::new(conf), + Shadow: shadow::Shadow = ::default(), + InconsistentStructConstructor: inconsistent_struct_constructor::InconsistentStructConstructor = inconsistent_struct_constructor::InconsistentStructConstructor::new( conf, ), + Methods: methods::Methods = methods::Methods::new(conf, format_args.clone()), + UnitTypes: unit_types::UnitTypes = unit_types::UnitTypes::new(format_args.clone()), + Loops: loops::Loops = loops::Loops::new(conf), + MainRecursion: main_recursion::MainRecursion = ::default(), + Lifetimes: lifetimes::Lifetimes = lifetimes::Lifetimes::new(conf), + HashMapPass: entry::HashMapPass = entry::HashMapPass, + MinMaxPass: minmax::MinMaxPass = minmax::MinMaxPass, + ZeroDiv: zero_div_zero::ZeroDiv = zero_div_zero::ZeroDiv, + Mutex: mutex_atomic::Mutex = mutex_atomic::Mutex, + NeedlessUpdate: needless_update::NeedlessUpdate = needless_update::NeedlessUpdate, + NeedlessBorrowedRef: needless_borrowed_ref::NeedlessBorrowedRef = needless_borrowed_ref::NeedlessBorrowedRef, + BorrowDerefRef: borrow_deref_ref::BorrowDerefRef = borrow_deref_ref::BorrowDerefRef, + NoEffect: no_effect::NoEffect = ::default(), + TemporaryAssignment: temporary_assignment::TemporaryAssignment = temporary_assignment::TemporaryAssignment, + Transmute: transmute::Transmute = transmute::Transmute::new(conf), + CognitiveComplexity: cognitive_complexity::CognitiveComplexity = cognitive_complexity::CognitiveComplexity::new(conf), + BoxedLocal: escape::BoxedLocal = escape::BoxedLocal::new(conf), + UselessVec: useless_vec::UselessVec = useless_vec::UselessVec::new(conf), + PanicUnimplemented: panic_unimplemented::PanicUnimplemented = panic_unimplemented::PanicUnimplemented::new(conf), + StringLitAsBytes: strings::StringLitAsBytes = strings::StringLitAsBytes, + Derive: derive::Derive = derive::Derive, + DerivableImpls: derivable_impls::DerivableImpls = derivable_impls::DerivableImpls::new(conf), + DropForgetRef: drop_forget_ref::DropForgetRef = drop_forget_ref::DropForgetRef, + EmptyEnums: empty_enums::EmptyEnums = empty_enums::EmptyEnums, + Regex: regex::Regex = ::default(), + CopyAndPaste: ifs::CopyAndPaste<'tcx> = ifs::CopyAndPaste::new(tcx, conf), + CopyIterator: copy_iterator::CopyIterator = copy_iterator::CopyIterator, + UselessFormat: format::UselessFormat = format::UselessFormat::new(format_args.clone()), + Swap: swap::Swap = swap::Swap, + PanickingOverflowChecks: panicking_overflow_checks::PanickingOverflowChecks = panicking_overflow_checks::PanickingOverflowChecks, + NewWithoutDefault: new_without_default::NewWithoutDefault = ::default(), + DisallowedNames: disallowed_names::DisallowedNames = disallowed_names::DisallowedNames::new(conf), + Functions: functions::Functions = functions::Functions::new(tcx, conf), + Documentation: doc::Documentation = doc::Documentation::new(conf), + NegMultiply: neg_multiply::NegMultiply = neg_multiply::NegMultiply, + LetIfSeq: let_if_seq::LetIfSeq = let_if_seq::LetIfSeq, + EvalOrderDependence: mixed_read_write_in_expression::EvalOrderDependence = mixed_read_write_in_expression::EvalOrderDependence, + MissingDoc: missing_doc::MissingDoc = missing_doc::MissingDoc::new(conf), + MissingInline: missing_inline::MissingInline = missing_inline::MissingInline, + ExhaustiveItems: exhaustive_items::ExhaustiveItems = exhaustive_items::ExhaustiveItems, + UnusedResultOk: unused_result_ok::UnusedResultOk = unused_result_ok::UnusedResultOk, + MatchResultOk: match_result_ok::MatchResultOk = match_result_ok::MatchResultOk, + PartialEqNeImpl: partialeq_ne_impl::PartialEqNeImpl = partialeq_ne_impl::PartialEqNeImpl, + UnusedIoAmount: unused_io_amount::UnusedIoAmount = unused_io_amount::UnusedIoAmount, + LargeEnumVariant: large_enum_variant::LargeEnumVariant = large_enum_variant::LargeEnumVariant::new(conf), + ExplicitWrite: explicit_write::ExplicitWrite = explicit_write::ExplicitWrite::new(format_args.clone()), + NeedlessPassByValue: needless_pass_by_value::NeedlessPassByValue = needless_pass_by_value::NeedlessPassByValue, + PassByRefOrValue: pass_by_ref_or_value::PassByRefOrValue = pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf), + RefOptionRef: ref_option_ref::RefOptionRef = ref_option_ref::RefOptionRef, + InfiniteIter: infinite_iter::InfiniteIter = infinite_iter::InfiniteIter, + InlineFnWithoutBody: inline_fn_without_body::InlineFnWithoutBody = inline_fn_without_body::InlineFnWithoutBody, + UselessConversion: useless_conversion::UselessConversion = ::default(), + ImplicitHasher: implicit_hasher::ImplicitHasher = implicit_hasher::ImplicitHasher, + FallibleImplFrom: fallible_impl_from::FallibleImplFrom = fallible_impl_from::FallibleImplFrom, + QuestionMark: question_mark::QuestionMark = question_mark::QuestionMark::new(conf), + QuestionMarkUsed: question_mark_used::QuestionMarkUsed = question_mark_used::QuestionMarkUsed, + SuspiciousImpl: suspicious_trait_impl::SuspiciousImpl = suspicious_trait_impl::SuspiciousImpl, + MapUnit: map_unit_fn::MapUnit = map_unit_fn::MapUnit, + MultipleInherentImpl: inherent_impl::MultipleInherentImpl = inherent_impl::MultipleInherentImpl::new(conf), + NoNegCompOpForPartialOrd: neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd = neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd, + Unwrap: unwrap::Unwrap = unwrap::Unwrap::new(conf), + IndexingSlicing: indexing_slicing::IndexingSlicing = indexing_slicing::IndexingSlicing::new(conf), + NonCopyConst: non_copy_const::NonCopyConst<'tcx> = non_copy_const::NonCopyConst::new(tcx, conf), + RedundantClone: redundant_clone::RedundantClone = redundant_clone::RedundantClone, + SlowVectorInit: slow_vector_initialization::SlowVectorInit = slow_vector_initialization::SlowVectorInit, + UnnecessaryWraps: unnecessary_wraps::UnnecessaryWraps = unnecessary_wraps::UnnecessaryWraps::new(conf), + AssertionsOnConstants: assertions_on_constants::AssertionsOnConstants = assertions_on_constants::AssertionsOnConstants::new(conf), + AssertionsOnResultStates: assertions_on_result_states::AssertionsOnResultStates = assertions_on_result_states::AssertionsOnResultStates, + InherentToString: inherent_to_string::InherentToString = inherent_to_string::InherentToString, + TraitBounds: trait_bounds::TraitBounds = trait_bounds::TraitBounds::new(conf), + ComparisonChain: comparison_chain::ComparisonChain = comparison_chain::ComparisonChain, + MutableKeyType: mut_key::MutableKeyType<'tcx> = mut_key::MutableKeyType::new(tcx, conf), + DerefAddrOf: reference::DerefAddrOf = reference::DerefAddrOf, + FormatImpl: format_impl::FormatImpl = format_impl::FormatImpl::new(format_args.clone()), + RedundantClosureCall: redundant_closure_call::RedundantClosureCall = redundant_closure_call::RedundantClosureCall, + UnusedUnit: unused_unit::UnusedUnit = unused_unit::UnusedUnit, + Return: returns::Return = returns::Return, + CollapsibleIf: collapsible_if::CollapsibleIf = collapsible_if::CollapsibleIf::new(conf), + ItemsAfterStatements: items_after_statements::ItemsAfterStatements = items_after_statements::ItemsAfterStatements, + NeedlessParensOnRangeLiterals: needless_parens_on_range_literals::NeedlessParensOnRangeLiterals = needless_parens_on_range_literals::NeedlessParensOnRangeLiterals, + NeedlessContinue: needless_continue::NeedlessContinue = needless_continue::NeedlessContinue, + CreateDir: create_dir::CreateDir = create_dir::CreateDir, + ItemNameRepetitions: item_name_repetitions::ItemNameRepetitions = item_name_repetitions::ItemNameRepetitions::new(conf), + UpperCaseAcronyms: upper_case_acronyms::UpperCaseAcronyms = upper_case_acronyms::UpperCaseAcronyms::new(conf), + Default: default::Default = ::default(), + UnusedSelf: unused_self::UnusedSelf = unused_self::UnusedSelf::new(conf), + DebugAssertWithMutCall: mutable_debug_assertion::DebugAssertWithMutCall = mutable_debug_assertion::DebugAssertWithMutCall, + Exit: exit::Exit = exit::Exit, + ToDigitIsSome: to_digit_is_some::ToDigitIsSome = to_digit_is_some::ToDigitIsSome::new(conf), + LargeStackArrays: large_stack_arrays::LargeStackArrays = large_stack_arrays::LargeStackArrays::new(conf), + LargeConstArrays: large_const_arrays::LargeConstArrays = large_const_arrays::LargeConstArrays::new(conf), + FloatingPointArithmetic: floating_point_arithmetic::FloatingPointArithmetic = floating_point_arithmetic::FloatingPointArithmetic, + AsConversions: as_conversions::AsConversions = as_conversions::AsConversions, + LetUnderscore: let_underscore::LetUnderscore = let_underscore::LetUnderscore, + ExcessiveBools: excessive_bools::ExcessiveBools = excessive_bools::ExcessiveBools::new(conf), + WildcardImports: wildcard_imports::WildcardImports = wildcard_imports::WildcardImports::new(conf), + RedundantPubCrate: redundant_pub_crate::RedundantPubCrate = ::default(), + Dereferencing: dereference::Dereferencing<'tcx> = >::default(), + OptionIfLetElse: option_if_let_else::OptionIfLetElse = option_if_let_else::OptionIfLetElse, + FutureNotSend: future_not_send::FutureNotSend = future_not_send::FutureNotSend, + LargeFuture: large_futures::LargeFuture = large_futures::LargeFuture::new(conf), + IfLetMutex: if_let_mutex::IfLetMutex = if_let_mutex::IfLetMutex, + IfNotElse: if_not_else::IfNotElse = if_not_else::IfNotElse, + PatternEquality: equatable_if_let::PatternEquality = equatable_if_let::PatternEquality, + ManualAsyncFn: manual_async_fn::ManualAsyncFn = manual_async_fn::ManualAsyncFn, + PanicInResultFn: panic_in_result_fn::PanicInResultFn = panic_in_result_fn::PanicInResultFn, + MacroUseImports: macro_use::MacroUseImports = ::default(), + PatternTypeMismatch: pattern_type_mismatch::PatternTypeMismatch = pattern_type_mismatch::PatternTypeMismatch, + UnwrapInResult: unwrap_in_result::UnwrapInResult = ::default(), + SemicolonIfNothingReturned: semicolon_if_nothing_returned::SemicolonIfNothingReturned = semicolon_if_nothing_returned::SemicolonIfNothingReturned, + AsyncYieldsAsync: async_yields_async::AsyncYieldsAsync = async_yields_async::AsyncYieldsAsync, + DisallowedMacros: disallowed_macros::DisallowedMacros = disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone()), + DisallowedMethods: disallowed_methods::DisallowedMethods = disallowed_methods::DisallowedMethods::new(tcx, conf), + EmptyDrop: empty_drop::EmptyDrop = empty_drop::EmptyDrop, + StrToString: strings::StrToString = strings::StrToString, + ZeroSizedMapValues: zero_sized_map_values::ZeroSizedMapValues = zero_sized_map_values::ZeroSizedMapValues, + VecInitThenPush: vec_init_then_push::VecInitThenPush = ::default(), + RedundantSlicing: redundant_slicing::RedundantSlicing = redundant_slicing::RedundantSlicing, + FromStrRadix10: from_str_radix_10::FromStrRadix10 = from_str_radix_10::FromStrRadix10, + IfThenSomeElseNone: if_then_some_else_none::IfThenSomeElseNone = if_then_some_else_none::IfThenSomeElseNone::new(conf), + BoolAssertComparison: bool_assert_comparison::BoolAssertComparison = bool_assert_comparison::BoolAssertComparison, + UnusedAsync: unused_async::UnusedAsync = ::default(), + DisallowedTypes: disallowed_types::DisallowedTypes = disallowed_types::DisallowedTypes::new(tcx, conf), + ImportRename: missing_enforced_import_rename::ImportRename = missing_enforced_import_rename::ImportRename::new(tcx, conf), + StrlenOnCStrings: strlen_on_c_strings::StrlenOnCStrings = strlen_on_c_strings::StrlenOnCStrings::new(conf), + SelfNamedConstructors: self_named_constructors::SelfNamedConstructors = self_named_constructors::SelfNamedConstructors, + IterNotReturningIterator: iter_not_returning_iterator::IterNotReturningIterator = iter_not_returning_iterator::IterNotReturningIterator, + ManualAssert: manual_assert::ManualAssert = manual_assert::ManualAssert, + NonSendFieldInSendTy: non_send_fields_in_send_ty::NonSendFieldInSendTy = non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf), + UndocumentedUnsafeBlocks: undocumented_unsafe_blocks::UndocumentedUnsafeBlocks = undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf), + FormatArgs: format_args::FormatArgs<'tcx> = format_args::FormatArgs::new(tcx, conf, format_args.clone()), + TrailingEmptyArray: trailing_empty_array::TrailingEmptyArray = trailing_empty_array::TrailingEmptyArray, + NeedlessLateInit: needless_late_init::NeedlessLateInit = needless_late_init::NeedlessLateInit, + ReturnSelfNotMustUse: return_self_not_must_use::ReturnSelfNotMustUse = return_self_not_must_use::ReturnSelfNotMustUse, + NumberedFields: init_numbered_fields::NumberedFields = init_numbered_fields::NumberedFields, + ManualBits: manual_bits::ManualBits = manual_bits::ManualBits::new(conf), + DefaultUnionRepresentation: default_union_representation::DefaultUnionRepresentation = default_union_representation::DefaultUnionRepresentation, + OnlyUsedInRecursion: only_used_in_recursion::OnlyUsedInRecursion = ::default(), + DbgMacro: dbg_macro::DbgMacro = dbg_macro::DbgMacro::new(conf), + Write: write::Write = write::Write::new(conf, format_args.clone()), + Cargo: cargo::Cargo = cargo::Cargo::new(conf), + EmptyWithBrackets: empty_with_brackets::EmptyWithBrackets = empty_with_brackets::EmptyWithBrackets::default(), + UnnecessaryOwnedEmptyStrings: unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings = unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings, + FormatPushString: format_push_string::FormatPushString = format_push_string::FormatPushString::new(format_args.clone()), + LargeIncludeFile: large_include_file::LargeIncludeFile = large_include_file::LargeIncludeFile::new(conf), + TrimSplitWhitespace: strings::TrimSplitWhitespace = strings::TrimSplitWhitespace, + RcCloneInVecInit: rc_clone_in_vec_init::RcCloneInVecInit = rc_clone_in_vec_init::RcCloneInVecInit, + SwapPtrToRef: swap_ptr_to_ref::SwapPtrToRef = swap_ptr_to_ref::SwapPtrToRef, + TypeParamMismatch: mismatching_type_param_order::TypeParamMismatch = mismatching_type_param_order::TypeParamMismatch, + ReadZeroByteVec: read_zero_byte_vec::ReadZeroByteVec = read_zero_byte_vec::ReadZeroByteVec, + DefaultIterEmpty: default_instead_of_iter_empty::DefaultIterEmpty = default_instead_of_iter_empty::DefaultIterEmpty, + ManualRemEuclid: manual_rem_euclid::ManualRemEuclid = manual_rem_euclid::ManualRemEuclid::new(conf), + ManualRetain: manual_retain::ManualRetain = manual_retain::ManualRetain::new(conf), + ManualRotate: manual_rotate::ManualRotate = manual_rotate::ManualRotate, + Operators: operators::Operators = operators::Operators::new(conf), + StdReexports: std_instead_of_core::StdReexports = std_instead_of_core::StdReexports::new(conf), + UncheckedTimeSubtraction: time_subtraction::UncheckedTimeSubtraction = time_subtraction::UncheckedTimeSubtraction::new(conf), + PartialeqToNone: partialeq_to_none::PartialeqToNone = partialeq_to_none::PartialeqToNone, + ManualAbsDiff: manual_abs_diff::ManualAbsDiff = manual_abs_diff::ManualAbsDiff::new(conf), + ManualClamp: manual_clamp::ManualClamp = manual_clamp::ManualClamp::new(conf), + ManualStringNew: manual_string_new::ManualStringNew = manual_string_new::ManualStringNew, + UnusedPeekable: unused_peekable::UnusedPeekable = unused_peekable::UnusedPeekable, + BoolToIntWithIf: bool_to_int_with_if::BoolToIntWithIf = bool_to_int_with_if::BoolToIntWithIf, + BoxDefault: box_default::BoxDefault = box_default::BoxDefault, + ImplicitSaturatingAdd: implicit_saturating_add::ImplicitSaturatingAdd = implicit_saturating_add::ImplicitSaturatingAdd, + MissingTraitMethods: missing_trait_methods::MissingTraitMethods = missing_trait_methods::MissingTraitMethods, + FromRawWithVoidPtr: from_raw_with_void_ptr::FromRawWithVoidPtr = from_raw_with_void_ptr::FromRawWithVoidPtr, + ConfusingXorAndPow: suspicious_xor_used_as_pow::ConfusingXorAndPow = suspicious_xor_used_as_pow::ConfusingXorAndPow, + ManualIsAsciiCheck: manual_is_ascii_check::ManualIsAsciiCheck = manual_is_ascii_check::ManualIsAsciiCheck::new(conf), + SemicolonBlock: semicolon_block::SemicolonBlock = semicolon_block::SemicolonBlock::new(conf), + PermissionsSetReadonlyFalse: permissions_set_readonly_false::PermissionsSetReadonlyFalse = permissions_set_readonly_false::PermissionsSetReadonlyFalse, + SizeOfRef: size_of_ref::SizeOfRef = size_of_ref::SizeOfRef, + MultipleUnsafeOpsPerBlock: multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock = multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock, + ExtraUnusedTypeParameters: extra_unused_type_parameters::ExtraUnusedTypeParameters = extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf), + NoMangleWithRustAbi: no_mangle_with_rust_abi::NoMangleWithRustAbi = no_mangle_with_rust_abi::NoMangleWithRustAbi, + CollectionIsNeverRead: collection_is_never_read::CollectionIsNeverRead = collection_is_never_read::CollectionIsNeverRead, + MissingAssertMessage: missing_assert_message::MissingAssertMessage = missing_assert_message::MissingAssertMessage, + NeedlessMaybeSized: needless_maybe_sized::NeedlessMaybeSized = needless_maybe_sized::NeedlessMaybeSized, + RedundantAsyncBlock: redundant_async_block::RedundantAsyncBlock = redundant_async_block::RedundantAsyncBlock, + ManualMainSeparatorStr: manual_main_separator_str::ManualMainSeparatorStr = manual_main_separator_str::ManualMainSeparatorStr::new(conf), + UnnecessaryStruct: unnecessary_struct_initialization::UnnecessaryStruct = unnecessary_struct_initialization::UnnecessaryStruct, + UnnecessaryBoxReturns: unnecessary_box_returns::UnnecessaryBoxReturns = unnecessary_box_returns::UnnecessaryBoxReturns::new(conf), + TestsOutsideTestModule: tests_outside_test_module::TestsOutsideTestModule = tests_outside_test_module::TestsOutsideTestModule, + ManualSliceSizeCalculation: manual_slice_size_calculation::ManualSliceSizeCalculation = manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf), + ItemsAfterTestModule: items_after_test_module::ItemsAfterTestModule = items_after_test_module::ItemsAfterTestModule, + DefaultConstructedUnitStructs: default_constructed_unit_structs::DefaultConstructedUnitStructs = default_constructed_unit_structs::DefaultConstructedUnitStructs, + MissingFieldsInDebug: missing_fields_in_debug::MissingFieldsInDebug = missing_fields_in_debug::MissingFieldsInDebug, + EndianBytes: endian_bytes::EndianBytes = endian_bytes::EndianBytes, + RedundantTypeAnnotations: redundant_type_annotations::RedundantTypeAnnotations = redundant_type_annotations::RedundantTypeAnnotations, + ArcWithNonSendSync: arc_with_non_send_sync::ArcWithNonSendSync = arc_with_non_send_sync::ArcWithNonSendSync, + NeedlessIfs: needless_ifs::NeedlessIfs = needless_ifs::NeedlessIfs, + MinIdentChars: min_ident_chars::MinIdentChars = min_ident_chars::MinIdentChars::new(conf), + LargeStackFrames: large_stack_frames::LargeStackFrames = large_stack_frames::LargeStackFrames::new(conf), + SingleRangeInVecInit: single_range_in_vec_init::SingleRangeInVecInit = single_range_in_vec_init::SingleRangeInVecInit, + NeedlessPassByRefMut: needless_pass_by_ref_mut::NeedlessPassByRefMut<'tcx> = needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf), + NonCanonicalImpls: non_canonical_impls::NonCanonicalImpls = non_canonical_impls::NonCanonicalImpls::new(tcx), + SingleCallFn: single_call_fn::SingleCallFn = single_call_fn::SingleCallFn::new(conf), + LegacyNumericConstants: legacy_numeric_constants::LegacyNumericConstants = legacy_numeric_constants::LegacyNumericConstants::new(conf), + ManualRangePatterns: manual_range_patterns::ManualRangePatterns = manual_range_patterns::ManualRangePatterns, + TupleArrayConversions: tuple_array_conversions::TupleArrayConversions = tuple_array_conversions::TupleArrayConversions::new(conf), + ManualFloatMethods: manual_float_methods::ManualFloatMethods = manual_float_methods::ManualFloatMethods::new(conf), + FourForwardSlashes: four_forward_slashes::FourForwardSlashes = four_forward_slashes::FourForwardSlashes, + ErrorImplError: error_impl_error::ErrorImplError = error_impl_error::ErrorImplError, + AbsolutePaths: absolute_paths::AbsolutePaths = absolute_paths::AbsolutePaths::new(conf), + RedundantLocals: redundant_locals::RedundantLocals = redundant_locals::RedundantLocals, + IgnoredUnitPatterns: ignored_unit_patterns::IgnoredUnitPatterns = ignored_unit_patterns::IgnoredUnitPatterns, + ReserveAfterInitialization: reserve_after_initialization::ReserveAfterInitialization = ::default(), + ImpliedBoundsInImpls: implied_bounds_in_impls::ImpliedBoundsInImpls = implied_bounds_in_impls::ImpliedBoundsInImpls, + MissingAssertsForIndexing: missing_asserts_for_indexing::MissingAssertsForIndexing = missing_asserts_for_indexing::MissingAssertsForIndexing, + UnnecessaryMapOnConstructor: unnecessary_map_on_constructor::UnnecessaryMapOnConstructor = unnecessary_map_on_constructor::UnnecessaryMapOnConstructor, + NeedlessBorrowsForGenericArgs: needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs<'tcx> = needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new( conf, ), + ManualHashOne: manual_hash_one::ManualHashOne = manual_hash_one::ManualHashOne::new(conf), + IterWithoutIntoIter: iter_without_into_iter::IterWithoutIntoIter = iter_without_into_iter::IterWithoutIntoIter, + PathbufThenPush: pathbuf_init_then_push::PathbufThenPush<'tcx> = >::default(), + IterOverHashType: iter_over_hash_type::IterOverHashType = iter_over_hash_type::IterOverHashType, + ImplHashWithBorrowStrBytes: impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes = impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes, + RepeatVecWithCapacity: repeat_vec_with_capacity::RepeatVecWithCapacity = repeat_vec_with_capacity::RepeatVecWithCapacity::new(conf), + UninhabitedReferences: uninhabited_references::UninhabitedReferences = uninhabited_references::UninhabitedReferences, + IneffectiveOpenOptions: ineffective_open_options::IneffectiveOpenOptions = ineffective_open_options::IneffectiveOpenOptions, + UnconditionalRecursion: unconditional_recursion::UnconditionalRecursion = ::default(), + PubUnderscoreFields: pub_underscore_fields::PubUnderscoreFields = pub_underscore_fields::PubUnderscoreFields::new(conf), + MissingConstForThreadLocal: missing_const_for_thread_local::MissingConstForThreadLocal = missing_const_for_thread_local::MissingConstForThreadLocal::new(conf), + IncompatibleMsrv: incompatible_msrv::IncompatibleMsrv = incompatible_msrv::IncompatibleMsrv::new(tcx, conf), + ToStringTraitImpl: to_string_trait_impl::ToStringTraitImpl = to_string_trait_impl::ToStringTraitImpl, + AssigningClones: assigning_clones::AssigningClones = assigning_clones::AssigningClones::new(conf), + ZeroRepeatSideEffects: zero_repeat_side_effects::ZeroRepeatSideEffects = zero_repeat_side_effects::ZeroRepeatSideEffects, + ExprMetavarsInUnsafe: macro_metavars_in_unsafe::ExprMetavarsInUnsafe = macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf), + StringPatterns: string_patterns::StringPatterns = string_patterns::StringPatterns::new(conf), + SetContainsOrInsert: set_contains_or_insert::SetContainsOrInsert = set_contains_or_insert::SetContainsOrInsert, + ZombieProcesses: zombie_processes::ZombieProcesses = zombie_processes::ZombieProcesses, + PointersInNomemAsmBlock: pointers_in_nomem_asm_block::PointersInNomemAsmBlock = pointers_in_nomem_asm_block::PointersInNomemAsmBlock, + ManualIsPowerOfTwo: manual_is_power_of_two::ManualIsPowerOfTwo = manual_is_power_of_two::ManualIsPowerOfTwo::new(conf), + NonZeroSuggestions: non_zero_suggestions::NonZeroSuggestions = non_zero_suggestions::NonZeroSuggestions, + LiteralStringWithFormattingArg: literal_string_with_formatting_args::LiteralStringWithFormattingArg = literal_string_with_formatting_args::LiteralStringWithFormattingArg, + UnusedTraitNames: unused_trait_names::UnusedTraitNames = unused_trait_names::UnusedTraitNames::new(conf), + ManualIgnoreCaseCmp: manual_ignore_case_cmp::ManualIgnoreCaseCmp = manual_ignore_case_cmp::ManualIgnoreCaseCmp, + UnnecessaryLiteralBound: unnecessary_literal_bound::UnnecessaryLiteralBound = unnecessary_literal_bound::UnnecessaryLiteralBound, + ArbitrarySourceItemOrdering: arbitrary_source_item_ordering::ArbitrarySourceItemOrdering = arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf), + UselessConcat: useless_concat::UselessConcat = useless_concat::UselessConcat, + UnneededStructPattern: unneeded_struct_pattern::UnneededStructPattern = unneeded_struct_pattern::UnneededStructPattern, + UnnecessarySemicolon: unnecessary_semicolon::UnnecessarySemicolon = ::default(), + NonStdLazyStatic: non_std_lazy_statics::NonStdLazyStatic = non_std_lazy_statics::NonStdLazyStatic::new(conf), + ManualOptionAsSlice: manual_option_as_slice::ManualOptionAsSlice = manual_option_as_slice::ManualOptionAsSlice::new(conf), + SingleOptionMap: single_option_map::SingleOptionMap = single_option_map::SingleOptionMap, + RedundantTestPrefix: redundant_test_prefix::RedundantTestPrefix = redundant_test_prefix::RedundantTestPrefix, + ClonedRefToSliceRefs: cloned_ref_to_slice_refs::ClonedRefToSliceRefs<'tcx> = cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf), + InfallibleTryFrom: infallible_try_from::InfallibleTryFrom = infallible_try_from::InfallibleTryFrom, + CoerceContainerToAny: coerce_container_to_any::CoerceContainerToAny = coerce_container_to_any::CoerceContainerToAny, + ToplevelRefArg: toplevel_ref_arg::ToplevelRefArg = toplevel_ref_arg::ToplevelRefArg, + VolatileComposites: volatile_composites::VolatileComposites = volatile_composites::VolatileComposites, + ReplaceBox: replace_box::ReplaceBox = ::default(), + DisallowedFields: disallowed_fields::DisallowedFields = disallowed_fields::DisallowedFields::new(tcx, conf), + ManualIlog2: manual_ilog2::ManualIlog2 = manual_ilog2::ManualIlog2::new(conf), + SameLengthAndCapacity: same_length_and_capacity::SameLengthAndCapacity = same_length_and_capacity::SameLengthAndCapacity, + DurationSuboptimalUnits: duration_suboptimal_units::DurationSuboptimalUnits = duration_suboptimal_units::DurationSuboptimalUnits::new(tcx, conf), + ManualTake: manual_take::ManualTake = manual_take::ManualTake::new(conf), + ManualCheckedOps: manual_checked_ops::ManualCheckedOps = manual_checked_ops::ManualCheckedOps, + ManualPopIf: manual_pop_if::ManualPopIf = manual_pop_if::ManualPopIf::new(tcx, conf), + ManualNoopWaker: manual_noop_waker::ManualNoopWaker = manual_noop_waker::ManualNoopWaker::new(conf), + ByteCharSlice: byte_char_slices::ByteCharSlice = byte_char_slices::ByteCharSlice, + ManualAssertEq: manual_assert_eq::ManualAssertEq = manual_assert_eq::ManualAssertEq, + WithCapacityZero: with_capacity_zero::WithCapacityZero = with_capacity_zero::WithCapacityZero, // add late passes here, used by `cargo dev new_lint` - ]; - store.late_passes.extend(late_lints); -} + ]] +); diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 8b50ce279b4b1..2b4828872b202 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -9,14 +9,14 @@ use rustc_errors::Applicability; use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - Visitor, VisitorExt, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, - walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_unambig_ty, walk_where_predicate, + Visitor, VisitorExt, walk_fn_decl, walk_generic_args, walk_generic_param, walk_generics, walk_impl_item_ref, + walk_param_bound, walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_unambig_ty, walk_where_predicate, }; use rustc_hir::{ AmbigArg, BodyId, FnDecl, FnPtrTy, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeKind, LifetimeParamKind, Node, - PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereBoundPredicate, WherePredicate, - WherePredicateKind, lang_items, + PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, TraitRef, Ty, TyKind, WhereBoundPredicate, + WherePredicate, WherePredicateKind, lang_items, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter as middle_nested_filter; @@ -160,6 +160,10 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } } + fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly_trait_ref: &'tcx PolyTraitRef<'tcx>) { + report_extra_trait_object_lifetimes(cx, poly_trait_ref.bound_generic_params, &poly_trait_ref.trait_ref); + } + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { if let ImplItemKind::Fn(ref sig, id) = item.kind { let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id).is_none(); @@ -579,9 +583,8 @@ impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> where F: NestedFilter<'tcx>, { - fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'_>) -> LifetimeChecker<'cx, 'tcx, F> { - let map = generics - .params + fn new(cx: &'cx LateContext<'tcx>, generic_params: &'tcx [GenericParam<'_>]) -> LifetimeChecker<'cx, 'tcx, F> { + let map = generic_params .iter() .filter_map(|par| match par.kind { GenericParamKind::Lifetime { @@ -590,6 +593,7 @@ where _ => None, }) .collect(); + Self { cx, map, @@ -703,8 +707,36 @@ fn is_candidate_for_elision(fd: &FnDecl<'_>) -> bool { } } +fn report_extra_trait_object_lifetimes<'tcx>( + cx: &LateContext<'tcx>, + generic_params: &'tcx [GenericParam<'_>], + trait_ref: &'tcx TraitRef<'tcx>, +) { + let mut checker = LifetimeChecker::::new(cx, generic_params); + + for param in generic_params { + walk_generic_param(&mut checker, param); + } + + walk_trait_ref(&mut checker, trait_ref); + + for (def_id, usages) in checker.map { + if usages + .iter() + .all(|usage| usage.in_where_predicate && !usage.in_bounded_ty && !usage.in_generics_arg) + { + span_lint( + cx, + EXTRA_UNUSED_LIFETIMES, + cx.tcx.def_span(def_id), + "this lifetime isn't used in the type", + ); + } + } +} + fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { - let mut checker = LifetimeChecker::::new(cx, generics); + let mut checker = LifetimeChecker::::new(cx, generics.params); walk_generics(&mut checker, generics); walk_fn_decl(&mut checker, func); @@ -725,7 +757,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, } fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) { - let mut checker = LifetimeChecker::::new(cx, impl_.generics); + let mut checker = LifetimeChecker::::new(cx, impl_.generics.params); walk_generics(&mut checker, impl_.generics); if let Some(of_trait) = impl_.of_trait { diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index b813a18b221ea..b10584fb9bd7c 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::Range; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::{EMPTY, Sugg}; -use clippy_utils::{get_enclosing_block, is_integer_const, is_integer_literal_untyped}; +use clippy_utils::{get_enclosing_block, is_integer_literal, is_integer_literal_untyped}; use rustc_ast::{Label, RangeLimits}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr}; @@ -44,7 +44,7 @@ pub(super) fn check<'tcx>( continue; } - let is_zero = is_integer_const(cx, initializer, 0); + let is_zero = is_integer_literal(initializer, 0); let mut applicability = Applicability::MaybeIncorrect; let span = expr.span.with_hi(arg.span.hi()); let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name)); @@ -88,7 +88,7 @@ pub(super) fn check<'tcx>( if pat_snippet == "_" && let Some(range) = Range::hir(cx, arg) && range.limits == RangeLimits::HalfOpen - && range.start.is_some_and(|start| is_integer_const(cx, start, 0)) + && range.start.is_some_and(|start| is_integer_literal(start, 0)) && let Some(end) = range.end { let end = snippet_with_applicability(cx, end.span, "..", &mut applicability); diff --git a/clippy_lints/src/loops/for_unbounded_range.rs b/clippy_lints/src/loops/for_unbounded_range.rs new file mode 100644 index 0000000000000..4691ee02d9539 --- /dev/null +++ b/clippy_lints/src/loops/for_unbounded_range.rs @@ -0,0 +1,42 @@ +use super::FOR_UNBOUNDED_RANGE; +use super::infinite_loop::LoopVisitor; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher; +use rustc_ast::Label; +use rustc_hir::Expr; +use rustc_hir::intravisit::Visitor as _; +use rustc_lint::LateContext; +use rustc_span::Span; + +pub fn check<'tcx>( + cx: &LateContext<'tcx>, + label: Option)` on an `Option` value - --> tests/ui/map_unwrap_or_fixable.rs:66:20 + --> tests/ui/map_unwrap_or_fixable.rs:61:20 | LL | println!("{}", o.map(|y| y + 1).unwrap_or(3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,13 +32,13 @@ LL + println!("{}", o.map_or(3, |y| y + 1)); | error: called `map().unwrap_or_else()` on an `Option` value - --> tests/ui/map_unwrap_or_fixable.rs:68:20 + --> tests/ui/map_unwrap_or_fixable.rs:63:20 | LL | println!("{}", o.map(|y| y + 1).unwrap_or_else(|| 3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `o.map_or_else(|| 3, |y| y + 1)` error: called `map().unwrap_or()` on a `Result` value - --> tests/ui/map_unwrap_or_fixable.rs:70:20 + --> tests/ui/map_unwrap_or_fixable.rs:65:20 | LL | println!("{}", r.map(|y| y + 1).unwrap_or(3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,13 +50,13 @@ LL + println!("{}", r.map_or(3, |y| y + 1)); | error: called `map().unwrap_or_else()` on a `Result` value - --> tests/ui/map_unwrap_or_fixable.rs:72:20 + --> tests/ui/map_unwrap_or_fixable.rs:67:20 | LL | println!("{}", r.map(|y| y + 1).unwrap_or_else(|()| 3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r.map_or_else(|()| 3, |y| y + 1)` error: called `map().unwrap_or(false)` on a `Result` value - --> tests/ui/map_unwrap_or_fixable.rs:75:20 + --> tests/ui/map_unwrap_or_fixable.rs:70:20 | LL | println!("{}", r.map(|y| y == 1).unwrap_or(false)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL + println!("{}", r.is_ok_and(|y| y == 1)); | error: called `map().unwrap_or()` on an `Option` value - --> tests/ui/map_unwrap_or_fixable.rs:81:20 + --> tests/ui/map_unwrap_or_fixable.rs:76:20 | LL | println!("{}", x.map(|y| y + 1).unwrap_or(3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL + println!("{}", x.map_or(3, |y| y + 1)); | error: called `map().unwrap_or()` on a `Result` value - --> tests/ui/map_unwrap_or_fixable.rs:85:20 + --> tests/ui/map_unwrap_or_fixable.rs:80:20 | LL | println!("{}", x.map(|y| y + 1).unwrap_or(3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,13 +92,13 @@ LL + println!("{}", x.map_or(3, |y| y + 1)); | error: called `map().unwrap_or_else()` on an `Option` value - --> tests/ui/map_unwrap_or_fixable.rs:89:20 + --> tests/ui/map_unwrap_or_fixable.rs:84:20 | LL | println!("{}", x.map(|y| y + 1).unwrap_or_else(|| 3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.map_or_else(|| 3, |y| y + 1)` error: called `map().unwrap_or_else()` on a `Result` value - --> tests/ui/map_unwrap_or_fixable.rs:93:20 + --> tests/ui/map_unwrap_or_fixable.rs:88:20 | LL | println!("{}", x.map(|y| y + 1).unwrap_or_else(|_| 3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.map_or_else(|_| 3, |y| y + 1)` diff --git a/tests/ui/map_with_unused_argument_over_ranges.fixed b/tests/ui/map_with_unused_argument_over_ranges.fixed index 97cb727488395..3d4432f8181ac 100644 --- a/tests/ui/map_with_unused_argument_over_ranges.fixed +++ b/tests/ui/map_with_unused_argument_over_ranges.fixed @@ -1,10 +1,5 @@ -#![allow( - unused, - clippy::redundant_closure, - clippy::reversed_empty_ranges, - clippy::identity_op -)] #![warn(clippy::map_with_unused_argument_over_ranges)] +#![allow(clippy::identity_op, clippy::redundant_closure, clippy::reversed_empty_ranges)] fn do_something() -> usize { todo!() diff --git a/tests/ui/map_with_unused_argument_over_ranges.rs b/tests/ui/map_with_unused_argument_over_ranges.rs index f0222f407b8ca..991c4cdbbcc87 100644 --- a/tests/ui/map_with_unused_argument_over_ranges.rs +++ b/tests/ui/map_with_unused_argument_over_ranges.rs @@ -1,10 +1,5 @@ -#![allow( - unused, - clippy::redundant_closure, - clippy::reversed_empty_ranges, - clippy::identity_op -)] #![warn(clippy::map_with_unused_argument_over_ranges)] +#![allow(clippy::identity_op, clippy::redundant_closure, clippy::reversed_empty_ranges)] fn do_something() -> usize { todo!() diff --git a/tests/ui/map_with_unused_argument_over_ranges.stderr b/tests/ui/map_with_unused_argument_over_ranges.stderr index ba72a6b9d89f4..c4bb1a90b1637 100644 --- a/tests/ui/map_with_unused_argument_over_ranges.stderr +++ b/tests/ui/map_with_unused_argument_over_ranges.stderr @@ -1,5 +1,5 @@ error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:25:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:20:5 | LL | (0..10).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + std::iter::repeat_with(|| do_something()).take(10); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:27:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:22:5 | LL | (0..10).map(|_foo| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + std::iter::repeat_with(|| do_something()).take(10); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:29:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:24:5 | LL | (0..=10).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + std::iter::repeat_with(|| do_something()).take(11); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:31:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:26:5 | LL | (3..10).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + std::iter::repeat_with(|| do_something()).take(7); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:33:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:28:5 | LL | (3..=10).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + std::iter::repeat_with(|| do_something()).take(8); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:35:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:30:5 | LL | (0..10).map(|_| 3); | ^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + std::iter::repeat_n(3, 10); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:37:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:32:5 | LL | / (0..10).map(|_| { LL | | @@ -92,7 +92,7 @@ LL ~ }).take(10); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:42:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:37:5 | LL | (0..10).map(|_| do_something()).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +104,7 @@ LL + std::iter::repeat_with(|| do_something()).take(10).collect::>(); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:45:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:40:5 | LL | (0..upper).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -116,7 +116,7 @@ LL + std::iter::repeat_with(|| do_something()).take(upper); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:48:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:43:5 | LL | (0..upper_fn()).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,7 +128,7 @@ LL + std::iter::repeat_with(|| do_something()).take(upper_fn()); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:50:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:45:5 | LL | (0..=upper_fn()).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -140,7 +140,7 @@ LL + std::iter::repeat_with(|| do_something()).take(upper_fn() + 1); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:52:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:47:5 | LL | (2..upper_fn()).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -152,7 +152,7 @@ LL + std::iter::repeat_with(|| do_something()).take(upper_fn() - 2); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:54:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:49:5 | LL | (2..=upper_fn()).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -164,7 +164,7 @@ LL + std::iter::repeat_with(|| do_something()).take(upper_fn() - 1); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:57:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:52:5 | LL | (9..3).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -176,7 +176,7 @@ LL + std::iter::repeat_with(|| do_something()).take(0); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:59:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:54:5 | LL | (9..=9).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -188,7 +188,7 @@ LL + std::iter::repeat_with(|| do_something()).take(1); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:61:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:56:5 | LL | (1..=1 << 4).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -200,7 +200,7 @@ LL + std::iter::repeat_with(|| do_something()).take((1 << 4) - 0); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:83:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:78:5 | LL | (0..10).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -212,7 +212,7 @@ LL + std::iter::repeat_with(|| do_something()).take(10); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:89:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:84:5 | LL | (0..10).map(|_| 3); | ^^^^^^^^^^^^^^^^^^ @@ -224,7 +224,7 @@ LL + std::iter::repeat(3).take(10); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:100:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:95:5 | LL | (0..test!(10)).map(|_| do_something()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -236,7 +236,7 @@ LL + std::iter::repeat_with(|| do_something()).take(test!(10)); | error: map of a closure that does not depend on its parameter over a range - --> tests/ui/map_with_unused_argument_over_ranges.rs:103:5 + --> tests/ui/map_with_unused_argument_over_ranges.rs:98:5 | LL | (0..10).map(|_| test!(3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_as_ref.fixed b/tests/ui/match_as_ref.fixed index b1b8ffb885f52..940d5e3a336a5 100644 --- a/tests/ui/match_as_ref.fixed +++ b/tests/ui/match_as_ref.fixed @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::match_as_ref)] fn match_as_ref() { diff --git a/tests/ui/match_as_ref.rs b/tests/ui/match_as_ref.rs index 3113167957d44..58873a5d1f85c 100644 --- a/tests/ui/match_as_ref.rs +++ b/tests/ui/match_as_ref.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::match_as_ref)] fn match_as_ref() { diff --git a/tests/ui/match_as_ref.stderr b/tests/ui/match_as_ref.stderr index 3eab499fe409b..e041d75ed9683 100644 --- a/tests/ui/match_as_ref.stderr +++ b/tests/ui/match_as_ref.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::as_ref` - --> tests/ui/match_as_ref.rs:6:33 + --> tests/ui/match_as_ref.rs:5:33 | LL | let borrowed: Option<&()> = match owned { | _________________________________^ @@ -22,7 +22,7 @@ LL + let borrowed: Option<&()> = owned.as_ref(); | error: manual implementation of `Option::as_mut` - --> tests/ui/match_as_ref.rs:13:39 + --> tests/ui/match_as_ref.rs:12:39 | LL | let borrow_mut: Option<&mut ()> = match mut_owned { | _______________________________________^ @@ -43,7 +43,7 @@ LL + let borrow_mut: Option<&mut ()> = mut_owned.as_mut(); | error: manual implementation of `Option::as_ref` - --> tests/ui/match_as_ref.rs:32:13 + --> tests/ui/match_as_ref.rs:31:13 | LL | / match self.source { LL | | @@ -63,7 +63,7 @@ LL + self.source.as_ref().map(|x| x as _) | error: manual implementation of `Option::as_ref` - --> tests/ui/match_as_ref.rs:97:13 + --> tests/ui/match_as_ref.rs:96:13 | LL | let _ = match !S { | _____________^ @@ -84,7 +84,7 @@ LL + let _ = (!S).as_ref(); | error: manual implementation of `Option::as_mut` - --> tests/ui/match_as_ref.rs:105:27 + --> tests/ui/match_as_ref.rs:104:27 | LL | let _: Option<&u32> = match Some(0) { | ___________________________^ @@ -106,7 +106,7 @@ LL + let _: Option<&u32> = Some(0).as_ref(); | error: manual implementation of `Option::as_mut` - --> tests/ui/match_as_ref.rs:111:43 + --> tests/ui/match_as_ref.rs:110:43 | LL | let _: Option<&dyn std::fmt::Debug> = match Some(0) { | ___________________________________________^ @@ -128,7 +128,7 @@ LL + let _: Option<&dyn std::fmt::Debug> = Some(0).as_ref().map(|x| x as _); | error: manual implementation of `Option::as_ref` - --> tests/ui/match_as_ref.rs:125:27 + --> tests/ui/match_as_ref.rs:124:27 | LL | let _: Option<&u32> = match test_expr!(42) { | ___________________________^ diff --git a/tests/ui/match_bool.fixed b/tests/ui/match_bool.fixed index 3d5d0a0d532c8..e813c208dc0ab 100644 --- a/tests/ui/match_bool.fixed +++ b/tests/ui/match_bool.fixed @@ -1,5 +1,5 @@ -#![deny(clippy::match_bool)] -#![allow(clippy::nonminimal_bool, clippy::eq_op)] +#![warn(clippy::match_bool)] +#![expect(clippy::eq_op, clippy::nonminimal_bool)] fn match_bool() { let test: bool = true; diff --git a/tests/ui/match_bool.rs b/tests/ui/match_bool.rs index 4db0aedf3260b..92061d0da8242 100644 --- a/tests/ui/match_bool.rs +++ b/tests/ui/match_bool.rs @@ -1,5 +1,5 @@ -#![deny(clippy::match_bool)] -#![allow(clippy::nonminimal_bool, clippy::eq_op)] +#![warn(clippy::match_bool)] +#![expect(clippy::eq_op, clippy::nonminimal_bool)] fn match_bool() { let test: bool = true; diff --git a/tests/ui/match_bool.stderr b/tests/ui/match_bool.stderr index 223acd17aead5..59982dd6b86c5 100644 --- a/tests/ui/match_bool.stderr +++ b/tests/ui/match_bool.stderr @@ -8,11 +8,8 @@ LL | | false => 42, LL | | }; | |_____^ help: consider using an `if`/`else` expression: `if test { 0 } else { 42 }` | -note: the lint level is defined here - --> tests/ui/match_bool.rs:1:9 - | -LL | #![deny(clippy::match_bool)] - | ^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::match-bool` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::match_bool)]` error: `match` on a boolean expression --> tests/ui/match_bool.rs:14:5 diff --git a/tests/ui/match_like_matches_macro.fixed b/tests/ui/match_like_matches_macro.fixed index 045ee32bd8bb8..9ca9005c8bcd7 100644 --- a/tests/ui/match_like_matches_macro.fixed +++ b/tests/ui/match_like_matches_macro.fixed @@ -1,11 +1,6 @@ #![warn(clippy::match_like_matches_macro)] -#![allow( - unreachable_patterns, - irrefutable_let_patterns, - clippy::equatable_if_let, - clippy::needless_borrowed_reference, - clippy::redundant_guards -)] +#![allow(irrefutable_let_patterns, clippy::redundant_guards)] +#![expect(clippy::needless_borrowed_reference)] fn main() { let x = Some(5); diff --git a/tests/ui/match_like_matches_macro.rs b/tests/ui/match_like_matches_macro.rs index 231e1ae98f86a..1d94afac952c2 100644 --- a/tests/ui/match_like_matches_macro.rs +++ b/tests/ui/match_like_matches_macro.rs @@ -1,11 +1,6 @@ #![warn(clippy::match_like_matches_macro)] -#![allow( - unreachable_patterns, - irrefutable_let_patterns, - clippy::equatable_if_let, - clippy::needless_borrowed_reference, - clippy::redundant_guards -)] +#![allow(irrefutable_let_patterns, clippy::redundant_guards)] +#![expect(clippy::needless_borrowed_reference)] fn main() { let x = Some(5); diff --git a/tests/ui/match_like_matches_macro.stderr b/tests/ui/match_like_matches_macro.stderr index bc3e3584938ed..3caa3572e43be 100644 --- a/tests/ui/match_like_matches_macro.stderr +++ b/tests/ui/match_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:14:14 + --> tests/ui/match_like_matches_macro.rs:9:14 | LL | let _y = match x { | ______________^ @@ -19,31 +19,48 @@ LL - }; LL + let _y = matches!(x, Some(0)); | -error: redundant pattern matching, consider using `is_some()` - --> tests/ui/match_like_matches_macro.rs:21:14 +error: redundant pattern matching + --> tests/ui/match_like_matches_macro.rs:16:14 | LL | let _w = match x { | ______________^ LL | | Some(_) => true, LL | | _ => false, LL | | }; - | |_____^ help: try: `x.is_some()` + | |_____^ | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` +help: consider using `is_some()` + | +LL - let _w = match x { +LL - Some(_) => true, +LL - _ => false, +LL - }; +LL + let _w = x.is_some(); + | -error: redundant pattern matching, consider using `is_none()` - --> tests/ui/match_like_matches_macro.rs:28:14 +error: redundant pattern matching + --> tests/ui/match_like_matches_macro.rs:23:14 | LL | let _z = match x { | ______________^ LL | | Some(_) => false, LL | | None => true, LL | | }; - | |_____^ help: try: `x.is_none()` + | |_____^ + | +help: consider using `is_none()` + | +LL - let _z = match x { +LL - Some(_) => false, +LL - None => true, +LL - }; +LL + let _z = x.is_none(); + | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:35:15 + --> tests/ui/match_like_matches_macro.rs:30:15 | LL | let _zz = match x { | _______________^ @@ -62,7 +79,7 @@ LL + let _zz = !matches!(x, Some(r) if r == 0); | error: `if let .. else` expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:42:16 + --> tests/ui/match_like_matches_macro.rs:37:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,7 +91,7 @@ LL + let _zzz = matches!(x, Some(5)); | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:67:20 + --> tests/ui/match_like_matches_macro.rs:62:20 | LL | let _ans = match x { | ____________________^ @@ -95,7 +112,7 @@ LL + let _ans = matches!(x, E::A(_) | E::B(_)); | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:78:20 + --> tests/ui/match_like_matches_macro.rs:73:20 | LL | let _ans = match x { | ____________________^ @@ -119,7 +136,7 @@ LL + let _ans = matches!(x, E::A(_) | E::B(_)); | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:89:20 + --> tests/ui/match_like_matches_macro.rs:84:20 | LL | let _ans = match x { | ____________________^ @@ -140,7 +157,7 @@ LL + let _ans = !matches!(x, E::B(_) | E::C); | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:150:18 + --> tests/ui/match_like_matches_macro.rs:145:18 | LL | let _z = match &z { | __________________^ @@ -159,7 +176,7 @@ LL + let _z = matches!(z, Some(3)); | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:160:18 + --> tests/ui/match_like_matches_macro.rs:155:18 | LL | let _z = match &z { | __________________^ @@ -178,7 +195,7 @@ LL + let _z = matches!(&z, Some(3)); | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:178:21 + --> tests/ui/match_like_matches_macro.rs:173:21 | LL | let _ = match &z { | _____________________^ @@ -197,7 +214,7 @@ LL + let _ = matches!(&z, AnEnum::X); | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:193:20 + --> tests/ui/match_like_matches_macro.rs:188:20 | LL | let _res = match &val { | ____________________^ @@ -216,7 +233,7 @@ LL + let _res = matches!(&val, &Some(ref _a)); | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:206:20 + --> tests/ui/match_like_matches_macro.rs:201:20 | LL | let _res = match &val { | ____________________^ @@ -235,7 +252,7 @@ LL + let _res = matches!(&val, &Some(ref _a)); | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:265:14 + --> tests/ui/match_like_matches_macro.rs:260:14 | LL | let _y = match Some(5) { | ______________^ @@ -254,7 +271,7 @@ LL + let _y = matches!(Some(5), Some(0)); | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:275:13 + --> tests/ui/match_like_matches_macro.rs:270:13 | LL | let _ = match opt { | _____________^ @@ -273,7 +290,7 @@ LL + let _ = matches!(opt, Some(first) if (if let Some(second) = first { tru | error: match expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:296:5 + --> tests/ui/match_like_matches_macro.rs:291:5 | LL | / match typeid!(T) { LL | | _ => true, @@ -291,7 +308,7 @@ LL + matches!(typeid!(T), _); | error: `if let .. else` expression looks like `matches!` macro - --> tests/ui/match_like_matches_macro.rs:302:5 + --> tests/ui/match_like_matches_macro.rs:297:5 | LL | if let _ = typeid!(U) { true } else { false } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 224cac055f251..25df602fe3afd 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -1,6 +1,6 @@ #![warn(clippy::match_overlapping_arm)] -#![allow(clippy::redundant_pattern_matching)] -#![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_ifs)] +#![expect(clippy::redundant_pattern_matching)] +#![allow(clippy::equatable_if_let, clippy::if_same_then_else, clippy::needless_ifs)] fn overlapping() { const FOO: u64 = 2; diff --git a/tests/ui/match_ref_pats.fixed b/tests/ui/match_ref_pats.fixed index f727546838b43..58a580cc099fd 100644 --- a/tests/ui/match_ref_pats.fixed +++ b/tests/ui/match_ref_pats.fixed @@ -1,12 +1,5 @@ #![warn(clippy::match_ref_pats)] -#![allow(dead_code, unused_variables)] -#![allow( - clippy::enum_variant_names, - clippy::equatable_if_let, - clippy::uninlined_format_args, - clippy::empty_loop, - clippy::diverging_sub_expression -)] +#![expect(clippy::diverging_sub_expression, clippy::empty_loop, clippy::enum_variant_names)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.rs b/tests/ui/match_ref_pats.rs index eca4d584acd2b..8b4f7e1febf4f 100644 --- a/tests/ui/match_ref_pats.rs +++ b/tests/ui/match_ref_pats.rs @@ -1,12 +1,5 @@ #![warn(clippy::match_ref_pats)] -#![allow(dead_code, unused_variables)] -#![allow( - clippy::enum_variant_names, - clippy::equatable_if_let, - clippy::uninlined_format_args, - clippy::empty_loop, - clippy::diverging_sub_expression -)] +#![expect(clippy::diverging_sub_expression, clippy::empty_loop, clippy::enum_variant_names)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index ecb08e6972d9a..0cc92391b31f8 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -1,5 +1,5 @@ error: you don't need to add `&` to all patterns - --> tests/ui/match_ref_pats.rs:14:9 + --> tests/ui/match_ref_pats.rs:7:9 | LL | / match v { LL | | @@ -19,7 +19,7 @@ LL ~ None => println!("none"), | error: you don't need to add `&` to both the expression and the patterns - --> tests/ui/match_ref_pats.rs:32:5 + --> tests/ui/match_ref_pats.rs:25:5 | LL | / match &w { LL | | @@ -36,23 +36,34 @@ LL ~ Some(v) => println!("{:?}", v), LL ~ None => println!("none"), | -error: redundant pattern matching, consider using `is_none()` - --> tests/ui/match_ref_pats.rs:45:12 +error: redundant pattern matching + --> tests/ui/match_ref_pats.rs:38:12 | LL | if let &None = a { - | -------^^^^^---- help: try: `if a.is_none()` + | ^^^^^ | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` +help: consider using `is_none()` + | +LL - if let &None = a { +LL + if a.is_none() { + | -error: redundant pattern matching, consider using `is_none()` - --> tests/ui/match_ref_pats.rs:51:12 +error: redundant pattern matching + --> tests/ui/match_ref_pats.rs:44:12 | LL | if let &None = &b { - | -------^^^^^----- help: try: `if b.is_none()` + | ^^^^^ + | +help: consider using `is_none()` + | +LL - if let &None = &b { +LL + if b.is_none() { + | error: you don't need to add `&` to all patterns - --> tests/ui/match_ref_pats.rs:112:9 + --> tests/ui/match_ref_pats.rs:105:9 | LL | / match foobar_variant!(0) { LL | | diff --git a/tests/ui/match_result_ok.fixed b/tests/ui/match_result_ok.fixed index 0e630e6a29045..076a7f5144389 100644 --- a/tests/ui/match_result_ok.fixed +++ b/tests/ui/match_result_ok.fixed @@ -1,11 +1,5 @@ #![warn(clippy::match_result_ok)] -#![allow(dead_code)] -#![allow( - clippy::boxed_local, - clippy::uninlined_format_args, - clippy::manual_unwrap_or_default, - clippy::manual_unwrap_or -)] +#![expect(clippy::boxed_local, clippy::manual_unwrap_or_default)] // Checking `if` cases diff --git a/tests/ui/match_result_ok.rs b/tests/ui/match_result_ok.rs index 63bf6e8ab5087..1d428f0907b61 100644 --- a/tests/ui/match_result_ok.rs +++ b/tests/ui/match_result_ok.rs @@ -1,11 +1,5 @@ #![warn(clippy::match_result_ok)] -#![allow(dead_code)] -#![allow( - clippy::boxed_local, - clippy::uninlined_format_args, - clippy::manual_unwrap_or_default, - clippy::manual_unwrap_or -)] +#![expect(clippy::boxed_local, clippy::manual_unwrap_or_default)] // Checking `if` cases diff --git a/tests/ui/match_result_ok.stderr b/tests/ui/match_result_ok.stderr index 822cc4de77f3d..a419e9931bfd3 100644 --- a/tests/ui/match_result_ok.stderr +++ b/tests/ui/match_result_ok.stderr @@ -1,5 +1,5 @@ error: matching on `Some` with `ok()` is redundant - --> tests/ui/match_result_ok.rs:13:5 + --> tests/ui/match_result_ok.rs:7:5 | LL | if let Some(y) = x.parse().ok() { y } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + if let Ok(y) = x.parse() { y } else { 0 } | error: matching on `Some` with `ok()` is redundant - --> tests/ui/match_result_ok.rs:24:9 + --> tests/ui/match_result_ok.rs:18:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + if let Ok(y) = x . parse() { | error: matching on `Some` with `ok()` is redundant - --> tests/ui/match_result_ok.rs:51:5 + --> tests/ui/match_result_ok.rs:45:5 | LL | while let Some(a) = wat.next().ok() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_same_arms2.fixed b/tests/ui/match_same_arms2.fixed index cb860cef1e688..0b3619ed9d472 100644 --- a/tests/ui/match_same_arms2.fixed +++ b/tests/ui/match_same_arms2.fixed @@ -1,11 +1,6 @@ #![warn(clippy::match_same_arms)] -#![allow( - clippy::disallowed_names, - clippy::diverging_sub_expression, - clippy::uninlined_format_args, - clippy::match_single_binding, - clippy::match_like_matches_macro -)] +#![allow(clippy::match_single_binding)] +#![expect(clippy::disallowed_names, clippy::match_like_matches_macro)] fn bar(_: T) {} fn foo() -> bool { diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index 0fd5d76e7d3e7..a4677c2fc7d2b 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -1,11 +1,6 @@ #![warn(clippy::match_same_arms)] -#![allow( - clippy::disallowed_names, - clippy::diverging_sub_expression, - clippy::uninlined_format_args, - clippy::match_single_binding, - clippy::match_like_matches_macro -)] +#![allow(clippy::match_single_binding)] +#![expect(clippy::disallowed_names, clippy::match_like_matches_macro)] fn bar(_: T) {} fn foo() -> bool { diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index f3031619cce56..135b6279eacc8 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -1,5 +1,5 @@ error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:17:9 + --> tests/ui/match_same_arms2.rs:12:9 | LL | / 42 => { LL | | foo(); @@ -36,7 +36,7 @@ LL - }, | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:39:9 + --> tests/ui/match_same_arms2.rs:34:9 | LL | 42 => foo(), | ^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL ~ 42 | 51 => foo(), | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:46:9 + --> tests/ui/match_same_arms2.rs:41:9 | LL | Some(_) => 24, | ^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL ~ Some(_) | None => 24, | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:69:9 + --> tests/ui/match_same_arms2.rs:64:9 | LL | (Some(a), None) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL ~ (Some(a), None) | (None, Some(a)) => bar(a), | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:84:9 + --> tests/ui/match_same_arms2.rs:79:9 | LL | (Some(a), None) if a == 42 => a, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL ~ (Some(a), None) | (None, Some(a)) if a == 42 => a, | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:91:9 + --> tests/ui/match_same_arms2.rs:86:9 | LL | (Some(a), ..) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -117,7 +117,7 @@ LL ~ _ => (), | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:126:9 + --> tests/ui/match_same_arms2.rs:121:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ LL ~ _ => println!("err"), | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:142:9 + --> tests/ui/match_same_arms2.rs:137:9 | LL | Ok(3) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -150,7 +150,7 @@ LL ~ Ok(3) | Ok(_) => println!("ok"), | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:168:9 + --> tests/ui/match_same_arms2.rs:163:9 | LL | / 0 => { LL | | empty!(0); @@ -170,7 +170,7 @@ LL ~ 0 | 1 => { | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:222:9 + --> tests/ui/match_same_arms2.rs:217:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ @@ -188,7 +188,7 @@ LL ~ _ => 0, | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:231:9 + --> tests/ui/match_same_arms2.rs:226:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ @@ -205,7 +205,7 @@ LL ~ Foo::X(0) | Foo::Z(_) => 1, | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:254:9 + --> tests/ui/match_same_arms2.rs:249:9 | LL | Some(Bar { x: 0, y: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -223,7 +223,7 @@ LL ~ Some(Bar { x: 0, y: 5, .. }) | Some(Bar { y: 0, x: 5, .. }) => 1, | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:271:9 + --> tests/ui/match_same_arms2.rs:266:9 | LL | 0 => cfg!(not_enable), | ^^^^^^^^^^^^^^^^^^^^^ @@ -239,7 +239,7 @@ LL ~ 0 | 1 => cfg!(not_enable), | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:288:17 + --> tests/ui/match_same_arms2.rs:283:17 | LL | MaybeStaticStr::Static(s) => s, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -255,7 +255,7 @@ LL ~ MaybeStaticStr::Static(s) | MaybeStaticStr::Borrowed(s) => | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:306:9 + --> tests/ui/match_same_arms2.rs:301:9 | LL | 1 => "b", | ^^^^^^^^ @@ -272,7 +272,7 @@ LL ~ #[allow(clippy::match_same_arms)] | error: these match arms have identical bodies - --> tests/ui/match_same_arms2.rs:315:9 + --> tests/ui/match_same_arms2.rs:310:9 | LL | 1 => "b", | ^^^^^^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index fa82a316d64d9..f2296c14f9b6e 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -1,11 +1,6 @@ #![warn(clippy::match_single_binding)] -#![allow( - unused, - clippy::let_unit_value, - clippy::no_effect, - clippy::toplevel_ref_arg, - clippy::useless_vec -)] +#![allow(clippy::no_effect, clippy::toplevel_ref_arg)] +#![expect(clippy::let_unit_value, clippy::useless_vec)] struct Point { x: i32, diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 6c1fae89e230d..03eaecff343aa 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -1,11 +1,6 @@ #![warn(clippy::match_single_binding)] -#![allow( - unused, - clippy::let_unit_value, - clippy::no_effect, - clippy::toplevel_ref_arg, - clippy::useless_vec -)] +#![allow(clippy::no_effect, clippy::toplevel_ref_arg)] +#![expect(clippy::let_unit_value, clippy::useless_vec)] struct Point { x: i32, diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 8a402dcca8470..686c8894f688f 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -1,5 +1,5 @@ error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:32:5 + --> tests/ui/match_single_binding.rs:27:5 | LL | / match (a, b, c) { LL | | @@ -20,7 +20,7 @@ LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:39:5 + --> tests/ui/match_single_binding.rs:34:5 | LL | / match (a, b, c) { LL | | @@ -35,7 +35,7 @@ LL + println!("{x} {y} {z}"); | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:57:5 + --> tests/ui/match_single_binding.rs:52:5 | LL | / match a { LL | | @@ -44,7 +44,7 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("whatever");` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:62:5 + --> tests/ui/match_single_binding.rs:57:5 | LL | / match a { LL | | @@ -64,7 +64,7 @@ LL + } | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:70:5 + --> tests/ui/match_single_binding.rs:65:5 | LL | / match a { LL | | @@ -86,7 +86,7 @@ LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:81:5 + --> tests/ui/match_single_binding.rs:76:5 | LL | / match p { LL | | @@ -101,7 +101,7 @@ LL + println!("Coords: ({x}, {y})"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:86:5 + --> tests/ui/match_single_binding.rs:81:5 | LL | / match p { LL | | @@ -116,7 +116,7 @@ LL + println!("Coords: ({x1}, {y1})"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:92:5 + --> tests/ui/match_single_binding.rs:87:5 | LL | / match x { LL | | @@ -131,7 +131,7 @@ LL + println!("Got a reference to {r}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:98:5 + --> tests/ui/match_single_binding.rs:93:5 | LL | / match x { LL | | @@ -146,7 +146,7 @@ LL + println!("Got a mutable reference to {mr}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:103:5 + --> tests/ui/match_single_binding.rs:98:5 | LL | / let product = match coords() { LL | | @@ -161,7 +161,7 @@ LL + let product = x * y; | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:112:18 + --> tests/ui/match_single_binding.rs:107:18 | LL | .map(|i| match i.unwrap() { | __________________^ @@ -179,7 +179,7 @@ LL ~ }) | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:139:5 + --> tests/ui/match_single_binding.rs:134:5 | LL | / match x { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("Not an array index start")` error: this assignment could be simplified - --> tests/ui/match_single_binding.rs:149:5 + --> tests/ui/match_single_binding.rs:144:5 | LL | / val = match val.split_at(idx) { LL | | @@ -210,7 +210,7 @@ LL ~ }; | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:163:16 + --> tests/ui/match_single_binding.rs:158:16 | LL | let _ = || match side_effects() { | ________________^ @@ -228,7 +228,7 @@ LL ~ }; | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:170:5 + --> tests/ui/match_single_binding.rs:165:5 | LL | / match r { LL | | @@ -253,7 +253,7 @@ LL ~ }; | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:184:5 + --> tests/ui/match_single_binding.rs:179:5 | LL | / match 1 { LL | | @@ -262,7 +262,7 @@ LL | | } | |_____^ help: consider using the match body instead: `();` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:189:13 + --> tests/ui/match_single_binding.rs:184:13 | LL | let a = match 1 { | _____________^ @@ -272,7 +272,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:194:5 + --> tests/ui/match_single_binding.rs:189:5 | LL | / match 1 { LL | | @@ -281,7 +281,7 @@ LL | | } | |_____^ help: consider using the match body instead: `side_effects();` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:199:13 + --> tests/ui/match_single_binding.rs:194:13 | LL | let b = match 1 { | _____________^ @@ -291,7 +291,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `side_effects()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:204:5 + --> tests/ui/match_single_binding.rs:199:5 | LL | / match 1 { LL | | @@ -300,7 +300,7 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("1");` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:209:13 + --> tests/ui/match_single_binding.rs:204:13 | LL | let c = match 1 { | _____________^ @@ -310,7 +310,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `println!("1")` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:215:9 + --> tests/ui/match_single_binding.rs:210:9 | LL | / match 1 { LL | | @@ -319,7 +319,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:219:9 + --> tests/ui/match_single_binding.rs:214:9 | LL | / match 1 { LL | | @@ -328,7 +328,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `side_effects()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:223:9 + --> tests/ui/match_single_binding.rs:218:9 | LL | / match 1 { LL | | @@ -337,7 +337,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `println!("1")` error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:238:5 + --> tests/ui/match_single_binding.rs:233:5 | LL | / match dbg!(3) { LL | | _ => println!("here"), @@ -351,7 +351,7 @@ LL + println!("here"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:242:5 + --> tests/ui/match_single_binding.rs:237:5 | LL | / match dbg!(3) { LL | | id!(a) => println!("found {a}"), @@ -365,7 +365,7 @@ LL + println!("found {a}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:246:5 + --> tests/ui/match_single_binding.rs:241:5 | LL | / let id!(_a) = match dbg!(3) { LL | | id!(b) => dbg!(b + 1), @@ -379,7 +379,7 @@ LL + let id!(_a) = dbg!(b + 1); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:254:21 + --> tests/ui/match_single_binding.rs:249:21 | LL | inner: [(); match 1 { | _____________________^ @@ -397,7 +397,7 @@ LL ~ }], | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:262:13 + --> tests/ui/match_single_binding.rs:257:13 | LL | / match 1 { LL | | @@ -412,7 +412,7 @@ LL + 42 | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:273:9 + --> tests/ui/match_single_binding.rs:268:9 | LL | / match (a, b, c) { LL | | @@ -429,7 +429,7 @@ LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:281:9 + --> tests/ui/match_single_binding.rs:276:9 | LL | / match (a, b, c) { LL | | @@ -444,7 +444,7 @@ LL + println!("{x} {y} {z}") | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:289:9 + --> tests/ui/match_single_binding.rs:284:9 | LL | / match (a, b, c) { LL | | @@ -459,7 +459,7 @@ LL + println!("{x} {y} {z}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:299:9 + --> tests/ui/match_single_binding.rs:294:9 | LL | / match (a, b, c) { LL | | @@ -474,7 +474,7 @@ LL + println!("{x} {x} {y}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:309:13 + --> tests/ui/match_single_binding.rs:304:13 | LL | / match (a, b, c) { LL | | @@ -491,7 +491,7 @@ LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:319:13 + --> tests/ui/match_single_binding.rs:314:13 | LL | / match (a, b, c) { LL | | @@ -506,7 +506,7 @@ LL + println!("{x} {y} {z}"); | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:334:12 + --> tests/ui/match_single_binding.rs:329:12 | LL | && match b { | ____________^ @@ -516,7 +516,7 @@ 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:340:12 + --> tests/ui/match_single_binding.rs:335:12 | LL | && match (a, b) { | ____________^ @@ -526,7 +526,7 @@ LL | | } | |_________^ help: consider using the match body instead: `b < c` error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:357:9 + --> tests/ui/match_single_binding.rs:352:9 | LL | / match { a } { LL | | @@ -543,7 +543,7 @@ LL ~ }, | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:366:9 + --> tests/ui/match_single_binding.rs:361:9 | LL | / match { a } { LL | | @@ -560,7 +560,7 @@ LL ~ }, | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:376:9 + --> tests/ui/match_single_binding.rs:371:9 | LL | / match { a } { LL | | @@ -577,7 +577,7 @@ LL ~ }, | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:390:9 + --> tests/ui/match_single_binding.rs:385:9 | LL | / match { a } { LL | | @@ -594,7 +594,7 @@ LL ~ }, | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:397:6 + --> tests/ui/match_single_binding.rs:392:6 | LL | -match { a } { | ______^ @@ -612,7 +612,7 @@ LL ~ }; | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:402:9 + --> tests/ui/match_single_binding.rs:397:9 | LL | _ = match { a } { | _________^ @@ -628,7 +628,7 @@ LL ~ 1; | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:407:16 + --> tests/ui/match_single_binding.rs:402:16 | LL | if let x = match { a } { | ________________^ @@ -646,7 +646,7 @@ LL ~ } {} | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:412:8 + --> tests/ui/match_single_binding.rs:407:8 | LL | if match { a } { | ________^ @@ -664,7 +664,7 @@ LL ~ } { | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:419:15 + --> tests/ui/match_single_binding.rs:414:15 | LL | [1, 2, 3][match { a } { | _______________^ diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed index f00987470ae1b..aad585d55a6da 100644 --- a/tests/ui/match_single_binding2.fixed +++ b/tests/ui/match_single_binding2.fixed @@ -1,5 +1,4 @@ #![warn(clippy::match_single_binding)] -#![allow(unused_variables)] fn main() { // Lint (additional curly braces needed, see #6572) @@ -10,7 +9,6 @@ fn main() { inner: Option<(I, ::Item)>, } - #[allow(dead_code)] fn size_hint(iter: &AppendIter) -> (usize, Option) { match &iter.inner { Some((iter, _item)) => { diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs index 5416f647b4e6c..e0d0dac054585 100644 --- a/tests/ui/match_single_binding2.rs +++ b/tests/ui/match_single_binding2.rs @@ -1,5 +1,4 @@ #![warn(clippy::match_single_binding)] -#![allow(unused_variables)] fn main() { // Lint (additional curly braces needed, see #6572) @@ -10,7 +9,6 @@ fn main() { inner: Option<(I, ::Item)>, } - #[allow(dead_code)] fn size_hint(iter: &AppendIter) -> (usize, Option) { match &iter.inner { Some((iter, _item)) => match iter.size_hint() { diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr index 65b8aa6acd5e1..4479c5f153aef 100644 --- a/tests/ui/match_single_binding2.stderr +++ b/tests/ui/match_single_binding2.stderr @@ -1,5 +1,5 @@ error: this match could be written as a `let` statement - --> tests/ui/match_single_binding2.rs:16:36 + --> tests/ui/match_single_binding2.rs:14:36 | LL | Some((iter, _item)) => match iter.size_hint() { | ____________________________________^ @@ -19,7 +19,7 @@ LL ~ }, | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding2.rs:30:13 + --> tests/ui/match_single_binding2.rs:28:13 | LL | / match get_tup() { LL | | @@ -34,7 +34,7 @@ LL + println!("a {a:?} and b {b:?}") | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding2.rs:42:5 + --> tests/ui/match_single_binding2.rs:40:5 | LL | / match side_effects() { LL | | @@ -49,7 +49,7 @@ LL + println!("Side effects"); | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding2.rs:50:5 + --> tests/ui/match_single_binding2.rs:48:5 | LL | / match match x { LL | | diff --git a/tests/ui/match_str_case_mismatch.fixed b/tests/ui/match_str_case_mismatch.fixed index 5f41f29f8a466..8ab6a33dab490 100644 --- a/tests/ui/match_str_case_mismatch.fixed +++ b/tests/ui/match_str_case_mismatch.fixed @@ -1,5 +1,4 @@ #![warn(clippy::match_str_case_mismatch)] -#![allow(dead_code)] // Valid diff --git a/tests/ui/match_str_case_mismatch.rs b/tests/ui/match_str_case_mismatch.rs index fa5d5a8ebc317..8533404bcea7b 100644 --- a/tests/ui/match_str_case_mismatch.rs +++ b/tests/ui/match_str_case_mismatch.rs @@ -1,5 +1,4 @@ #![warn(clippy::match_str_case_mismatch)] -#![allow(dead_code)] // Valid diff --git a/tests/ui/match_str_case_mismatch.stderr b/tests/ui/match_str_case_mismatch.stderr index c2b58b952aaa5..e1f7ef8de7cd1 100644 --- a/tests/ui/match_str_case_mismatch.stderr +++ b/tests/ui/match_str_case_mismatch.stderr @@ -1,5 +1,5 @@ error: this `match` arm has a differing case than its expression - --> tests/ui/match_str_case_mismatch.rs:112:9 + --> tests/ui/match_str_case_mismatch.rs:111:9 | LL | "Bar" => {}, | ^^^^^ @@ -13,7 +13,7 @@ LL + "bar" => {}, | error: this `match` arm has a differing case than its expression - --> tests/ui/match_str_case_mismatch.rs:123:9 + --> tests/ui/match_str_case_mismatch.rs:122:9 | LL | "~!@#$%^&*()-_=+Foo" => {}, | ^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + "~!@#$%^&*()-_=+foo" => {}, | error: this `match` arm has a differing case than its expression - --> tests/ui/match_str_case_mismatch.rs:136:9 + --> tests/ui/match_str_case_mismatch.rs:135:9 | LL | "Воды" => {}, | ^^^^^^ @@ -37,7 +37,7 @@ LL + "воды" => {}, | error: this `match` arm has a differing case than its expression - --> tests/ui/match_str_case_mismatch.rs:148:9 + --> tests/ui/match_str_case_mismatch.rs:147:9 | LL | "barDz" => {}, | ^^^^^^ @@ -49,7 +49,7 @@ LL + "bardz" => {}, | error: this `match` arm has a differing case than its expression - --> tests/ui/match_str_case_mismatch.rs:159:9 + --> tests/ui/match_str_case_mismatch.rs:158:9 | LL | "bARʁ" => {}, | ^^^^^^ @@ -61,7 +61,7 @@ LL + "BARʁ" => {}, | error: this `match` arm has a differing case than its expression - --> tests/ui/match_str_case_mismatch.rs:170:9 + --> tests/ui/match_str_case_mismatch.rs:169:9 | LL | "Bar" => {}, | ^^^^^ @@ -73,7 +73,7 @@ LL + "bar" => {}, | error: this `match` arm has a differing case than its expression - --> tests/ui/match_str_case_mismatch.rs:186:9 + --> tests/ui/match_str_case_mismatch.rs:185:9 | LL | "bAR" => {}, | ^^^^^ diff --git a/tests/ui/match_wild_err_arm.rs b/tests/ui/match_wild_err_arm.rs index d18d4047e2a64..11c096e2d1e01 100644 --- a/tests/ui/match_wild_err_arm.rs +++ b/tests/ui/match_wild_err_arm.rs @@ -1,5 +1,5 @@ -#![allow(clippy::match_same_arms, dead_code)] #![warn(clippy::match_wild_err_arm)] +#![expect(clippy::match_same_arms)] fn issue_10635() { enum Error { diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index c34f49df6549f..de5cdb14ccc2f 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -1,5 +1,4 @@ #![warn(clippy::match_wildcard_for_single_variants)] -#![allow(dead_code)] enum Foo { A, diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index ecdb7d2fa3af2..23e873482cd89 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -1,5 +1,4 @@ #![warn(clippy::match_wildcard_for_single_variants)] -#![allow(dead_code)] enum Foo { A, diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 3c0cc53b1bf27..c2f47835b578a 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -1,5 +1,5 @@ error: wildcard matches only a single variant and will also match any future added variants - --> tests/ui/match_wildcard_for_single_variants.rs:22:13 + --> tests/ui/match_wildcard_for_single_variants.rs:21:13 | LL | _ => (), | ^ help: try: `Self::Rgb(..)` @@ -8,55 +8,55 @@ LL | _ => (), = help: to override `-D warnings` add `#[allow(clippy::match_wildcard_for_single_variants)]` error: wildcard matches only a single variant and will also match any future added variants - --> tests/ui/match_wildcard_for_single_variants.rs:33:9 + --> tests/ui/match_wildcard_for_single_variants.rs:32:9 | LL | _ => {}, | ^ help: try: `Foo::C` error: wildcard matches only a single variant and will also match any future added variants - --> tests/ui/match_wildcard_for_single_variants.rs:44:9 + --> tests/ui/match_wildcard_for_single_variants.rs:43:9 | LL | _ => {}, | ^ help: try: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants - --> tests/ui/match_wildcard_for_single_variants.rs:53:9 + --> tests/ui/match_wildcard_for_single_variants.rs:52:9 | LL | _ => {}, | ^ help: try: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants - --> tests/ui/match_wildcard_for_single_variants.rs:60:9 + --> tests/ui/match_wildcard_for_single_variants.rs:59:9 | LL | _ => {}, | ^ help: try: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants - --> tests/ui/match_wildcard_for_single_variants.rs:78:9 + --> tests/ui/match_wildcard_for_single_variants.rs:77:9 | LL | &_ => (), | ^^ help: try: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants - --> tests/ui/match_wildcard_for_single_variants.rs:88:9 + --> tests/ui/match_wildcard_for_single_variants.rs:87:9 | LL | _ => (), | ^ help: try: `C::Blue` error: wildcard matches only a single variant and will also match any future added variants - --> tests/ui/match_wildcard_for_single_variants.rs:96:9 + --> tests/ui/match_wildcard_for_single_variants.rs:95:9 | LL | _ => (), | ^ help: try: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants - --> tests/ui/match_wildcard_for_single_variants.rs:132:13 + --> tests/ui/match_wildcard_for_single_variants.rs:131:13 | LL | _ => (), | ^ help: try: `Enum::__Private` error: wildcard matches only a single variant and will also match any future added variants - --> tests/ui/match_wildcard_for_single_variants.rs:160:13 + --> tests/ui/match_wildcard_for_single_variants.rs:159:13 | LL | _ => 2, | ^ help: try: `Foo::B` diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr deleted file mode 100644 index bc374930cf0f2..0000000000000 --- a/tests/ui/mem_replace.stderr +++ /dev/null @@ -1,191 +0,0 @@ -error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:13:13 - | -LL | let _ = mem::replace(&mut an_option, None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` - | - = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_none)]` - -error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:16:13 - | -LL | let _ = mem::replace(an_option, None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:22:13 - | -LL | let _ = std::mem::replace(&mut s, String::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` - | - = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::mem_replace_with_default)]` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:24:13 - | -LL | let _ = std::mem::replace(&mut s, String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:28:13 - | -LL | let _ = std::mem::replace(s, String::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:30:13 - | -LL | let _ = std::mem::replace(s, String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:32:13 - | -LL | let _ = std::mem::replace(s, Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:36:13 - | -LL | let _ = std::mem::replace(&mut v, Vec::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:38:13 - | -LL | let _ = std::mem::replace(&mut v, Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:40:13 - | -LL | let _ = std::mem::replace(&mut v, Vec::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:42:13 - | -LL | let _ = std::mem::replace(&mut v, vec![]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:46:13 - | -LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:50:13 - | -LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:54:13 - | -LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:58:13 - | -LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:62:13 - | -LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:66:13 - | -LL | let _ = std::mem::replace(&mut list, LinkedList::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:70:13 - | -LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:74:13 - | -LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:78:13 - | -LL | let _ = std::mem::replace(&mut refstr, ""); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:82:13 - | -LL | let _ = std::mem::replace(&mut slice, &[]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:115:13 - | -LL | let _ = std::mem::replace(&mut s, String::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` - -error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:146:13 - | -LL | let _ = std::mem::replace(&mut f.0, None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `f.0.take()` - -error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:148:13 - | -LL | let _ = std::mem::replace(&mut *f, None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `(*f).take()` - -error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:150:13 - | -LL | let _ = std::mem::replace(&mut b.opt, None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `b.opt.take()` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:153:13 - | -LL | let _ = std::mem::replace(&mut b.val, String::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut b.val)` - -error: replacing an `Option` with `Some(..)` - --> tests/ui/mem_replace.rs:160:20 - | -LL | let replaced = mem::replace(&mut an_option, Some(1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::replace()` instead: `an_option.replace(1)` - | - = note: `-D clippy::mem-replace-option-with-some` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_some)]` - -error: replacing an `Option` with `Some(..)` - --> tests/ui/mem_replace.rs:164:20 - | -LL | let replaced = mem::replace(an_option, Some(1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::replace()` instead: `an_option.replace(1)` - -error: replacing an `Option` with `Some(..)` - --> tests/ui/mem_replace.rs:169:20 - | -LL | let replaced = mem::replace(if b { &mut opt1 } else { &mut opt2 }, Some(1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::replace()` instead: `(if b { &mut opt1 } else { &mut opt2 }).replace(1)` - -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:181:20 - | -LL | let replaced = std::mem::replace(dbg!(&mut text), String::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(dbg!(&mut text))` - -error: aborting due to 30 previous errors - diff --git a/tests/ui/mem_replace_macro.rs b/tests/ui/mem_replace_macro.rs deleted file mode 100644 index 9458dade3a9c7..0000000000000 --- a/tests/ui/mem_replace_macro.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@aux-build:proc_macros.rs -#![warn(clippy::mem_replace_with_default)] - -extern crate proc_macros; -use proc_macros::{external, inline_macros}; - -#[inline_macros] -fn main() { - let s = &mut String::from("foo"); - let _ = inline!(std::mem::replace($s, Default::default())); - //~^ mem_replace_with_default - let _ = external!(std::mem::replace($s, Default::default())); -} diff --git a/tests/ui/mem_replace_macro.stderr b/tests/ui/mem_replace_macro.stderr deleted file mode 100644 index 0c98200bf04e5..0000000000000 --- a/tests/ui/mem_replace_macro.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace_macro.rs:10:21 - | -LL | let _ = inline!(std::mem::replace($s, Default::default())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::mem_replace_with_default)]` - = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - diff --git a/tests/ui/mem_replace_no_std.fixed b/tests/ui/mem_replace_no_std.fixed deleted file mode 100644 index 4e2d413af6cdf..0000000000000 --- a/tests/ui/mem_replace_no_std.fixed +++ /dev/null @@ -1,84 +0,0 @@ -#![allow(unused, clippy::needless_lifetimes)] -#![warn( - clippy::style, - clippy::mem_replace_option_with_none, - clippy::mem_replace_with_default -)] -#![feature(lang_items)] -#![no_std] - -use core::mem; -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn replace_option_with_none() { - let mut an_option = Some(1); - let _ = an_option.take(); - //~^ mem_replace_option_with_none - let an_option = &mut Some(1); - let _ = an_option.take(); - //~^ mem_replace_option_with_none -} - -fn replace_with_default() { - let mut refstr = "hello"; - let _ = core::mem::take(&mut refstr); - //~^ mem_replace_with_default - - let mut slice: &[i32] = &[1, 2, 3]; - let _ = core::mem::take(&mut slice); - //~^ mem_replace_with_default -} - -// lint is disabled for primitives because in this case `take` -// has no clear benefit over `replace` and sometimes is harder to read -fn dont_lint_primitive() { - let mut pbool = true; - let _ = mem::replace(&mut pbool, false); - - let mut pint = 5; - let _ = mem::replace(&mut pint, 0); -} - -fn main() {} - -fn issue9824() { - struct Foo<'a>(Option<&'a str>); - impl<'a> core::ops::Deref for Foo<'a> { - type Target = Option<&'a str>; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - impl<'a> core::ops::DerefMut for Foo<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - - struct Bar { - opt: Option, - val: u8, - } - - let mut f = Foo(Some("foo")); - let mut b = Bar { opt: Some(1), val: 12 }; - - // replace option with none - let _ = f.0.take(); - //~^ mem_replace_option_with_none - let _ = (*f).take(); - //~^ mem_replace_option_with_none - let _ = b.opt.take(); - //~^ mem_replace_option_with_none - // replace with default - let _ = mem::replace(&mut b.val, u8::default()); -} diff --git a/tests/ui/mem_replace_no_std.rs b/tests/ui/mem_replace_no_std.rs deleted file mode 100644 index c0892304aba86..0000000000000 --- a/tests/ui/mem_replace_no_std.rs +++ /dev/null @@ -1,84 +0,0 @@ -#![allow(unused, clippy::needless_lifetimes)] -#![warn( - clippy::style, - clippy::mem_replace_option_with_none, - clippy::mem_replace_with_default -)] -#![feature(lang_items)] -#![no_std] - -use core::mem; -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn replace_option_with_none() { - let mut an_option = Some(1); - let _ = mem::replace(&mut an_option, None); - //~^ mem_replace_option_with_none - let an_option = &mut Some(1); - let _ = mem::replace(an_option, None); - //~^ mem_replace_option_with_none -} - -fn replace_with_default() { - let mut refstr = "hello"; - let _ = mem::replace(&mut refstr, ""); - //~^ mem_replace_with_default - - let mut slice: &[i32] = &[1, 2, 3]; - let _ = mem::replace(&mut slice, &[]); - //~^ mem_replace_with_default -} - -// lint is disabled for primitives because in this case `take` -// has no clear benefit over `replace` and sometimes is harder to read -fn dont_lint_primitive() { - let mut pbool = true; - let _ = mem::replace(&mut pbool, false); - - let mut pint = 5; - let _ = mem::replace(&mut pint, 0); -} - -fn main() {} - -fn issue9824() { - struct Foo<'a>(Option<&'a str>); - impl<'a> core::ops::Deref for Foo<'a> { - type Target = Option<&'a str>; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - impl<'a> core::ops::DerefMut for Foo<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - - struct Bar { - opt: Option, - val: u8, - } - - let mut f = Foo(Some("foo")); - let mut b = Bar { opt: Some(1), val: 12 }; - - // replace option with none - let _ = mem::replace(&mut f.0, None); - //~^ mem_replace_option_with_none - let _ = mem::replace(&mut *f, None); - //~^ mem_replace_option_with_none - let _ = mem::replace(&mut b.opt, None); - //~^ mem_replace_option_with_none - // replace with default - let _ = mem::replace(&mut b.val, u8::default()); -} diff --git a/tests/ui/mem_replace_no_std.stderr b/tests/ui/mem_replace_no_std.stderr deleted file mode 100644 index 34e81a9f07504..0000000000000 --- a/tests/ui/mem_replace_no_std.stderr +++ /dev/null @@ -1,50 +0,0 @@ -error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:23:13 - | -LL | let _ = mem::replace(&mut an_option, None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` - | - = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_none)]` - -error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:26:13 - | -LL | let _ = mem::replace(an_option, None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` - -error: replacing a value of type `T` with `T::default()` is better expressed using `core::mem::take` - --> tests/ui/mem_replace_no_std.rs:32:13 - | -LL | let _ = mem::replace(&mut refstr, ""); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::mem::take(&mut refstr)` - | - = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::mem_replace_with_default)]` - -error: replacing a value of type `T` with `T::default()` is better expressed using `core::mem::take` - --> tests/ui/mem_replace_no_std.rs:36:13 - | -LL | let _ = mem::replace(&mut slice, &[]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::mem::take(&mut slice)` - -error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:76:13 - | -LL | let _ = mem::replace(&mut f.0, None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `f.0.take()` - -error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:78:13 - | -LL | let _ = mem::replace(&mut *f, None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `(*f).take()` - -error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:80:13 - | -LL | let _ = mem::replace(&mut b.opt, None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `b.opt.take()` - -error: aborting due to 7 previous errors - diff --git a/tests/ui/mem_replace_option_with_none.fixed b/tests/ui/mem_replace_option_with_none.fixed new file mode 100644 index 0000000000000..9041e8595d6dd --- /dev/null +++ b/tests/ui/mem_replace_option_with_none.fixed @@ -0,0 +1,42 @@ +#![warn(clippy::mem_replace_option_with_none)] + +use std::mem; + +fn main() { + let mut an_option = Some(1); + let _ = an_option.take(); + //~^ mem_replace_option_with_none + let an_option = &mut Some(1); + let _ = an_option.take(); + //~^ mem_replace_option_with_none +} + +fn issue9824() { + struct Foo<'a>(Option<&'a str>); + impl<'a> std::ops::Deref for Foo<'a> { + type Target = Option<&'a str>; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl<'a> std::ops::DerefMut for Foo<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + struct Bar { + opt: Option, + } + + let mut f = Foo(Some("foo")); + let mut b = Bar { opt: Some(1) }; + + let _ = f.0.take(); + //~^ mem_replace_option_with_none + let _ = (*f).take(); + //~^ mem_replace_option_with_none + let _ = b.opt.take(); + //~^ mem_replace_option_with_none +} diff --git a/tests/ui/mem_replace_option_with_none.rs b/tests/ui/mem_replace_option_with_none.rs new file mode 100644 index 0000000000000..b51115f02e1b2 --- /dev/null +++ b/tests/ui/mem_replace_option_with_none.rs @@ -0,0 +1,42 @@ +#![warn(clippy::mem_replace_option_with_none)] + +use std::mem; + +fn main() { + let mut an_option = Some(1); + let _ = mem::replace(&mut an_option, None); + //~^ mem_replace_option_with_none + let an_option = &mut Some(1); + let _ = mem::replace(an_option, None); + //~^ mem_replace_option_with_none +} + +fn issue9824() { + struct Foo<'a>(Option<&'a str>); + impl<'a> std::ops::Deref for Foo<'a> { + type Target = Option<&'a str>; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl<'a> std::ops::DerefMut for Foo<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + struct Bar { + opt: Option, + } + + let mut f = Foo(Some("foo")); + let mut b = Bar { opt: Some(1) }; + + let _ = std::mem::replace(&mut f.0, None); + //~^ mem_replace_option_with_none + let _ = std::mem::replace(&mut *f, None); + //~^ mem_replace_option_with_none + let _ = std::mem::replace(&mut b.opt, None); + //~^ mem_replace_option_with_none +} diff --git a/tests/ui/mem_replace_option_with_none.stderr b/tests/ui/mem_replace_option_with_none.stderr new file mode 100644 index 0000000000000..d72aef7195535 --- /dev/null +++ b/tests/ui/mem_replace_option_with_none.stderr @@ -0,0 +1,35 @@ +error: replacing an `Option` with `None` + --> tests/ui/mem_replace_option_with_none.rs:7:13 + | +LL | let _ = mem::replace(&mut an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::take()` instead: `an_option.take()` + | + = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_none)]` + +error: replacing an `Option` with `None` + --> tests/ui/mem_replace_option_with_none.rs:10:13 + | +LL | let _ = mem::replace(an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::take()` instead: `an_option.take()` + +error: replacing an `Option` with `None` + --> tests/ui/mem_replace_option_with_none.rs:36:13 + | +LL | let _ = std::mem::replace(&mut f.0, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::take()` instead: `f.0.take()` + +error: replacing an `Option` with `None` + --> tests/ui/mem_replace_option_with_none.rs:38:13 + | +LL | let _ = std::mem::replace(&mut *f, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::take()` instead: `(*f).take()` + +error: replacing an `Option` with `None` + --> tests/ui/mem_replace_option_with_none.rs:40:13 + | +LL | let _ = std::mem::replace(&mut b.opt, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::take()` instead: `b.opt.take()` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/mem_replace_option_with_none_no_std.fixed b/tests/ui/mem_replace_option_with_none_no_std.fixed new file mode 100644 index 0000000000000..a018547e920b7 --- /dev/null +++ b/tests/ui/mem_replace_option_with_none_no_std.fixed @@ -0,0 +1,13 @@ +#![warn(clippy::mem_replace_option_with_none)] +#![no_std] + +use core::mem; + +fn it_works() { + let mut an_option = Some(1); + let _ = an_option.take(); + //~^ mem_replace_option_with_none + let an_option = &mut Some(1); + let _ = an_option.take(); + //~^ mem_replace_option_with_none +} diff --git a/tests/ui/mem_replace_option_with_none_no_std.rs b/tests/ui/mem_replace_option_with_none_no_std.rs new file mode 100644 index 0000000000000..a2cc68e49d7df --- /dev/null +++ b/tests/ui/mem_replace_option_with_none_no_std.rs @@ -0,0 +1,13 @@ +#![warn(clippy::mem_replace_option_with_none)] +#![no_std] + +use core::mem; + +fn it_works() { + let mut an_option = Some(1); + let _ = mem::replace(&mut an_option, None); + //~^ mem_replace_option_with_none + let an_option = &mut Some(1); + let _ = mem::replace(an_option, None); + //~^ mem_replace_option_with_none +} diff --git a/tests/ui/mem_replace_option_with_none_no_std.stderr b/tests/ui/mem_replace_option_with_none_no_std.stderr new file mode 100644 index 0000000000000..085ef4462d5f1 --- /dev/null +++ b/tests/ui/mem_replace_option_with_none_no_std.stderr @@ -0,0 +1,17 @@ +error: replacing an `Option` with `None` + --> tests/ui/mem_replace_option_with_none_no_std.rs:8:13 + | +LL | let _ = mem::replace(&mut an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::take()` instead: `an_option.take()` + | + = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_none)]` + +error: replacing an `Option` with `None` + --> tests/ui/mem_replace_option_with_none_no_std.rs:11:13 + | +LL | let _ = mem::replace(an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::take()` instead: `an_option.take()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/mem_replace_option_with_some.fixed b/tests/ui/mem_replace_option_with_some.fixed new file mode 100644 index 0000000000000..dc0652ae3645e --- /dev/null +++ b/tests/ui/mem_replace_option_with_some.fixed @@ -0,0 +1,25 @@ +#![warn(clippy::mem_replace_option_with_some)] + +use std::mem; + +#[clippy::msrv = "1.31"] +fn main() { + let mut an_option = Some(0); + let replaced = an_option.replace(1); + //~^ mem_replace_option_with_some + + let mut an_option = &mut Some(0); + let replaced = an_option.replace(1); + //~^ mem_replace_option_with_some + + let (mut opt1, mut opt2) = (Some(0), Some(0)); + let b = true; + let replaced = (if b { &mut opt1 } else { &mut opt2 }).replace(1); + //~^ mem_replace_option_with_some +} + +#[clippy::msrv = "1.30"] +fn bad_msrv() { + let mut an_option = Some(0); + let replaced = mem::replace(&mut an_option, Some(1)); +} diff --git a/tests/ui/mem_replace_option_with_some.rs b/tests/ui/mem_replace_option_with_some.rs new file mode 100644 index 0000000000000..dca2849837952 --- /dev/null +++ b/tests/ui/mem_replace_option_with_some.rs @@ -0,0 +1,25 @@ +#![warn(clippy::mem_replace_option_with_some)] + +use std::mem; + +#[clippy::msrv = "1.31"] +fn main() { + let mut an_option = Some(0); + let replaced = mem::replace(&mut an_option, Some(1)); + //~^ mem_replace_option_with_some + + let mut an_option = &mut Some(0); + let replaced = mem::replace(an_option, Some(1)); + //~^ mem_replace_option_with_some + + let (mut opt1, mut opt2) = (Some(0), Some(0)); + let b = true; + let replaced = mem::replace(if b { &mut opt1 } else { &mut opt2 }, Some(1)); + //~^ mem_replace_option_with_some +} + +#[clippy::msrv = "1.30"] +fn bad_msrv() { + let mut an_option = Some(0); + let replaced = mem::replace(&mut an_option, Some(1)); +} diff --git a/tests/ui/mem_replace_option_with_some.stderr b/tests/ui/mem_replace_option_with_some.stderr new file mode 100644 index 0000000000000..a064084c5face --- /dev/null +++ b/tests/ui/mem_replace_option_with_some.stderr @@ -0,0 +1,23 @@ +error: replacing an `Option` with `Some(..)` + --> tests/ui/mem_replace_option_with_some.rs:8:20 + | +LL | let replaced = mem::replace(&mut an_option, Some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::replace()` instead: `an_option.replace(1)` + | + = note: `-D clippy::mem-replace-option-with-some` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_some)]` + +error: replacing an `Option` with `Some(..)` + --> tests/ui/mem_replace_option_with_some.rs:12:20 + | +LL | let replaced = mem::replace(an_option, Some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::replace()` instead: `an_option.replace(1)` + +error: replacing an `Option` with `Some(..)` + --> tests/ui/mem_replace_option_with_some.rs:17:20 + | +LL | let replaced = mem::replace(if b { &mut opt1 } else { &mut opt2 }, Some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::replace()` instead: `(if b { &mut opt1 } else { &mut opt2 }).replace(1)` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/mem_replace_option_with_some_no_std.fixed b/tests/ui/mem_replace_option_with_some_no_std.fixed new file mode 100644 index 0000000000000..7f882519aa624 --- /dev/null +++ b/tests/ui/mem_replace_option_with_some_no_std.fixed @@ -0,0 +1,26 @@ +#![warn(clippy::mem_replace_option_with_some)] +#![no_std] + +use core::mem; + +#[clippy::msrv = "1.31"] +fn it_works() { + let mut an_option = Some(0); + let replaced = an_option.replace(1); + //~^ mem_replace_option_with_some + + let mut an_option = &mut Some(0); + let replaced = an_option.replace(1); + //~^ mem_replace_option_with_some + + let (mut opt1, mut opt2) = (Some(0), Some(0)); + let b = true; + let replaced = (if b { &mut opt1 } else { &mut opt2 }).replace(1); + //~^ mem_replace_option_with_some +} + +#[clippy::msrv = "1.30"] +fn bad_msrv() { + let mut an_option = Some(0); + let replaced = mem::replace(&mut an_option, Some(1)); +} diff --git a/tests/ui/mem_replace_option_with_some_no_std.rs b/tests/ui/mem_replace_option_with_some_no_std.rs new file mode 100644 index 0000000000000..539c911a7efd9 --- /dev/null +++ b/tests/ui/mem_replace_option_with_some_no_std.rs @@ -0,0 +1,26 @@ +#![warn(clippy::mem_replace_option_with_some)] +#![no_std] + +use core::mem; + +#[clippy::msrv = "1.31"] +fn it_works() { + let mut an_option = Some(0); + let replaced = mem::replace(&mut an_option, Some(1)); + //~^ mem_replace_option_with_some + + let mut an_option = &mut Some(0); + let replaced = mem::replace(an_option, Some(1)); + //~^ mem_replace_option_with_some + + let (mut opt1, mut opt2) = (Some(0), Some(0)); + let b = true; + let replaced = mem::replace(if b { &mut opt1 } else { &mut opt2 }, Some(1)); + //~^ mem_replace_option_with_some +} + +#[clippy::msrv = "1.30"] +fn bad_msrv() { + let mut an_option = Some(0); + let replaced = mem::replace(&mut an_option, Some(1)); +} diff --git a/tests/ui/mem_replace_option_with_some_no_std.stderr b/tests/ui/mem_replace_option_with_some_no_std.stderr new file mode 100644 index 0000000000000..67965f3335b0a --- /dev/null +++ b/tests/ui/mem_replace_option_with_some_no_std.stderr @@ -0,0 +1,23 @@ +error: replacing an `Option` with `Some(..)` + --> tests/ui/mem_replace_option_with_some_no_std.rs:9:20 + | +LL | let replaced = mem::replace(&mut an_option, Some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::replace()` instead: `an_option.replace(1)` + | + = note: `-D clippy::mem-replace-option-with-some` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_some)]` + +error: replacing an `Option` with `Some(..)` + --> tests/ui/mem_replace_option_with_some_no_std.rs:13:20 + | +LL | let replaced = mem::replace(an_option, Some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::replace()` instead: `an_option.replace(1)` + +error: replacing an `Option` with `Some(..)` + --> tests/ui/mem_replace_option_with_some_no_std.rs:18:20 + | +LL | let replaced = mem::replace(if b { &mut opt1 } else { &mut opt2 }, Some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Option::replace()` instead: `(if b { &mut opt1 } else { &mut opt2 }).replace(1)` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace_with_default.fixed similarity index 65% rename from tests/ui/mem_replace.fixed rename to tests/ui/mem_replace_with_default.fixed index 26aa7aa1b2703..af88a473e19ac 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace_with_default.fixed @@ -1,23 +1,13 @@ -#![allow(unused, clippy::needless_lifetimes)] -#![warn( - clippy::style, - clippy::mem_replace_option_with_none, - clippy::mem_replace_with_default -)] +//@aux-build:proc_macros.rs +#![warn(clippy::mem_replace_with_default)] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; use std::mem; -fn replace_option_with_none() { - let mut an_option = Some(1); - let _ = an_option.take(); - //~^ mem_replace_option_with_none - let an_option = &mut Some(1); - let _ = an_option.take(); - //~^ mem_replace_option_with_none -} - -fn replace_with_default() { +fn main() { let mut s = String::from("foo"); let _ = std::mem::take(&mut s); //~^ mem_replace_with_default @@ -83,6 +73,13 @@ fn replace_with_default() { //~^ mem_replace_with_default } +#[inline_macros] +fn macros(s: &mut String) { + let _ = inline!(std::mem::take($s)); + //~^ mem_replace_with_default + let _ = external!(std::mem::replace($s, Default::default())); +} + // lint is disabled for primitives because in this case `take` // has no clear benefit over `replace` and sometimes is harder to read fn dont_lint_primitive() { @@ -101,8 +98,6 @@ fn dont_lint_not_used() { std::mem::replace(&mut s, String::default()); } -fn main() {} - #[clippy::msrv = "1.39"] fn msrv_1_39() { let mut s = String::from("foo"); @@ -117,65 +112,18 @@ fn msrv_1_40() { } fn issue9824() { - struct Foo<'a>(Option<&'a str>); - impl<'a> std::ops::Deref for Foo<'a> { - type Target = Option<&'a str>; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - impl<'a> std::ops::DerefMut for Foo<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - struct Bar { - opt: Option, val: String, } - let mut f = Foo(Some("foo")); let mut b = Bar { - opt: Some(1), val: String::from("bar"), }; - // replace option with none - let _ = f.0.take(); - //~^ mem_replace_option_with_none - let _ = (*f).take(); - //~^ mem_replace_option_with_none - let _ = b.opt.take(); - //~^ mem_replace_option_with_none - // replace with default let _ = std::mem::take(&mut b.val); //~^ mem_replace_with_default } -#[clippy::msrv = "1.31"] -fn mem_replace_option_with_some() { - let mut an_option = Some(0); - let replaced = an_option.replace(1); - //~^ ERROR: replacing an `Option` with `Some(..)` - - let mut an_option = &mut Some(0); - let replaced = an_option.replace(1); - //~^ ERROR: replacing an `Option` with `Some(..)` - - let (mut opt1, mut opt2) = (Some(0), Some(0)); - let b = true; - let replaced = (if b { &mut opt1 } else { &mut opt2 }).replace(1); - //~^ ERROR: replacing an `Option` with `Some(..)` -} - -#[clippy::msrv = "1.30"] -fn mem_replace_option_with_some_bad_msrv() { - let mut an_option = Some(0); - let replaced = mem::replace(&mut an_option, Some(1)); -} - fn issue15785() { let mut text = String::from("foo"); let replaced = std::mem::take(dbg!(&mut text)); diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace_with_default.rs similarity index 66% rename from tests/ui/mem_replace.rs rename to tests/ui/mem_replace_with_default.rs index cd675f5735a19..6642a8340d5fa 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace_with_default.rs @@ -1,23 +1,13 @@ -#![allow(unused, clippy::needless_lifetimes)] -#![warn( - clippy::style, - clippy::mem_replace_option_with_none, - clippy::mem_replace_with_default -)] +//@aux-build:proc_macros.rs +#![warn(clippy::mem_replace_with_default)] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; use std::mem; -fn replace_option_with_none() { - let mut an_option = Some(1); - let _ = mem::replace(&mut an_option, None); - //~^ mem_replace_option_with_none - let an_option = &mut Some(1); - let _ = mem::replace(an_option, None); - //~^ mem_replace_option_with_none -} - -fn replace_with_default() { +fn main() { let mut s = String::from("foo"); let _ = std::mem::replace(&mut s, String::default()); //~^ mem_replace_with_default @@ -83,6 +73,13 @@ fn replace_with_default() { //~^ mem_replace_with_default } +#[inline_macros] +fn macros(s: &mut String) { + let _ = inline!(std::mem::replace($s, Default::default())); + //~^ mem_replace_with_default + let _ = external!(std::mem::replace($s, Default::default())); +} + // lint is disabled for primitives because in this case `take` // has no clear benefit over `replace` and sometimes is harder to read fn dont_lint_primitive() { @@ -101,8 +98,6 @@ fn dont_lint_not_used() { std::mem::replace(&mut s, String::default()); } -fn main() {} - #[clippy::msrv = "1.39"] fn msrv_1_39() { let mut s = String::from("foo"); @@ -117,65 +112,18 @@ fn msrv_1_40() { } fn issue9824() { - struct Foo<'a>(Option<&'a str>); - impl<'a> std::ops::Deref for Foo<'a> { - type Target = Option<&'a str>; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - impl<'a> std::ops::DerefMut for Foo<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - struct Bar { - opt: Option, val: String, } - let mut f = Foo(Some("foo")); let mut b = Bar { - opt: Some(1), val: String::from("bar"), }; - // replace option with none - let _ = std::mem::replace(&mut f.0, None); - //~^ mem_replace_option_with_none - let _ = std::mem::replace(&mut *f, None); - //~^ mem_replace_option_with_none - let _ = std::mem::replace(&mut b.opt, None); - //~^ mem_replace_option_with_none - // replace with default let _ = std::mem::replace(&mut b.val, String::default()); //~^ mem_replace_with_default } -#[clippy::msrv = "1.31"] -fn mem_replace_option_with_some() { - let mut an_option = Some(0); - let replaced = mem::replace(&mut an_option, Some(1)); - //~^ ERROR: replacing an `Option` with `Some(..)` - - let mut an_option = &mut Some(0); - let replaced = mem::replace(an_option, Some(1)); - //~^ ERROR: replacing an `Option` with `Some(..)` - - let (mut opt1, mut opt2) = (Some(0), Some(0)); - let b = true; - let replaced = mem::replace(if b { &mut opt1 } else { &mut opt2 }, Some(1)); - //~^ ERROR: replacing an `Option` with `Some(..)` -} - -#[clippy::msrv = "1.30"] -fn mem_replace_option_with_some_bad_msrv() { - let mut an_option = Some(0); - let replaced = mem::replace(&mut an_option, Some(1)); -} - fn issue15785() { let mut text = String::from("foo"); let replaced = std::mem::replace(dbg!(&mut text), String::default()); diff --git a/tests/ui/mem_replace_with_default.stderr b/tests/ui/mem_replace_with_default.stderr new file mode 100644 index 0000000000000..8690ba4a198a9 --- /dev/null +++ b/tests/ui/mem_replace_with_default.stderr @@ -0,0 +1,145 @@ +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:12:13 + | +LL | let _ = std::mem::replace(&mut s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut s)` + | + = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mem_replace_with_default)]` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:14:13 + | +LL | let _ = std::mem::replace(&mut s, String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut s)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:18:13 + | +LL | let _ = std::mem::replace(s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(s)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:20:13 + | +LL | let _ = std::mem::replace(s, String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(s)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:22:13 + | +LL | let _ = std::mem::replace(s, Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(s)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:26:13 + | +LL | let _ = std::mem::replace(&mut v, Vec::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:28:13 + | +LL | let _ = std::mem::replace(&mut v, Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:30:13 + | +LL | let _ = std::mem::replace(&mut v, Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:32:13 + | +LL | let _ = std::mem::replace(&mut v, vec![]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:36:13 + | +LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut hash_map)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:40:13 + | +LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut btree_map)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:44:13 + | +LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut vd)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:48:13 + | +LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut hash_set)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:52:13 + | +LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut btree_set)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:56:13 + | +LL | let _ = std::mem::replace(&mut list, LinkedList::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut list)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:60:13 + | +LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut binary_heap)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:64:13 + | +LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut tuple)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:68:13 + | +LL | let _ = std::mem::replace(&mut refstr, ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut refstr)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:72:13 + | +LL | let _ = std::mem::replace(&mut slice, &[]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut slice)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:78:21 + | +LL | let _ = inline!(std::mem::replace($s, Default::default())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take($s)` + | + = note: this error originates in the macro `__inline_mac_fn_macros` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:110:13 + | +LL | let _ = std::mem::replace(&mut s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut s)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:123:13 + | +LL | let _ = std::mem::replace(&mut b.val, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(&mut b.val)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default.rs:129:20 + | +LL | let replaced = std::mem::replace(dbg!(&mut text), String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::mem::take` instead: `std::mem::take(dbg!(&mut text))` + +error: aborting due to 23 previous errors + diff --git a/tests/ui/mem_replace_with_default_no_std.fixed b/tests/ui/mem_replace_with_default_no_std.fixed new file mode 100644 index 0000000000000..0486bf1a173f5 --- /dev/null +++ b/tests/ui/mem_replace_with_default_no_std.fixed @@ -0,0 +1,25 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::mem_replace_with_default)] +#![no_std] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; + +use core::mem; + +fn it_works() { + let mut refstr = "hello"; + let _ = core::mem::take(&mut refstr); + //~^ mem_replace_with_default + + let mut slice: &[i32] = &[1, 2, 3]; + let _ = core::mem::take(&mut slice); + //~^ mem_replace_with_default +} + +#[inline_macros] +fn macros(mut refstr: &str) { + let _ = inline!(core::mem::take(&mut $refstr)); + //~^ mem_replace_with_default + let _ = external!(mem::replace(&mut $refstr, "")); +} diff --git a/tests/ui/mem_replace_with_default_no_std.rs b/tests/ui/mem_replace_with_default_no_std.rs new file mode 100644 index 0000000000000..c2670ece76ae1 --- /dev/null +++ b/tests/ui/mem_replace_with_default_no_std.rs @@ -0,0 +1,25 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::mem_replace_with_default)] +#![no_std] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; + +use core::mem; + +fn it_works() { + let mut refstr = "hello"; + let _ = mem::replace(&mut refstr, ""); + //~^ mem_replace_with_default + + let mut slice: &[i32] = &[1, 2, 3]; + let _ = mem::replace(&mut slice, &[]); + //~^ mem_replace_with_default +} + +#[inline_macros] +fn macros(mut refstr: &str) { + let _ = inline!(mem::replace(&mut $refstr, "")); + //~^ mem_replace_with_default + let _ = external!(mem::replace(&mut $refstr, "")); +} diff --git a/tests/ui/mem_replace_with_default_no_std.stderr b/tests/ui/mem_replace_with_default_no_std.stderr new file mode 100644 index 0000000000000..7fb72cdf5a480 --- /dev/null +++ b/tests/ui/mem_replace_with_default_no_std.stderr @@ -0,0 +1,25 @@ +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default_no_std.rs:12:13 + | +LL | let _ = mem::replace(&mut refstr, ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `core::mem::take` instead: `core::mem::take(&mut refstr)` + | + = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mem_replace_with_default)]` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default_no_std.rs:16:13 + | +LL | let _ = mem::replace(&mut slice, &[]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `core::mem::take` instead: `core::mem::take(&mut slice)` + +error: replacing a value of type `T` with `T::default()` + --> tests/ui/mem_replace_with_default_no_std.rs:22:21 + | +LL | let _ = inline!(mem::replace(&mut $refstr, "")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `core::mem::take` instead: `core::mem::take(&mut $refstr)` + | + = note: this error originates in the macro `__inline_mac_fn_macros` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/tests/ui/mem_replace_with_uninit.fixed b/tests/ui/mem_replace_with_uninit.fixed new file mode 100644 index 0000000000000..d55ac85dfc71f --- /dev/null +++ b/tests/ui/mem_replace_with_uninit.fixed @@ -0,0 +1,50 @@ +#![warn(clippy::mem_replace_with_uninit)] +#![allow( + // These get removed by the suggestion + deprecated, // for `std::mem::uninitialized` + invalid_value, + clippy::uninit_assumed_init, + + // Added because the suggestion is `std::ptr::read(&mut v)` + // (which might be considered a bug) + clippy::unnecessary_mut_passed, +)] + +use std::mem; + +fn might_panic(x: X) -> X { + // in practice this would be a possibly-panicky operation + x +} + +fn main() { + let mut v = vec![0i32; 4]; + // the following is UB if `might_panic` panics + unsafe { + let taken_v = std::ptr::read(&mut v); + //~^ mem_replace_with_uninit + + let new_v = might_panic(taken_v); + std::mem::forget(mem::replace(&mut v, new_v)); + } + + unsafe { + let taken_v = std::ptr::read(&mut v); + //~^ mem_replace_with_uninit + + let new_v = might_panic(taken_v); + std::mem::forget(mem::replace(&mut v, new_v)); + } + + // this is silly but OK, because usize is a primitive type + let mut u: usize = 42; + let uref = &mut u; + let taken_u = unsafe { mem::replace(uref, mem::zeroed()) }; + *uref = taken_u + 1; + + // this is still not OK, because uninit + let taken_u = unsafe { std::ptr::read(uref) }; + //~^ mem_replace_with_uninit + + *uref = taken_u + 1; +} diff --git a/tests/ui/repl_uninit.rs b/tests/ui/mem_replace_with_uninit.rs similarity index 78% rename from tests/ui/repl_uninit.rs rename to tests/ui/mem_replace_with_uninit.rs index e9469d4c5e2ff..0e6ede307f02c 100644 --- a/tests/ui/repl_uninit.rs +++ b/tests/ui/mem_replace_with_uninit.rs @@ -1,6 +1,15 @@ -#![allow(deprecated, invalid_value, clippy::uninit_assumed_init)] #![warn(clippy::mem_replace_with_uninit)] -//@no-rustfix +#![allow( + // These get removed by the suggestion + deprecated, // for `std::mem::uninitialized` + invalid_value, + clippy::uninit_assumed_init, + + // Added because the suggestion is `std::ptr::read(&mut v)` + // (which might be considered a bug) + clippy::unnecessary_mut_passed, +)] + use std::mem; fn might_panic(x: X) -> X { @@ -27,14 +36,6 @@ fn main() { std::mem::forget(mem::replace(&mut v, new_v)); } - unsafe { - let taken_v = mem::replace(&mut v, mem::zeroed()); - //~^ mem_replace_with_uninit - - let new_v = might_panic(taken_v); - std::mem::forget(mem::replace(&mut v, new_v)); - } - // this is silly but OK, because usize is a primitive type let mut u: usize = 42; let uref = &mut u; diff --git a/tests/ui/repl_uninit.stderr b/tests/ui/mem_replace_with_uninit.stderr similarity index 55% rename from tests/ui/repl_uninit.stderr rename to tests/ui/mem_replace_with_uninit.stderr index 08b0b265942d7..b5616ab722261 100644 --- a/tests/ui/repl_uninit.stderr +++ b/tests/ui/mem_replace_with_uninit.stderr @@ -1,31 +1,23 @@ error: replacing with `mem::uninitialized()` - --> tests/ui/repl_uninit.rs:15:23 + --> tests/ui/mem_replace_with_uninit.rs:24:23 | LL | let taken_v = mem::replace(&mut v, mem::uninitialized()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(&mut v)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::ptr::read` instead: `std::ptr::read(&mut v)` | = note: `-D clippy::mem-replace-with-uninit` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mem_replace_with_uninit)]` error: replacing with `mem::MaybeUninit::uninit().assume_init()` - --> tests/ui/repl_uninit.rs:23:23 + --> tests/ui/mem_replace_with_uninit.rs:32:23 | LL | let taken_v = mem::replace(&mut v, mem::MaybeUninit::uninit().assume_init()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(&mut v)` - -error: replacing with `mem::zeroed()` - --> tests/ui/repl_uninit.rs:31:23 - | -LL | let taken_v = mem::replace(&mut v, mem::zeroed()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using a default value or the `take_mut` crate instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::ptr::read` instead: `std::ptr::read(&mut v)` error: replacing with `mem::uninitialized()` - --> tests/ui/repl_uninit.rs:45:28 + --> tests/ui/mem_replace_with_uninit.rs:46:28 | LL | let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(uref)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `std::ptr::read` instead: `std::ptr::read(uref)` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/mem_replace_with_uninit_unfixable.rs b/tests/ui/mem_replace_with_uninit_unfixable.rs new file mode 100644 index 0000000000000..7abb7f2c5dc35 --- /dev/null +++ b/tests/ui/mem_replace_with_uninit_unfixable.rs @@ -0,0 +1,22 @@ +// The lint does not offer a suggestion for the `zeroed` case +//@ no-rustfix +#![warn(clippy::mem_replace_with_uninit)] +#![expect(invalid_value)] + +use std::mem; + +fn might_panic(x: X) -> X { + // in practice this would be a possibly-panicky operation + x +} + +fn main() { + let mut v = vec![0i32; 4]; + unsafe { + let taken_v = mem::replace(&mut v, mem::zeroed()); + //~^ mem_replace_with_uninit + + let new_v = might_panic(taken_v); + std::mem::forget(mem::replace(&mut v, new_v)); + } +} diff --git a/tests/ui/mem_replace_with_uninit_unfixable.stderr b/tests/ui/mem_replace_with_uninit_unfixable.stderr new file mode 100644 index 0000000000000..166d9fdd2ea8f --- /dev/null +++ b/tests/ui/mem_replace_with_uninit_unfixable.stderr @@ -0,0 +1,12 @@ +error: replacing with `mem::zeroed()` + --> tests/ui/mem_replace_with_uninit_unfixable.rs:16:23 + | +LL | let taken_v = mem::replace(&mut v, mem::zeroed()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a default value or the `take_mut` crate instead + = note: `-D clippy::mem-replace-with-uninit` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mem_replace_with_uninit)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/missing_const_for_fn/could_be_const.fixed b/tests/ui/missing_const_for_fn/could_be_const.fixed index 95bf63ed1df6f..46a7f79ac16ce 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -278,3 +278,9 @@ mod issue_15079 { } } } + +pub const fn issue17119(s: &str) -> bool { + //~^ missing_const_for_fn + let [_] = s.as_bytes() else { return false }; + true +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 8290be6754621..78e1939a85973 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -278,3 +278,9 @@ mod issue_15079 { } } } + +pub fn issue17119(s: &str) -> bool { + //~^ missing_const_for_fn + let [_] = s.as_bytes() else { return false }; + true +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 17cbc4312766a..4465b51e47719 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -402,5 +402,20 @@ help: make the function `const` LL | pub const fn new_1_61() -> Self { | +++++ -error: aborting due to 30 previous errors +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:282:1 + | +LL | / pub fn issue17119(s: &str) -> bool { +LL | | +LL | | let [_] = s.as_bytes() else { return false }; +LL | | true +LL | | } + | |_^ + | +help: make the function `const` + | +LL | pub const fn issue17119(s: &str) -> bool { + | +++++ + +error: aborting due to 31 previous errors diff --git a/tests/ui/needless_borrow_false_positive_16200.fixed b/tests/ui/needless_borrow_false_positive_16200.fixed new file mode 100644 index 0000000000000..45c827ab09463 --- /dev/null +++ b/tests/ui/needless_borrow_false_positive_16200.fixed @@ -0,0 +1,38 @@ +trait Trait { + fn run(&self); +} + +struct Test; +impl Test { + fn run(self, x: f32) {} +} +impl Trait for Test { + fn run(&self) {} +} + +struct Test2; +trait Trait2: Sized { + fn run(self, _x: f32) {} +} +impl Trait2 for Test2 { + fn run(self, _x: f32) {} +} +impl Trait for Test2 { + fn run(&self) {} +} + +struct Test3; +impl Trait for Test3 { + fn run(&self) {} +} + +fn main() { + (&Test).run(); + (&Test).run(); //~ needless_borrow + + (&Test2).run(); + (&Test2).run(); //~ needless_borrow + + Test3.run(); //~ needless_borrow + Test3.run(); //~ needless_borrow +} diff --git a/tests/ui/needless_borrow_false_positive_16200.rs b/tests/ui/needless_borrow_false_positive_16200.rs new file mode 100644 index 0000000000000..686dded57b504 --- /dev/null +++ b/tests/ui/needless_borrow_false_positive_16200.rs @@ -0,0 +1,38 @@ +trait Trait { + fn run(&self); +} + +struct Test; +impl Test { + fn run(self, x: f32) {} +} +impl Trait for Test { + fn run(&self) {} +} + +struct Test2; +trait Trait2: Sized { + fn run(self, _x: f32) {} +} +impl Trait2 for Test2 { + fn run(self, _x: f32) {} +} +impl Trait for Test2 { + fn run(&self) {} +} + +struct Test3; +impl Trait for Test3 { + fn run(&self) {} +} + +fn main() { + (&Test).run(); + (&&Test).run(); //~ needless_borrow + + (&Test2).run(); + (&&Test2).run(); //~ needless_borrow + + (&Test3).run(); //~ needless_borrow + (&&Test3).run(); //~ needless_borrow +} diff --git a/tests/ui/needless_borrow_false_positive_16200.stderr b/tests/ui/needless_borrow_false_positive_16200.stderr new file mode 100644 index 0000000000000..de70635dc50c1 --- /dev/null +++ b/tests/ui/needless_borrow_false_positive_16200.stderr @@ -0,0 +1,29 @@ +error: this expression creates a reference which is immediately dereferenced by the compiler + --> tests/ui/needless_borrow_false_positive_16200.rs:31:5 + | +LL | (&&Test).run(); + | ^^^^^^^^ help: change this to: `(&Test)` + | + = note: `-D clippy::needless-borrow` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]` + +error: this expression creates a reference which is immediately dereferenced by the compiler + --> tests/ui/needless_borrow_false_positive_16200.rs:34:5 + | +LL | (&&Test2).run(); + | ^^^^^^^^^ help: change this to: `(&Test2)` + +error: this expression borrows a value the compiler would automatically borrow + --> tests/ui/needless_borrow_false_positive_16200.rs:36:5 + | +LL | (&Test3).run(); + | ^^^^^^^^ help: change this to: `Test3` + +error: this expression creates a reference which is immediately dereferenced by the compiler + --> tests/ui/needless_borrow_false_positive_16200.rs:37:5 + | +LL | (&&Test3).run(); + | ^^^^^^^^^ help: change this to: `Test3` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 7b2d66450f171..5d9d93dc49c3c 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -541,3 +541,106 @@ fn issue16462() { n >= 0 || break; } } + +fn issue16056_basic() { + let x = || Some(panic!()); + + //~v never_loop + loop { + x().unwrap(); + } +} + +fn issue16056_nested_loops() { + let x = || Some(panic!()); + + //~v never_loop + loop { + //~v never_loop + loop { + x().unwrap(); + } + } +} + +fn issue16056_nested_block() { + let x = || Some(panic!()); + + //~v never_loop + loop { + { + x().unwrap(); + } + } +} + +fn issue16056_nested_blocks() { + let x = || Some(panic!()); + + //~v never_loop + loop { + { + { + x().unwrap(); + } + } + } +} + +fn issue16056_match() { + let x = || Some(panic!()); + + let mut y = 1; + //~v never_loop + loop { + y += 1; + match y { + 5 => return, + _ => { + x().unwrap(); + }, + } + } +} + +fn issue16056_if_no_trigger() { + let x = || Some(panic!()); + let mut y = 0; + //~v never_loop + loop { + y += 1; + if y == 1 { + // No extra note for this + x().unwrap(); + } + break; + } +} + +fn issue16056_if_trigger() { + let x = || Some(panic!()); + + //~v never_loop + loop { + if true { + x().unwrap(); + } else { + break; + } + } +} + +macro_rules! test_macro { + ($e:expr) => { + $e + }; +} + +fn issue16056_macro() { + let x = || Some(panic!()); + + //~v never_loop + loop { + test_macro!(x().unwrap()); + } +} diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 815758107884c..7bc53289e76ff 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -404,5 +404,142 @@ LL | | return; LL | | } | |_____^ -error: aborting due to 30 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:549:5 + | +LL | / loop { +LL | | x().unwrap(); +LL | | } + | |_____^ + | +note: this expression never returns + --> tests/ui/never_loop.rs:550:9 + | +LL | x().unwrap(); + | ^^^^^^^^^^^^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:558:5 + | +LL | / loop { +LL | | +LL | | loop { +LL | | x().unwrap(); +LL | | } +LL | | } + | |_____^ + | +note: this expression never returns + --> tests/ui/never_loop.rs:561:13 + | +LL | x().unwrap(); + | ^^^^^^^^^^^^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:560:9 + | +LL | / loop { +LL | | x().unwrap(); +LL | | } + | |_________^ + | +note: this expression never returns + --> tests/ui/never_loop.rs:561:13 + | +LL | x().unwrap(); + | ^^^^^^^^^^^^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:570:5 + | +LL | / loop { +LL | | { +LL | | x().unwrap(); +LL | | } +LL | | } + | |_____^ + | +note: this expression never returns + --> tests/ui/never_loop.rs:572:13 + | +LL | x().unwrap(); + | ^^^^^^^^^^^^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:581:5 + | +LL | / loop { +LL | | { +LL | | { +LL | | x().unwrap(); +... | +LL | | } + | |_____^ + | +note: this expression never returns + --> tests/ui/never_loop.rs:584:17 + | +LL | x().unwrap(); + | ^^^^^^^^^^^^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:595:5 + | +LL | / loop { +LL | | y += 1; +LL | | match y { +LL | | 5 => return, +... | +LL | | } + | |_____^ + | +note: this expression never returns + --> tests/ui/never_loop.rs:600:17 + | +LL | x().unwrap(); + | ^^^^^^^^^^^^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:610:5 + | +LL | / loop { +LL | | y += 1; +LL | | if y == 1 { +... | +LL | | break; +LL | | } + | |_____^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:624:5 + | +LL | / loop { +LL | | if true { +LL | | x().unwrap(); +LL | | } else { +... | +LL | | } + | |_____^ + | +note: this expression never returns + --> tests/ui/never_loop.rs:626:13 + | +LL | x().unwrap(); + | ^^^^^^^^^^^^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:643:5 + | +LL | / loop { +LL | | test_macro!(x().unwrap()); +LL | | } + | |_____^ + | +note: this expression never returns + --> tests/ui/never_loop.rs:644:21 + | +LL | test_macro!(x().unwrap()); + | ^^^^^^^^^^^^ + +error: aborting due to 39 previous errors diff --git a/tests/ui/range_plus_minus_one.fixed b/tests/ui/range_plus_minus_one.fixed index e07a0e07368b5..4c0cb86611973 100644 --- a/tests/ui/range_plus_minus_one.fixed +++ b/tests/ui/range_plus_minus_one.fixed @@ -56,9 +56,7 @@ fn main() { let _ = (f() + 1)..(f() + 1); const ONE: usize = 1; - // integer consts are linted, too - for _ in 1..=ONE {} - //~^ range_plus_one + for _ in 1..ONE + ONE {} let mut vec: Vec<()> = std::vec::Vec::new(); vec.drain(..); diff --git a/tests/ui/range_plus_minus_one.rs b/tests/ui/range_plus_minus_one.rs index 3e6e4f629a51a..c853d1f9387a2 100644 --- a/tests/ui/range_plus_minus_one.rs +++ b/tests/ui/range_plus_minus_one.rs @@ -56,9 +56,7 @@ fn main() { let _ = (f() + 1)..(f() + 1); const ONE: usize = 1; - // integer consts are linted, too for _ in 1..ONE + ONE {} - //~^ range_plus_one let mut vec: Vec<()> = std::vec::Vec::new(); vec.drain(..); diff --git a/tests/ui/range_plus_minus_one.stderr b/tests/ui/range_plus_minus_one.stderr index 79c482aeaa6bb..93d74b1d840c9 100644 --- a/tests/ui/range_plus_minus_one.stderr +++ b/tests/ui/range_plus_minus_one.stderr @@ -26,61 +26,55 @@ LL | for _ in 0..(1 + f()) {} | ^^^^^^^^^^^^ help: use: `0..=f()` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:60:14 - | -LL | for _ in 1..ONE + ONE {} - | ^^^^^^^^^^^^ help: use: `1..=ONE` - -error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:70:6 + --> tests/ui/range_plus_minus_one.rs:68:6 | LL | (1..10 + 1).for_each(|_| {}); | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:75:6 + --> tests/ui/range_plus_minus_one.rs:73:6 | LL | (1..10 + 1).into_iter().for_each(|_| {}); | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:80:18 + --> tests/ui/range_plus_minus_one.rs:78:18 | LL | let _ = (1..10 + 1).start_bound(); | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:86:16 + --> tests/ui/range_plus_minus_one.rs:84:16 | LL | let _ = &a[1..1 + 1]; | ^^^^^^^^ help: use: `1..=1` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:90:15 + --> tests/ui/range_plus_minus_one.rs:88:15 | LL | vec.drain(2..3 + 1); | ^^^^^^^^ help: use: `2..=3` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:94:14 + --> tests/ui/range_plus_minus_one.rs:92:14 | LL | take_arg(10..20 + 1); | ^^^^^^^^^^ help: use: `10..=20` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:98:16 + --> tests/ui/range_plus_minus_one.rs:96:16 | LL | take_arg({ 10..20 + 1 }); | ^^^^^^^^^^ help: use: `10..=20` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:112:7 + --> tests/ui/range_plus_minus_one.rs:110:7 | LL | a[0..2 + 1][0] = 1; | ^^^^^^^^ help: use: `0..=2` error: an exclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:180:6 + --> tests/ui/range_plus_minus_one.rs:178:6 | LL | (1..=n - 1).sum() | ^^^^^^^^^ help: use: `1..n` @@ -89,10 +83,10 @@ LL | (1..=n - 1).sum() = help: to override `-D warnings` add `#[allow(clippy::range_minus_one)]` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:192:14 + --> tests/ui/range_plus_minus_one.rs:190:14 | LL | for _ in test!(x)..test!(x) + 1 { | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `test!(x)..=test!(x)` -error: aborting due to 15 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/redundant_closure_call_early.rs b/tests/ui/redundant_closure_call_early.rs index 722f7c03909f0..a78d6c07a5e03 100644 --- a/tests/ui/redundant_closure_call_early.rs +++ b/tests/ui/redundant_closure_call_early.rs @@ -1,6 +1,7 @@ // non rustfixable, see redundant_closure_call_fixable.rs -#![warn(clippy::redundant_closure_call)] +#![expect(incomplete_features)] +#![feature(ergonomic_clones)] fn main() { let mut i = 1; @@ -14,9 +15,60 @@ fn main() { //~^ redundant_closure_call // don't lint these - #[allow(clippy::needless_return)] + #[expect(clippy::needless_return)] (|| return 2)(); (|| -> Option { None? })(); - #[allow(clippy::try_err)] + #[expect(clippy::try_err)] (|| -> Result { Err(2)? })(); + + // don't lint async equivalents either + #[expect(clippy::needless_return)] + (|| async { return })(); + (|| async { + let x: Option = None; + x?; + Some(1) + })(); + #[expect(clippy::try_err)] + (|| async { + Err::<(), i32>(2)?; + Ok::<(), i32>(()) + })(); + + #[expect(clippy::needless_return)] + (|| async move { return })(); + (|| async move { + let x: Option = None; + x?; + Some(1) + })(); + + #[expect(clippy::needless_return)] + (async || return)(); + (async || { + let x: Option = None; + x?; + Some(1) + })(); + #[expect(clippy::try_err)] + (async || { + Err::<(), i32>(2)?; + Ok::<(), i32>(()) + })(); + + #[expect(clippy::needless_return)] + (async move || return)(); + (async move || { + let x: Option = None; + x?; + Some(1) + })(); + + #[expect(clippy::needless_return)] + (async use || return)(); + (async use || { + let x: Option = None; + x?; + Some(1) + })(); } diff --git a/tests/ui/redundant_closure_call_early.stderr b/tests/ui/redundant_closure_call_early.stderr index 1dd24baf9daeb..e72499a7fb438 100644 --- a/tests/ui/redundant_closure_call_early.stderr +++ b/tests/ui/redundant_closure_call_early.stderr @@ -1,5 +1,5 @@ error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_early.rs:9:17 + --> tests/ui/redundant_closure_call_early.rs:10:17 | LL | let mut k = (|m| m + 1)(i); | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let mut k = (|m| m + 1)(i); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_call)]` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_early.rs:13:9 + --> tests/ui/redundant_closure_call_early.rs:14:9 | LL | k = (|a, b| a * b)(1, 5); | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed index 9f6643e8d52e8..d10063251accd 100644 --- a/tests/ui/redundant_closure_call_fixable.fixed +++ b/tests/ui/redundant_closure_call_fixable.fixed @@ -1,7 +1,6 @@ -#![warn(clippy::redundant_closure_call)] -#![allow(clippy::redundant_async_block)] -#![allow(clippy::type_complexity)] -#![allow(unused)] +#![expect(unused)] +#![expect(incomplete_features)] +#![feature(ergonomic_clones)] async fn something() -> u32 { 21 @@ -67,6 +66,7 @@ fn issue9956() { // nested async closures let a = async { 1 }; //~^ redundant_closure_call + #[expect(clippy::redundant_async_block)] let h = async { a.await }; // macro expansion tests @@ -83,6 +83,7 @@ fn issue9956() { assert_eq!(a, 123); // chaining calls, but not closures + #[expect(clippy::type_complexity)] fn x() -> fn() -> fn() -> fn() -> i32 { || || || 42 } @@ -112,6 +113,37 @@ mod issue11707 { spawn_on(async move {}); //~^ redundant_closure_call } + + fn demo_with_captures() { + let name = String::from("world"); + spawn_on(async move { drop(format!("hello, {name}")) }); + //~^ redundant_closure_call + } + + fn demo_async_move_closure() { + let name = String::from("world"); + spawn_on(async move { drop(format!("hello, {name}")) }); + //~^ redundant_closure_call + } +} + +mod issue16232 { + use core::future::Future; + + fn spawn_on(fut: impl Future) {} + + fn closure_async_use_block() { + let name = String::from("world"); + #[rustfmt::skip] + spawn_on(async use { drop(format!("hello, {name:?}")) }); + //~^ redundant_closure_call + } + + fn async_use_closure() { + let name = String::from("world"); + spawn_on(async use { drop(format!("hello, {name}")) }); + //~^ redundant_closure_call + } } fn avoid_double_parens() { diff --git a/tests/ui/redundant_closure_call_fixable.rs b/tests/ui/redundant_closure_call_fixable.rs index 34f228786786b..eccfb2e3c3a74 100644 --- a/tests/ui/redundant_closure_call_fixable.rs +++ b/tests/ui/redundant_closure_call_fixable.rs @@ -1,7 +1,6 @@ -#![warn(clippy::redundant_closure_call)] -#![allow(clippy::redundant_async_block)] -#![allow(clippy::type_complexity)] -#![allow(unused)] +#![expect(unused)] +#![expect(incomplete_features)] +#![feature(ergonomic_clones)] async fn something() -> u32 { 21 @@ -67,6 +66,7 @@ fn issue9956() { // nested async closures let a = (|| || || || async || 1)()()()()(); //~^ redundant_closure_call + #[expect(clippy::redundant_async_block)] let h = async { a.await }; // macro expansion tests @@ -83,6 +83,7 @@ fn issue9956() { assert_eq!(a, 123); // chaining calls, but not closures + #[expect(clippy::type_complexity)] fn x() -> fn() -> fn() -> fn() -> i32 { || || || 42 } @@ -112,6 +113,37 @@ mod issue11707 { spawn_on((|| async move {})()); //~^ redundant_closure_call } + + fn demo_with_captures() { + let name = String::from("world"); + spawn_on((|| async move { drop(format!("hello, {name}")) })()); + //~^ redundant_closure_call + } + + fn demo_async_move_closure() { + let name = String::from("world"); + spawn_on((async move || drop(format!("hello, {name}")))()); + //~^ redundant_closure_call + } +} + +mod issue16232 { + use core::future::Future; + + fn spawn_on(fut: impl Future) {} + + fn closure_async_use_block() { + let name = String::from("world"); + #[rustfmt::skip] + spawn_on((|| async use { drop(format!("hello, {name:?}")) })()); + //~^ redundant_closure_call + } + + fn async_use_closure() { + let name = String::from("world"); + spawn_on((async use || drop(format!("hello, {name}")))()); + //~^ redundant_closure_call + } } fn avoid_double_parens() { diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index a5591cf7813b7..d9d2335ef9031 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -1,5 +1,5 @@ error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:15:13 + --> tests/ui/redundant_closure_call_fixable.rs:14:13 | LL | let a = (|| 42)(); | ^^^^^^^^^ help: try doing something like: `42` @@ -8,7 +8,7 @@ LL | let a = (|| 42)(); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_call)]` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:17:13 + --> tests/ui/redundant_closure_call_fixable.rs:16:13 | LL | let b = (async || { | _____________^ @@ -30,7 +30,7 @@ LL ~ }; | error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:23:13 + --> tests/ui/redundant_closure_call_fixable.rs:22:13 | LL | let c = (|| { | _____________^ @@ -52,13 +52,13 @@ LL ~ }; | error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:29:13 + --> tests/ui/redundant_closure_call_fixable.rs:28:13 | LL | let d = (async || something().await)(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:39:13 + --> tests/ui/redundant_closure_call_fixable.rs:38:13 | LL | (|| m!())() | ^^^^^^^^^^^ help: try doing something like: `m!()` @@ -69,7 +69,7 @@ LL | m2!(); = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:34:13 + --> tests/ui/redundant_closure_call_fixable.rs:33:13 | LL | (|| 0)() | ^^^^^^^^ help: try doing something like: `0` @@ -80,25 +80,25 @@ LL | m2!(); = note: this error originates in the macro `m` which comes from the expansion of the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:49:16 + --> tests/ui/redundant_closure_call_fixable.rs:48:16 | LL | assert_eq!((|| || 43)()(), 42); | ^^^^^^^^^^^^^^ help: try doing something like: `43` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:59:10 + --> tests/ui/redundant_closure_call_fixable.rs:58:10 | LL | dbg!((|| 42)()); | ^^^^^^^^^ help: try doing something like: `42` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:63:13 + --> tests/ui/redundant_closure_call_fixable.rs:62:13 | LL | let a = (|| || || 123)(); | ^^^^^^^^^^^^^^^^ help: try doing something like: `|| || 123` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:68:13 + --> tests/ui/redundant_closure_call_fixable.rs:67:13 | LL | let a = (|| || || || async || 1)()()()()(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { 1 }` @@ -116,58 +116,82 @@ LL | let a = (|| echo!((|| 123)))()(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `123` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:95:11 + --> tests/ui/redundant_closure_call_fixable.rs:96:11 | LL | bar()((|| || 42)()(), 5); | ^^^^^^^^^^^^^^ help: try doing something like: `42` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:97:9 + --> tests/ui/redundant_closure_call_fixable.rs:98:9 | LL | foo((|| || 42)()(), 5); | ^^^^^^^^^^^^^^ help: try doing something like: `42` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:102:5 + --> tests/ui/redundant_closure_call_fixable.rs:103:5 | LL | (|| async {})().await; | ^^^^^^^^^^^^^^^ help: try doing something like: `async {}` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:112:18 + --> tests/ui/redundant_closure_call_fixable.rs:113:18 | LL | spawn_on((|| async move {})()); | ^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async move {}` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:118:28 + --> tests/ui/redundant_closure_call_fixable.rs:119:18 + | +LL | spawn_on((|| async move { drop(format!("hello, {name}")) })()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async move { drop(format!("hello, {name}")) }` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:125:18 + | +LL | spawn_on((async move || drop(format!("hello, {name}")))()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async move { drop(format!("hello, {name}")) }` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:138:18 + | +LL | spawn_on((|| async use { drop(format!("hello, {name:?}")) })()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async use { drop(format!("hello, {name:?}")) }` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:144:18 + | +LL | spawn_on((async use || drop(format!("hello, {name}")))()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async use { drop(format!("hello, {name}")) }` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:150:28 | LL | std::convert::identity((|| 13_i32 + 36_i32)()).leading_zeros(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `13_i32 + 36_i32` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:150:5 + --> tests/ui/redundant_closure_call_fixable.rs:182:5 | LL | (|| { Some(true) })() == Some(true); | ^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:152:5 + --> tests/ui/redundant_closure_call_fixable.rs:184:5 | LL | (|| Some(true))() == Some(true); | ^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:154:5 + --> tests/ui/redundant_closure_call_fixable.rs:186:5 | LL | (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(if 1 > 2 {1} else {2})` error: try not to call a closure in the expression where it is declared - --> tests/ui/redundant_closure_call_fixable.rs:156:5 + --> tests/ui/redundant_closure_call_fixable.rs:188:5 | LL | (|| { Some( 1 > 2 ) })() == Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some( 1 > 2 )` -error: aborting due to 21 previous errors +error: aborting due to 25 previous errors diff --git a/tests/ui/redundant_closure_call_late.rs b/tests/ui/redundant_closure_call_late.rs index fd997b1b5fa7b..ceb42dc0ce75d 100644 --- a/tests/ui/redundant_closure_call_late.rs +++ b/tests/ui/redundant_closure_call_late.rs @@ -1,9 +1,8 @@ // non rustfixable, see redundant_closure_call_fixable.rs - -#![warn(clippy::redundant_closure_call)] -#![allow(clippy::needless_late_init)] +#![expect(unused_assignments)] fn main() { + #[expect(unused_variables)] let mut i = 1; // don't lint here, the closure is used more than once @@ -31,6 +30,7 @@ fn main() { i = shadowed_closure(); // Fix FP in #5916 + #[expect(unused_variables)] let mut x; let create = || 2 * 2; x = create(); diff --git a/tests/ui/redundant_closure_call_late.stderr b/tests/ui/redundant_closure_call_late.stderr index ce2a21c23872e..1455ec8c78a5c 100644 --- a/tests/ui/redundant_closure_call_late.stderr +++ b/tests/ui/redundant_closure_call_late.stderr @@ -1,5 +1,5 @@ error: closure called just once immediately after it was declared - --> tests/ui/redundant_closure_call_late.rs:16:5 + --> tests/ui/redundant_closure_call_late.rs:15:5 | LL | i = redun_closure(); | ^^^^^^^^^^^^^^^^^^^ @@ -8,13 +8,13 @@ LL | i = redun_closure(); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_call)]` error: closure called just once immediately after it was declared - --> tests/ui/redundant_closure_call_late.rs:21:5 + --> tests/ui/redundant_closure_call_late.rs:20:5 | LL | i = shadowed_closure(); | ^^^^^^^^^^^^^^^^^^^^^^ error: closure called just once immediately after it was declared - --> tests/ui/redundant_closure_call_late.rs:25:5 + --> tests/ui/redundant_closure_call_late.rs:24:5 | LL | i = shadowed_closure(); | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/redundant_pattern_matching_drop_order.stderr b/tests/ui/redundant_pattern_matching_drop_order.stderr index 74462f022f702..7f0171b8424ab 100644 --- a/tests/ui/redundant_pattern_matching_drop_order.stderr +++ b/tests/ui/redundant_pattern_matching_drop_order.stderr @@ -1,172 +1,292 @@ -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:15:12 | LL | if let Ok(_) = m.lock() {} - | -------^^^^^----------- help: try: `if m.lock().is_ok()` + | ^^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` +help: consider using `is_ok()` + | +LL - if let Ok(_) = m.lock() {} +LL + if m.lock().is_ok() {} + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:17:12 | LL | if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {} - | -------^^^^^^------------------------------------ help: try: `if Err::<(), _>(m.lock().unwrap().0).is_err()` + | ^^^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important +help: consider using `is_err()` + | +LL - if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {} +LL + if Err::<(), _>(m.lock().unwrap().0).is_err() {} + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:21:16 | LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} - | -------^^^^^----------------------------------------- help: try: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | ^^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important +help: consider using `is_ok()` + | +LL - if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} +LL + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {} + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:24:12 | LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) { - | -------^^^^^----------------------------------------- help: try: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | ^^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important +help: consider using `is_ok()` + | +LL - if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) { +LL + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() { + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:28:12 | LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} - | -------^^^^^----------------------------------------- help: try: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | ^^^^^ + | +help: consider using `is_ok()` + | +LL - if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} +LL + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {} + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:30:12 | LL | if let Err(_) = Err::, _>(()) {} - | -------^^^^^^------------------------------------------ help: try: `if Err::, _>(()).is_err()` + | ^^^^^^ + | +help: consider using `is_err()` + | +LL - if let Err(_) = Err::, _>(()) {} +LL + if Err::, _>(()).is_err() {} + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:33:12 | LL | if let Ok(_) = Ok::<_, ()>(String::new()) {} - | -------^^^^^----------------------------- help: try: `if Ok::<_, ()>(String::new()).is_ok()` + | ^^^^^ + | +help: consider using `is_ok()` + | +LL - if let Ok(_) = Ok::<_, ()>(String::new()) {} +LL + if Ok::<_, ()>(String::new()).is_ok() {} + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:35:12 | LL | if let Err(_) = Err::<(), _>((String::new(), ())) {} - | -------^^^^^^------------------------------------ help: try: `if Err::<(), _>((String::new(), ())).is_err()` + | ^^^^^^ + | +help: consider using `is_err()` + | +LL - if let Err(_) = Err::<(), _>((String::new(), ())) {} +LL + if Err::<(), _>((String::new(), ())).is_err() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:39:12 | LL | if let Some(_) = Some(m.lock()) {} - | -------^^^^^^^----------------- help: try: `if Some(m.lock()).is_some()` + | ^^^^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important +help: consider using `is_some()` + | +LL - if let Some(_) = Some(m.lock()) {} +LL + if Some(m.lock()).is_some() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:41:12 | LL | if let Some(_) = Some(m.lock().unwrap().0) {} - | -------^^^^^^^---------------------------- help: try: `if Some(m.lock().unwrap().0).is_some()` + | ^^^^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important +help: consider using `is_some()` + | +LL - if let Some(_) = Some(m.lock().unwrap().0) {} +LL + if Some(m.lock().unwrap().0).is_some() {} + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:45:16 | LL | if let None = None::> {} - | -------^^^^------------------------------------ help: try: `if None::>.is_none()` + | ^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important +help: consider using `is_none()` + | +LL - if let None = None::> {} +LL + if None::>.is_none() {} + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:48:12 | LL | if let None = None::> { - | -------^^^^------------------------------------ help: try: `if None::>.is_none()` + | ^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important +help: consider using `is_none()` + | +LL - if let None = None::> { +LL + if None::>.is_none() { + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:53:12 | LL | if let None = None::> {} - | -------^^^^------------------------------------ help: try: `if None::>.is_none()` + | ^^^^ + | +help: consider using `is_none()` + | +LL - if let None = None::> {} +LL + if None::>.is_none() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:56:12 | LL | if let Some(_) = Some(String::new()) {} - | -------^^^^^^^---------------------- help: try: `if Some(String::new()).is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - if let Some(_) = Some(String::new()) {} +LL + if Some(String::new()).is_some() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:58:12 | LL | if let Some(_) = Some((String::new(), ())) {} - | -------^^^^^^^---------------------------- help: try: `if Some((String::new(), ())).is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - if let Some(_) = Some((String::new(), ())) {} +LL + if Some((String::new(), ())).is_some() {} + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:62:12 | LL | if let Ready(_) = Ready(m.lock()) {} - | -------^^^^^^^^------------------ help: try: `if Ready(m.lock()).is_ready()` + | ^^^^^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important +help: consider using `is_ready()` + | +LL - if let Ready(_) = Ready(m.lock()) {} +LL + if Ready(m.lock()).is_ready() {} + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:64:12 | LL | if let Ready(_) = Ready(m.lock().unwrap().0) {} - | -------^^^^^^^^----------------------------- help: try: `if Ready(m.lock().unwrap().0).is_ready()` + | ^^^^^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important +help: consider using `is_ready()` + | +LL - if let Ready(_) = Ready(m.lock().unwrap().0) {} +LL + if Ready(m.lock().unwrap().0).is_ready() {} + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:68:16 | LL | if let Pending = Pending::> {} - | -------^^^^^^^--------------------------------------- help: try: `if Pending::>.is_pending()` + | ^^^^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important +help: consider using `is_pending()` + | +LL - if let Pending = Pending::> {} +LL + if Pending::>.is_pending() {} + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:71:12 | LL | if let Pending = Pending::> { - | -------^^^^^^^--------------------------------------- help: try: `if Pending::>.is_pending()` + | ^^^^^^^ | = note: this will change drop order of the result, as well as all temporaries = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important +help: consider using `is_pending()` + | +LL - if let Pending = Pending::> { +LL + if Pending::>.is_pending() { + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:76:12 | LL | if let Pending = Pending::> {} - | -------^^^^^^^--------------------------------------- help: try: `if Pending::>.is_pending()` + | ^^^^^^^ + | +help: consider using `is_pending()` + | +LL - if let Pending = Pending::> {} +LL + if Pending::>.is_pending() {} + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:79:12 | LL | if let Ready(_) = Ready(String::new()) {} - | -------^^^^^^^^----------------------- help: try: `if Ready(String::new()).is_ready()` + | ^^^^^^^^ + | +help: consider using `is_ready()` + | +LL - if let Ready(_) = Ready(String::new()) {} +LL + if Ready(String::new()).is_ready() {} + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_drop_order.rs:81:12 | LL | if let Ready(_) = Ready((String::new(), ())) {} - | -------^^^^^^^^----------------------------- help: try: `if Ready((String::new(), ())).is_ready()` + | ^^^^^^^^ + | +help: consider using `is_ready()` + | +LL - if let Ready(_) = Ready((String::new(), ())) {} +LL + if Ready((String::new(), ())).is_ready() {} + | error: aborting due to 22 previous errors diff --git a/tests/ui/redundant_pattern_matching_if_let_true.stderr b/tests/ui/redundant_pattern_matching_if_let_true.stderr index db86462706097..1e26fc759d4ea 100644 --- a/tests/ui/redundant_pattern_matching_if_let_true.stderr +++ b/tests/ui/redundant_pattern_matching_if_let_true.stderr @@ -2,46 +2,87 @@ error: using `if let` to pattern match a bool --> tests/ui/redundant_pattern_matching_if_let_true.rs:22:8 | LL | if let true = k > 1 {} - | ^^^^^^^^^^^^^^^^ help: consider using the condition directly: `k > 1` + | ^^^^^^^^^^^^^^^^ | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` +help: consider using the condition directly + | +LL - if let true = k > 1 {} +LL + if k > 1 {} + | error: using `if let` to pattern match a bool --> tests/ui/redundant_pattern_matching_if_let_true.rs:24:8 | LL | if let false = k > 5 {} - | ^^^^^^^^^^^^^^^^^ help: consider using the condition directly: `!(k > 5)` + | ^^^^^^^^^^^^^^^^^ + | +help: consider using the condition directly + | +LL - if let false = k > 5 {} +LL + if !(k > 5) {} + | error: using `if let` to pattern match a bool --> tests/ui/redundant_pattern_matching_if_let_true.rs:26:8 | LL | if let (true) = k > 1 {} - | ^^^^^^^^^^^^^^^^^^ help: consider using the condition directly: `k > 1` + | ^^^^^^^^^^^^^^^^^^ + | +help: consider using the condition directly + | +LL - if let (true) = k > 1 {} +LL + if k > 1 {} + | error: using `if let` to pattern match a bool --> tests/ui/redundant_pattern_matching_if_let_true.rs:29:11 | LL | while let true = k > 1 { - | ^^^^^^^^^^^^^^^^ help: consider using the condition directly: `k > 1` + | ^^^^^^^^^^^^^^^^ + | +help: consider using the condition directly + | +LL - while let true = k > 1 { +LL + while k > 1 { + | error: using `if let` to pattern match a bool --> tests/ui/redundant_pattern_matching_if_let_true.rs:33:11 | LL | while let true = condition!() { - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the condition directly: `condition!()` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using the condition directly + | +LL - while let true = condition!() { +LL + while condition!() { + | error: using `matches!` to pattern match a bool --> tests/ui/redundant_pattern_matching_if_let_true.rs:38:5 | LL | matches!(k > 5, true); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using the condition directly: `k > 5` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using the condition directly + | +LL - matches!(k > 5, true); +LL + k > 5; + | error: using `matches!` to pattern match a bool --> tests/ui/redundant_pattern_matching_if_let_true.rs:40:5 | LL | matches!(k > 5, false); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using the condition directly: `!(k > 5)` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using the condition directly + | +LL - matches!(k > 5, false); +LL + !(k > 5); + | error: aborting due to 7 previous errors diff --git a/tests/ui/redundant_pattern_matching_ipaddr.stderr b/tests/ui/redundant_pattern_matching_ipaddr.stderr index 3be7cf81afe95..9f65f85fad436 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.stderr +++ b/tests/ui/redundant_pattern_matching_ipaddr.stderr @@ -1,49 +1,90 @@ -error: redundant pattern matching, consider using `is_ipv4()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:14:12 | LL | if let V4(_) = &ipaddr {} - | -------^^^^^---------- help: try: `if ipaddr.is_ipv4()` + | ^^^^^ | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` +help: consider using `is_ipv4()` + | +LL - if let V4(_) = &ipaddr {} +LL + if ipaddr.is_ipv4() {} + | -error: redundant pattern matching, consider using `is_ipv4()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:17:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} - | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | ^^^^^ + | +help: consider using `is_ipv4()` + | +LL - if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} +LL + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + | -error: redundant pattern matching, consider using `is_ipv6()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:20:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} - | -------^^^^^-------------------------- help: try: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | ^^^^^ + | +help: consider using `is_ipv6()` + | +LL - if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} +LL + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + | -error: redundant pattern matching, consider using `is_ipv4()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:24:8 | LL | if matches!(V4(Ipv4Addr::LOCALHOST), V4(_)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `is_ipv4()` + | +LL - if matches!(V4(Ipv4Addr::LOCALHOST), V4(_)) {} +LL + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + | -error: redundant pattern matching, consider using `is_ipv6()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:28:8 | LL | if matches!(V6(Ipv6Addr::LOCALHOST), V6(_)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `is_ipv6()` + | +LL - if matches!(V6(Ipv6Addr::LOCALHOST), V6(_)) {} +LL + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + | -error: redundant pattern matching, consider using `is_ipv4()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:31:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} - | ----------^^^^^-------------------------- help: try: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | ^^^^^ + | +help: consider using `is_ipv4()` + | +LL - while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} +LL + while V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + | -error: redundant pattern matching, consider using `is_ipv6()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:34:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} - | ----------^^^^^-------------------------- help: try: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | ^^^^^ + | +help: consider using `is_ipv6()` + | +LL - while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} +LL + while V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + | -error: redundant pattern matching, consider using `is_ipv4()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:45:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { @@ -51,9 +92,19 @@ LL | | LL | | V4(_) => true, LL | | V6(_) => false, LL | | }; - | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | |_____^ + | +help: consider using `is_ipv4()` + | +LL - match V4(Ipv4Addr::LOCALHOST) { +LL - +LL - V4(_) => true, +LL - V6(_) => false, +LL - }; +LL + V4(Ipv4Addr::LOCALHOST).is_ipv4(); + | -error: redundant pattern matching, consider using `is_ipv6()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:51:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { @@ -61,9 +112,19 @@ LL | | LL | | V4(_) => false, LL | | V6(_) => true, LL | | }; - | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv6()` + | |_____^ + | +help: consider using `is_ipv6()` + | +LL - match V4(Ipv4Addr::LOCALHOST) { +LL - +LL - V4(_) => false, +LL - V6(_) => true, +LL - }; +LL + V4(Ipv4Addr::LOCALHOST).is_ipv6(); + | -error: redundant pattern matching, consider using `is_ipv6()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:57:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { @@ -71,9 +132,19 @@ LL | | LL | | V4(_) => false, LL | | V6(_) => true, LL | | }; - | |_____^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | |_____^ + | +help: consider using `is_ipv6()` + | +LL - match V6(Ipv6Addr::LOCALHOST) { +LL - +LL - V4(_) => false, +LL - V6(_) => true, +LL - }; +LL + V6(Ipv6Addr::LOCALHOST).is_ipv6(); + | -error: redundant pattern matching, consider using `is_ipv4()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:63:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { @@ -81,51 +152,103 @@ LL | | LL | | V4(_) => true, LL | | V6(_) => false, LL | | }; - | |_____^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv4()` + | |_____^ + | +help: consider using `is_ipv4()` + | +LL - match V6(Ipv6Addr::LOCALHOST) { +LL - +LL - V4(_) => true, +LL - V6(_) => false, +LL - }; +LL + V6(Ipv6Addr::LOCALHOST).is_ipv4(); + | -error: redundant pattern matching, consider using `is_ipv4()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:69:20 | LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { - | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | ^^^^^ + | +help: consider using `is_ipv4()` + | +LL - let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { +LL + let _ = if V4(Ipv4Addr::LOCALHOST).is_ipv4() { + | -error: redundant pattern matching, consider using `is_ipv4()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:78:20 | LL | let _ = if let V4(_) = gen_ipaddr() { - | -------^^^^^--------------- help: try: `if gen_ipaddr().is_ipv4()` + | ^^^^^ + | +help: consider using `is_ipv4()` + | +LL - let _ = if let V4(_) = gen_ipaddr() { +LL + let _ = if gen_ipaddr().is_ipv4() { + | -error: redundant pattern matching, consider using `is_ipv6()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:81:19 | LL | } else if let V6(_) = gen_ipaddr() { - | -------^^^^^--------------- help: try: `if gen_ipaddr().is_ipv6()` + | ^^^^^ + | +help: consider using `is_ipv6()` + | +LL - } else if let V6(_) = gen_ipaddr() { +LL + } else if gen_ipaddr().is_ipv6() { + | -error: redundant pattern matching, consider using `is_ipv4()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:94:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} - | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | ^^^^^ + | +help: consider using `is_ipv4()` + | +LL - if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} +LL + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + | -error: redundant pattern matching, consider using `is_ipv6()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:97:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} - | -------^^^^^-------------------------- help: try: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | ^^^^^ + | +help: consider using `is_ipv6()` + | +LL - if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} +LL + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + | -error: redundant pattern matching, consider using `is_ipv4()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:100:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} - | ----------^^^^^-------------------------- help: try: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | ^^^^^ + | +help: consider using `is_ipv4()` + | +LL - while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} +LL + while V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + | -error: redundant pattern matching, consider using `is_ipv6()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:103:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} - | ----------^^^^^-------------------------- help: try: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | ^^^^^ + | +help: consider using `is_ipv6()` + | +LL - while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} +LL + while V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + | -error: redundant pattern matching, consider using `is_ipv4()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:106:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { @@ -133,9 +256,19 @@ LL | | LL | | V4(_) => true, LL | | V6(_) => false, LL | | }; - | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` + | |_____^ + | +help: consider using `is_ipv4()` + | +LL - match V4(Ipv4Addr::LOCALHOST) { +LL - +LL - V4(_) => true, +LL - V6(_) => false, +LL - }; +LL + V4(Ipv4Addr::LOCALHOST).is_ipv4(); + | -error: redundant pattern matching, consider using `is_ipv6()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_ipaddr.rs:112:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { @@ -143,7 +276,17 @@ LL | | LL | | V4(_) => false, LL | | V6(_) => true, LL | | }; - | |_____^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` + | |_____^ + | +help: consider using `is_ipv6()` + | +LL - match V6(Ipv6Addr::LOCALHOST) { +LL - +LL - V4(_) => false, +LL - V6(_) => true, +LL - }; +LL + V6(Ipv6Addr::LOCALHOST).is_ipv6(); + | error: aborting due to 20 previous errors diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 62ead95235aba..8f02838feb355 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -1,61 +1,114 @@ -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:11:5 | LL | matches!(maybe_some, None if !boolean) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `maybe_some.is_none() && (!boolean)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` +help: consider using `is_none()` + | +LL - matches!(maybe_some, None if !boolean) +LL + maybe_some.is_none() && (!boolean) + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:16:13 | LL | let _ = matches!(maybe_some, None if boolean || boolean2); // guard needs parentheses - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `maybe_some.is_none() && (boolean || boolean2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `is_none()` + | +LL - let _ = matches!(maybe_some, None if boolean || boolean2); // guard needs parentheses +LL + let _ = maybe_some.is_none() && (boolean || boolean2); // guard needs parentheses + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:32:12 | LL | if let None = None::<()> {} - | -------^^^^------------- help: try: `if None::<()>.is_none()` + | ^^^^ + | +help: consider using `is_none()` + | +LL - if let None = None::<()> {} +LL + if None::<()>.is_none() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:35:12 | LL | if let Some(_) = Some(42) {} - | -------^^^^^^^----------- help: try: `if Some(42).is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - if let Some(_) = Some(42) {} +LL + if Some(42).is_some() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:38:12 | LL | if let Some(_) = Some(42) { - | -------^^^^^^^----------- help: try: `if Some(42).is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - if let Some(_) = Some(42) { +LL + if Some(42).is_some() { + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:45:15 | LL | while let Some(_) = Some(42) {} - | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - while let Some(_) = Some(42) {} +LL + while Some(42).is_some() {} + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:48:15 | LL | while let None = Some(42) {} - | ----------^^^^----------- help: try: `while Some(42).is_none()` + | ^^^^ + | +help: consider using `is_none()` + | +LL - while let None = Some(42) {} +LL + while Some(42).is_none() {} + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:51:15 | LL | while let None = None::<()> {} - | ----------^^^^------------- help: try: `while None::<()>.is_none()` + | ^^^^ + | +help: consider using `is_none()` + | +LL - while let None = None::<()> {} +LL + while None::<()>.is_none() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:55:15 | LL | while let Some(_) = v.pop() { - | ----------^^^^^^^---------- help: try: `while v.pop().is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - while let Some(_) = v.pop() { +LL + while v.pop().is_some() { + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:64:5 | LL | / match Some(42) { @@ -63,9 +116,19 @@ LL | | LL | | Some(_) => true, LL | | None => false, LL | | }; - | |_____^ help: try: `Some(42).is_some()` + | |_____^ + | +help: consider using `is_some()` + | +LL - match Some(42) { +LL - +LL - Some(_) => true, +LL - None => false, +LL - }; +LL + Some(42).is_some(); + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:70:5 | LL | / match None::<()> { @@ -73,9 +136,19 @@ LL | | LL | | Some(_) => false, LL | | None => true, LL | | }; - | |_____^ help: try: `None::<()>.is_none()` + | |_____^ + | +help: consider using `is_none()` + | +LL - match None::<()> { +LL - +LL - Some(_) => false, +LL - None => true, +LL - }; +LL + None::<()>.is_none(); + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:76:13 | LL | let _ = match None::<()> { @@ -84,57 +157,115 @@ LL | | LL | | Some(_) => false, LL | | None => true, LL | | }; - | |_____^ help: try: `None::<()>.is_none()` + | |_____^ + | +help: consider using `is_none()` + | +LL - let _ = match None::<()> { +LL - +LL - Some(_) => false, +LL - None => true, +LL - }; +LL + let _ = None::<()>.is_none(); + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:83:20 | LL | let _ = if let Some(_) = opt { true } else { false }; - | -------^^^^^^^------ help: try: `if opt.is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - let _ = if let Some(_) = opt { true } else { false }; +LL + let _ = if opt.is_some() { true } else { false }; + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:90:20 | LL | let _ = if let Some(_) = gen_opt() { - | -------^^^^^^^------------ help: try: `if gen_opt().is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - let _ = if let Some(_) = gen_opt() { +LL + let _ = if gen_opt().is_some() { + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:93:19 | LL | } else if let None = gen_opt() { - | -------^^^^------------ help: try: `if gen_opt().is_none()` + | ^^^^ + | +help: consider using `is_none()` + | +LL - } else if let None = gen_opt() { +LL + } else if gen_opt().is_none() { + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:100:12 | LL | if let Some(..) = gen_opt() {} - | -------^^^^^^^^------------ help: try: `if gen_opt().is_some()` + | ^^^^^^^^ + | +help: consider using `is_some()` + | +LL - if let Some(..) = gen_opt() {} +LL + if gen_opt().is_some() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:116:12 | LL | if let Some(_) = Some(42) {} - | -------^^^^^^^----------- help: try: `if Some(42).is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - if let Some(_) = Some(42) {} +LL + if Some(42).is_some() {} + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:119:12 | LL | if let None = None::<()> {} - | -------^^^^------------- help: try: `if None::<()>.is_none()` + | ^^^^ + | +help: consider using `is_none()` + | +LL - if let None = None::<()> {} +LL + if None::<()>.is_none() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:122:15 | LL | while let Some(_) = Some(42) {} - | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - while let Some(_) = Some(42) {} +LL + while Some(42).is_some() {} + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:125:15 | LL | while let None = None::<()> {} - | ----------^^^^------------- help: try: `while None::<()>.is_none()` + | ^^^^ + | +help: consider using `is_none()` + | +LL - while let None = None::<()> {} +LL + while None::<()>.is_none() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:128:5 | LL | / match Some(42) { @@ -142,9 +273,19 @@ LL | | LL | | Some(_) => true, LL | | None => false, LL | | }; - | |_____^ help: try: `Some(42).is_some()` + | |_____^ + | +help: consider using `is_some()` + | +LL - match Some(42) { +LL - +LL - Some(_) => true, +LL - None => false, +LL - }; +LL + Some(42).is_some(); + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:134:5 | LL | / match None::<()> { @@ -152,21 +293,43 @@ LL | | LL | | Some(_) => false, LL | | None => true, LL | | }; - | |_____^ help: try: `None::<()>.is_none()` + | |_____^ + | +help: consider using `is_none()` + | +LL - match None::<()> { +LL - +LL - Some(_) => false, +LL - None => true, +LL - }; +LL + None::<()>.is_none(); + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:143:12 | LL | if let None = *(&None::<()>) {} - | -------^^^^----------------- help: try: `if (&None::<()>).is_none()` + | ^^^^ + | +help: consider using `is_none()` + | +LL - if let None = *(&None::<()>) {} +LL + if (&None::<()>).is_none() {} + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:145:12 | LL | if let None = *&None::<()> {} - | -------^^^^--------------- help: try: `if (&None::<()>).is_none()` + | ^^^^ + | +help: consider using `is_none()` + | +LL - if let None = *&None::<()> {} +LL + if (&None::<()>).is_none() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:152:5 | LL | / match x { @@ -174,9 +337,19 @@ LL | | LL | | Some(_) => true, LL | | _ => false, LL | | }; - | |_____^ help: try: `x.is_some()` + | |_____^ + | +help: consider using `is_some()` + | +LL - match x { +LL - +LL - Some(_) => true, +LL - _ => false, +LL - }; +LL + x.is_some(); + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:158:5 | LL | / match x { @@ -184,9 +357,19 @@ LL | | LL | | None => true, LL | | _ => false, LL | | }; - | |_____^ help: try: `x.is_none()` + | |_____^ + | +help: consider using `is_none()` + | +LL - match x { +LL - +LL - None => true, +LL - _ => false, +LL - }; +LL + x.is_none(); + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:164:5 | LL | / match x { @@ -194,9 +377,19 @@ LL | | LL | | Some(_) => false, LL | | _ => true, LL | | }; - | |_____^ help: try: `x.is_none()` + | |_____^ + | +help: consider using `is_none()` + | +LL - match x { +LL - +LL - Some(_) => false, +LL - _ => true, +LL - }; +LL + x.is_none(); + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:170:5 | LL | / match x { @@ -204,49 +397,101 @@ LL | | LL | | None => false, LL | | _ => true, LL | | }; - | |_____^ help: try: `x.is_some()` + | |_____^ + | +help: consider using `is_some()` + | +LL - match x { +LL - +LL - None => false, +LL - _ => true, +LL - }; +LL + x.is_some(); + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:186:13 | LL | let _ = matches!(x, Some(_)); - | ^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_some()` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `is_some()` + | +LL - let _ = matches!(x, Some(_)); +LL + let _ = x.is_some(); + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:189:13 | LL | let _ = matches!(x, None); - | ^^^^^^^^^^^^^^^^^ help: try: `x.is_none()` + | ^^^^^^^^^^^^^^^^^ + | +help: consider using `is_none()` + | +LL - let _ = matches!(x, None); +LL + let _ = x.is_none(); + | -error: redundant pattern matching, consider using `is_none()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:200:17 | LL | let _ = matches!(*p, None); - | ^^^^^^^^^^^^^^^^^^ help: try: `(*p).is_none()` + | ^^^^^^^^^^^^^^^^^^ + | +help: consider using `is_none()` + | +LL - let _ = matches!(*p, None); +LL + let _ = (*p).is_none(); + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:208:16 | LL | if let Some(_) = x? { - | -------^^^^^^^----- help: try: `if x?.is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - if let Some(_) = x? { +LL + if x?.is_some() { + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:228:16 | LL | if let Some(_) = x.await { - | -------^^^^^^^---------- help: try: `if x.await.is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - if let Some(_) = x.await { +LL + if x.await.is_some() { + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:241:12 | LL | if let Some(_) = (x! {}) {}; - | -------^^^^^^^---------- help: try: `if x! {}.is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - if let Some(_) = (x! {}) {}; +LL + if x! {}.is_some() {}; + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_option.rs:243:15 | LL | while let Some(_) = (x! {}) {} - | ----------^^^^^^^---------- help: try: `while x! {}.is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - while let Some(_) = (x! {}) {} +LL + while x! {}.is_some() {} + | error: aborting due to 35 previous errors diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr index 5cd9d9636e466..5b94965c06daa 100644 --- a/tests/ui/redundant_pattern_matching_poll.stderr +++ b/tests/ui/redundant_pattern_matching_poll.stderr @@ -1,55 +1,102 @@ -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:13:12 | LL | if let Pending = Pending::<()> {} - | -------^^^^^^^---------------- help: try: `if Pending::<()>.is_pending()` + | ^^^^^^^ | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` +help: consider using `is_pending()` + | +LL - if let Pending = Pending::<()> {} +LL + if Pending::<()>.is_pending() {} + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:16:12 | LL | if let Ready(_) = Ready(42) {} - | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` + | ^^^^^^^^ + | +help: consider using `is_ready()` + | +LL - if let Ready(_) = Ready(42) {} +LL + if Ready(42).is_ready() {} + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:19:12 | LL | if let Ready(_) = Ready(42) { - | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` + | ^^^^^^^^ + | +help: consider using `is_ready()` + | +LL - if let Ready(_) = Ready(42) { +LL + if Ready(42).is_ready() { + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:27:8 | LL | if matches!(Ready(42), Ready(_)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ready(42).is_ready()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `is_ready()` + | +LL - if matches!(Ready(42), Ready(_)) {} +LL + if Ready(42).is_ready() {} + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:31:8 | LL | if matches!(Pending::<()>, Pending) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Pending::<()>.is_pending()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `is_pending()` + | +LL - if matches!(Pending::<()>, Pending) {} +LL + if Pending::<()>.is_pending() {} + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:34:15 | LL | while let Ready(_) = Ready(42) {} - | ----------^^^^^^^^------------ help: try: `while Ready(42).is_ready()` + | ^^^^^^^^ + | +help: consider using `is_ready()` + | +LL - while let Ready(_) = Ready(42) {} +LL + while Ready(42).is_ready() {} + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:37:15 | LL | while let Pending = Ready(42) {} - | ----------^^^^^^^------------ help: try: `while Ready(42).is_pending()` + | ^^^^^^^ + | +help: consider using `is_pending()` + | +LL - while let Pending = Ready(42) {} +LL + while Ready(42).is_pending() {} + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:40:15 | LL | while let Pending = Pending::<()> {} - | ----------^^^^^^^---------------- help: try: `while Pending::<()>.is_pending()` + | ^^^^^^^ + | +help: consider using `is_pending()` + | +LL - while let Pending = Pending::<()> {} +LL + while Pending::<()>.is_pending() {} + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:47:5 | LL | / match Ready(42) { @@ -57,9 +104,19 @@ LL | | LL | | Ready(_) => true, LL | | Pending => false, LL | | }; - | |_____^ help: try: `Ready(42).is_ready()` + | |_____^ + | +help: consider using `is_ready()` + | +LL - match Ready(42) { +LL - +LL - Ready(_) => true, +LL - Pending => false, +LL - }; +LL + Ready(42).is_ready(); + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:53:5 | LL | / match Pending::<()> { @@ -67,9 +124,19 @@ LL | | LL | | Ready(_) => false, LL | | Pending => true, LL | | }; - | |_____^ help: try: `Pending::<()>.is_pending()` + | |_____^ + | +help: consider using `is_pending()` + | +LL - match Pending::<()> { +LL - +LL - Ready(_) => false, +LL - Pending => true, +LL - }; +LL + Pending::<()>.is_pending(); + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:59:13 | LL | let _ = match Pending::<()> { @@ -78,51 +145,103 @@ LL | | LL | | Ready(_) => false, LL | | Pending => true, LL | | }; - | |_____^ help: try: `Pending::<()>.is_pending()` + | |_____^ + | +help: consider using `is_pending()` + | +LL - let _ = match Pending::<()> { +LL - +LL - Ready(_) => false, +LL - Pending => true, +LL - }; +LL + let _ = Pending::<()>.is_pending(); + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:66:20 | LL | let _ = if let Ready(_) = poll { true } else { false }; - | -------^^^^^^^^------- help: try: `if poll.is_ready()` + | ^^^^^^^^ + | +help: consider using `is_ready()` + | +LL - let _ = if let Ready(_) = poll { true } else { false }; +LL + let _ = if poll.is_ready() { true } else { false }; + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:71:20 | LL | let _ = if let Ready(_) = gen_poll() { - | -------^^^^^^^^------------- help: try: `if gen_poll().is_ready()` + | ^^^^^^^^ + | +help: consider using `is_ready()` + | +LL - let _ = if let Ready(_) = gen_poll() { +LL + let _ = if gen_poll().is_ready() { + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:74:19 | LL | } else if let Pending = gen_poll() { - | -------^^^^^^^------------- help: try: `if gen_poll().is_pending()` + | ^^^^^^^ + | +help: consider using `is_pending()` + | +LL - } else if let Pending = gen_poll() { +LL + } else if gen_poll().is_pending() { + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:91:12 | LL | if let Ready(_) = Ready(42) {} - | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` + | ^^^^^^^^ + | +help: consider using `is_ready()` + | +LL - if let Ready(_) = Ready(42) {} +LL + if Ready(42).is_ready() {} + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:94:12 | LL | if let Pending = Pending::<()> {} - | -------^^^^^^^---------------- help: try: `if Pending::<()>.is_pending()` + | ^^^^^^^ + | +help: consider using `is_pending()` + | +LL - if let Pending = Pending::<()> {} +LL + if Pending::<()>.is_pending() {} + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:97:15 | LL | while let Ready(_) = Ready(42) {} - | ----------^^^^^^^^------------ help: try: `while Ready(42).is_ready()` + | ^^^^^^^^ + | +help: consider using `is_ready()` + | +LL - while let Ready(_) = Ready(42) {} +LL + while Ready(42).is_ready() {} + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:100:15 | LL | while let Pending = Pending::<()> {} - | ----------^^^^^^^---------------- help: try: `while Pending::<()>.is_pending()` + | ^^^^^^^ + | +help: consider using `is_pending()` + | +LL - while let Pending = Pending::<()> {} +LL + while Pending::<()>.is_pending() {} + | -error: redundant pattern matching, consider using `is_ready()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:103:5 | LL | / match Ready(42) { @@ -130,9 +249,19 @@ LL | | LL | | Ready(_) => true, LL | | Pending => false, LL | | }; - | |_____^ help: try: `Ready(42).is_ready()` + | |_____^ + | +help: consider using `is_ready()` + | +LL - match Ready(42) { +LL - +LL - Ready(_) => true, +LL - Pending => false, +LL - }; +LL + Ready(42).is_ready(); + | -error: redundant pattern matching, consider using `is_pending()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_poll.rs:109:5 | LL | / match Pending::<()> { @@ -140,7 +269,17 @@ LL | | LL | | Ready(_) => false, LL | | Pending => true, LL | | }; - | |_____^ help: try: `Pending::<()>.is_pending()` + | |_____^ + | +help: consider using `is_pending()` + | +LL - match Pending::<()> { +LL - +LL - Ready(_) => false, +LL - Pending => true, +LL - }; +LL + Pending::<()>.is_pending(); + | error: aborting due to 20 previous errors diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr index dda203b753c3e..ef44731a59a40 100644 --- a/tests/ui/redundant_pattern_matching_result.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -1,37 +1,66 @@ -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:14:12 | LL | if let Ok(_) = &result {} - | -------^^^^^---------- help: try: `if result.is_ok()` + | ^^^^^ | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` +help: consider using `is_ok()` + | +LL - if let Ok(_) = &result {} +LL + if result.is_ok() {} + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:17:12 | LL | if let Ok(_) = Ok::(42) {} - | -------^^^^^--------------------- help: try: `if Ok::(42).is_ok()` + | ^^^^^ + | +help: consider using `is_ok()` + | +LL - if let Ok(_) = Ok::(42) {} +LL + if Ok::(42).is_ok() {} + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:20:12 | LL | if let Err(_) = Err::(42) {} - | -------^^^^^^---------------------- help: try: `if Err::(42).is_err()` + | ^^^^^^ + | +help: consider using `is_err()` + | +LL - if let Err(_) = Err::(42) {} +LL + if Err::(42).is_err() {} + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:23:15 | LL | while let Ok(_) = Ok::(10) {} - | ----------^^^^^--------------------- help: try: `while Ok::(10).is_ok()` + | ^^^^^ + | +help: consider using `is_ok()` + | +LL - while let Ok(_) = Ok::(10) {} +LL + while Ok::(10).is_ok() {} + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:26:15 | LL | while let Err(_) = Ok::(10) {} - | ----------^^^^^^--------------------- help: try: `while Ok::(10).is_err()` + | ^^^^^^ + | +help: consider using `is_err()` + | +LL - while let Err(_) = Ok::(10) {} +LL + while Ok::(10).is_err() {} + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:37:5 | LL | / match Ok::(42) { @@ -39,9 +68,19 @@ LL | | LL | | Ok(_) => true, LL | | Err(_) => false, LL | | }; - | |_____^ help: try: `Ok::(42).is_ok()` + | |_____^ + | +help: consider using `is_ok()` + | +LL - match Ok::(42) { +LL - +LL - Ok(_) => true, +LL - Err(_) => false, +LL - }; +LL + Ok::(42).is_ok(); + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:43:5 | LL | / match Ok::(42) { @@ -49,9 +88,19 @@ LL | | LL | | Ok(_) => false, LL | | Err(_) => true, LL | | }; - | |_____^ help: try: `Ok::(42).is_err()` + | |_____^ + | +help: consider using `is_err()` + | +LL - match Ok::(42) { +LL - +LL - Ok(_) => false, +LL - Err(_) => true, +LL - }; +LL + Ok::(42).is_err(); + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:49:5 | LL | / match Err::(42) { @@ -59,9 +108,19 @@ LL | | LL | | Ok(_) => false, LL | | Err(_) => true, LL | | }; - | |_____^ help: try: `Err::(42).is_err()` + | |_____^ + | +help: consider using `is_err()` + | +LL - match Err::(42) { +LL - +LL - Ok(_) => false, +LL - Err(_) => true, +LL - }; +LL + Err::(42).is_err(); + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:55:5 | LL | / match Err::(42) { @@ -69,75 +128,151 @@ LL | | LL | | Ok(_) => true, LL | | Err(_) => false, LL | | }; - | |_____^ help: try: `Err::(42).is_ok()` + | |_____^ + | +help: consider using `is_ok()` + | +LL - match Err::(42) { +LL - +LL - Ok(_) => true, +LL - Err(_) => false, +LL - }; +LL + Err::(42).is_ok(); + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:61:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; - | -------^^^^^--------------------- help: try: `if Ok::(4).is_ok()` + | ^^^^^ + | +help: consider using `is_ok()` + | +LL - let _ = if let Ok(_) = Ok::(4) { true } else { false }; +LL + let _ = if Ok::(4).is_ok() { true } else { false }; + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:70:20 | LL | let _ = if let Ok(_) = gen_res() { - | -------^^^^^------------ help: try: `if gen_res().is_ok()` + | ^^^^^ + | +help: consider using `is_ok()` + | +LL - let _ = if let Ok(_) = gen_res() { +LL + let _ = if gen_res().is_ok() { + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:73:19 | LL | } else if let Err(_) = gen_res() { - | -------^^^^^^------------ help: try: `if gen_res().is_err()` + | ^^^^^^ + | +help: consider using `is_err()` + | +LL - } else if let Err(_) = gen_res() { +LL + } else if gen_res().is_err() { + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:97:19 | LL | while let Some(_) = r#try!(result_opt()) {} - | ----------^^^^^^^----------------------- help: try: `while r#try!(result_opt()).is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - while let Some(_) = r#try!(result_opt()) {} +LL + while r#try!(result_opt()).is_some() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:99:16 | LL | if let Some(_) = r#try!(result_opt()) {} - | -------^^^^^^^----------------------- help: try: `if r#try!(result_opt()).is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - if let Some(_) = r#try!(result_opt()) {} +LL + if r#try!(result_opt()).is_some() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:106:12 | LL | if let Some(_) = m!() {} - | -------^^^^^^^------- help: try: `if m!().is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - if let Some(_) = m!() {} +LL + if m!().is_some() {} + | -error: redundant pattern matching, consider using `is_some()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:108:15 | LL | while let Some(_) = m!() {} - | ----------^^^^^^^------- help: try: `while m!().is_some()` + | ^^^^^^^ + | +help: consider using `is_some()` + | +LL - while let Some(_) = m!() {} +LL + while m!().is_some() {} + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:127:12 | LL | if let Ok(_) = Ok::(42) {} - | -------^^^^^--------------------- help: try: `if Ok::(42).is_ok()` + | ^^^^^ + | +help: consider using `is_ok()` + | +LL - if let Ok(_) = Ok::(42) {} +LL + if Ok::(42).is_ok() {} + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:130:12 | LL | if let Err(_) = Err::(42) {} - | -------^^^^^^---------------------- help: try: `if Err::(42).is_err()` + | ^^^^^^ + | +help: consider using `is_err()` + | +LL - if let Err(_) = Err::(42) {} +LL + if Err::(42).is_err() {} + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:133:15 | LL | while let Ok(_) = Ok::(10) {} - | ----------^^^^^--------------------- help: try: `while Ok::(10).is_ok()` + | ^^^^^ + | +help: consider using `is_ok()` + | +LL - while let Ok(_) = Ok::(10) {} +LL + while Ok::(10).is_ok() {} + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:136:15 | LL | while let Err(_) = Ok::(10) {} - | ----------^^^^^^--------------------- help: try: `while Ok::(10).is_err()` + | ^^^^^^ + | +help: consider using `is_err()` + | +LL - while let Err(_) = Ok::(10) {} +LL + while Ok::(10).is_err() {} + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:139:5 | LL | / match Ok::(42) { @@ -145,9 +280,19 @@ LL | | LL | | Ok(_) => true, LL | | Err(_) => false, LL | | }; - | |_____^ help: try: `Ok::(42).is_ok()` + | |_____^ + | +help: consider using `is_ok()` + | +LL - match Ok::(42) { +LL - +LL - Ok(_) => true, +LL - Err(_) => false, +LL - }; +LL + Ok::(42).is_ok(); + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:145:5 | LL | / match Err::(42) { @@ -155,9 +300,19 @@ LL | | LL | | Ok(_) => false, LL | | Err(_) => true, LL | | }; - | |_____^ help: try: `Err::(42).is_err()` + | |_____^ + | +help: consider using `is_err()` + | +LL - match Err::(42) { +LL - +LL - Ok(_) => false, +LL - Err(_) => true, +LL - }; +LL + Err::(42).is_err(); + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:156:5 | LL | / match x { @@ -165,9 +320,19 @@ LL | | LL | | Ok(_) => true, LL | | _ => false, LL | | }; - | |_____^ help: try: `x.is_ok()` + | |_____^ + | +help: consider using `is_ok()` + | +LL - match x { +LL - +LL - Ok(_) => true, +LL - _ => false, +LL - }; +LL + x.is_ok(); + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:162:5 | LL | / match x { @@ -175,9 +340,19 @@ LL | | LL | | Ok(_) => false, LL | | _ => true, LL | | }; - | |_____^ help: try: `x.is_err()` + | |_____^ + | +help: consider using `is_err()` + | +LL - match x { +LL - +LL - Ok(_) => false, +LL - _ => true, +LL - }; +LL + x.is_err(); + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:168:5 | LL | / match x { @@ -185,9 +360,19 @@ LL | | LL | | Err(_) => true, LL | | _ => false, LL | | }; - | |_____^ help: try: `x.is_err()` + | |_____^ + | +help: consider using `is_err()` + | +LL - match x { +LL - +LL - Err(_) => true, +LL - _ => false, +LL - }; +LL + x.is_err(); + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:174:5 | LL | / match x { @@ -195,21 +380,43 @@ LL | | LL | | Err(_) => false, LL | | _ => true, LL | | }; - | |_____^ help: try: `x.is_ok()` + | |_____^ + | +help: consider using `is_ok()` + | +LL - match x { +LL - +LL - Err(_) => false, +LL - _ => true, +LL - }; +LL + x.is_ok(); + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:196:13 | LL | let _ = matches!(x, Ok(_)); - | ^^^^^^^^^^^^^^^^^^ help: try: `x.is_ok()` + | ^^^^^^^^^^^^^^^^^^ + | +help: consider using `is_ok()` + | +LL - let _ = matches!(x, Ok(_)); +LL + let _ = x.is_ok(); + | -error: redundant pattern matching, consider using `is_err()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:199:13 | LL | let _ = matches!(x, Err(_)); - | ^^^^^^^^^^^^^^^^^^^ help: try: `x.is_err()` + | ^^^^^^^^^^^^^^^^^^^ + | +help: consider using `is_err()` + | +LL - let _ = matches!(x, Err(_)); +LL + let _ = x.is_err(); + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:216:13 | LL | let _ = match test_expr!(42) { @@ -218,13 +425,29 @@ LL | | LL | | Ok(_) => true, LL | | Err(_) => false, LL | | }; - | |_____^ help: try: `test_expr!(42).is_ok()` + | |_____^ + | +help: consider using `is_ok()` + | +LL - let _ = match test_expr!(42) { +LL - +LL - Ok(_) => true, +LL - Err(_) => false, +LL - }; +LL + let _ = test_expr!(42).is_ok(); + | -error: redundant pattern matching, consider using `is_ok()` +error: redundant pattern matching --> tests/ui/redundant_pattern_matching_result.rs:229:13 | LL | let _ = matches!(x, Ok(_) if test_guard!(42)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_ok() && test_guard!(42)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `is_ok()` + | +LL - let _ = matches!(x, Ok(_) if test_guard!(42)); +LL + let _ = x.is_ok() && test_guard!(42); + | error: aborting due to 30 previous errors diff --git a/tests/ui/renamed_builtin_attr.fixed b/tests/ui/renamed_builtin_attr.fixed index 1ad7d8702bc00..3768ff1976ffc 100644 --- a/tests/ui/renamed_builtin_attr.fixed +++ b/tests/ui/renamed_builtin_attr.fixed @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #[clippy::cognitive_complexity = "1"] //~^ ERROR: usage of deprecated attribute fn main() {} diff --git a/tests/ui/renamed_builtin_attr.rs b/tests/ui/renamed_builtin_attr.rs index 0fb43b7f49016..232e2d4920e8c 100644 --- a/tests/ui/renamed_builtin_attr.rs +++ b/tests/ui/renamed_builtin_attr.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #[clippy::cyclomatic_complexity = "1"] //~^ ERROR: usage of deprecated attribute fn main() {} diff --git a/tests/ui/renamed_builtin_attr.stderr b/tests/ui/renamed_builtin_attr.stderr index 0ebc43739d6b7..5f3f9fdcc32a0 100644 --- a/tests/ui/renamed_builtin_attr.stderr +++ b/tests/ui/renamed_builtin_attr.stderr @@ -1,5 +1,5 @@ error: usage of deprecated attribute - --> tests/ui/renamed_builtin_attr.rs:3:3 + --> tests/ui/renamed_builtin_attr.rs:1:3 | LL | #[clippy::cyclomatic_complexity = "1"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `clippy::cognitive_complexity` diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index b4ad050df3b79..0056c54ff5d42 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -150,3 +150,17 @@ fn issue16249() { let closure = || Ok::<(), Large>(()); //~^ result_large_err } + +pub async fn async_large_err() -> Result<(), [u8; 512]> { + //~^ result_large_err + Ok(()) +} + +pub async fn async_small_err() -> Result<(), u128> { + Ok(()) +} + +pub async fn async_struct_error() -> Result<(), FullyDefinedLargeError> { + //~^ result_large_err + Ok(()) +} diff --git a/tests/ui/result_large_err.stderr b/tests/ui/result_large_err.stderr index fd39179c61cb5..653b7e354f431 100644 --- a/tests/ui/result_large_err.stderr +++ b/tests/ui/result_large_err.stderr @@ -120,5 +120,21 @@ LL | let closure = || Ok::<(), Large>(()); | = help: try reducing the size of `[u8; 1024]`, for example by boxing large elements or replacing it with `Box<[u8; 1024]>` -error: aborting due to 14 previous errors +error: the `Err`-variant returned from this function is very large + --> tests/ui/result_large_err.rs:154:35 + | +LL | pub async fn async_large_err() -> Result<(), [u8; 512]> { + | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes + | + = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` + +error: the `Err`-variant returned from this function is very large + --> tests/ui/result_large_err.rs:163:38 + | +LL | pub async fn async_struct_error() -> Result<(), FullyDefinedLargeError> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes + | + = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box` + +error: aborting due to 16 previous errors diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs index ddbe0eb556c85..fdf155ff291d5 100644 --- a/tests/ui/result_unit_error.rs +++ b/tests/ui/result_unit_error.rs @@ -62,4 +62,16 @@ pub mod issue_6546 { } } +pub mod issue_17070 { + pub async fn returns_unit_error_async() -> Result { + //~^ result_unit_err + + Err(()) + } + + async fn private_unit_errors_async() -> Result { + Err(()) + } +} + fn main() {} diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr index d6c0924d88689..02ef9f3a1b7ea 100644 --- a/tests/ui/result_unit_error.stderr +++ b/tests/ui/result_unit_error.stderr @@ -40,5 +40,13 @@ LL | pub fn should_lint() -> ResInv<(), usize> { | = help: use a custom `Error` type instead -error: aborting due to 5 previous errors +error: this returns a `Result<_, ()>` + --> tests/ui/result_unit_error.rs:66:5 + | +LL | pub async fn returns_unit_error_async() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom `Error` type instead + +error: aborting due to 6 previous errors diff --git a/tests/ui/single_range_in_vec_init.1.fixed b/tests/ui/single_range_in_vec_init.1.fixed index 0af91907ad05f..e0c1dba073d86 100644 --- a/tests/ui/single_range_in_vec_init.1.fixed +++ b/tests/ui/single_range_in_vec_init.1.fixed @@ -82,3 +82,10 @@ fn issue16044() { let input = (0..as_i32!(10)).collect::>(); //~^ single_range_in_vec_init } + +fn issue16508() { + (0..=10).collect::>(); + //~^ single_range_in_vec_init + (0..=10).collect::>(); + //~^ single_range_in_vec_init +} diff --git a/tests/ui/single_range_in_vec_init.2.fixed b/tests/ui/single_range_in_vec_init.2.fixed index fd6b91360aeb2..5784702661926 100644 --- a/tests/ui/single_range_in_vec_init.2.fixed +++ b/tests/ui/single_range_in_vec_init.2.fixed @@ -82,3 +82,10 @@ fn issue16044() { let input = (0..as_i32!(10)).collect::>(); //~^ single_range_in_vec_init } + +fn issue16508() { + (0..=10).collect::>(); + //~^ single_range_in_vec_init + (0..=10).collect::>(); + //~^ single_range_in_vec_init +} diff --git a/tests/ui/single_range_in_vec_init.rs b/tests/ui/single_range_in_vec_init.rs index 1cc2b894c0348..3ff07c386d218 100644 --- a/tests/ui/single_range_in_vec_init.rs +++ b/tests/ui/single_range_in_vec_init.rs @@ -82,3 +82,10 @@ fn issue16044() { let input = vec![0..as_i32!(10)]; //~^ single_range_in_vec_init } + +fn issue16508() { + [0..=10]; + //~^ single_range_in_vec_init + vec![0..=10]; + //~^ single_range_in_vec_init +} diff --git a/tests/ui/single_range_in_vec_init.stderr b/tests/ui/single_range_in_vec_init.stderr index d93379777d39c..1fec5119d8155 100644 --- a/tests/ui/single_range_in_vec_init.stderr +++ b/tests/ui/single_range_in_vec_init.stderr @@ -172,5 +172,29 @@ LL - let input = vec![0..as_i32!(10)]; LL + let input = (0..as_i32!(10)).collect::>(); | -error: aborting due to 11 previous errors +error: an array of `RangeInclusive` that is only one element + --> tests/ui/single_range_in_vec_init.rs:87:5 + | +LL | [0..=10]; + | ^^^^^^^^ + | +help: if you wanted a `Vec` that contains the entire range, try + | +LL - [0..=10]; +LL + (0..=10).collect::>(); + | + +error: a `Vec` of `RangeInclusive` that is only one element + --> tests/ui/single_range_in_vec_init.rs:89:5 + | +LL | vec![0..=10]; + | ^^^^^^^^^^^^ + | +help: if you wanted a `Vec` that contains the entire range, try + | +LL - vec![0..=10]; +LL + (0..=10).collect::>(); + | + +error: aborting due to 13 previous errors diff --git a/tests/ui/single_range_in_vec_init_unfixable.rs b/tests/ui/single_range_in_vec_init_unfixable.rs index 33378b386f344..5574c51155ec9 100644 --- a/tests/ui/single_range_in_vec_init_unfixable.rs +++ b/tests/ui/single_range_in_vec_init_unfixable.rs @@ -10,3 +10,26 @@ fn issue16306(v: &[i32]) { takes_range_slice(&[0..len as i64]); //~^ single_range_in_vec_init } + +#[allow(clippy::no_effect, clippy::useless_vec)] +fn issue16508_open_ended() { + [..10]; + //~^ single_range_in_vec_init + vec![..10]; + //~^ single_range_in_vec_init + + [10..]; + //~^ single_range_in_vec_init + vec![10..]; + //~^ single_range_in_vec_init + + [..=10]; + //~^ single_range_in_vec_init + vec![..=10]; + //~^ single_range_in_vec_init + + [..]; + //~^ single_range_in_vec_init + vec![..]; + //~^ single_range_in_vec_init +} diff --git a/tests/ui/single_range_in_vec_init_unfixable.stderr b/tests/ui/single_range_in_vec_init_unfixable.stderr index b10af21ed21c2..32caefb4f3dde 100644 --- a/tests/ui/single_range_in_vec_init_unfixable.stderr +++ b/tests/ui/single_range_in_vec_init_unfixable.stderr @@ -12,5 +12,53 @@ LL - takes_range_slice(&[0..len as i64]); LL + takes_range_slice(&(0..len as i64).collect::>()); | -error: aborting due to 1 previous error +error: an array of `RangeTo` that is only one element + --> tests/ui/single_range_in_vec_init_unfixable.rs:16:5 + | +LL | [..10]; + | ^^^^^^ + +error: a `Vec` of `RangeTo` that is only one element + --> tests/ui/single_range_in_vec_init_unfixable.rs:18:5 + | +LL | vec![..10]; + | ^^^^^^^^^^ + +error: an array of `RangeFrom` that is only one element + --> tests/ui/single_range_in_vec_init_unfixable.rs:21:5 + | +LL | [10..]; + | ^^^^^^ + +error: a `Vec` of `RangeFrom` that is only one element + --> tests/ui/single_range_in_vec_init_unfixable.rs:23:5 + | +LL | vec![10..]; + | ^^^^^^^^^^ + +error: an array of `RangeToInclusive` that is only one element + --> tests/ui/single_range_in_vec_init_unfixable.rs:26:5 + | +LL | [..=10]; + | ^^^^^^^ + +error: a `Vec` of `RangeToInclusive` that is only one element + --> tests/ui/single_range_in_vec_init_unfixable.rs:28:5 + | +LL | vec![..=10]; + | ^^^^^^^^^^^ + +error: an array of `RangeFull` that is only one element + --> tests/ui/single_range_in_vec_init_unfixable.rs:31:5 + | +LL | [..]; + | ^^^^ + +error: a `Vec` of `RangeFull` that is only one element + --> tests/ui/single_range_in_vec_init_unfixable.rs:33:5 + | +LL | vec![..]; + | ^^^^^^^^ + +error: aborting due to 9 previous errors diff --git a/tests/ui/ty_fn_sig.rs b/tests/ui/ty_fn_sig.rs index 60767bfad135c..d7aa8f9a2c4d9 100644 --- a/tests/ui/ty_fn_sig.rs +++ b/tests/ui/ty_fn_sig.rs @@ -2,7 +2,7 @@ // Regression test pub fn retry(f: F) { - for _i in 0.. { + for _i in 0..=i32::MAX { f(); } } diff --git a/tests/ui/unknown_attribute.rs b/tests/ui/unknown_attribute.rs index 3241742b5d464..932f284d5b713 100644 --- a/tests/ui/unknown_attribute.rs +++ b/tests/ui/unknown_attribute.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #[clippy::unknown] //~^ ERROR: usage of unknown attribute #[clippy::cognitive_complexity = "1"] diff --git a/tests/ui/unknown_attribute.stderr b/tests/ui/unknown_attribute.stderr index 1d4d50ffc02a4..17c592b3aa078 100644 --- a/tests/ui/unknown_attribute.stderr +++ b/tests/ui/unknown_attribute.stderr @@ -1,5 +1,5 @@ error: usage of unknown attribute - --> tests/ui/unknown_attribute.rs:3:3 + --> tests/ui/unknown_attribute.rs:1:3 | LL | #[clippy::unknown] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/unused_async_trait_impl.fixed b/tests/ui/unused_async_trait_impl.fixed index 384bb3449f90b..d8a96afdf1be5 100644 --- a/tests/ui/unused_async_trait_impl.fixed +++ b/tests/ui/unused_async_trait_impl.fixed @@ -107,3 +107,36 @@ mod macros { } } } + +mod issue17179 { + struct Test; + + impl crate::HasAsyncMethod for Test { + fn do_something() -> impl Future { + //~^ unused_async_trait_impl + + // Test that local functions are not touched by the suggestion. + fn local_func() -> u32 { + if 5 == 2 { + return 1; + } + 2 + } + + // Test that we do not change the tail expr or return in a (unrelated) closure. + let f = || { + if 5 == 2 { + return 1; + } + 2 + }; + + // However the following return statement and tail expression should be changed. + if f() == 5 { + return std::future::ready(3); + } + + std::future::ready(5) + } + } +} diff --git a/tests/ui/unused_async_trait_impl.rs b/tests/ui/unused_async_trait_impl.rs index 0a93635c264f5..f2b69eb46428f 100644 --- a/tests/ui/unused_async_trait_impl.rs +++ b/tests/ui/unused_async_trait_impl.rs @@ -107,3 +107,36 @@ mod macros { } } } + +mod issue17179 { + struct Test; + + impl crate::HasAsyncMethod for Test { + async fn do_something() -> u32 { + //~^ unused_async_trait_impl + + // Test that local functions are not touched by the suggestion. + fn local_func() -> u32 { + if 5 == 2 { + return 1; + } + 2 + } + + // Test that we do not change the tail expr or return in a (unrelated) closure. + let f = || { + if 5 == 2 { + return 1; + } + 2 + }; + + // However the following return statement and tail expression should be changed. + if f() == 5 { + return 3; + } + + 5 + } + } +} diff --git a/tests/ui/unused_async_trait_impl.stderr b/tests/ui/unused_async_trait_impl.stderr index 7265dc2dd0595..b1da2e2e351a1 100644 --- a/tests/ui/unused_async_trait_impl.stderr +++ b/tests/ui/unused_async_trait_impl.stderr @@ -90,5 +90,27 @@ LL | LL ~ std::future::ready(vec![]) | -error: aborting due to 5 previous errors +error: unused `async` for async trait impl function with no `.await` statements + --> tests/ui/unused_async_trait_impl.rs:115:9 + | +LL | / async fn do_something() -> u32 { +... | +LL | | 5 +LL | | } + | |_________^ + | + = note: `std::future::ready` creates a `Future` which returns the value immediately when `poll`ed +help: consider removing the `async` from this function and returning `impl Future` instead + | +LL ~ fn do_something() -> impl Future { +LL | +... +LL | if f() == 5 { +LL ~ return std::future::ready(3); +LL | } +LL | +LL ~ std::future::ready(5) + | + +error: aborting due to 6 previous errors diff --git a/tests/ui/with_capacity_zero.fixed b/tests/ui/with_capacity_zero.fixed new file mode 100644 index 0000000000000..6da614d000248 --- /dev/null +++ b/tests/ui/with_capacity_zero.fixed @@ -0,0 +1,50 @@ +#![warn(clippy::with_capacity_zero)] +#![allow(unused)] +#![allow(clippy::eq_op)] + +use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque}; +use std::ffi::OsString; +use std::io::BufReader; +use std::path::PathBuf; + +struct MyStruct; +impl MyStruct { + fn with_capacity(cap: usize) -> Self { + MyStruct + } +} + +fn main() { + // Positive cases + let v: Vec = Vec::new(); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let s = String::new(); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let v2 = std::vec::Vec::::new(); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let map = HashMap::::new(); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let set = HashSet::::new(); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let deque = VecDeque::::new(); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let heap = BinaryHeap::::new(); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let path = PathBuf::new(); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let os_str = OsString::new(); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + + // Negative cases + let v_non_zero = Vec::::with_capacity(10); + let s_non_zero = String::with_capacity(5); + + let cap = 0; + let v_variable = Vec::::with_capacity(cap); + + // Custom struct with with_capacity method + let custom = MyStruct::with_capacity(0); + + // Two-argument capacity call (BufReader) + let reader = BufReader::with_capacity(0, std::io::empty()); +} diff --git a/tests/ui/with_capacity_zero.rs b/tests/ui/with_capacity_zero.rs new file mode 100644 index 0000000000000..0f8bfb12c34f5 --- /dev/null +++ b/tests/ui/with_capacity_zero.rs @@ -0,0 +1,50 @@ +#![warn(clippy::with_capacity_zero)] +#![allow(unused)] +#![allow(clippy::eq_op)] + +use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque}; +use std::ffi::OsString; +use std::io::BufReader; +use std::path::PathBuf; + +struct MyStruct; +impl MyStruct { + fn with_capacity(cap: usize) -> Self { + MyStruct + } +} + +fn main() { + // Positive cases + let v: Vec = Vec::with_capacity(0); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let s = String::with_capacity(0); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let v2 = std::vec::Vec::::with_capacity(0); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let map = HashMap::::with_capacity(0); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let set = HashSet::::with_capacity(0); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let deque = VecDeque::::with_capacity(0); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let heap = BinaryHeap::::with_capacity(0); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let path = PathBuf::with_capacity(0); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + let os_str = OsString::with_capacity(0); + //~^ ERROR: calling `with_capacity(0)` is equivalent to `new()` + + // Negative cases + let v_non_zero = Vec::::with_capacity(10); + let s_non_zero = String::with_capacity(5); + + let cap = 0; + let v_variable = Vec::::with_capacity(cap); + + // Custom struct with with_capacity method + let custom = MyStruct::with_capacity(0); + + // Two-argument capacity call (BufReader) + let reader = BufReader::with_capacity(0, std::io::empty()); +} diff --git a/tests/ui/with_capacity_zero.stderr b/tests/ui/with_capacity_zero.stderr new file mode 100644 index 0000000000000..5fce9d90e0360 --- /dev/null +++ b/tests/ui/with_capacity_zero.stderr @@ -0,0 +1,112 @@ +error: calling `with_capacity(0)` is equivalent to `new()` + --> tests/ui/with_capacity_zero.rs:19:23 + | +LL | let v: Vec = Vec::with_capacity(0); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::with-capacity-zero` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::with_capacity_zero)]` +help: use `new()` instead + | +LL - let v: Vec = Vec::with_capacity(0); +LL + let v: Vec = Vec::new(); + | + +error: calling `with_capacity(0)` is equivalent to `new()` + --> tests/ui/with_capacity_zero.rs:21:13 + | +LL | let s = String::with_capacity(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `new()` instead + | +LL - let s = String::with_capacity(0); +LL + let s = String::new(); + | + +error: calling `with_capacity(0)` is equivalent to `new()` + --> tests/ui/with_capacity_zero.rs:23:14 + | +LL | let v2 = std::vec::Vec::::with_capacity(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `new()` instead + | +LL - let v2 = std::vec::Vec::::with_capacity(0); +LL + let v2 = std::vec::Vec::::new(); + | + +error: calling `with_capacity(0)` is equivalent to `new()` + --> tests/ui/with_capacity_zero.rs:25:15 + | +LL | let map = HashMap::::with_capacity(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `new()` instead + | +LL - let map = HashMap::::with_capacity(0); +LL + let map = HashMap::::new(); + | + +error: calling `with_capacity(0)` is equivalent to `new()` + --> tests/ui/with_capacity_zero.rs:27:15 + | +LL | let set = HashSet::::with_capacity(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `new()` instead + | +LL - let set = HashSet::::with_capacity(0); +LL + let set = HashSet::::new(); + | + +error: calling `with_capacity(0)` is equivalent to `new()` + --> tests/ui/with_capacity_zero.rs:29:17 + | +LL | let deque = VecDeque::::with_capacity(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `new()` instead + | +LL - let deque = VecDeque::::with_capacity(0); +LL + let deque = VecDeque::::new(); + | + +error: calling `with_capacity(0)` is equivalent to `new()` + --> tests/ui/with_capacity_zero.rs:31:16 + | +LL | let heap = BinaryHeap::::with_capacity(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `new()` instead + | +LL - let heap = BinaryHeap::::with_capacity(0); +LL + let heap = BinaryHeap::::new(); + | + +error: calling `with_capacity(0)` is equivalent to `new()` + --> tests/ui/with_capacity_zero.rs:33:16 + | +LL | let path = PathBuf::with_capacity(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `new()` instead + | +LL - let path = PathBuf::with_capacity(0); +LL + let path = PathBuf::new(); + | + +error: calling `with_capacity(0)` is equivalent to `new()` + --> tests/ui/with_capacity_zero.rs:35:18 + | +LL | let os_str = OsString::with_capacity(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `new()` instead + | +LL - let os_str = OsString::with_capacity(0); +LL + let os_str = OsString::new(); + | + +error: aborting due to 9 previous errors + diff --git a/triagebot.toml b/triagebot.toml index e357c528ed65f..667468474b3c1 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -94,6 +94,7 @@ users_on_vacation = [ "Alexendoo", "y21", "blyxyas", + "ada4a" ] [assign.owners] From bbe21a31d78e4944f2bda925ac550da607ca4300 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Wed, 22 Apr 2026 22:22:13 +1000 Subject: [PATCH 024/278] Adjust `std::io::Error` tests to only assess public API Viewing internals wont be possible from `std` once moved into `core`. --- library/std/src/io/error/tests.rs | 45 ++++++++----------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index a8eef06381dae..a3a2f5830ae91 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,4 +1,4 @@ -use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error}; +use crate::io::{Error, ErrorKind, const_error}; use crate::sys::io::{decode_error_kind, error_string}; use crate::{assert_matches, error, fmt}; @@ -12,12 +12,7 @@ fn test_debug_error() { let code = 6; let msg = error_string(code); let kind = decode_error_kind(code); - let err = Error { - repr: Repr::new_custom(Box::new(Custom { - kind: ErrorKind::InvalidInput, - error: Box::new(Error { repr: super::Repr::new_os(code) }), - })), - }; + let err = Error::new(ErrorKind::InvalidInput, Error::from_raw_os_error(code)); let expected = format!( "Custom {{ \ kind: InvalidInput, \ @@ -70,10 +65,6 @@ fn test_os_packing() { for code in -20..20 { let e = Error::from_raw_os_error(code); assert_eq!(e.raw_os_error(), Some(code)); - assert_matches!( - e.repr.data(), - ErrorData::Os(c) if c == code, - ); } } @@ -82,28 +73,17 @@ fn test_errorkind_packing() { assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound); assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied); assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized); - // Check that the innards look like what we want. - assert_matches!( - Error::from(ErrorKind::OutOfMemory).repr.data(), - ErrorData::Simple(ErrorKind::OutOfMemory), - ); } #[test] fn test_simple_message_packing() { - use super::ErrorKind::*; - use super::SimpleMessage; + use ErrorKind::*; macro_rules! check_simple_msg { ($err:expr, $kind:ident, $msg:literal) => {{ let e = &$err; // Check that the public api is right. assert_eq!(e.kind(), $kind); assert!(format!("{e:?}").contains($msg)); - // and we got what we expected - assert_matches!( - e.repr.data(), - ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg }) - ); }}; } @@ -128,14 +108,11 @@ impl fmt::Display for Bojji { #[test] fn test_custom_error_packing() { - use super::Custom; let test = Error::new(ErrorKind::Uncategorized, Bojji(true)); + assert_eq!(test.kind(), ErrorKind::Uncategorized); assert_matches!( - test.repr.data(), - ErrorData::Custom(Custom { - kind: ErrorKind::Uncategorized, - error, - }) if error.downcast_ref::().as_deref() == Some(&Bojji(true)), + test.get_ref(), + Some(error) if error.downcast_ref::().as_deref() == Some(&Bojji(true)), ); } @@ -181,11 +158,11 @@ fn test_std_io_error_downcast() { assert_eq!(kind, io_error.kind()); // Case 5: simple message - const SIMPLE_MESSAGE: SimpleMessage = - SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" }; - let io_error = Error::from_static_message(&SIMPLE_MESSAGE); + const KIND: ErrorKind = ErrorKind::Other; + const MESSAGE: &str = "simple message error test"; + let io_error = const_error!(KIND, MESSAGE); let io_error = io_error.downcast::().unwrap_err(); - assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind()); - assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}")); + assert_eq!(KIND, io_error.kind()); + assert_eq!(MESSAGE, format!("{io_error}")); } From ca2299f921abda98087de537ce4259f73cb446cb Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Wed, 22 Apr 2026 22:26:02 +1000 Subject: [PATCH 025/278] Adjust `Error` documentation Adjust `Error` documentation `core` is more restrictive with documentation quality and linking to other items. Methods that will be implemented through incoherence must also be explicitly linked. --- library/std/src/io/error.rs | 53 ++++++++++++---------- library/std/src/io/error/repr_bitpacked.rs | 19 ++++---- library/std/src/os/unix/process.rs | 4 +- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 360ca83c65a91..67ffc79525b3a 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -35,11 +35,11 @@ use crate::{error, fmt, result, sys}; /// /// While usual Rust style is to import types directly, aliases of [`Result`] /// often are not, to make it easier to distinguish between them. [`Result`] is -/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias +/// generally assumed to be [`core::result::Result`][`Result`], and so users of this alias /// will generally use `io::Result` instead of shadowing the [prelude]'s import -/// of [`std::result::Result`][`Result`]. +/// of [`core::result::Result`][`Result`]. /// -/// [`std::io`]: crate::io +/// [`std::io`]: ../../std/io/index.html /// [`io::Error`]: Error /// [`Result`]: crate::result::Result /// [prelude]: crate::prelude @@ -63,16 +63,16 @@ use crate::{error, fmt, result, sys}; #[doc(search_unbox)] pub type Result = result::Result; -/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and +/// The error type for I/O operations of the [`Read`][Read], [`Write`][Write], [`Seek`][Seek], and /// associated traits. /// /// Errors mostly originate from the underlying OS, but custom instances of /// `Error` can be created with crafted error messages and a particular value of /// [`ErrorKind`]. /// -/// [`Read`]: crate::io::Read -/// [`Write`]: crate::io::Write -/// [`Seek`]: crate::io::Seek +/// [Read]: ../../std/io/trait.Read.html +/// [Write]: ../../std/io/trait.Write.html +/// [Seek]: ../../std/io/trait.Seek.html #[stable(feature = "rust1", since = "1.0.0")] pub struct Error { repr: Repr, @@ -154,7 +154,7 @@ enum ErrorData { // have on 32 bit platforms. // // (For the sake of being explicit: the alignment requirement here only matters -// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't +// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't // matter at all) #[doc(hidden)] #[unstable(feature = "io_const_error_internals", issue = "none")] @@ -167,9 +167,11 @@ pub struct SimpleMessage { /// Creates a new I/O error from a known kind of error and a string literal. /// -/// Contrary to [`Error::new`], this macro does not allocate and can be used in +/// Contrary to [`Error::new`][new], this macro does not allocate and can be used in /// `const` contexts. /// +/// [new]: ../../std/io/struct.Error.html#method.new +/// /// # Example /// ``` /// #![feature(io_const_error)] @@ -262,9 +264,11 @@ impl Error { /// Creates a new I/O error from an arbitrary error payload. /// /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. It is a shortcut for [`Error::new`] + /// originate from the OS itself. It is a shortcut for [`Error::new`][new] /// with [`ErrorKind::Other`]. /// + /// [new]: struct.Error.html#method.new + /// /// # Examples /// /// ``` @@ -367,12 +371,12 @@ impl Error { /// Returns the OS error that this error represents (if any). /// - /// If this [`Error`] was constructed via [`last_os_error`] or - /// [`from_raw_os_error`], then this function will return [`Some`], otherwise + /// If this [`Error`] was constructed via [`last_os_error`][last_os_error] or + /// [`from_raw_os_error`][from_raw_os_error], then this function will return [`Some`], otherwise /// it will return [`None`]. /// - /// [`last_os_error`]: Error::last_os_error - /// [`from_raw_os_error`]: Error::from_raw_os_error + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error + /// [from_raw_os_error]: ../../std/io/struct.Error.html#method.from_raw_os_error /// /// # Examples /// @@ -408,10 +412,10 @@ impl Error { /// Returns a reference to the inner error wrapped by this error (if any). /// - /// If this [`Error`] was constructed via [`new`] then this function will + /// If this [`Error`] was constructed via [`new`][new] then this function will /// return [`Some`], otherwise it will return [`None`]. /// - /// [`new`]: Error::new + /// [new]: ../../std/io/struct.Error.html#method.new /// /// # Examples /// @@ -448,10 +452,10 @@ impl Error { /// Returns a mutable reference to the inner error wrapped by this error /// (if any). /// - /// If this [`Error`] was constructed via [`new`] then this function will + /// If this [`Error`] was constructed via [`new`][new] then this function will /// return [`Some`], otherwise it will return [`None`]. /// - /// [`new`]: Error::new + /// [new]: ../../std/io/struct.Error.html#method.new /// /// # Examples /// @@ -521,12 +525,12 @@ impl Error { /// Consumes the `Error`, returning its inner error (if any). /// - /// If this [`Error`] was constructed via [`new`] or [`other`], + /// If this [`Error`] was constructed via [`new`][new] or [`other`][other], /// then this function will return [`Some`], /// otherwise it will return [`None`]. /// - /// [`new`]: Error::new - /// [`other`]: Error::other + /// [new]: struct.Error.html#method.new + /// [other]: struct.Error.html#method.other /// /// # Examples /// @@ -571,8 +575,9 @@ impl Error { /// /// This method is meant to be a convenience routine for calling /// `Box::downcast` on the custom boxed error, returned by - /// [`Error::into_inner`]. + /// [`Error::into_inner`][into_inner]. /// + /// [into_inner]: struct.Error.html#method.into_inner /// /// # Examples /// @@ -655,9 +660,9 @@ impl Error { /// This may be a value set by Rust code constructing custom `io::Error`s, /// or if this `io::Error` was sourced from the operating system, /// it will be a value inferred from the system's error encoding. - /// See [`last_os_error`] for more details. + /// See [`last_os_error`][last_os_error] for more details. /// - /// [`last_os_error`]: Error::last_os_error + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error /// /// # Examples /// diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 7c237825459af..3b81759eb86e2 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -146,7 +146,8 @@ impl Repr { // (rather than `ptr::wrapping_add`), but it's unclear this would give // any benefit, so we just use `wrapping_add` instead. let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>(); - // Safety: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, + // SAFETY: + // `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, // because `p`'s alignment means it isn't allowed to have any of the // `TAG_BITS` set (you can verify that addition and bitwise-or are the // same when the operands have no bits in common using a truth table). @@ -166,7 +167,7 @@ impl Repr { #[inline] pub(super) fn new_os(code: RawOsError) -> Self { let utagged = ((code as usize) << 32) | TAG_OS; - // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. + // SAFETY: `TAG_OS` is not zero, so the result of the `|` is not 0. let res = Self( NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, @@ -183,7 +184,7 @@ impl Repr { #[inline] pub(super) fn new_simple(kind: ErrorKind) -> Self { let utagged = ((kind as usize) << 32) | TAG_SIMPLE; - // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. + // SAFETY: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. let res = Self( NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, @@ -200,26 +201,26 @@ impl Repr { #[inline] pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { - // Safety: References are never null. + // SAFETY: References are never null. Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData) } #[inline] pub(super) fn data(&self) -> ErrorData<&Custom> { - // Safety: We're a Repr, decode_repr is fine. + // SAFETY: We're a Repr, decode_repr is fine. unsafe { decode_repr(self.0, |c| &*c) } } #[inline] pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> { - // Safety: We're a Repr, decode_repr is fine. + // SAFETY: We're a Repr, decode_repr is fine. unsafe { decode_repr(self.0, |c| &mut *c) } } #[inline] pub(super) fn into_data(self) -> ErrorData> { let this = core::mem::ManuallyDrop::new(self); - // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we prevent double-drop using `ManuallyDrop`. unsafe { decode_repr(this.0, |p| Box::from_raw(p)) } } @@ -228,7 +229,7 @@ impl Repr { impl Drop for Repr { #[inline] fn drop(&mut self) { - // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we're being dropped. unsafe { let _ = decode_repr(self.0, |p| Box::::from_raw(p)); @@ -255,7 +256,7 @@ where let kind_bits = (bits >> 32) as u32; let kind = ErrorKind::from_prim(kind_bits).unwrap_or_else(|| { debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits); - // This means the `ptr` passed in was not valid, which violates + // SAFETY: This means the `ptr` passed in was not valid, which violates // the unsafe contract of `decode_repr`. // // Using this rather than unwrap meaningfully improves the code diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index b0a41502614f6..8a7b94d914ab0 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -102,8 +102,8 @@ pub impl(self) trait CommandExt { /// [POSIX fork() specification]: /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html /// [`std::env`]: mod@crate::env - /// [`Error::new`]: crate::io::Error::new - /// [`Error::other`]: crate::io::Error::other + /// [`Error::new`]: ../../../io/struct.Error.html#method.new + /// [`Error::other`]: ../../../io/struct.Error.html#method.other #[stable(feature = "process_pre_exec", since = "1.34.0")] unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command where From 0f4195a27e5c1e32f26101bcaccc359365c0e0c6 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 11:27:51 +1000 Subject: [PATCH 026/278] Change `repr` module definition to use `path` attribute Personal preference that will be used for another module in a later commit. Purely stylistic. --- library/std/src/io/error.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 67ffc79525b3a..b6fe334b9cd83 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -13,16 +13,17 @@ pub use core::io::RawOsError; // This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. // Therefore, the packed representation is explicitly disabled for UEFI // targets, and the unpacked representation must be used instead. -#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] -mod repr_bitpacked; -#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] -use repr_bitpacked::Repr; - -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -mod repr_unpacked; -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -use repr_unpacked::Repr; - +#[cfg_attr( + all(target_pointer_width = "64", not(target_os = "uefi")), + path = "error/repr_bitpacked.rs" +)] +#[cfg_attr( + not(all(target_pointer_width = "64", not(target_os = "uefi"))), + path = "error/repr_unpacked.rs" +)] +mod repr; + +use self::repr::Repr; use crate::{error, fmt, result, sys}; /// A specialized [`Result`] type for I/O operations. From 76ace520c3dc6985d21e2935aa4b552514bc55c0 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Wed, 22 Apr 2026 22:32:12 +1000 Subject: [PATCH 027/278] Setup `alloc::io` --- library/alloc/src/io/mod.rs | 13 +++++++++++++ library/alloc/src/lib.rs | 6 ++++++ library/std/src/io/error.rs | 4 ++-- library/std/src/io/mod.rs | 9 +++++---- library/std/src/lib.rs | 1 + src/tools/linkchecker/main.rs | 16 ++++++++++++++++ 6 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 library/alloc/src/io/mod.rs diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs new file mode 100644 index 0000000000000..411b3cd9e8b87 --- /dev/null +++ b/library/alloc/src/io/mod.rs @@ -0,0 +1,13 @@ +//! Traits, helpers, and type definitions for core I/O functionality. + +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub use core::io::RawOsError; +#[unstable(feature = "core_io_borrowed_buf", issue = "117693")] +pub use core::io::{BorrowedBuf, BorrowedCursor}; +#[unstable(feature = "alloc_io", issue = "154046")] +pub use core::io::{ + Chain, Cursor, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, Sink, Take, empty, repeat, sink, +}; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use core::io::{chain, take}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index f66dc648f809b..ff7729eed7073 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -113,6 +113,9 @@ #![feature(const_try)] #![feature(copied_into_inner)] #![feature(core_intrinsics)] +#![feature(core_io)] +#![feature(core_io_borrowed_buf)] +#![feature(core_io_internals)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] #![feature(diagnostic_on_move)] @@ -144,6 +147,7 @@ #![feature(ptr_cast_slice)] #![feature(ptr_internals)] #![feature(ptr_metadata)] +#![feature(raw_os_error_ty)] #![feature(rev_into_inner)] #![feature(set_ptr_value)] #![feature(share_trait)] @@ -233,6 +237,8 @@ pub mod collections; pub mod ffi; pub mod fmt; pub mod intrinsics; +#[unstable(feature = "alloc_io", issue = "154046")] +pub mod io; #[cfg(not(no_rc))] pub mod rc; pub mod slice; diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index b6fe334b9cd83..5e67fb4f1419f 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -2,9 +2,9 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::io::ErrorKind; +pub use alloc_crate::io::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use core::io::RawOsError; +pub use alloc_crate::io::RawOsError; // On 64-bit platforms, `io::Error` may use a bit-packed representation to // reduce size. However, this representation assumes that error codes are diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 91092579a884e..2ddd6c66091dc 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -297,13 +297,14 @@ #[cfg(test)] mod tests; +use core::slice::memchr; + #[unstable(feature = "read_buf", issue = "78485")] -pub use core::io::{BorrowedBuf, BorrowedCursor}; +pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::io::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; +pub use alloc_crate::io::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; #[stable(feature = "iovec", since = "1.36.0")] -pub use core::io::{IoSlice, IoSliceMut}; -use core::slice::memchr; +pub use alloc_crate::io::{IoSlice, IoSliceMut}; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ee7f108bc4e3d..34983095b69f5 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -391,6 +391,7 @@ // // Library features (alloc): // tidy-alphabetical-start +#![feature(alloc_io)] #![feature(allocator_api)] #![feature(clone_from_ref)] #![feature(get_mut_unchecked)] diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index e7455fc701cfb..0d3870f456a07 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -92,6 +92,22 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ "core\\io\\slice::sort_by_key", "#method.sort_by_cached_key" ]), + ("alloc/io/struct.IoSlice.html", &[ + "#method.to_ascii_uppercase", + "#method.to_ascii_lowercase", + "alloc/io/slice::sort_by_key", + "alloc\\io\\slice::sort_by_key", + "#method.sort_by_key", + "#method.sort_by_cached_key" + ]), + ("alloc/io/struct.IoSliceMut.html", &[ + "#method.to_ascii_uppercase", + "#method.to_ascii_lowercase", + "alloc/io/slice::sort_by_key", + "alloc\\io\\slice::sort_by_key", + "#method.sort_by_key", + "#method.sort_by_cached_key" + ]), ]; #[rustfmt::skip] From f880e6d0e8fb2b92b41a6cbcb3cf7d70edc4878e Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 12:12:27 +1000 Subject: [PATCH 028/278] Make common `Error` constants public and hidden They'll be moved into `core` but must be accessible from `std`. --- library/std/src/io/error.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 5e67fb4f1419f..0d31214651494 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -87,29 +87,43 @@ impl fmt::Debug for Error { } /// Common errors constants for use in std -#[allow(dead_code)] +#[doc(hidden)] impl Error { - pub(crate) const INVALID_UTF8: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const INVALID_UTF8: Self = const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); - pub(crate) const READ_EXACT_EOF: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const READ_EXACT_EOF: Self = const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); - pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!( + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNKNOWN_THREAD_COUNT: Self = const_error!( ErrorKind::NotFound, "the number of hardware threads is not known for the target platform", ); - pub(crate) const UNSUPPORTED_PLATFORM: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNSUPPORTED_PLATFORM: Self = const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); - pub(crate) const WRITE_ALL_EOF: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const WRITE_ALL_EOF: Self = const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); - pub(crate) const ZERO_TIMEOUT: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const ZERO_TIMEOUT: Self = const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); - pub(crate) const NO_ADDRESSES: Self = + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const NO_ADDRESSES: Self = const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); } From d56bc59d2a4658bde0791a4de3616252cd0feadd Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 12:28:38 +1000 Subject: [PATCH 029/278] Allow use of incoherence Incoherence is required to define inherit methods on `Error` from `alloc` and `std` once it is moved into `core`. This is required because: 1. `Box` is part of `Error`'s public API and that is only accessible from `alloc`. 2. `RawOsError` methods must ensure the `OsFunctions` atomic pointer is appropriately set, which can only be done from `std`. --- library/std/src/io/error.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 0d31214651494..7ac9a5a97ac31 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -75,6 +75,7 @@ pub type Result = result::Result; /// [Write]: ../../std/io/trait.Write.html /// [Seek]: ../../std/io/trait.Seek.html #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_has_incoherent_inherent_impls] pub struct Error { repr: Repr, } @@ -200,7 +201,7 @@ pub struct SimpleMessage { /// ``` #[rustc_macro_transparency = "semiopaque"] #[unstable(feature = "io_const_error", issue = "133448")] -#[allow_internal_unstable(hint_must_use, io_const_error_internals)] +#[allow_internal_unstable(core_io, hint_must_use, io_const_error_internals)] pub macro const_error($kind:expr, $message:expr $(,)?) { $crate::hint::must_use($crate::io::Error::from_static_message( const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, @@ -269,6 +270,7 @@ impl Error { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")] #[inline(never)] + #[rustc_allow_incoherent_impl] pub fn new(kind: ErrorKind, error: E) -> Error where E: Into>, @@ -296,6 +298,7 @@ impl Error { /// let custom_error2 = Error::other(custom_error); /// ``` #[stable(feature = "io_error_other", since = "1.74.0")] + #[rustc_allow_incoherent_impl] pub fn other(error: E) -> Error where E: Into>, @@ -343,6 +346,7 @@ impl Error { /// let os_error = Error::last_os_error(); /// println!("last OS error: {os_error:?}"); /// ``` + #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[doc(alias = "GetLastError")] #[doc(alias = "errno")] @@ -377,6 +381,7 @@ impl Error { /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); /// # } /// ``` + #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] @@ -570,6 +575,7 @@ impl Error { #[stable(feature = "io_error_inner", since = "1.3.0")] #[must_use = "`self` will be dropped if the result is not used"] #[inline] + #[rustc_allow_incoherent_impl] pub fn into_inner(self) -> Option> { match self.repr.into_data() { ErrorData::Os(..) => None, @@ -650,6 +656,7 @@ impl Error { /// # } /// ``` #[stable(feature = "io_error_downcast", since = "1.79.0")] + #[rustc_allow_incoherent_impl] pub fn downcast(self) -> result::Result where E: error::Error + Send + Sync + 'static, From 988dbbf14efdf823b51fca6bd7908f0f1387090a Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 12:16:25 +1000 Subject: [PATCH 030/278] Make `Error::is_interrupted` public and hidden Required to allow `std` access from `core` --- library/std/src/io/error.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 7ac9a5a97ac31..512c2f2ed2edd 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -715,8 +715,10 @@ impl Error { } } + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[doc(hidden)] #[inline] - pub(crate) fn is_interrupted(&self) -> bool { + pub fn is_interrupted(&self) -> bool { match self.repr.data() { ErrorData::Os(code) => sys::io::is_interrupted(code), ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, From 6faa6e059969c6b5e52e26a03f41c9150d25db36 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 14:25:03 +1000 Subject: [PATCH 031/278] Remove usage of `Box` from `Error` internals This allows `Error` to be moved into `core` while still retaining the ability to store custom error data. This may have performance implications! --- library/alloc/src/io/error.rs | 38 ++++++ library/alloc/src/io/mod.rs | 9 +- library/core/src/io/error.rs | 139 ++++++++++++++++++++- library/core/src/io/mod.rs | 3 + library/std/src/io/error.rs | 89 ++++++++----- library/std/src/io/error/repr_bitpacked.rs | 38 +++--- library/std/src/io/error/repr_unpacked.rs | 12 +- 7 files changed, 270 insertions(+), 58 deletions(-) create mode 100644 library/alloc/src/io/error.rs diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs new file mode 100644 index 0000000000000..6612804915c3d --- /dev/null +++ b/library/alloc/src/io/error.rs @@ -0,0 +1,38 @@ +#[cfg_attr(no_global_oom_handling, expect(unused_imports))] +use crate::{ + boxed::Box, + io::{Custom, CustomOwner, ErrorKind}, +}; + +#[cfg(not(no_global_oom_handling))] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn custom_owner_from_box( + kind: ErrorKind, + error: Box, +) -> CustomOwner { + /// # Safety + /// + /// `ptr` must be valid to pass into `Box::from_raw`. + unsafe fn drop_box_raw(ptr: *mut T) { + // SAFETY + // Caller ensures `ptr` is valid to pass into `Box::from_raw`. + drop(unsafe { Box::from_raw(ptr) }) + } + + // SAFETY: the pointer returned by Box::into_raw is non-null. + let error = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(error)) }; + + // SAFETY: + // * `error` is valid up to a static lifetime, and owns its pointee. + // * `drop_box_raw` is safe to call for the pointer `error` exactly once. + // * `drop_box_raw` is safe to call on a pointer to this instance of `Custom`, + // and will be stored in a `CustomOwner`. + let custom = unsafe { Custom::from_raw(kind, error, drop_box_raw, drop_box_raw) }; + + // SAFETY: the pointer returned by Box::into_raw is non-null. + let custom = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(Box::new(custom))) }; + + // SAFETY: the `outer_drop` provided to `custom` is valid for itself. + unsafe { CustomOwner::from_raw(custom) } +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 411b3cd9e8b87..2941198d0f551 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,5 +1,7 @@ //! Traits, helpers, and type definitions for core I/O functionality. +mod error; + #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use core::io::RawOsError; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] @@ -10,4 +12,9 @@ pub use core::io::{ }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{chain, take}; +pub use core::io::{Custom, CustomOwner, chain, take}; + +#[cfg(not(no_global_oom_handling))] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::error::custom_owner_from_box; diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 67cd1eebf44f2..c2c191893f9c6 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -1,6 +1,143 @@ #![unstable(feature = "core_io", issue = "154046")] -use crate::fmt; +use crate::{error, fmt}; + +// As with `SimpleMessage`: `#[repr(align(4))]` here is just because +// repr_bitpacked's encoding requires it. In practice it almost certainly be +// already be this high or higher. +#[doc(hidden)] +#[repr(align(4))] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub struct Custom { + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub kind: ErrorKind, + error: crate::ptr::NonNull, + error_drop: unsafe fn(*mut (dyn error::Error + Send + Sync)), + outer_drop: unsafe fn(*mut Self), +} + +// SAFETY: All members of `Custom` are `Send` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Send for Custom {} + +// SAFETY: All members of `Custom` are `Sync` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Sync for Custom {} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl fmt::Debug for Custom { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Custom").field("kind", &self.kind).field("error", self.error_ref()).finish() + } +} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl Drop for Custom { + fn drop(&mut self) { + // SAFETY: `Custom::from_raw` ensures this call is safe. + unsafe { + (self.error_drop)(self.error.as_ptr()); + } + } +} + +impl Custom { + /// # Safety + /// + /// * `error` must be valid for up to a static lifetime, and own its pointee. + /// * `error_drop` must be safe to call for the pointer `error` exactly once. + /// * `outer_drop` must be safe to call on a pointer to this instance of `Custom` + /// if it were stored within a [`CustomOwner`]. + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub unsafe fn from_raw( + kind: ErrorKind, + error: crate::ptr::NonNull, + error_drop: unsafe fn(*mut (dyn error::Error + Send + Sync)), + outer_drop: unsafe fn(*mut Self), + ) -> Custom { + Custom { kind, error, error_drop, outer_drop } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn into_raw(self) -> crate::ptr::NonNull { + let ptr = self.error; + core::mem::forget(self); + ptr + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn error_ref(&self) -> &(dyn error::Error + Send + Sync + 'static) { + // SAFETY: + // `from_raw` ensures `error` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.error.as_ref() } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn error_mut(&mut self) -> &mut (dyn error::Error + Send + Sync + 'static) { + // SAFETY: + // `from_raw` ensures `error` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.error.as_mut() } + } +} + +#[derive(Debug)] +#[repr(transparent)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +#[doc(hidden)] +pub struct CustomOwner(crate::ptr::NonNull); + +// SAFETY: Custom is `Send` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Send for CustomOwner {} + +// SAFETY: Custom is `Sync` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Sync for CustomOwner {} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl Drop for CustomOwner { + fn drop(&mut self) { + // SAFETY: `CustomOwner::from_raw` ensures this call is safe. + unsafe { + (self.0.as_ref().outer_drop)(self.0.as_ptr()); + } + } +} + +impl CustomOwner { + /// # Safety + /// + /// * The `outer_drop` of the provided `custom` must be safe to call exactly once. + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub unsafe fn from_raw(custom: crate::ptr::NonNull) -> CustomOwner { + CustomOwner(custom) + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn into_raw(self) -> crate::ptr::NonNull { + let ptr = self.0; + core::mem::forget(self); + ptr + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn custom_ref(&self) -> &Custom { + // SAFETY: + // `from_raw` ensures `0` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.0.as_ref() } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn custom_mut(&mut self) -> &mut Custom { + // SAFETY: + // `from_raw` ensures `0` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.0.as_mut() } + } +} /// The type of raw OS error codes. /// diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 43b6e09bfc4f0..fe6c6958f65cb 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -14,6 +14,9 @@ pub use self::cursor::Cursor; pub use self::error::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::error::{Custom, CustomOwner}; #[unstable(feature = "core_io", issue = "154046")] pub use self::io_slice::{IoSlice, IoSliceMut}; #[unstable(feature = "core_io", issue = "154046")] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 512c2f2ed2edd..7683270007ebb 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -5,6 +5,7 @@ mod tests; pub use alloc_crate::io::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; +use alloc_crate::io::{Custom, CustomOwner, custom_owner_from_box}; // On 64-bit platforms, `io::Error` may use a bit-packed representation to // reduce size. However, this representation assumes that error codes are @@ -208,16 +209,6 @@ pub macro const_error($kind:expr, $message:expr $(,)?) { )) } -// As with `SimpleMessage`: `#[repr(align(4))]` here is just because -// repr_bitpacked's encoding requires it. In practice it almost certainly be -// already be this high or higher. -#[derive(Debug)] -#[repr(align(4))] -struct Custom { - kind: ErrorKind, - error: Box, -} - /// Intended for use for errors not exposed to the user, where allocating onto /// the heap (for normal construction via Error::new) is too costly. #[stable(feature = "io_error_from_errorkind", since = "1.14.0")] @@ -242,6 +233,33 @@ impl From for Error { } impl Error { + /// # Safety + /// + /// The provided `CustomOwner` must have been constructed from a `Box` from the `alloc` crate. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_custom_owner(custom: CustomOwner) -> Error { + Error { repr: Repr::new_custom(custom) } + } + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub fn into_custom_owner(self) -> result::Result { + if matches!(self.repr.data(), ErrorData::Custom(..)) { + let ErrorData::Custom(c) = self.repr.into_data() else { + // SAFETY: Checked above using `matches!`. + unsafe { crate::hint::unreachable_unchecked() } + }; + Ok(c) + } else { + Err(self) + } + } + /// Creates a new I/O error from a known kind of error as well as an /// arbitrary error payload. /// @@ -275,7 +293,10 @@ impl Error { where E: Into>, { - Self::_new(kind, error.into()) + let custom = custom_owner_from_box(kind, error.into()); + + // SAFETY: `custom_owner` has been constructed from a `Box` from the `alloc` crate. + unsafe { Error::from_custom_owner(custom) } } /// Creates a new I/O error from an arbitrary error payload. @@ -303,11 +324,7 @@ impl Error { where E: Into>, { - Self::_new(ErrorKind::Other, error.into()) - } - - fn _new(kind: ErrorKind, error: Box) -> Error { - Error { repr: Repr::new_custom(Box::new(Custom { kind, error })) } + Self::new(ErrorKind::Other, error) } /// Creates a new I/O error from a known kind of error as well as a constant @@ -465,7 +482,7 @@ impl Error { ErrorData::Os(..) => None, ErrorData::Simple(..) => None, ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(&*c.error), + ErrorData::Custom(c) => Some(c.error_ref()), } } @@ -539,7 +556,7 @@ impl Error { ErrorData::Os(..) => None, ErrorData::Simple(..) => None, ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(&mut *c.error), + ErrorData::Custom(c) => Some(c.error_mut()), } } @@ -577,12 +594,20 @@ impl Error { #[inline] #[rustc_allow_incoherent_impl] pub fn into_inner(self) -> Option> { - match self.repr.into_data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(c.error), - } + let custom_owner = self.into_custom_owner().ok()?; + + let ptr = custom_owner.into_raw().as_ptr(); + + // SAFETY: + // `Error` can only contain a `CustomOwner` if it was constructed using `Box::into_raw`. + let custom = unsafe { Box::::from_raw(ptr) }; + + let ptr = custom.into_raw().as_ptr(); + + // SAFETY: + // Any `CustomOwner` from an `Error` was constructed by the `alloc` crate + // to contain a `Custom` which itself was constructed with `Box::into_raw`. + Some(unsafe { Box::from_raw(ptr) }) } /// Attempts to downcast the custom boxed error to `E`. @@ -661,16 +686,16 @@ impl Error { where E: error::Error + Send + Sync + 'static, { - if let ErrorData::Custom(c) = self.repr.data() - && c.error.is::() + if let Some(e) = self.get_ref() + && e.is::() { - if let ErrorData::Custom(b) = self.repr.into_data() - && let Ok(err) = b.error.downcast::() + if let Some(b) = self.into_inner() + && let Ok(err) = b.downcast::() { Ok(*err) } else { // Safety: We have just checked that the condition is true - unsafe { crate::hint::unreachable_unchecked() } + unsafe { core::hint::unreachable_unchecked() } } } else { Err(self) @@ -756,7 +781,7 @@ impl fmt::Display for Error { let detail = sys::io::error_string(code); write!(fmt, "{detail} (os error {code})") } - ErrorData::Custom(ref c) => c.error.fmt(fmt), + ErrorData::Custom(c) => fmt::Display::fmt(c.error_ref(), fmt), ErrorData::Simple(kind) => kind.fmt(fmt), ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), } @@ -771,7 +796,7 @@ impl error::Error for Error { ErrorData::Os(..) => None, ErrorData::Simple(..) => None, ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error.cause(), + ErrorData::Custom(c) => c.error_ref().cause(), } } @@ -780,7 +805,7 @@ impl error::Error for Error { ErrorData::Os(..) => None, ErrorData::Simple(..) => None, ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error.source(), + ErrorData::Custom(c) => c.error_ref().source(), } } } diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 3b81759eb86e2..22cb331924081 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -106,7 +106,7 @@ use core::marker::PhantomData; use core::num::NonZeroUsize; use core::ptr::NonNull; -use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; +use super::{Custom, CustomOwner, ErrorData, ErrorKind, RawOsError, SimpleMessage}; // The 2 least-significant bits are used as tag. const TAG_MASK: usize = 0b11; @@ -126,15 +126,15 @@ const TAG_SIMPLE: usize = 0b11; /// ``` #[repr(transparent)] #[rustc_insignificant_dtor] -pub(super) struct Repr(NonNull<()>, PhantomData>>); +pub(super) struct Repr(NonNull<()>, PhantomData>); // All the types `Repr` stores internally are Send + Sync, and so is it. unsafe impl Send for Repr {} unsafe impl Sync for Repr {} impl Repr { - pub(super) fn new_custom(b: Box) -> Self { - let p = Box::into_raw(b).cast::(); + pub(super) fn new_custom(b: CustomOwner) -> Self { + let p = b.into_raw().as_ptr().cast::(); // Should only be possible if an allocator handed out a pointer with // wrong alignment. debug_assert_eq!(p.addr() & TAG_MASK, 0); @@ -157,7 +157,8 @@ impl Repr { // box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore, // `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the // `new_unchecked` is safe. - let res = Self(unsafe { NonNull::new_unchecked(tagged) }, PhantomData); + let ptr = unsafe { NonNull::new_unchecked(tagged) }; + let res = Self(ptr, PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed"); @@ -168,10 +169,8 @@ impl Repr { pub(super) fn new_os(code: RawOsError) -> Self { let utagged = ((code as usize) << 32) | TAG_OS; // SAFETY: `TAG_OS` is not zero, so the result of the `|` is not 0. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); + let utagged = unsafe { NonZeroUsize::new_unchecked(utagged) }; + let res = Self(NonNull::without_provenance(utagged), PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!( @@ -185,10 +184,8 @@ impl Repr { pub(super) fn new_simple(kind: ErrorKind) -> Self { let utagged = ((kind as usize) << 32) | TAG_SIMPLE; // SAFETY: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); + let utagged = unsafe { NonZeroUsize::new_unchecked(utagged) }; + let res = Self(NonNull::without_provenance(utagged), PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!( @@ -202,7 +199,8 @@ impl Repr { #[inline] pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { // SAFETY: References are never null. - Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData) + let ptr = unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }; + Self(ptr, PhantomData) } #[inline] @@ -218,11 +216,13 @@ impl Repr { } #[inline] - pub(super) fn into_data(self) -> ErrorData> { + pub(super) fn into_data(self) -> ErrorData { let this = core::mem::ManuallyDrop::new(self); // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we prevent double-drop using `ManuallyDrop`. - unsafe { decode_repr(this.0, |p| Box::from_raw(p)) } + unsafe { + decode_repr(this.0, |p| CustomOwner::from_raw(core::ptr::NonNull::new_unchecked(p))) + } } } @@ -232,7 +232,9 @@ impl Drop for Repr { // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we're being dropped. unsafe { - let _ = decode_repr(self.0, |p| Box::::from_raw(p)); + let _ = decode_repr(self.0, |p| { + CustomOwner::from_raw(core::ptr::NonNull::new_unchecked(p)) + }); } } } @@ -307,7 +309,7 @@ static_assert!(@usize_eq: size_of::>(), size_of::()); // `Custom` and `SimpleMessage` need to be thin pointers. static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8); -static_assert!(@usize_eq: size_of::>(), 8); +static_assert!(@usize_eq: size_of::(), 8); static_assert!((TAG_MASK + 1).is_power_of_two()); // And they must have sufficient alignment. diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/std/src/io/error/repr_unpacked.rs index b3e7b5f024ea0..a32ed58ce1b74 100644 --- a/library/std/src/io/error/repr_unpacked.rs +++ b/library/std/src/io/error/repr_unpacked.rs @@ -2,15 +2,15 @@ //! non-64bit targets, where the packed 64 bit representation wouldn't work, and //! would have no benefit. -use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; +use super::{Custom, CustomOwner, ErrorData, ErrorKind, RawOsError, SimpleMessage}; -type Inner = ErrorData>; +type Inner = ErrorData; pub(super) struct Repr(Inner); impl Repr { #[inline] - pub(super) fn new_custom(b: Box) -> Self { + pub(super) fn new_custom(b: CustomOwner) -> Self { Self(Inner::Custom(b)) } #[inline] @@ -26,7 +26,7 @@ impl Repr { Self(Inner::SimpleMessage(m)) } #[inline] - pub(super) fn into_data(self) -> ErrorData> { + pub(super) fn into_data(self) -> ErrorData { self.0 } #[inline] @@ -35,7 +35,7 @@ impl Repr { Inner::Os(c) => ErrorData::Os(*c), Inner::Simple(k) => ErrorData::Simple(*k), Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), - Inner::Custom(m) => ErrorData::Custom(&*m), + Inner::Custom(m) => ErrorData::Custom(m.custom_ref()), } } #[inline] @@ -44,7 +44,7 @@ impl Repr { Inner::Os(c) => ErrorData::Os(*c), Inner::Simple(k) => ErrorData::Simple(*k), Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), - Inner::Custom(m) => ErrorData::Custom(&mut *m), + Inner::Custom(m) => ErrorData::Custom(m.custom_mut()), } } } From ee2b4953606cd679700ca7c888335ed417fd889e Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 13:49:49 +1000 Subject: [PATCH 032/278] Delegate `RawOsError` functionality to `OsFunctions` Stored in a `static` `AtomicPtr` to allow definition in `core` and setting in `std`. Should be replaced with Externally Implemented Items (EII) or similar once stable. --- library/alloc/src/io/mod.rs | 5 +- library/core/src/io/error.rs | 24 +++++++ library/core/src/io/error/os_functions.rs | 38 ++++++++++ .../core/src/io/error/os_functions_atomic.rs | 69 +++++++++++++++++++ library/core/src/io/mod.rs | 5 +- library/std/src/io/error.rs | 48 +++++++++++-- 6 files changed, 180 insertions(+), 9 deletions(-) create mode 100644 library/core/src/io/error/os_functions.rs create mode 100644 library/core/src/io/error/os_functions_atomic.rs diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 2941198d0f551..2935b8518a5d4 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -12,7 +12,10 @@ pub use core::io::{ }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{Custom, CustomOwner, chain, take}; +pub use core::io::{ + Custom, CustomOwner, OsFunctions, chain, decode_error_kind, format_os_error, is_interrupted, + set_functions, take, +}; #[cfg(not(no_global_oom_handling))] #[doc(hidden)] diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index c2c191893f9c6..bdf0530047068 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -1,7 +1,31 @@ #![unstable(feature = "core_io", issue = "154046")] +#[cfg_attr(target_has_atomic_load_store = "ptr", path = "error/os_functions_atomic.rs")] +#[cfg_attr(not(target_has_atomic_load_store = "ptr"), path = "error/os_functions.rs")] +mod os_functions; + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; use crate::{error, fmt}; +#[doc(hidden)] +#[derive(Debug)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub struct OsFunctions { + pub format_os_error: fn(_: RawOsError, _: &mut fmt::Formatter<'_>) -> fmt::Result, + pub decode_error_kind: fn(_: RawOsError) -> ErrorKind, + pub is_interrupted: fn(_: RawOsError) -> bool, +} + +impl OsFunctions { + const DEFAULT: &'static OsFunctions = &OsFunctions { + format_os_error: |_, _| Ok(()), + decode_error_kind: |_| ErrorKind::Uncategorized, + is_interrupted: |_| false, + }; +} + // As with `SimpleMessage`: `#[repr(align(4))]` here is just because // repr_bitpacked's encoding requires it. In practice it almost certainly be // already be this high or higher. diff --git a/library/core/src/io/error/os_functions.rs b/library/core/src/io/error/os_functions.rs new file mode 100644 index 0000000000000..ce769fa53df9a --- /dev/null +++ b/library/core/src/io/error/os_functions.rs @@ -0,0 +1,38 @@ +use super::{ErrorKind, OsFunctions, RawOsError}; +use crate::fmt; + +/// # Safety +/// +/// The provided reference must point to data that is entirely constant; it must +/// not be created during runtime. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub unsafe fn set_functions(f: &'static OsFunctions) { + // FIXME: externally implementable items may allow for weak linkage, allowing + // these methods to be overridden even when atomic pointers are not supported. +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let f = OsFunctions::DEFAULT; + (f.format_os_error)(errno, fmt) +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn decode_error_kind(errno: RawOsError) -> ErrorKind { + let f = OsFunctions::DEFAULT; + (f.decode_error_kind)(errno) +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn is_interrupted(errno: RawOsError) -> bool { + let f = OsFunctions::DEFAULT; + (f.is_interrupted)(errno) +} diff --git a/library/core/src/io/error/os_functions_atomic.rs b/library/core/src/io/error/os_functions_atomic.rs new file mode 100644 index 0000000000000..cc36f2953479f --- /dev/null +++ b/library/core/src/io/error/os_functions_atomic.rs @@ -0,0 +1,69 @@ +//! OS-dependent functions +//! +//! `Error` needs OS functionalities to work interpret raw OS errors, but +//! we can't link to anythink here in `alloc`. Therefore, we restrict +//! creation of `Error` from raw OS errors in `std`, and require providing +//! a vtable of operations when creating one. + +// FIXME: replace this with externally implementable items once they are more stable + +use super::{ErrorKind, OsFunctions, RawOsError}; +use crate::fmt; +use crate::sync::atomic; + +/// These default functions are not reachable, but have them just to be safe. +static OS_FUNCTIONS: atomic::AtomicPtr = + atomic::AtomicPtr::new(OsFunctions::DEFAULT as *const _ as *mut _); + +fn get_os_functions() -> &'static OsFunctions { + // SAFETY: + // * `OS_FUNCTIONS` is initially a pointer to `OsFunctions::DEFAULT`, which is valid for a static lifetime. + // * `OS_FUNCTIONS` can only be changed by `set_functions`, which only accepts `&'static OsFunctions`. + // * Therefore, `OS_FUNCTIONS` must always contain a valid non-null pointer with a static lifetime. + // * `Relaxed` ordering is sufficient as the only way to write to `OS_FUNCTIONS` is through + // `set_functions`, which has as a safety precondition that any value passed in must + // be constant and not created during runtime. + unsafe { &*OS_FUNCTIONS.load(atomic::Ordering::Relaxed) } +} + +/// # Safety +/// +/// The provided reference must point to data that is entirely constant; it must +/// not be created during runtime. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub unsafe fn set_functions(f: &'static OsFunctions) { + #[cold] + fn set_functions_inner(f: &'static OsFunctions) { + OS_FUNCTIONS.store(f as *const _ as *mut _, atomic::Ordering::Relaxed); + } + + if OS_FUNCTIONS.load(atomic::Ordering::Relaxed) != f as *const _ as *mut _ { + set_functions_inner(f); + } +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let f = get_os_functions(); + (f.format_os_error)(errno, fmt) +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn decode_error_kind(errno: RawOsError) -> ErrorKind { + let f = get_os_functions(); + (f.decode_error_kind)(errno) +} + +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn is_interrupted(errno: RawOsError) -> bool { + let f = get_os_functions(); + (f.is_interrupted)(errno) +} diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index fe6c6958f65cb..89713241b2e79 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -16,7 +16,10 @@ pub use self::error::ErrorKind; pub use self::error::RawOsError; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::error::{Custom, CustomOwner}; +pub use self::error::{ + Custom, CustomOwner, OsFunctions, decode_error_kind, format_os_error, is_interrupted, + set_functions, +}; #[unstable(feature = "core_io", issue = "154046")] pub use self::io_slice::{IoSlice, IoSliceMut}; #[unstable(feature = "core_io", issue = "154046")] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 7683270007ebb..89ec4902e25a5 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -5,7 +5,10 @@ mod tests; pub use alloc_crate::io::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; -use alloc_crate::io::{Custom, CustomOwner, custom_owner_from_box}; +use alloc_crate::io::{ + Custom, CustomOwner, OsFunctions, custom_owner_from_box, decode_error_kind, format_os_error, + is_interrupted, set_functions, +}; // On 64-bit platforms, `io::Error` may use a bit-packed representation to // reduce size. However, this representation assumes that error codes are @@ -344,6 +347,25 @@ impl Error { Self { repr: Repr::new_simple_message(msg) } } + /// # Safety + /// + /// `functions` must point to data that is entirely constant; it must + /// not be created during runtime. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_raw_os_error_with_functions( + code: RawOsError, + functions: &'static OsFunctions, + ) -> Error { + // SAFETY: Caller ensures `functions` is a constant not created at runtime. + unsafe { + set_functions(functions); + } + Error { repr: Repr::new_os(code) } + } + /// Returns an error representing the last OS error which occurred. /// /// This function reads the value of `errno` for the target platform (e.g. @@ -403,7 +425,14 @@ impl Error { #[must_use] #[inline] pub fn from_raw_os_error(code: RawOsError) -> Error { - Error { repr: Repr::new_os(code) } + const FUNCTIONS: &'static OsFunctions = &OsFunctions { + format_os_error: |code, fmt| fmt.write_str(&sys::io::error_string(code)), + decode_error_kind: sys::io::decode_error_kind, + is_interrupted: sys::io::is_interrupted, + }; + + // SAFETY: `FUNCTIONS` is a constant and not created at runtime. + unsafe { Error::from_raw_os_error_with_functions(code, FUNCTIONS) } } /// Returns the OS error that this error represents (if any). @@ -733,7 +762,7 @@ impl Error { #[inline] pub fn kind(&self) -> ErrorKind { match self.repr.data() { - ErrorData::Os(code) => sys::io::decode_error_kind(code), + ErrorData::Os(code) => decode_error_kind(code), ErrorData::Custom(c) => c.kind, ErrorData::Simple(kind) => kind, ErrorData::SimpleMessage(m) => m.kind, @@ -745,7 +774,7 @@ impl Error { #[inline] pub fn is_interrupted(&self) -> bool { match self.repr.data() { - ErrorData::Os(code) => sys::io::is_interrupted(code), + ErrorData::Os(code) => is_interrupted(code), ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, @@ -759,8 +788,13 @@ impl fmt::Debug for Repr { ErrorData::Os(code) => fmt .debug_struct("Os") .field("code", &code) - .field("kind", &sys::io::decode_error_kind(code)) - .field("message", &sys::io::error_string(code)) + .field("kind", &decode_error_kind(code)) + .field( + "message", + &fmt::from_fn(|fmt| { + write!(fmt, "\"{}\"", fmt::from_fn(|fmt| format_os_error(code, fmt))) + }), + ) .finish(), ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), @@ -778,7 +812,7 @@ impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self.repr.data() { ErrorData::Os(code) => { - let detail = sys::io::error_string(code); + let detail = fmt::from_fn(|fmt| format_os_error(code, fmt)); write!(fmt, "{detail} (os error {code})") } ErrorData::Custom(c) => fmt::Display::fmt(c.error_ref(), fmt), From 9ef79e2bff0605883f7135de32c02650c7f9f3f2 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 13:57:54 +1000 Subject: [PATCH 033/278] Move `std::io::Error` and friends into `core` and `alloc` --- library/alloc/src/io/error.rs | 245 +++++- library/alloc/src/io/mod.rs | 7 +- library/alloc/src/lib.rs | 2 + library/core/src/io/error.rs | 541 +++++++++++- .../src/io/error/repr_bitpacked.rs | 0 .../src/io/error/repr_unpacked.rs | 0 library/core/src/io/mod.rs | 31 +- library/core/src/lib.rs | 1 + library/std/src/io/error.rs | 792 +----------------- library/std/src/io/mod.rs | 20 +- .../binary-op-not-allowed-issue-125631.stderr | 6 +- 11 files changed, 835 insertions(+), 810 deletions(-) rename library/{std => core}/src/io/error/repr_bitpacked.rs (100%) rename library/{std => core}/src/io/error/repr_unpacked.rs (100%) diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs index 6612804915c3d..e2ac82c95e10e 100644 --- a/library/alloc/src/io/error.rs +++ b/library/alloc/src/io/error.rs @@ -1,8 +1,245 @@ +use core::{error, result}; + +use crate::boxed::Box; #[cfg_attr(no_global_oom_handling, expect(unused_imports))] -use crate::{ - boxed::Box, - io::{Custom, CustomOwner, ErrorKind}, -}; +use crate::io::CustomOwner; +#[cfg_attr(any(no_rc, no_sync, no_global_oom_handling), expect(unused_imports))] +use crate::io::const_error; +use crate::io::{Custom, Error, ErrorKind}; + +impl Error { + /// Creates a new I/O error from a known kind of error as well as an + /// arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. The `error` argument is an arbitrary + /// payload which will be contained in this [`Error`]. + /// + /// Note that this function allocates memory on the heap. + /// If no extra payload is required, use the `From` conversion from + /// `ErrorKind`. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// // errors can be created from strings + /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); + /// + /// // creating an error without payload (and without memory allocation) + /// let eof_error = Error::from(ErrorKind::UnexpectedEof); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")] + #[inline(never)] + #[rustc_allow_incoherent_impl] + pub fn new(kind: ErrorKind, error: E) -> Error + where + E: Into>, + { + let custom = custom_owner_from_box(kind, error.into()); + + // SAFETY: `custom_owner` has been constructed from a `Box` from the `alloc` crate. + unsafe { Self::from_custom_owner(custom) } + } + + /// Creates a new I/O error from an arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. It is a shortcut for [`Error::new`][new] + /// with [`ErrorKind::Other`]. + /// + /// [new]: struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::Error; + /// + /// // errors can be created from strings + /// let custom_error = Error::other("oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::other(custom_error); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[stable(feature = "io_error_other", since = "1.74.0")] + #[rustc_allow_incoherent_impl] + pub fn other(error: E) -> Error + where + E: Into>, + { + Self::new(ErrorKind::Other, error) + } + + /// Consumes the `Error`, returning its inner error (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] or [`other`][other], + /// then this function will return [`Some`], + /// otherwise it will return [`None`]. + /// + /// [new]: struct.Error.html#method.new + /// [other]: struct.Error.html#method.other + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// if let Some(inner_err) = err.into_inner() { + /// println!("Inner error: {inner_err}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use = "`self` will be dropped if the result is not used"] + #[inline] + #[rustc_allow_incoherent_impl] + pub fn into_inner(self) -> Option> { + let custom_owner = self.into_custom_owner().ok()?; + + let ptr = custom_owner.into_raw().as_ptr(); + + // SAFETY: + // `Error` can only contain a `CustomOwner` if it was constructed using `Box::into_raw`. + let custom = unsafe { Box::::from_raw(ptr) }; + + let ptr = custom.into_raw().as_ptr(); + + // SAFETY: + // Any `CustomOwner` from an `Error` was constructed by the `alloc` crate + // to contain a `Custom` which itself was constructed with `Box::into_raw`. + Some(unsafe { Box::from_raw(ptr) }) + } + + /// Attempts to downcast the custom boxed error to `E`. + /// + /// If this [`Error`] contains a custom boxed error, + /// then it would attempt downcasting on the boxed error, + /// otherwise it will return [`Err`]. + /// + /// If the custom boxed error has the same type as `E`, it will return [`Ok`], + /// otherwise it will also return [`Err`]. + /// + /// This method is meant to be a convenience routine for calling + /// `Box::downcast` on the custom boxed error, returned by + /// [`Error::into_inner`][into_inner]. + /// + /// [into_inner]: struct.Error.html#method.into_inner + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// use std::io; + /// use std::error::Error; + /// + /// #[derive(Debug)] + /// enum E { + /// Io(io::Error), + /// SomeOtherVariant, + /// } + /// + /// impl fmt::Display for E { + /// // ... + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # todo!() + /// # } + /// } + /// impl Error for E {} + /// + /// impl From for E { + /// fn from(err: io::Error) -> E { + /// err.downcast::() + /// .unwrap_or_else(E::Io) + /// } + /// } + /// + /// impl From for io::Error { + /// fn from(err: E) -> io::Error { + /// match err { + /// E::Io(io_error) => io_error, + /// e => io::Error::new(io::ErrorKind::Other, e), + /// } + /// } + /// } + /// + /// # fn main() { + /// let e = E::SomeOtherVariant; + /// // Convert it to an io::Error + /// let io_error = io::Error::from(e); + /// // Cast it back to the original variant + /// let e = E::from(io_error); + /// assert!(matches!(e, E::SomeOtherVariant)); + /// + /// let io_error = io::Error::from(io::ErrorKind::AlreadyExists); + /// // Convert it to E + /// let e = E::from(io_error); + /// // Cast it back to the original variant + /// let io_error = io::Error::from(e); + /// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists); + /// assert!(io_error.get_ref().is_none()); + /// assert!(io_error.raw_os_error().is_none()); + /// # } + /// ``` + #[stable(feature = "io_error_downcast", since = "1.79.0")] + #[rustc_allow_incoherent_impl] + pub fn downcast(self) -> result::Result + where + E: error::Error + Send + Sync + 'static, + { + if let Some(e) = self.get_ref() + && e.is::() + { + if let Some(b) = self.into_inner() + && let Ok(err) = b.downcast::() + { + Ok(*err) + } else { + // Safety: We have just checked that the condition is true + unsafe { core::hint::unreachable_unchecked() } + } + } else { + Err(self) + } + } +} + +#[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))] +#[stable(feature = "rust1", since = "1.0.0")] +impl From for Error { + /// Converts a [`crate::ffi::NulError`] into a [`Error`]. + fn from(_: crate::ffi::NulError) -> Error { + const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") + } +} + +#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")] +impl From for Error { + /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. + /// + /// `TryReserveError` won't be available as the error `source()`, + /// but this may change in the future. + fn from(_: crate::collections::TryReserveError) -> Error { + // ErrorData::Custom allocates, which isn't great for handling OOM errors. + ErrorKind::OutOfMemory.into() + } +} #[cfg(not(no_global_oom_handling))] #[doc(hidden)] diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 2935b8518a5d4..d868cc66b8e2a 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -4,11 +4,16 @@ mod error; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use core::io::RawOsError; +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use core::io::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use core::io::const_error; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use core::io::{BorrowedBuf, BorrowedCursor}; #[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ - Chain, Cursor, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, Sink, Take, empty, repeat, sink, + Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Sink, Take, empty, + repeat, sink, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index ff7729eed7073..d00ec5e0c714d 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -133,6 +133,8 @@ #![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(inplace_iteration)] +#![feature(io_const_error)] +#![feature(io_const_error_internals)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] #![feature(layout_for_ptr)] diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index bdf0530047068..36e91a55e5264 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -1,5 +1,22 @@ #![unstable(feature = "core_io", issue = "154046")] +// On 64-bit platforms, `io::Error` may use a bit-packed representation to +// reduce size. However, this representation assumes that error codes are +// always 32-bit wide. +// +// This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. +// Therefore, the packed representation is explicitly disabled for UEFI +// targets, and the unpacked representation must be used instead. +#[cfg_attr( + all(target_pointer_width = "64", not(target_os = "uefi")), + path = "error/repr_bitpacked.rs" +)] +#[cfg_attr( + not(all(target_pointer_width = "64", not(target_os = "uefi"))), + path = "error/repr_unpacked.rs" +)] +mod repr; + #[cfg_attr(target_has_atomic_load_store = "ptr", path = "error/os_functions_atomic.rs")] #[cfg_attr(not(target_has_atomic_load_store = "ptr"), path = "error/os_functions.rs")] mod os_functions; @@ -7,7 +24,529 @@ mod os_functions; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; -use crate::{error, fmt}; +use self::repr::Repr; +use crate::{error, fmt, result}; + +/// A specialized [`Result`] type for I/O operations. +/// +/// This type is broadly used across [`std::io`] for any operation which may +/// produce an error. +/// +/// This type alias is generally used to avoid writing out [`io::Error`] directly and +/// is otherwise a direct mapping to [`Result`]. +/// +/// While usual Rust style is to import types directly, aliases of [`Result`] +/// often are not, to make it easier to distinguish between them. [`Result`] is +/// generally assumed to be [`core::result::Result`][`Result`], and so users of this alias +/// will generally use `io::Result` instead of shadowing the [prelude]'s import +/// of [`core::result::Result`][`Result`]. +/// +/// [`std::io`]: ../../std/io/index.html +/// [`io::Error`]: Error +/// [`Result`]: crate::result::Result +/// [prelude]: crate::prelude +/// +/// # Examples +/// +/// A convenience function that bubbles an `io::Result` to its caller: +/// +/// ``` +/// use std::io; +/// +/// fn get_string() -> io::Result { +/// let mut buffer = String::new(); +/// +/// io::stdin().read_line(&mut buffer)?; +/// +/// Ok(buffer) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(search_unbox)] +pub type Result = result::Result; + +/// The error type for I/O operations of the [`Read`][Read], [`Write`][Write], [`Seek`][Seek], and +/// associated traits. +/// +/// Errors mostly originate from the underlying OS, but custom instances of +/// `Error` can be created with crafted error messages and a particular value of +/// [`ErrorKind`]. +/// +/// [Read]: ../../std/io/trait.Read.html +/// [Write]: ../../std/io/trait.Write.html +/// [Seek]: ../../std/io/trait.Seek.html +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_has_incoherent_inherent_impls] +pub struct Error { + repr: Repr, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.repr, f) + } +} + +/// Common errors constants for use in std +#[doc(hidden)] +impl Error { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const INVALID_UTF8: Self = + const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const READ_EXACT_EOF: Self = + const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNKNOWN_THREAD_COUNT: Self = const_error!( + ErrorKind::NotFound, + "the number of hardware threads is not known for the target platform", + ); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNSUPPORTED_PLATFORM: Self = + const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const WRITE_ALL_EOF: Self = + const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const ZERO_TIMEOUT: Self = + const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const NO_ADDRESSES: Self = + const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); +} + +// Only derive debug in tests, to make sure it +// doesn't accidentally get printed. +#[cfg_attr(test, derive(Debug))] +enum ErrorData { + Os(RawOsError), + Simple(ErrorKind), + SimpleMessage(&'static SimpleMessage), + Custom(C), +} + +// `#[repr(align(4))]` is probably redundant, it should have that value or +// higher already. We include it just because repr_bitpacked.rs's encoding +// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the +// alignment required by the struct, only increase it). +// +// If we add more variants to ErrorData, this can be increased to 8, but it +// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or +// whatever cfg we're using to enable the `repr_bitpacked` code, since only the +// that version needs the alignment, and 8 is higher than the alignment we'll +// have on 32 bit platforms. +// +// (For the sake of being explicit: the alignment requirement here only matters +// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't +// matter at all) +#[doc(hidden)] +#[unstable(feature = "io_const_error_internals", issue = "none")] +#[repr(align(4))] +#[derive(Debug)] +pub struct SimpleMessage { + pub kind: ErrorKind, + pub message: &'static str, +} + +/// Creates a new I/O error from a known kind of error and a string literal. +/// +/// Contrary to [`Error::new`][new], this macro does not allocate and can be used in +/// `const` contexts. +/// +/// [new]: ../../alloc/io/struct.Error.html#method.new +/// +/// # Example +/// ``` +/// #![feature(io_const_error)] +/// use std::io::{const_error, Error, ErrorKind}; +/// +/// const FAIL: Error = const_error!(ErrorKind::Unsupported, "tried something that never works"); +/// +/// fn not_here() -> Result<(), Error> { +/// Err(FAIL) +/// } +/// ``` +#[rustc_macro_transparency = "semiopaque"] +#[unstable(feature = "io_const_error", issue = "133448")] +#[allow_internal_unstable(core_io, hint_must_use, io_const_error_internals)] +pub macro const_error($kind:expr, $message:expr $(,)?) { + $crate::hint::must_use($crate::io::Error::from_static_message( + const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, + )) +} + +/// Intended for use for errors not exposed to the user, where allocating onto +/// the heap (for normal construction via Error::new) is too costly. +#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] +impl From for Error { + /// Converts an [`ErrorKind`] into an [`Error`]. + /// + /// This conversion creates a new error with a simple representation of error kind. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// let not_found = ErrorKind::NotFound; + /// let error = Error::from(not_found); + /// assert_eq!("entity not found", format!("{error}")); + /// ``` + #[inline] + fn from(kind: ErrorKind) -> Error { + Error { repr: Repr::new_simple(kind) } + } +} + +impl Error { + /// # Safety + /// + /// The provided `CustomOwner` must have been constructed from a `Box` from the `alloc` crate. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_custom_owner(custom: CustomOwner) -> Error { + Error { repr: Repr::new_custom(custom) } + } + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub fn into_custom_owner(self) -> result::Result { + if matches!(self.repr.data(), ErrorData::Custom(..)) { + let ErrorData::Custom(c) = self.repr.into_data() else { + // SAFETY: Checked above using `matches!`. + unsafe { crate::hint::unreachable_unchecked() } + }; + Ok(c) + } else { + Err(self) + } + } + + /// Creates a new I/O error from a known kind of error as well as a constant + /// message. + /// + /// This function does not allocate. + /// + /// You should not use this directly, and instead use the `const_error!` + /// macro: `io::const_error!(ErrorKind::Something, "some_message")`. + /// + /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. + #[inline] + #[doc(hidden)] + #[unstable(feature = "io_const_error_internals", issue = "none")] + pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { + Self { repr: Repr::new_simple_message(msg) } + } + + /// # Safety + /// + /// `functions` must point to data that is entirely constant; it must + /// not be created during runtime. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_raw_os_error_with_functions( + code: RawOsError, + functions: &'static OsFunctions, + ) -> Error { + // SAFETY: Caller ensures `functions` is a constant not created at runtime. + unsafe { + set_functions(functions); + } + Error { repr: Repr::new_os(code) } + } + + /// Returns the OS error that this error represents (if any). + /// + /// If this [`Error`] was constructed via [`last_os_error`][last_os_error] or + /// [`from_raw_os_error`][from_raw_os_error], then this function will return [`Some`], otherwise + /// it will return [`None`]. + /// + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error + /// [from_raw_os_error]: ../../std/io/struct.Error.html#method.from_raw_os_error + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_os_error(err: &Error) { + /// if let Some(raw_os_err) = err.raw_os_error() { + /// println!("raw OS error: {raw_os_err:?}"); + /// } else { + /// println!("Not an OS error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "raw OS error: ...". + /// print_os_error(&Error::last_os_error()); + /// // Will print "Not an OS error". + /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn raw_os_error(&self) -> Option { + match self.repr.data() { + ErrorData::Os(i) => Some(i), + ErrorData::Custom(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + } + } + + /// Returns a reference to the inner error wrapped by this error (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [new]: ../../alloc/io/struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {inner_err:?}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use] + #[inline] + pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(c.error_ref()), + } + } + + /// Returns a mutable reference to the inner error wrapped by this error + /// (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [new]: ../../alloc/io/struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// use std::{error, fmt}; + /// use std::fmt::Display; + /// + /// #[derive(Debug)] + /// struct MyError { + /// v: String, + /// } + /// + /// impl MyError { + /// fn new() -> MyError { + /// MyError { + /// v: "oh no!".to_string() + /// } + /// } + /// + /// fn change_message(&mut self, new_message: &str) { + /// self.v = new_message.to_string(); + /// } + /// } + /// + /// impl error::Error for MyError {} + /// + /// impl Display for MyError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "MyError: {}", self.v) + /// } + /// } + /// + /// fn change_error(mut err: Error) -> Error { + /// if let Some(inner_err) = err.get_mut() { + /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); + /// } + /// err + /// } + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {inner_err}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&change_error(Error::last_os_error())); + /// // Will print "Inner error: ...". + /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use] + #[inline] + pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { + match self.repr.data_mut() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(c.error_mut()), + } + } + + /// Returns the corresponding [`ErrorKind`] for this error. + /// + /// This may be a value set by Rust code constructing custom `io::Error`s, + /// or if this `io::Error` was sourced from the operating system, + /// it will be a value inferred from the system's error encoding. + /// See [`last_os_error`][last_os_error] for more details. + /// + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// println!("{:?}", err.kind()); + /// } + /// + /// fn main() { + /// // As no error has (visibly) occurred, this may print anything! + /// // It likely prints a placeholder for unidentified (non-)errors. + /// print_error(Error::last_os_error()); + /// // Will print "AddrInUse". + /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn kind(&self) -> ErrorKind { + match self.repr.data() { + ErrorData::Os(code) => decode_error_kind(code), + ErrorData::Custom(c) => c.kind, + ErrorData::Simple(kind) => kind, + ErrorData::SimpleMessage(m) => m.kind, + } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[doc(hidden)] + #[inline] + pub fn is_interrupted(&self) -> bool { + match self.repr.data() { + ErrorData::Os(code) => is_interrupted(code), + ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, + ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, + ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, + } + } +} + +impl fmt::Debug for Repr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.data() { + ErrorData::Os(code) => fmt + .debug_struct("Os") + .field("code", &code) + .field("kind", &decode_error_kind(code)) + .field( + "message", + &fmt::from_fn(|fmt| { + write!(fmt, "\"{}\"", fmt::from_fn(|fmt| format_os_error(code, fmt))) + }), + ) + .finish(), + ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), + ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), + ErrorData::SimpleMessage(msg) => fmt + .debug_struct("Error") + .field("kind", &msg.kind) + .field("message", &msg.message) + .finish(), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.repr.data() { + ErrorData::Os(code) => { + let detail = fmt::from_fn(|fmt| format_os_error(code, fmt)); + write!(fmt, "{detail} (os error {code})") + } + ErrorData::Custom(c) => fmt::Display::fmt(c.error_ref(), fmt), + ErrorData::Simple(kind) => kind.fmt(fmt), + ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for Error { + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn error::Error> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error_ref().cause(), + } + } + + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error_ref().source(), + } + } +} + +fn _assert_error_is_sync_send() { + fn _is_sync_send() {} + _is_sync_send::(); +} #[doc(hidden)] #[derive(Debug)] diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/core/src/io/error/repr_bitpacked.rs similarity index 100% rename from library/std/src/io/error/repr_bitpacked.rs rename to library/core/src/io/error/repr_bitpacked.rs diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/core/src/io/error/repr_unpacked.rs similarity index 100% rename from library/std/src/io/error/repr_unpacked.rs rename to library/core/src/io/error/repr_unpacked.rs diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 89713241b2e79..f4d7934102475 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -8,22 +8,25 @@ mod util; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use self::borrowed_buf::{BorrowedBuf, BorrowedCursor}; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::cursor::Cursor; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::error::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::error::{ - Custom, CustomOwner, OsFunctions, decode_error_kind, format_os_error, is_interrupted, - set_functions, -}; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::io_slice::{IoSlice, IoSliceMut}; +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use self::error::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use self::error::const_error; #[unstable(feature = "core_io", issue = "154046")] -pub use self::util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; +pub use self::{ + cursor::Cursor, + error::{Error, ErrorKind, Result}, + io_slice::{IoSlice, IoSliceMut}, + util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, +}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::util::{chain, take}; +pub use self::{ + error::{ + Custom, CustomOwner, OsFunctions, decode_error_kind, format_os_error, is_interrupted, + set_functions, + }, + util::{chain, take}, +}; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index b2ccb6994d676..cdf7d79dfb83a 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -108,6 +108,7 @@ #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] +#![feature(io_const_error)] #![feature(offset_of_enum)] #![feature(panic_internals)] #![feature(pattern_type_macro)] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 89ec4902e25a5..6dbe37ca9fba8 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,371 +1,20 @@ #[cfg(test)] mod tests; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::io::ErrorKind; -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use alloc_crate::io::RawOsError; -use alloc_crate::io::{ - Custom, CustomOwner, OsFunctions, custom_owner_from_box, decode_error_kind, format_os_error, - is_interrupted, set_functions, -}; - -// On 64-bit platforms, `io::Error` may use a bit-packed representation to -// reduce size. However, this representation assumes that error codes are -// always 32-bit wide. -// -// This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. -// Therefore, the packed representation is explicitly disabled for UEFI -// targets, and the unpacked representation must be used instead. -#[cfg_attr( - all(target_pointer_width = "64", not(target_os = "uefi")), - path = "error/repr_bitpacked.rs" -)] #[cfg_attr( - not(all(target_pointer_width = "64", not(target_os = "uefi"))), - path = "error/repr_unpacked.rs" + test, + expect(unused, reason = "only used in implementation for non-test compilation") )] -mod repr; - -use self::repr::Repr; -use crate::{error, fmt, result, sys}; - -/// A specialized [`Result`] type for I/O operations. -/// -/// This type is broadly used across [`std::io`] for any operation which may -/// produce an error. -/// -/// This type alias is generally used to avoid writing out [`io::Error`] directly and -/// is otherwise a direct mapping to [`Result`]. -/// -/// While usual Rust style is to import types directly, aliases of [`Result`] -/// often are not, to make it easier to distinguish between them. [`Result`] is -/// generally assumed to be [`core::result::Result`][`Result`], and so users of this alias -/// will generally use `io::Result` instead of shadowing the [prelude]'s import -/// of [`core::result::Result`][`Result`]. -/// -/// [`std::io`]: ../../std/io/index.html -/// [`io::Error`]: Error -/// [`Result`]: crate::result::Result -/// [prelude]: crate::prelude -/// -/// # Examples -/// -/// A convenience function that bubbles an `io::Result` to its caller: -/// -/// ``` -/// use std::io; -/// -/// fn get_string() -> io::Result { -/// let mut buffer = String::new(); -/// -/// io::stdin().read_line(&mut buffer)?; -/// -/// Ok(buffer) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(search_unbox)] -pub type Result = result::Result; - -/// The error type for I/O operations of the [`Read`][Read], [`Write`][Write], [`Seek`][Seek], and -/// associated traits. -/// -/// Errors mostly originate from the underlying OS, but custom instances of -/// `Error` can be created with crafted error messages and a particular value of -/// [`ErrorKind`]. -/// -/// [Read]: ../../std/io/trait.Read.html -/// [Write]: ../../std/io/trait.Write.html -/// [Seek]: ../../std/io/trait.Seek.html -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_has_incoherent_inherent_impls] -pub struct Error { - repr: Repr, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.repr, f) - } -} - -/// Common errors constants for use in std -#[doc(hidden)] -impl Error { - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const INVALID_UTF8: Self = - const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const READ_EXACT_EOF: Self = - const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const UNKNOWN_THREAD_COUNT: Self = const_error!( - ErrorKind::NotFound, - "the number of hardware threads is not known for the target platform", - ); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const UNSUPPORTED_PLATFORM: Self = - const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const WRITE_ALL_EOF: Self = - const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const ZERO_TIMEOUT: Self = - const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub const NO_ADDRESSES: Self = - const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From for Error { - /// Converts a [`alloc::ffi::NulError`] into a [`Error`]. - fn from(_: alloc::ffi::NulError) -> Error { - const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") - } -} - -#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")] -impl From for Error { - /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. - /// - /// `TryReserveError` won't be available as the error `source()`, - /// but this may change in the future. - fn from(_: alloc::collections::TryReserveError) -> Error { - // ErrorData::Custom allocates, which isn't great for handling OOM errors. - ErrorKind::OutOfMemory.into() - } -} - -// Only derive debug in tests, to make sure it -// doesn't accidentally get printed. -#[cfg_attr(test, derive(Debug))] -enum ErrorData { - Os(RawOsError), - Simple(ErrorKind), - SimpleMessage(&'static SimpleMessage), - Custom(C), -} - -// `#[repr(align(4))]` is probably redundant, it should have that value or -// higher already. We include it just because repr_bitpacked.rs's encoding -// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the -// alignment required by the struct, only increase it). -// -// If we add more variants to ErrorData, this can be increased to 8, but it -// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or -// whatever cfg we're using to enable the `repr_bitpacked` code, since only the -// that version needs the alignment, and 8 is higher than the alignment we'll -// have on 32 bit platforms. -// -// (For the sake of being explicit: the alignment requirement here only matters -// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't -// matter at all) -#[doc(hidden)] -#[unstable(feature = "io_const_error_internals", issue = "none")] -#[repr(align(4))] -#[derive(Debug)] -pub struct SimpleMessage { - pub kind: ErrorKind, - pub message: &'static str, -} - -/// Creates a new I/O error from a known kind of error and a string literal. -/// -/// Contrary to [`Error::new`][new], this macro does not allocate and can be used in -/// `const` contexts. -/// -/// [new]: ../../std/io/struct.Error.html#method.new -/// -/// # Example -/// ``` -/// #![feature(io_const_error)] -/// use std::io::{const_error, Error, ErrorKind}; -/// -/// const FAIL: Error = const_error!(ErrorKind::Unsupported, "tried something that never works"); -/// -/// fn not_here() -> Result<(), Error> { -/// Err(FAIL) -/// } -/// ``` -#[rustc_macro_transparency = "semiopaque"] -#[unstable(feature = "io_const_error", issue = "133448")] -#[allow_internal_unstable(core_io, hint_must_use, io_const_error_internals)] -pub macro const_error($kind:expr, $message:expr $(,)?) { - $crate::hint::must_use($crate::io::Error::from_static_message( - const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, - )) -} - -/// Intended for use for errors not exposed to the user, where allocating onto -/// the heap (for normal construction via Error::new) is too costly. -#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] -impl From for Error { - /// Converts an [`ErrorKind`] into an [`Error`]. - /// - /// This conversion creates a new error with a simple representation of error kind. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// let not_found = ErrorKind::NotFound; - /// let error = Error::from(not_found); - /// assert_eq!("entity not found", format!("{error}")); - /// ``` - #[inline] - fn from(kind: ErrorKind) -> Error { - Error { repr: Repr::new_simple(kind) } - } -} +use crate::{ + io::{Error, OsFunctions, RawOsError}, + sys::io::{decode_error_kind, errno, error_string, is_interrupted}, +}; +// Because std is linked in during testing, these incoherent implementations would +// be duplicated if this was unconditionally included. +// See #2912 for details. +#[cfg(not(test))] impl Error { - /// # Safety - /// - /// The provided `CustomOwner` must have been constructed from a `Box` from the `alloc` crate. - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[must_use] - #[inline] - pub unsafe fn from_custom_owner(custom: CustomOwner) -> Error { - Error { repr: Repr::new_custom(custom) } - } - - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[must_use] - #[inline] - pub fn into_custom_owner(self) -> result::Result { - if matches!(self.repr.data(), ErrorData::Custom(..)) { - let ErrorData::Custom(c) = self.repr.into_data() else { - // SAFETY: Checked above using `matches!`. - unsafe { crate::hint::unreachable_unchecked() } - }; - Ok(c) - } else { - Err(self) - } - } - - /// Creates a new I/O error from a known kind of error as well as an - /// arbitrary error payload. - /// - /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. The `error` argument is an arbitrary - /// payload which will be contained in this [`Error`]. - /// - /// Note that this function allocates memory on the heap. - /// If no extra payload is required, use the `From` conversion from - /// `ErrorKind`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// // errors can be created from strings - /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); - /// - /// // errors can also be created from other errors - /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); - /// - /// // creating an error without payload (and without memory allocation) - /// let eof_error = Error::from(ErrorKind::UnexpectedEof); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")] - #[inline(never)] - #[rustc_allow_incoherent_impl] - pub fn new(kind: ErrorKind, error: E) -> Error - where - E: Into>, - { - let custom = custom_owner_from_box(kind, error.into()); - - // SAFETY: `custom_owner` has been constructed from a `Box` from the `alloc` crate. - unsafe { Error::from_custom_owner(custom) } - } - - /// Creates a new I/O error from an arbitrary error payload. - /// - /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. It is a shortcut for [`Error::new`][new] - /// with [`ErrorKind::Other`]. - /// - /// [new]: struct.Error.html#method.new - /// - /// # Examples - /// - /// ``` - /// use std::io::Error; - /// - /// // errors can be created from strings - /// let custom_error = Error::other("oh no!"); - /// - /// // errors can also be created from other errors - /// let custom_error2 = Error::other(custom_error); - /// ``` - #[stable(feature = "io_error_other", since = "1.74.0")] - #[rustc_allow_incoherent_impl] - pub fn other(error: E) -> Error - where - E: Into>, - { - Self::new(ErrorKind::Other, error) - } - - /// Creates a new I/O error from a known kind of error as well as a constant - /// message. - /// - /// This function does not allocate. - /// - /// You should not use this directly, and instead use the `const_error!` - /// macro: `io::const_error!(ErrorKind::Something, "some_message")`. - /// - /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. - #[inline] - #[doc(hidden)] - #[unstable(feature = "io_const_error_internals", issue = "none")] - pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { - Self { repr: Repr::new_simple_message(msg) } - } - - /// # Safety - /// - /// `functions` must point to data that is entirely constant; it must - /// not be created during runtime. - #[doc(hidden)] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[must_use] - #[inline] - pub unsafe fn from_raw_os_error_with_functions( - code: RawOsError, - functions: &'static OsFunctions, - ) -> Error { - // SAFETY: Caller ensures `functions` is a constant not created at runtime. - unsafe { - set_functions(functions); - } - Error { repr: Repr::new_os(code) } - } - /// Returns an error representing the last OS error which occurred. /// /// This function reads the value of `errno` for the target platform (e.g. @@ -392,7 +41,7 @@ impl Error { #[must_use] #[inline] pub fn last_os_error() -> Error { - Error::from_raw_os_error(sys::io::errno()) + Error::from_raw_os_error(errno()) } /// Creates a new instance of an [`Error`] from a particular OS error code. @@ -426,425 +75,12 @@ impl Error { #[inline] pub fn from_raw_os_error(code: RawOsError) -> Error { const FUNCTIONS: &'static OsFunctions = &OsFunctions { - format_os_error: |code, fmt| fmt.write_str(&sys::io::error_string(code)), - decode_error_kind: sys::io::decode_error_kind, - is_interrupted: sys::io::is_interrupted, + format_os_error: |code, fmt| fmt.write_str(&error_string(code)), + decode_error_kind, + is_interrupted, }; // SAFETY: `FUNCTIONS` is a constant and not created at runtime. unsafe { Error::from_raw_os_error_with_functions(code, FUNCTIONS) } } - - /// Returns the OS error that this error represents (if any). - /// - /// If this [`Error`] was constructed via [`last_os_error`][last_os_error] or - /// [`from_raw_os_error`][from_raw_os_error], then this function will return [`Some`], otherwise - /// it will return [`None`]. - /// - /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error - /// [from_raw_os_error]: ../../std/io/struct.Error.html#method.from_raw_os_error - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_os_error(err: &Error) { - /// if let Some(raw_os_err) = err.raw_os_error() { - /// println!("raw OS error: {raw_os_err:?}"); - /// } else { - /// println!("Not an OS error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "raw OS error: ...". - /// print_os_error(&Error::last_os_error()); - /// // Will print "Not an OS error". - /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn raw_os_error(&self) -> Option { - match self.repr.data() { - ErrorData::Os(i) => Some(i), - ErrorData::Custom(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - } - } - - /// Returns a reference to the inner error wrapped by this error (if any). - /// - /// If this [`Error`] was constructed via [`new`][new] then this function will - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [new]: ../../std/io/struct.Error.html#method.new - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {inner_err:?}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use] - #[inline] - pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(c.error_ref()), - } - } - - /// Returns a mutable reference to the inner error wrapped by this error - /// (if any). - /// - /// If this [`Error`] was constructed via [`new`][new] then this function will - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [new]: ../../std/io/struct.Error.html#method.new - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// use std::{error, fmt}; - /// use std::fmt::Display; - /// - /// #[derive(Debug)] - /// struct MyError { - /// v: String, - /// } - /// - /// impl MyError { - /// fn new() -> MyError { - /// MyError { - /// v: "oh no!".to_string() - /// } - /// } - /// - /// fn change_message(&mut self, new_message: &str) { - /// self.v = new_message.to_string(); - /// } - /// } - /// - /// impl error::Error for MyError {} - /// - /// impl Display for MyError { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "MyError: {}", self.v) - /// } - /// } - /// - /// fn change_error(mut err: Error) -> Error { - /// if let Some(inner_err) = err.get_mut() { - /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); - /// } - /// err - /// } - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {inner_err}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&change_error(Error::last_os_error())); - /// // Will print "Inner error: ...". - /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use] - #[inline] - pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { - match self.repr.data_mut() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(c.error_mut()), - } - } - - /// Consumes the `Error`, returning its inner error (if any). - /// - /// If this [`Error`] was constructed via [`new`][new] or [`other`][other], - /// then this function will return [`Some`], - /// otherwise it will return [`None`]. - /// - /// [new]: struct.Error.html#method.new - /// [other]: struct.Error.html#method.other - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// if let Some(inner_err) = err.into_inner() { - /// println!("Inner error: {inner_err}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use = "`self` will be dropped if the result is not used"] - #[inline] - #[rustc_allow_incoherent_impl] - pub fn into_inner(self) -> Option> { - let custom_owner = self.into_custom_owner().ok()?; - - let ptr = custom_owner.into_raw().as_ptr(); - - // SAFETY: - // `Error` can only contain a `CustomOwner` if it was constructed using `Box::into_raw`. - let custom = unsafe { Box::::from_raw(ptr) }; - - let ptr = custom.into_raw().as_ptr(); - - // SAFETY: - // Any `CustomOwner` from an `Error` was constructed by the `alloc` crate - // to contain a `Custom` which itself was constructed with `Box::into_raw`. - Some(unsafe { Box::from_raw(ptr) }) - } - - /// Attempts to downcast the custom boxed error to `E`. - /// - /// If this [`Error`] contains a custom boxed error, - /// then it would attempt downcasting on the boxed error, - /// otherwise it will return [`Err`]. - /// - /// If the custom boxed error has the same type as `E`, it will return [`Ok`], - /// otherwise it will also return [`Err`]. - /// - /// This method is meant to be a convenience routine for calling - /// `Box::downcast` on the custom boxed error, returned by - /// [`Error::into_inner`][into_inner]. - /// - /// [into_inner]: struct.Error.html#method.into_inner - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// use std::io; - /// use std::error::Error; - /// - /// #[derive(Debug)] - /// enum E { - /// Io(io::Error), - /// SomeOtherVariant, - /// } - /// - /// impl fmt::Display for E { - /// // ... - /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// # todo!() - /// # } - /// } - /// impl Error for E {} - /// - /// impl From for E { - /// fn from(err: io::Error) -> E { - /// err.downcast::() - /// .unwrap_or_else(E::Io) - /// } - /// } - /// - /// impl From for io::Error { - /// fn from(err: E) -> io::Error { - /// match err { - /// E::Io(io_error) => io_error, - /// e => io::Error::new(io::ErrorKind::Other, e), - /// } - /// } - /// } - /// - /// # fn main() { - /// let e = E::SomeOtherVariant; - /// // Convert it to an io::Error - /// let io_error = io::Error::from(e); - /// // Cast it back to the original variant - /// let e = E::from(io_error); - /// assert!(matches!(e, E::SomeOtherVariant)); - /// - /// let io_error = io::Error::from(io::ErrorKind::AlreadyExists); - /// // Convert it to E - /// let e = E::from(io_error); - /// // Cast it back to the original variant - /// let io_error = io::Error::from(e); - /// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists); - /// assert!(io_error.get_ref().is_none()); - /// assert!(io_error.raw_os_error().is_none()); - /// # } - /// ``` - #[stable(feature = "io_error_downcast", since = "1.79.0")] - #[rustc_allow_incoherent_impl] - pub fn downcast(self) -> result::Result - where - E: error::Error + Send + Sync + 'static, - { - if let Some(e) = self.get_ref() - && e.is::() - { - if let Some(b) = self.into_inner() - && let Ok(err) = b.downcast::() - { - Ok(*err) - } else { - // Safety: We have just checked that the condition is true - unsafe { core::hint::unreachable_unchecked() } - } - } else { - Err(self) - } - } - - /// Returns the corresponding [`ErrorKind`] for this error. - /// - /// This may be a value set by Rust code constructing custom `io::Error`s, - /// or if this `io::Error` was sourced from the operating system, - /// it will be a value inferred from the system's error encoding. - /// See [`last_os_error`][last_os_error] for more details. - /// - /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// println!("{:?}", err.kind()); - /// } - /// - /// fn main() { - /// // As no error has (visibly) occurred, this may print anything! - /// // It likely prints a placeholder for unidentified (non-)errors. - /// print_error(Error::last_os_error()); - /// // Will print "AddrInUse". - /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn kind(&self) -> ErrorKind { - match self.repr.data() { - ErrorData::Os(code) => decode_error_kind(code), - ErrorData::Custom(c) => c.kind, - ErrorData::Simple(kind) => kind, - ErrorData::SimpleMessage(m) => m.kind, - } - } - - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[doc(hidden)] - #[inline] - pub fn is_interrupted(&self) -> bool { - match self.repr.data() { - ErrorData::Os(code) => is_interrupted(code), - ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, - ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, - ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, - } - } -} - -impl fmt::Debug for Repr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.data() { - ErrorData::Os(code) => fmt - .debug_struct("Os") - .field("code", &code) - .field("kind", &decode_error_kind(code)) - .field( - "message", - &fmt::from_fn(|fmt| { - write!(fmt, "\"{}\"", fmt::from_fn(|fmt| format_os_error(code, fmt))) - }), - ) - .finish(), - ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), - ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), - ErrorData::SimpleMessage(msg) => fmt - .debug_struct("Error") - .field("kind", &msg.kind) - .field("message", &msg.message) - .finish(), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.repr.data() { - ErrorData::Os(code) => { - let detail = fmt::from_fn(|fmt| format_os_error(code, fmt)); - write!(fmt, "{detail} (os error {code})") - } - ErrorData::Custom(c) => fmt::Display::fmt(c.error_ref(), fmt), - ErrorData::Simple(kind) => kind.fmt(fmt), - ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for Error { - #[allow(deprecated)] - fn cause(&self) -> Option<&dyn error::Error> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error_ref().cause(), - } - } - - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error_ref().source(), - } - } -} - -fn _assert_error_is_sync_send() { - fn _is_sync_send() {} - _is_sync_send::(); } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 2ddd6c66091dc..36e61ce68401e 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -299,22 +299,25 @@ mod tests; use core::slice::memchr; +use alloc_crate::io::OsFunctions; +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub use alloc_crate::io::RawOsError; +#[doc(hidden)] +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use alloc_crate::io::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use alloc_crate::io::const_error; #[unstable(feature = "read_buf", issue = "78485")] pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::io::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; +pub use alloc_crate::io::{ + Chain, Empty, Error, ErrorKind, Repeat, Result, Sink, Take, empty, repeat, sink, +}; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use self::error::RawOsError; -#[doc(hidden)] -#[unstable(feature = "io_const_error_internals", issue = "none")] -pub use self::error::SimpleMessage; -#[unstable(feature = "io_const_error", issue = "133448")] -pub use self::error::const_error; #[stable(feature = "anonymous_pipe", since = "1.87.0")] pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] @@ -331,7 +334,6 @@ pub use self::{ buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, copy::copy, cursor::Cursor, - error::{Error, ErrorKind, Result}, stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; use crate::mem::MaybeUninit; diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr index a997fbee1f2a0..ad8802bf1370f 100644 --- a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr +++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr @@ -12,7 +12,7 @@ note: an implementation of `PartialEq` might be missing for `T1` LL | struct T1; | ^^^^^^^^^ must implement `PartialEq` note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` @@ -34,7 +34,7 @@ note: `Thread` does not implement `PartialEq` | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate @@ -58,7 +58,7 @@ note: `Thread` does not implement `PartialEq` | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` From ffff5fb71ea4c1b97d03f3693fd4710ee2f8f55d Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 15:27:15 +1000 Subject: [PATCH 034/278] Reduce usage of unstable public items --- library/alloc/src/io/error.rs | 11 ++++--- library/alloc/src/io/mod.rs | 10 +------ library/core/src/io/error.rs | 29 ++++++++----------- library/core/src/io/error/os_functions.rs | 16 +++------- .../core/src/io/error/os_functions_atomic.rs | 16 +++------- library/core/src/io/mod.rs | 5 +--- 6 files changed, 27 insertions(+), 60 deletions(-) diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs index e2ac82c95e10e..8053e73917c7f 100644 --- a/library/alloc/src/io/error.rs +++ b/library/alloc/src/io/error.rs @@ -1,11 +1,12 @@ +use core::io::Custom; +#[cfg_attr(no_global_oom_handling, expect(unused_imports))] +use core::io::CustomOwner; use core::{error, result}; use crate::boxed::Box; -#[cfg_attr(no_global_oom_handling, expect(unused_imports))] -use crate::io::CustomOwner; #[cfg_attr(any(no_rc, no_sync, no_global_oom_handling), expect(unused_imports))] use crate::io::const_error; -use crate::io::{Custom, Error, ErrorKind}; +use crate::io::{Error, ErrorKind}; impl Error { /// Creates a new I/O error from a known kind of error as well as an @@ -242,9 +243,7 @@ impl From for Error { } #[cfg(not(no_global_oom_handling))] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn custom_owner_from_box( +fn custom_owner_from_box( kind: ErrorKind, error: Box, ) -> CustomOwner { diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index d868cc66b8e2a..58e5ffa39d0c4 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -17,12 +17,4 @@ pub use core::io::{ }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{ - Custom, CustomOwner, OsFunctions, chain, decode_error_kind, format_os_error, is_interrupted, - set_functions, take, -}; - -#[cfg(not(no_global_oom_handling))] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::error::custom_owner_from_box; +pub use core::io::{OsFunctions, chain, take}; diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 36e91a55e5264..393873c57621b 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -21,9 +21,7 @@ mod repr; #[cfg_attr(not(target_has_atomic_load_store = "ptr"), path = "error/os_functions.rs")] mod os_functions; -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; +use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; use self::repr::Repr; use crate::{error, fmt, result}; @@ -572,8 +570,7 @@ impl OsFunctions { #[repr(align(4))] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub struct Custom { - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub kind: ErrorKind, + kind: ErrorKind, error: crate::ptr::NonNull, error_drop: unsafe fn(*mut (dyn error::Error + Send + Sync)), outer_drop: unsafe fn(*mut Self), @@ -628,16 +625,14 @@ impl Custom { ptr } - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub fn error_ref(&self) -> &(dyn error::Error + Send + Sync + 'static) { + fn error_ref(&self) -> &(dyn error::Error + Send + Sync + 'static) { // SAFETY: // `from_raw` ensures `error` is a valid pointer up to a static lifetime // and is owned by `self` unsafe { self.error.as_ref() } } - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub fn error_mut(&mut self) -> &mut (dyn error::Error + Send + Sync + 'static) { + fn error_mut(&mut self) -> &mut (dyn error::Error + Send + Sync + 'static) { // SAFETY: // `from_raw` ensures `error` is a valid pointer up to a static lifetime // and is owned by `self` @@ -673,6 +668,7 @@ impl CustomOwner { /// # Safety /// /// * The `outer_drop` of the provided `custom` must be safe to call exactly once. + #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub unsafe fn from_raw(custom: crate::ptr::NonNull) -> CustomOwner { CustomOwner(custom) @@ -685,16 +681,16 @@ impl CustomOwner { ptr } - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub fn custom_ref(&self) -> &Custom { + #[allow(dead_code, reason = "only used for unpacked representation")] + fn custom_ref(&self) -> &Custom { // SAFETY: // `from_raw` ensures `0` is a valid pointer up to a static lifetime // and is owned by `self` unsafe { self.0.as_ref() } } - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - pub fn custom_mut(&mut self) -> &mut Custom { + #[allow(dead_code, reason = "only used for unpacked representation")] + fn custom_mut(&mut self) -> &mut Custom { // SAFETY: // `from_raw` ensures `0` is a valid pointer up to a static lifetime // and is owned by `self` @@ -968,7 +964,7 @@ pub enum ErrorKind { } impl ErrorKind { - pub(crate) const fn as_str(&self) -> &'static str { + const fn as_str(&self) -> &'static str { use ErrorKind::*; match *self { // tidy-alphabetical-start @@ -1022,9 +1018,8 @@ impl ErrorKind { // unsafe, or to hard-code max ErrorKind or its size in a way the compiler // couldn't verify. #[inline] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[doc(hidden)] - pub const fn from_prim(ek: u32) -> Option { + #[allow(dead_code, reason = "only used for packed representation")] + const fn from_prim(ek: u32) -> Option { macro_rules! from_prim { ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{ // Force a compile error if the list gets out of date. diff --git a/library/core/src/io/error/os_functions.rs b/library/core/src/io/error/os_functions.rs index ce769fa53df9a..b34b7c7dc7926 100644 --- a/library/core/src/io/error/os_functions.rs +++ b/library/core/src/io/error/os_functions.rs @@ -6,33 +6,25 @@ use crate::fmt; /// The provided reference must point to data that is entirely constant; it must /// not be created during runtime. #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub unsafe fn set_functions(f: &'static OsFunctions) { +pub(super) unsafe fn set_functions(f: &'static OsFunctions) { // FIXME: externally implementable items may allow for weak linkage, allowing // these methods to be overridden even when atomic pointers are not supported. } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { +pub(super) fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let f = OsFunctions::DEFAULT; (f.format_os_error)(errno, fmt) } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn decode_error_kind(errno: RawOsError) -> ErrorKind { +pub(super) fn decode_error_kind(errno: RawOsError) -> ErrorKind { let f = OsFunctions::DEFAULT; (f.decode_error_kind)(errno) } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn is_interrupted(errno: RawOsError) -> bool { +pub(super) fn is_interrupted(errno: RawOsError) -> bool { let f = OsFunctions::DEFAULT; (f.is_interrupted)(errno) } diff --git a/library/core/src/io/error/os_functions_atomic.rs b/library/core/src/io/error/os_functions_atomic.rs index cc36f2953479f..dfaf59ae742de 100644 --- a/library/core/src/io/error/os_functions_atomic.rs +++ b/library/core/src/io/error/os_functions_atomic.rs @@ -31,9 +31,7 @@ fn get_os_functions() -> &'static OsFunctions { /// The provided reference must point to data that is entirely constant; it must /// not be created during runtime. #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub unsafe fn set_functions(f: &'static OsFunctions) { +pub(super) unsafe fn set_functions(f: &'static OsFunctions) { #[cold] fn set_functions_inner(f: &'static OsFunctions) { OS_FUNCTIONS.store(f as *const _ as *mut _, atomic::Ordering::Relaxed); @@ -45,25 +43,19 @@ pub unsafe fn set_functions(f: &'static OsFunctions) { } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { +pub(super) fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let f = get_os_functions(); (f.format_os_error)(errno, fmt) } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn decode_error_kind(errno: RawOsError) -> ErrorKind { +pub(super) fn decode_error_kind(errno: RawOsError) -> ErrorKind { let f = get_os_functions(); (f.decode_error_kind)(errno) } #[inline] -#[doc(hidden)] -#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub fn is_interrupted(errno: RawOsError) -> bool { +pub(super) fn is_interrupted(errno: RawOsError) -> bool { let f = get_os_functions(); (f.is_interrupted)(errno) } diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index f4d7934102475..b3c8815cb062c 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -24,9 +24,6 @@ pub use self::{ #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ - error::{ - Custom, CustomOwner, OsFunctions, decode_error_kind, format_os_error, is_interrupted, - set_functions, - }, + error::{Custom, CustomOwner, OsFunctions}, util::{chain, take}, }; From 31f35326e625c2f5d4fb9e38f97e46b57bc7378b Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 18 May 2026 15:30:01 +1000 Subject: [PATCH 035/278] Simplify `core::io` documentation links Now that `Error` lives in `core::io`, doc links to it from `core` can be simplified. --- library/core/src/io/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 393873c57621b..032231ed05262 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -719,7 +719,7 @@ pub type RawOsError = cfg_select! { /// /// It is used with the [`io::Error`][error] type. /// -/// [error]: ../../std/io/struct.Error.html +/// [error]: Error /// /// # Handling errors and matching on `ErrorKind` /// @@ -949,7 +949,7 @@ pub enum ErrorKind { /// error kinds cannot be `match`ed on, and will only match a wildcard (`_`) pattern. /// New [`ErrorKind`]s might be added in the future for some of those. /// - /// [error]: ../../std/io/struct.Error.html + /// [error]: Error #[stable(feature = "rust1", since = "1.0.0")] Other, From 6ccea6c7fb880adc6c1fe9cac306fb8b5b753d30 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Thu, 11 Jun 2026 13:48:00 +0200 Subject: [PATCH 036/278] remove AliasTerm::def_id() --- clippy_utils/src/ty/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index a944e91db0c0e..60d19ecf68bb5 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -712,7 +712,7 @@ fn sig_from_bounds<'tcx>( inputs = Some(i); }, ty::ClauseKind::Projection(p) - if Some(p.projection_term.def_id()) == lang_items.fn_once_output() + if Some(p.projection_term.expect_projection_def_id()) == lang_items.fn_once_output() && p.projection_term.self_ty() == ty => { if output.is_some() { @@ -753,7 +753,9 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option } inputs = Some(i); }, - ty::ClauseKind::Projection(p) if Some(p.projection_term.def_id()) == lang_items.fn_once_output() => { + ty::ClauseKind::Projection(p) + if Some(p.projection_term.expect_projection_def_id()) == lang_items.fn_once_output() => + { if output.is_some() { // Multiple different fn trait impls. Is this even allowed? return None; From 1d67d0d8ccd0913eb4326212cc3f79bec7745594 Mon Sep 17 00:00:00 2001 From: Ruy Rocha <108208+ruyrocha@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:42:04 -0300 Subject: [PATCH 037/278] Update rand to 0.9.3 to fix GHSA-cq8v-f236-94qc --- library/stdarch/Cargo.lock | 50 +++++++++---------- .../crates/stdarch-gen-loongarch/Cargo.toml | 2 +- library/stdarch/examples/Cargo.toml | 2 +- library/stdarch/examples/connect5.rs | 4 +- library/stdarch/examples/hex.rs | 4 +- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/library/stdarch/Cargo.lock b/library/stdarch/Cargo.lock index a1c31fa9f0cea..804879c8fdc6a 100644 --- a/library/stdarch/Cargo.lock +++ b/library/stdarch/Cargo.lock @@ -282,13 +282,14 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "getrandom" -version = "0.2.17" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "wasi", + "r-efi 5.3.0", + "wasip2", ] [[package]] @@ -299,7 +300,7 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "rand_core 0.10.0", "wasip2", "wasip3", @@ -529,7 +530,7 @@ checksum = "95c589f335db0f6aaa168a7cd27b1fc6920f5e1470c804f814d9cd6e62a0f70b" dependencies = [ "env_logger 0.11.10", "log", - "rand 0.10.0", + "rand 0.10.1", ] [[package]] @@ -541,6 +542,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r-efi" version = "6.0.0" @@ -549,20 +556,19 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "libc", "rand_chacha", - "rand_core 0.6.4", + "rand_core 0.9.5", ] [[package]] name = "rand" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "getrandom 0.4.2", "rand_core 0.10.0", @@ -570,21 +576,21 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core 0.9.5", ] [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.3.4", ] [[package]] @@ -805,7 +811,7 @@ dependencies = [ name = "stdarch-gen-loongarch" version = "0.1.0" dependencies = [ - "rand 0.8.5", + "rand 0.9.4", ] [[package]] @@ -838,7 +844,7 @@ version = "0.0.0" dependencies = [ "core_arch", "quickcheck", - "rand 0.8.5", + "rand 0.9.4", ] [[package]] @@ -921,12 +927,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - [[package]] name = "wasip2" version = "1.0.2+wasi-0.2.9" diff --git a/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml b/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml index d3ac607c5576c..1a8c052ebe318 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml +++ b/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml @@ -7,4 +7,4 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rand = "0.8.5" +rand = "0.9.3" diff --git a/library/stdarch/examples/Cargo.toml b/library/stdarch/examples/Cargo.toml index c4fc4c7e374c8..8752f206526c7 100644 --- a/library/stdarch/examples/Cargo.toml +++ b/library/stdarch/examples/Cargo.toml @@ -13,7 +13,7 @@ default-run = "hex" [dependencies] core_arch = { path = "../crates/core_arch" } quickcheck = "1.0" -rand = "0.8" +rand = "0.9.3" [[bin]] name = "hex" diff --git a/library/stdarch/examples/connect5.rs b/library/stdarch/examples/connect5.rs index f24657b148394..2df294b16b71e 100644 --- a/library/stdarch/examples/connect5.rs +++ b/library/stdarch/examples/connect5.rs @@ -33,8 +33,8 @@ #![cfg_attr(target_arch = "x86_64", feature(stdarch_internal))] #![feature(stmt_expr_attributes)] +use rand::rng; use rand::seq::SliceRandom; -use rand::thread_rng; use std::cmp; use std::time::Instant; @@ -374,7 +374,7 @@ impl List { } pub fn shuffle(&mut self) { - let mut rng = thread_rng(); + let mut rng = rng(); let num = self.p_size as usize; self.p_move[..num].shuffle(&mut rng); diff --git a/library/stdarch/examples/hex.rs b/library/stdarch/examples/hex.rs index 21827b375adaf..6417562074d9c 100644 --- a/library/stdarch/examples/hex.rs +++ b/library/stdarch/examples/hex.rs @@ -354,9 +354,9 @@ mod benches { len: usize, f: for<'a> unsafe fn(&[u8], &'a mut [u8]) -> Result<&'a str, usize>, ) { - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let input = std::iter::repeat(()) - .map(|()| rng.r#gen::()) + .map(|()| rng.r#random::()) .take(len) .collect::>(); let mut dst = vec![0; input.len() * 2]; From 5076a6b5589db7e8182c0b17fb46e6444c21bcce Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 11 Jun 2026 10:46:08 -0400 Subject: [PATCH 038/278] Refactor `drain_collect` and clarify the lint messages. --- clippy_lints/src/methods/drain_collect.rs | 78 +++++++---------------- clippy_lints/src/methods/mod.rs | 6 +- tests/ui/drain_collect.stderr | 40 ++++++------ tests/ui/drain_collect_nostd.stderr | 4 +- 4 files changed, 50 insertions(+), 78 deletions(-) diff --git a/clippy_lints/src/methods/drain_collect.rs b/clippy_lints/src/methods/drain_collect.rs index 6c222640e2601..b9d8a0d26cc7e 100644 --- a/clippy_lints/src/methods/drain_collect.rs +++ b/clippy_lints/src/methods/drain_collect.rs @@ -4,64 +4,34 @@ use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; use clippy_utils::{is_range_full, std_or_core, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_middle::ty::Ty; -use rustc_span::Symbol; -/// Checks if both types match the given diagnostic item, e.g.: -/// -/// `vec![1,2].drain(..).collect::>()` -/// ^^^^^^^^^ ^^^^^^ true -/// `vec![1,2].drain(..).collect::>()` -/// ^^^^^^^^^ ^^^^^^^^^^ false -fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>, sym: Symbol) -> bool { - if let Some(expr_adt) = expr.ty_adt_def() - && let Some(recv_adt) = recv.ty_adt_def() - { - cx.tcx.is_diagnostic_item(sym, expr_adt.did()) && cx.tcx.is_diagnostic_item(sym, recv_adt.did()) - } else { - false - } -} - -/// Checks `std::{vec::Vec, collections::VecDeque}`. -fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - (types_match_diagnostic_item(cx, expr, recv, sym::Vec) - || types_match_diagnostic_item(cx, expr, recv, sym::VecDeque)) - && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) -} - -/// Checks `std::string::String` -fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - expr.is_lang_item(cx, LangItem::String) - && recv.is_lang_item(cx, LangItem::String) - && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) -} - -/// Checks `std::collections::{HashSet, HashMap, BinaryHeap}`. -fn check_collections(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>) -> Option<&'static str> { - types_match_diagnostic_item(cx, expr, recv, sym::HashSet) - .then_some("HashSet") - .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::HashMap).then_some("HashMap")) - .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::BinaryHeap).then_some("BinaryHeap")) -} - -pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, recv: &Expr<'_>) { - let expr_ty = cx.typeck_results().expr_ty(expr); - let recv_ty = cx.typeck_results().expr_ty(recv); - let recv_ty_no_refs = recv_ty.peel_refs(); - - if let ExprKind::Path(QPath::Resolved(_, recv_path)) = recv.kind - && let Some(typename) = check_vec(cx, args, expr_ty, recv_ty_no_refs, recv_path) - .then_some("Vec") - .or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String")) - .or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs)) +pub(super) fn check(cx: &LateContext<'_>, arg: Option<&Expr<'_>>, expr: &Expr<'_>, recv: &Expr<'_>) { + let ty = cx.typeck_results().expr_ty(recv); + let (is_ref, ty) = match *ty.kind() { + ty::Ref(_, ty, _) => (true, ty), + _ => (false, ty), + }; + if cx.typeck_results().expr_ty(expr) == ty + && let Some(did) = ty.opt_def_id() + && (cx.tcx.lang_items().string() == Some(did) + || matches!( + ty.opt_diag_name(cx), + Some(sym::HashMap | sym::HashSet | sym::BinaryHeap | sym::Vec | sym::VecDeque) + )) + && arg.is_none_or(|arg| { + if let ExprKind::Path(QPath::Resolved(None, path)) = recv.kind { + is_range_full(cx, arg, Some(path)) + } else { + false + } + }) && let Some(exec_context) = std_or_core(cx) { let recv = snippet(cx, recv.span, ""); - let sugg = if let ty::Ref(..) = recv_ty.kind() { + let sugg = if is_ref { format!("{exec_context}::mem::take({recv})") } else { format!("{exec_context}::mem::take(&mut {recv})") @@ -71,8 +41,8 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re cx, DRAIN_COLLECT, expr.span, - format!("you seem to be trying to move all elements into a new `{typename}`"), - "consider using `mem::take`", + "draining all elements of a collection into a new collection of the same type", + "use `mem::take` to avoid creating a new allocation", sugg, Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b01cd49739431..2ccab8685a7c6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5277,8 +5277,10 @@ impl Methods { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } }, - Some((sym::drain, recv, args, ..)) => { - drain_collect::check(cx, args, expr, recv); + Some((sym::drain, recv, args, ..)) => match args { + [arg] => drain_collect::check(cx, Some(arg), expr, recv), + [] => drain_collect::check(cx, None, expr, recv), + _ => {}, }, _ => {}, } diff --git a/tests/ui/drain_collect.stderr b/tests/ui/drain_collect.stderr index 46da4e799d2e8..7c1008aebe58a 100644 --- a/tests/ui/drain_collect.stderr +++ b/tests/ui/drain_collect.stderr @@ -1,8 +1,8 @@ -error: you seem to be trying to move all elements into a new `BinaryHeap` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:6:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` | note: the lint level is defined here --> tests/ui/drain_collect.rs:1:9 @@ -10,59 +10,59 @@ note: the lint level is defined here LL | #![deny(clippy::drain_collect)] | ^^^^^^^^^^^^^^^^^^^^^ -error: you seem to be trying to move all elements into a new `HashMap` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:15:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `HashSet` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:24:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:33:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:42:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:47:5 | LL | b.drain(0..).collect() - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:52:5 | LL | b.drain(..b.len()).collect() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:57:5 | LL | b.drain(0..b.len()).collect() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:63:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(&mut b)` -error: you seem to be trying to move all elements into a new `String` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:72:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` error: aborting due to 10 previous errors diff --git a/tests/ui/drain_collect_nostd.stderr b/tests/ui/drain_collect_nostd.stderr index 91b38932fee79..77fcae84d570e 100644 --- a/tests/ui/drain_collect_nostd.stderr +++ b/tests/ui/drain_collect_nostd.stderr @@ -1,8 +1,8 @@ -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect_nostd.rs:7:5 | LL | v.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `core::mem::take(v)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `core::mem::take(v)` | = note: `-D clippy::drain-collect` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::drain_collect)]` From a71d1184a526b96b0df48b7580eb65667b6390e1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 11 Jun 2026 10:46:25 -0400 Subject: [PATCH 039/278] Replace `is_range_full` with `is_full_collection_range`. --- clippy_lints/src/methods/clear_with_drain.rs | 9 ++- clippy_lints/src/methods/drain_collect.rs | 14 ++--- clippy_lints/src/methods/iter_with_drain.rs | 8 +-- clippy_utils/src/lib.rs | 59 ++++------------- tests/ui/clear_with_drain.fixed | 30 --------- tests/ui/clear_with_drain.rs | 30 --------- tests/ui/clear_with_drain.stderr | 66 +++++--------------- 7 files changed, 38 insertions(+), 178 deletions(-) diff --git a/clippy_lints/src/methods/clear_with_drain.rs b/clippy_lints/src/methods/clear_with_drain.rs index cd6b39b15a1b9..0b6ead0747a80 100644 --- a/clippy_lints/src/methods/clear_with_drain.rs +++ b/clippy_lints/src/methods/clear_with_drain.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; -use clippy_utils::{is_range_full, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{is_full_collection_range, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, QPath}; +use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; use rustc_span::Span; @@ -16,8 +16,7 @@ const ACCEPTABLE_TYPES_WITHOUT_ARG: [rustc_span::Symbol; 3] = [sym::BinaryHeap, pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: Option<&Expr<'_>>) { if let Some(arg) = arg { if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITH_ARG) - && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind - && is_range_full(cx, arg, Some(container_path)) + && is_full_collection_range(cx, recv.res_local_id(), arg) { suggest(cx, expr, recv, span); } diff --git a/clippy_lints/src/methods/drain_collect.rs b/clippy_lints/src/methods/drain_collect.rs index b9d8a0d26cc7e..2b4b200904b2e 100644 --- a/clippy_lints/src/methods/drain_collect.rs +++ b/clippy_lints/src/methods/drain_collect.rs @@ -1,10 +1,10 @@ use crate::methods::DRAIN_COLLECT; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_range_full, std_or_core, sym}; +use clippy_utils::{is_full_collection_range, std_or_core, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; @@ -21,13 +21,7 @@ pub(super) fn check(cx: &LateContext<'_>, arg: Option<&Expr<'_>>, expr: &Expr<'_ ty.opt_diag_name(cx), Some(sym::HashMap | sym::HashSet | sym::BinaryHeap | sym::Vec | sym::VecDeque) )) - && arg.is_none_or(|arg| { - if let ExprKind::Path(QPath::Resolved(None, path)) = recv.kind { - is_range_full(cx, arg, Some(path)) - } else { - false - } - }) + && arg.is_none_or(|arg| is_full_collection_range(cx, recv.res_local_id(), arg)) && let Some(exec_context) = std_or_core(cx) { let recv = snippet(cx, recv.span, ""); diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs index 8d1585baac451..2ef09fa3f8d2c 100644 --- a/clippy_lints/src/methods/iter_with_drain.rs +++ b/clippy_lints/src/methods/iter_with_drain.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_range_full, sym}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{is_full_collection_range, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::Span; @@ -12,8 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did()) && matches!(ty_name, sym::Vec | sym::VecDeque) - && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind - && is_range_full(cx, arg, Some(container_path)) + && is_full_collection_range(cx, recv.res_local_id(), arg) { span_lint_and_sugg( cx, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index c208b8b97506f..39c00337c6f77 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -120,7 +120,6 @@ use source::{SpanRangeExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::ast_utils::unordered_over; -use crate::consts::ConstEvalCtxt; use crate::higher::Range; use crate::msrvs::Msrv; use crate::res::{MaybeDef, MaybeQPath, MaybeResPath}; @@ -1329,59 +1328,23 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { }) } -/// Checks whether the given `Expr` is a range equivalent to a `RangeFull`. -/// -/// For the lower bound, this means that: -/// - either there is none -/// - or it is the smallest value that can be represented by the range's integer type -/// -/// For the upper bound, this means that: -/// - either there is none -/// - or it is the largest value that can be represented by the range's integer type and is -/// inclusive -/// - or it is a call to some container's `len` method and is exclusive, and the range is passed to -/// a method call on that same container (e.g. `v.drain(..v.len())`) -/// -/// If the given `Expr` is not some kind of range, the function returns `false`. -pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); +/// Checks whether the given `Expr` is a range over the entire container. +pub fn is_full_collection_range(cx: &LateContext<'_>, container: Option, expr: &Expr<'_>) -> bool { if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) { - let start_is_none_or_min = start.is_none_or(|start| { - if let rustc_ty::Adt(_, subst) = ty.kind() - && let bnd_ty = subst.type_at(0) - && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start) - { - start_const.is_numeric_min(cx.tcx, bnd_ty) - } else { - false - } - }); - let end_is_none_or_max = end.is_none_or(|end| match limits { - RangeLimits::Closed => { - if let rustc_ty::Adt(_, subst) = ty.kind() - && let bnd_ty = subst.type_at(0) - && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end) + start.is_none_or(|start| is_integer_literal(start, 0)) + && end.is_none_or(|end| { + if limits == RangeLimits::HalfOpen + && let Some(container) = container + && let ExprKind::MethodCall(seg, recv, [], _) = end.kind { - end_const.is_numeric_max(cx.tcx, bnd_ty) + seg.ident.name == sym::len && recv.res_local_id() == Some(container) } else { false } - }, - RangeLimits::HalfOpen => { - if let Some(container_path) = container_path - && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind - && name.ident.name == sym::len - && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind - { - container_path.res == path.res - } else { - false - } - }, - }); - return start_is_none_or_min && end_is_none_or_max; + }) + } else { + false } - false } /// Checks whether the given expression is a constant literal of the given value. diff --git a/tests/ui/clear_with_drain.fixed b/tests/ui/clear_with_drain.fixed index aaaec700cc9d2..5f8441c0470dc 100644 --- a/tests/ui/clear_with_drain.fixed +++ b/tests/ui/clear_with_drain.fixed @@ -20,11 +20,6 @@ fn vec_range() { let mut v = vec![1, 2, 3]; v.clear(); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.clear(); - //~^ clear_with_drain } fn vec_range_from() { @@ -45,11 +40,6 @@ fn vec_range_from() { let mut v = vec![1, 2, 3]; v.clear(); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.clear(); - //~^ clear_with_drain } fn vec_range_full() { @@ -124,11 +114,6 @@ fn vec_deque_range() { let mut deque = VecDeque::from([1, 2, 3]); deque.clear(); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.clear(); - //~^ clear_with_drain } fn vec_deque_range_from() { @@ -149,11 +134,6 @@ fn vec_deque_range_from() { let mut deque = VecDeque::from([1, 2, 3]); deque.clear(); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.clear(); - //~^ clear_with_drain } fn vec_deque_range_full() { @@ -228,11 +208,6 @@ fn string_range() { let mut s = String::from("Hello, world!"); s.clear(); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.clear(); - //~^ clear_with_drain } fn string_range_from() { @@ -253,11 +228,6 @@ fn string_range_from() { let mut s = String::from("Hello, world!"); s.clear(); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.clear(); - //~^ clear_with_drain } fn string_range_full() { diff --git a/tests/ui/clear_with_drain.rs b/tests/ui/clear_with_drain.rs index e36384e09c693..59024dcfe93d8 100644 --- a/tests/ui/clear_with_drain.rs +++ b/tests/ui/clear_with_drain.rs @@ -20,11 +20,6 @@ fn vec_range() { let mut v = vec![1, 2, 3]; v.drain(0..v.len()); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.drain(usize::MIN..v.len()); - //~^ clear_with_drain } fn vec_range_from() { @@ -45,11 +40,6 @@ fn vec_range_from() { let mut v = vec![1, 2, 3]; v.drain(0..); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.drain(usize::MIN..); - //~^ clear_with_drain } fn vec_range_full() { @@ -124,11 +114,6 @@ fn vec_deque_range() { let mut deque = VecDeque::from([1, 2, 3]); deque.drain(0..deque.len()); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.drain(usize::MIN..deque.len()); - //~^ clear_with_drain } fn vec_deque_range_from() { @@ -149,11 +134,6 @@ fn vec_deque_range_from() { let mut deque = VecDeque::from([1, 2, 3]); deque.drain(0..); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.drain(usize::MIN..); - //~^ clear_with_drain } fn vec_deque_range_full() { @@ -228,11 +208,6 @@ fn string_range() { let mut s = String::from("Hello, world!"); s.drain(0..s.len()); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.drain(usize::MIN..s.len()); - //~^ clear_with_drain } fn string_range_from() { @@ -253,11 +228,6 @@ fn string_range_from() { let mut s = String::from("Hello, world!"); s.drain(0..); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.drain(usize::MIN..); - //~^ clear_with_drain } fn string_range_full() { diff --git a/tests/ui/clear_with_drain.stderr b/tests/ui/clear_with_drain.stderr index 8b09c26e46af2..b4c71bf60be7f 100644 --- a/tests/ui/clear_with_drain.stderr +++ b/tests/ui/clear_with_drain.stderr @@ -8,124 +8,88 @@ LL | v.drain(0..v.len()); = help: to override `-D warnings` add `#[allow(clippy::clear_with_drain)]` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:26:7 - | -LL | v.drain(usize::MIN..v.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:46:7 + --> tests/ui/clear_with_drain.rs:41:7 | LL | v.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:51:7 - | -LL | v.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:68:7 + --> tests/ui/clear_with_drain.rs:58:7 | LL | v.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:86:7 + --> tests/ui/clear_with_drain.rs:76:7 | LL | v.drain(..v.len()); | ^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:125:11 + --> tests/ui/clear_with_drain.rs:115:11 | LL | deque.drain(0..deque.len()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:130:11 - | -LL | deque.drain(usize::MIN..deque.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:150:11 + --> tests/ui/clear_with_drain.rs:135:11 | LL | deque.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:155:11 - | -LL | deque.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:172:11 + --> tests/ui/clear_with_drain.rs:152:11 | LL | deque.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:190:11 + --> tests/ui/clear_with_drain.rs:170:11 | LL | deque.drain(..deque.len()); | ^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:229:7 + --> tests/ui/clear_with_drain.rs:209:7 | LL | s.drain(0..s.len()); | ^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:234:7 - | -LL | s.drain(usize::MIN..s.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:254:7 + --> tests/ui/clear_with_drain.rs:229:7 | LL | s.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:259:7 - | -LL | s.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:276:7 + --> tests/ui/clear_with_drain.rs:246:7 | LL | s.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:294:7 + --> tests/ui/clear_with_drain.rs:264:7 | LL | s.drain(..s.len()); | ^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `HashSet` - --> tests/ui/clear_with_drain.rs:333:9 + --> tests/ui/clear_with_drain.rs:303:9 | LL | set.drain(); | ^^^^^^^ help: try: `clear()` error: `drain` used to clear a `HashMap` - --> tests/ui/clear_with_drain.rs:353:9 + --> tests/ui/clear_with_drain.rs:323:9 | LL | map.drain(); | ^^^^^^^ help: try: `clear()` error: `drain` used to clear a `BinaryHeap` - --> tests/ui/clear_with_drain.rs:373:10 + --> tests/ui/clear_with_drain.rs:343:10 | LL | heap.drain(); | ^^^^^^^ help: try: `clear()` -error: aborting due to 21 previous errors +error: aborting due to 15 previous errors From 372057a78f0068766e547a993932ddf0daeeaba4 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Thu, 11 Jun 2026 13:47:09 -0400 Subject: [PATCH 040/278] Added PhantomPinned diagnostic item and prevented dead field warning on PhantomPinned --- clippy_lints/src/missing_fields_in_debug.rs | 1 + clippy_lints/src/pub_underscore_fields.rs | 4 +++- clippy_utils/src/sym.rs | 1 + .../pub_underscore_fields.all_pub_fields.stderr | 12 ++++++------ .../pub_underscore_fields/pub_underscore_fields.rs | 4 +++- tests/ui/missing_fields_in_debug.rs | 12 +++++++----- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 332c19e140b3e..634745422204f 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -185,6 +185,7 @@ fn check_struct<'tcx>( .filter_map(|field| { if field_accesses.contains(&field.ident.name) || field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) + || field.ty.basic_res().is_diag_item(cx, sym::PhantomPinned) { None } else { diff --git a/clippy_lints/src/pub_underscore_fields.rs b/clippy_lints/src/pub_underscore_fields.rs index 64724f7d01d00..92d1bf505bfaf 100644 --- a/clippy_lints/src/pub_underscore_fields.rs +++ b/clippy_lints/src/pub_underscore_fields.rs @@ -3,6 +3,7 @@ use clippy_config::types::PubUnderscoreFieldsBehaviour; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::sym::PhantomPinned; use rustc_hir::{FieldDef, Item, ItemKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -75,8 +76,9 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { if field.ident.as_str().starts_with('_') && is_visible(field) // We ignore fields that have `#[doc(hidden)]`. && !is_doc_hidden(cx.tcx.hir_attrs(field.hir_id)) - // We ignore fields that are `PhantomData`. + // We ignore fields that are `PhantomData` and `PhantomPinned`. && !field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) + && !field.ty.basic_res().is_diag_item(cx, PhantomPinned) { span_lint_hir_and_then( cx, diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index f414c0ac47fa4..59e98cdc7bef8 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -100,6 +100,7 @@ generate! { Path, PathBuf, PathLookup, + PhantomPinned, RangeBounds, RefCellRef, RefCellRefMut, diff --git a/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.all_pub_fields.stderr b/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.all_pub_fields.stderr index 40112fbf4c743..4632c06f095a3 100644 --- a/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.all_pub_fields.stderr +++ b/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.all_pub_fields.stderr @@ -9,7 +9,7 @@ LL | pub _b: u8, = help: to override `-D warnings` add `#[allow(clippy::pub_underscore_fields)]` error: field marked as public but also inferred as unused because it's prefixed with `_` - --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:23:13 + --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:24:13 | LL | pub(in crate::inner) _f: Option<()>, | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | pub(in crate::inner) _f: Option<()>, = help: consider removing the underscore, or making the field private error: field marked as public but also inferred as unused because it's prefixed with `_` - --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:28:13 + --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:29:13 | LL | pub _g: String, | ^^^^^^ @@ -25,7 +25,7 @@ LL | pub _g: String, = help: consider removing the underscore, or making the field private error: field marked as public but also inferred as unused because it's prefixed with `_` - --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:36:9 + --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:37:9 | LL | pub _a: usize, | ^^^^^^ @@ -33,7 +33,7 @@ LL | pub _a: usize, = help: consider removing the underscore, or making the field private error: field marked as public but also inferred as unused because it's prefixed with `_` - --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:44:9 + --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:45:9 | LL | pub _c: i64, | ^^^^^^ @@ -41,7 +41,7 @@ LL | pub _c: i64, = help: consider removing the underscore, or making the field private error: field marked as public but also inferred as unused because it's prefixed with `_` - --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:48:9 + --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:49:9 | LL | pub _e: Option, | ^^^^^^ @@ -49,7 +49,7 @@ LL | pub _e: Option, = help: consider removing the underscore, or making the field private error: field marked as public but also inferred as unused because it's prefixed with `_` - --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:62:9 + --> tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs:63:9 | LL | pub(crate) _b: Option, | ^^^^^^^^^^^^^ diff --git a/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs b/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs index 807469e2d8c5a..5a52cf78821b6 100644 --- a/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs +++ b/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs @@ -4,7 +4,7 @@ #![warn(clippy::pub_underscore_fields)] -use std::marker::PhantomData; +use std::marker::{PhantomData, PhantomPinned}; pub mod inner { use std::marker; @@ -15,6 +15,7 @@ pub mod inner { //~^ pub_underscore_fields _c: i32, pub _mark: marker::PhantomData, + pub _pinned: marker::PhantomPinned, } mod inner2 { @@ -68,6 +69,7 @@ fn main() { r#pub: bool, _pub: String, pub(crate) _mark: PhantomData, + pub(crate) _pinned: PhantomPinned, } // shouldn't warn when `#[allow]` is used on field level diff --git a/tests/ui/missing_fields_in_debug.rs b/tests/ui/missing_fields_in_debug.rs index 14803b5485a11..b206f4d8f8ce3 100644 --- a/tests/ui/missing_fields_in_debug.rs +++ b/tests/ui/missing_fields_in_debug.rs @@ -2,7 +2,7 @@ #![warn(clippy::missing_fields_in_debug)] use std::fmt; -use std::marker::PhantomData; +use std::marker::{PhantomData, PhantomPinned}; use std::ops::Deref; use std::thread::LocalKey; @@ -179,16 +179,18 @@ mod comment1175473620 { } // https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1175488757 -// PhantomData is an exception and does not need to be included -struct WithPD { +// https://github.com/rust-lang/rust/issues/154888 +// PhantomData & PhantomPinned are exceptions and do not need to be included +struct WithPDPP { a: u8, b: u8, c: PhantomData, + d: PhantomPinned, } -impl fmt::Debug for WithPD { +impl fmt::Debug for WithPDPP { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("WithPD") + f.debug_struct("WithPDPP") .field("a", &self.a) .field("b", &self.b) .finish() From ebedc9bd33913fb5bb473ccfbeba204271ef9c66 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 12 Jun 2026 13:17:07 -0700 Subject: [PATCH 041/278] unnecessary_lazy_evaluations: handle closure `->` Instead of bailing, this changes the lint to handle the return type and write out the correct result. If the function we're translating into can handle it, we use a turbofish, since that will influence type inference but not cause integer truncation. But, if that can't be done, it'll use `as` and let a future lint take care of that. --- clippy_lints/src/methods/mod.rs | 12 ++-- .../src/methods/unnecessary_lazy_eval.rs | 45 ++++++++---- tests/ui/unnecessary_lazy_eval.fixed | 12 ++++ tests/ui/unnecessary_lazy_eval.rs | 12 ++++ tests/ui/unnecessary_lazy_eval.stderr | 72 ++++++++++++------- tests/ui/unnecessary_lazy_eval_unfixable.rs | 6 -- .../ui/unnecessary_lazy_eval_unfixable.stderr | 14 +--- 7 files changed, 111 insertions(+), 62 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a7043d1359fa4..91cd6bff2fe66 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5238,7 +5238,7 @@ impl Methods { let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg); if !biom_option_linted && !biom_result_linted { - let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); + let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and", true); if !ule_and_linted { return_and_then::check(cx, expr, recv, arg); } @@ -5474,7 +5474,7 @@ impl Methods { get_last_with_len::check(cx, expr, recv, arg); }, (sym::get_or_insert_with, [arg]) => { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert", false); }, (sym::hash, [arg]) => { unit_hash::check(cx, expr, recv, arg); @@ -5629,14 +5629,14 @@ impl Methods { ptr_offset_by_literal::check(cx, expr, self.msrv); }, (sym::ok_or_else, [arg]) => { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or", true); }, (sym::open, [_]) => { open_options::check(cx, expr, recv); }, (sym::or_else, [arg]) => { if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "or", false); } }, (sym::peek, []) => { @@ -5737,7 +5737,7 @@ impl Methods { if !self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { return; } - unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some", true); }, (sym::try_into, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); @@ -5827,7 +5827,7 @@ impl Methods { ); }, _ => { - unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); + unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or", false); }, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 2869547650f31..8db54a0a28b8b 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; use hir::FnRetTy; use rustc_errors::Applicability; @@ -18,6 +18,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, simplify_using: &str, + use_turbofish: bool, ) -> bool { let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); @@ -46,23 +47,38 @@ pub(super) fn check<'tcx>( } else { "unnecessary closure used with `bool::then`" }; - let applicability = if body + let mut applicability = Applicability::MachineApplicable; + let (ascription, turbofish) = if body .params .iter() // bindings are checked to be unused above .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) - && matches!( - fn_decl.output, + { + match fn_decl.output { FnRetTy::DefaultReturn(_) - | FnRetTy::Return(hir::Ty { - kind: hir::TyKind::Infer(()), - .. - }) - ) { - Applicability::MachineApplicable + | FnRetTy::Return(hir::Ty { + kind: hir::TyKind::Infer(()), + .. + }) => { + // type ascription is definitely not needed here + (String::new(), String::new()) + }, + FnRetTy::Return(ty) => { + // explicit type was given on the closure + // try to use turbofish for this, since it's less dangerous, + // but, failing that, use `as` + let ty = snippet_with_applicability(cx, ty.span, "_", &mut applicability); + if use_turbofish { + (String::new(), format!("::<{ty}>")) + } else { + (format!(" as {ty}"), String::new()) + } + }, + } } else { - // replacing the lambda may break type inference - Applicability::MaybeIncorrect + // can't infer the actual type + applicability = Applicability::MaybeIncorrect; + (String::new(), String::new()) }; // This is a duplicate of what's happening in clippy_lints::methods::method_call, @@ -73,7 +89,10 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( span, format!("use `{simplify_using}` instead"), - format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), + format!( + "{simplify_using}{turbofish}({}{ascription})", + snippet(cx, body_expr.span, "..") + ), applicability, ); }); diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index 409a8efbfeb95..c8d6b1832a710 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -240,6 +240,18 @@ fn main() { let _: Result = res.or_else(|err| Ok(err)); } +fn issue11672() { + // needs turbofish to disambiguate + let _ = true.then_some::<&[u8]>({ &[] }); + //~^ unnecessary_lazy_evaluations +} + +fn issue11672_as() { + // Return type annotation helps type inference and removing it can break code + let _ = None.get_or_insert({ &[] } as &[u8]); + //~^ unnecessary_lazy_evaluations +} + #[allow(unused)] fn issue9485() { // should not lint, is in proc macro diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 54735023a935d..87e903c5bbb8c 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -240,6 +240,18 @@ fn main() { let _: Result = res.or_else(|err| Ok(err)); } +fn issue11672() { + // needs turbofish to disambiguate + let _ = true.then(|| -> &[u8] { &[] }); + //~^ unnecessary_lazy_evaluations +} + +fn issue11672_as() { + // Return type annotation helps type inference and removing it can break code + let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); + //~^ unnecessary_lazy_evaluations +} + #[allow(unused)] fn issue9485() { // should not lint, is in proc macro diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 75a06a419c71b..9b91b22f80d3a 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -484,7 +484,31 @@ LL + or(Ok(ext_str.some_field)); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:259:14 + --> tests/ui/unnecessary_lazy_eval.rs:245:13 + | +LL | let _ = true.then(|| -> &[u8] { &[] }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL - let _ = true.then(|| -> &[u8] { &[] }); +LL + let _ = true.then_some::<&[u8]>({ &[] }); + | + +error: unnecessary closure used to substitute value for `Option::None` + --> tests/ui/unnecessary_lazy_eval.rs:251:13 + | +LL | let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `get_or_insert` instead + | +LL - let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); +LL + let _ = None.get_or_insert({ &[] } as &[u8]); + | + +error: unnecessary closure used with `bool::then` + --> tests/ui/unnecessary_lazy_eval.rs:271:14 | LL | let _x = false.then(|| i32::MAX + 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -496,7 +520,7 @@ LL + let _x = false.then_some(i32::MAX + 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:261:14 + --> tests/ui/unnecessary_lazy_eval.rs:273:14 | LL | let _x = false.then(|| i32::MAX * 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -508,7 +532,7 @@ LL + let _x = false.then_some(i32::MAX * 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:263:14 + --> tests/ui/unnecessary_lazy_eval.rs:275:14 | LL | let _x = false.then(|| i32::MAX - 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -520,7 +544,7 @@ LL + let _x = false.then_some(i32::MAX - 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:265:14 + --> tests/ui/unnecessary_lazy_eval.rs:277:14 | LL | let _x = false.then(|| i32::MIN - 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -532,7 +556,7 @@ LL + let _x = false.then_some(i32::MIN - 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:267:14 + --> tests/ui/unnecessary_lazy_eval.rs:279:14 | LL | let _x = false.then(|| (1 + 2 * 3 - 2 / 3 + 9) << 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -544,7 +568,7 @@ LL + let _x = false.then_some((1 + 2 * 3 - 2 / 3 + 9) << 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:269:14 + --> tests/ui/unnecessary_lazy_eval.rs:281:14 | LL | let _x = false.then(|| 255u8 << 7); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -556,7 +580,7 @@ LL + let _x = false.then_some(255u8 << 7); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:271:14 + --> tests/ui/unnecessary_lazy_eval.rs:283:14 | LL | let _x = false.then(|| 255u8 << 8); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -568,7 +592,7 @@ LL + let _x = false.then_some(255u8 << 8); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:273:14 + --> tests/ui/unnecessary_lazy_eval.rs:285:14 | LL | let _x = false.then(|| 255u8 >> 8); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -580,7 +604,7 @@ LL + let _x = false.then_some(255u8 >> 8); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:276:14 + --> tests/ui/unnecessary_lazy_eval.rs:288:14 | LL | let _x = false.then(|| i32::MAX + -1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -592,7 +616,7 @@ LL + let _x = false.then_some(i32::MAX + -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:278:14 + --> tests/ui/unnecessary_lazy_eval.rs:290:14 | LL | let _x = false.then(|| -i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -604,7 +628,7 @@ LL + let _x = false.then_some(-i32::MAX); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:280:14 + --> tests/ui/unnecessary_lazy_eval.rs:292:14 | LL | let _x = false.then(|| -i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -616,7 +640,7 @@ LL + let _x = false.then_some(-i32::MIN); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:283:14 + --> tests/ui/unnecessary_lazy_eval.rs:295:14 | LL | let _x = false.then(|| 255 >> -7); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -628,7 +652,7 @@ LL + let _x = false.then_some(255 >> -7); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:285:14 + --> tests/ui/unnecessary_lazy_eval.rs:297:14 | LL | let _x = false.then(|| 255 << -1); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -640,7 +664,7 @@ LL + let _x = false.then_some(255 << -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:287:14 + --> tests/ui/unnecessary_lazy_eval.rs:299:14 | LL | let _x = false.then(|| 1 / 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -652,7 +676,7 @@ LL + let _x = false.then_some(1 / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:289:14 + --> tests/ui/unnecessary_lazy_eval.rs:301:14 | LL | let _x = false.then(|| x << -1); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -664,7 +688,7 @@ LL + let _x = false.then_some(x << -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:291:14 + --> tests/ui/unnecessary_lazy_eval.rs:303:14 | LL | let _x = false.then(|| x << 2); | ^^^^^^^^^^^^^^^^^^^^^ @@ -676,7 +700,7 @@ LL + let _x = false.then_some(x << 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:301:14 + --> tests/ui/unnecessary_lazy_eval.rs:313:14 | LL | let _x = false.then(|| x / 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -688,7 +712,7 @@ LL + let _x = false.then_some(x / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:303:14 + --> tests/ui/unnecessary_lazy_eval.rs:315:14 | LL | let _x = false.then(|| x % 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -700,7 +724,7 @@ LL + let _x = false.then_some(x % 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:306:14 + --> tests/ui/unnecessary_lazy_eval.rs:318:14 | LL | let _x = false.then(|| 1 / -1); | ^^^^^^^^^^^^^^^^^^^^^ @@ -712,7 +736,7 @@ LL + let _x = false.then_some(1 / -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:308:14 + --> tests/ui/unnecessary_lazy_eval.rs:320:14 | LL | let _x = false.then(|| i32::MIN / -1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -724,7 +748,7 @@ LL + let _x = false.then_some(i32::MIN / -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:311:14 + --> tests/ui/unnecessary_lazy_eval.rs:323:14 | LL | let _x = false.then(|| i32::MIN / 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -736,7 +760,7 @@ LL + let _x = false.then_some(i32::MIN / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:313:14 + --> tests/ui/unnecessary_lazy_eval.rs:325:14 | LL | let _x = false.then(|| 4 / 2); | ^^^^^^^^^^^^^^^^^^^^ @@ -748,7 +772,7 @@ LL + let _x = false.then_some(4 / 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:321:14 + --> tests/ui/unnecessary_lazy_eval.rs:333:14 | LL | let _x = false.then(|| f1 + f2); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -759,5 +783,5 @@ LL - let _x = false.then(|| f1 + f2); LL + let _x = false.then_some(f1 + f2); | -error: aborting due to 63 previous errors +error: aborting due to 65 previous errors diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.rs b/tests/ui/unnecessary_lazy_eval_unfixable.rs index 6d28d544dfe0b..adb386bd31623 100644 --- a/tests/ui/unnecessary_lazy_eval_unfixable.rs +++ b/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -26,9 +26,3 @@ fn main() { let arr = [(Some(1),)]; Some(&0).and_then(|&i| arr[i].0); } - -fn issue11672() { - // Return type annotation helps type inference and removing it can break code - let _ = true.then(|| -> &[u8] { &[] }); - //~^ unnecessary_lazy_evaluations -} diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/tests/ui/unnecessary_lazy_eval_unfixable.stderr index 5174acc1e9f95..35e68957ca394 100644 --- a/tests/ui/unnecessary_lazy_eval_unfixable.stderr +++ b/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -36,17 +36,5 @@ LL - let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); LL + let _ = Ok(1).unwrap_or(2); | -error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:32:13 - | -LL | let _ = true.then(|| -> &[u8] { &[] }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `then_some` instead - | -LL - let _ = true.then(|| -> &[u8] { &[] }); -LL + let _ = true.then_some({ &[] }); - | - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors From 934018e8c72e817b98b4e9d41ae6b45c98749b20 Mon Sep 17 00:00:00 2001 From: xmakro Date: Fri, 12 Jun 2026 15:09:44 -0700 Subject: [PATCH 042/278] perf: skip doc_markdown text collection and word scan when the lint is allowed --- clippy_lints/src/doc/mod.rs | 43 +++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 81da571bdd601..c5816cb8b2d63 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1124,6 +1124,9 @@ fn check_doc<'a, Events: Iterator, Range, Range, Range Date: Fri, 12 Jun 2026 15:48:48 -0700 Subject: [PATCH 043/278] perf: check is_in_test last in incompatible_msrv --- clippy_lints/src/incompatible_msrv.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 1ae8198a432dd..99e7d202fe46f 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -193,10 +193,11 @@ impl IncompatibleMsrv { } } - if (self.check_in_tests || !is_in_test(cx.tcx, node)) - && let Some(current) = self.msrv.current(cx) + // Check `is_in_test` last as it walks the HIR parent chain. + if let Some(current) = self.msrv.current(cx) && let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id, needs_const) && version > current + && (self.check_in_tests || !is_in_test(cx.tcx, node)) { span_lint_and_then( cx, From 81c4ee5d01a08abed69b1d2002b53e6a3f72e750 Mon Sep 17 00:00:00 2001 From: xmakro Date: Fri, 12 Jun 2026 20:29:50 -0700 Subject: [PATCH 044/278] perf: check the token kind before extracting source in early literal lints --- clippy_lints/src/literal_representation.rs | 6 +++++- clippy_lints/src/misc_early/mod.rs | 14 +++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 2ec046c8d6ba4..db99f601c8932 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -207,7 +207,9 @@ pub struct LiteralDigitGrouping { impl EarlyLintPass for LiteralDigitGrouping { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + // `NumericLiteral::from_lit_kind` only accepts integer and float literals. if let ExprKind::Lit(lit) = expr.kind + && matches!(lit.kind, token::LitKind::Integer | token::LitKind::Float) && !expr.span.in_external_macro(cx.sess().source_map()) { self.check_lit(cx, lit, expr.span); @@ -418,7 +420,9 @@ pub struct DecimalLiteralRepresentation { impl EarlyLintPass for DecimalLiteralRepresentation { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + // Only integer tokens can produce `LitKind::Int`. if let ExprKind::Lit(lit) = expr.kind + && lit.kind == token::LitKind::Integer && !expr.span.in_external_macro(cx.sess().source_map()) { self.check_lit(cx, lit, expr.span); @@ -436,10 +440,10 @@ impl DecimalLiteralRepresentation { // Lint integral literals. if let Ok(lit_kind) = LitKind::from_token_lit(lit) && let LitKind::Int(val, _) = lit_kind + && val >= u128::from(self.threshold) && let Some(src) = span.get_source_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) && num_lit.radix == Radix::Decimal - && val >= u128::from(self.threshold) { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 314004621ce05..79e56b7de41f8 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -333,11 +333,15 @@ impl EarlyLintPass for MiscEarlyLints { } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if expr.span.in_external_macro(cx.sess().source_map()) { - return; - } - - if let ExprKind::Lit(lit) = expr.kind { + // `check_lit` only lints integer literals and suffixed float literals. + if let ExprKind::Lit(lit) = expr.kind + && match lit.kind { + token::LitKind::Integer => true, + token::LitKind::Float => lit.suffix.is_some(), + _ => false, + } + && !expr.span.in_external_macro(cx.sess().source_map()) + { MiscEarlyLints::check_lit(cx, lit, expr.span); } } From aa783f197ece05f2d1b13928119ef44d747be0c1 Mon Sep 17 00:00:00 2001 From: xmakro Date: Fri, 12 Jun 2026 15:56:16 -0700 Subject: [PATCH 045/278] perf: match expression shape before MSRV check in cloned_ref_to_slice_refs --- clippy_lints/src/cloned_ref_to_slice_refs.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/cloned_ref_to_slice_refs.rs b/clippy_lints/src/cloned_ref_to_slice_refs.rs index 4f663f4aa9098..980584e9a1eec 100644 --- a/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -64,15 +64,8 @@ impl<'a> ClonedRefToSliceRefs<'a> { impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if self.msrv.meets(cx, { - if is_in_const_context(cx) { - msrvs::CONST_SLICE_FROM_REF - } else { - msrvs::SLICE_FROM_REF - } - }) - // `&[foo.clone()]` expressions - && let ExprKind::AddrOf(_, mutability, arr) = &expr.kind + // `&[foo.clone()]` expressions + if let ExprKind::AddrOf(_, mutability, arr) = &expr.kind // mutable references would have a different meaning && mutability.is_not() @@ -81,6 +74,14 @@ impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { // check for clones && let ExprKind::MethodCall(path, recv, _, _) = item.kind + + && self.msrv.meets(cx, { + if is_in_const_context(cx) { + msrvs::CONST_SLICE_FROM_REF + } else { + msrvs::SLICE_FROM_REF + } + }) && let Some(adjustment) = is_needless_clone_or_equivalent(cx, recv, path.ident.name, item.hir_id) // check for immutability or purity From 75b49dfb9cc46fdd4521556bb052c613e63642fe Mon Sep 17 00:00:00 2001 From: xmakro Date: Fri, 12 Jun 2026 15:19:46 -0700 Subject: [PATCH 046/278] perf: compare method names before type queries in three lint passes --- clippy_lints/src/minmax.rs | 11 ++++++----- clippy_lints/src/string_patterns.rs | 4 ++-- clippy_lints/src/useless_conversion.rs | 8 ++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index ba62853c74571..74da699e3c006 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -79,14 +79,15 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { + let m = match path.ident.name { + sym::max => MinMax::Max, + sym::min => MinMax::Min, + _ => return None, + }; if cx.typeck_results().expr_ty(receiver).is_floating_point() || cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Ord) { - match path.ident.name { - sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), - sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), - _ => None, - } + fetch_const(cx, expr.span.ctxt(), Some(receiver), args, m) } else { None } diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index b4eb8977bf0fb..7ddd6b1b89499 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -230,13 +230,13 @@ impl<'tcx> LateLintPass<'tcx> for StringPatterns { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && let ExprKind::MethodCall(method, receiver, args, _) = expr.kind - && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() - && ty.is_str() && let method_name = method.ident.name && let Some(&(_, pos)) = PATTERN_METHODS .iter() .find(|(array_method_name, _)| *array_method_name == method_name) && let Some(arg) = args.get(pos) + && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() + && ty.is_str() { check_single_char_pattern_lint(cx, arg); diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index d6db088ba76c0..766659f426aea 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -132,8 +132,8 @@ fn into_iter_bound<'tcx>( /// Extracts the receiver of a `.into_iter()` method call. fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> { if let ExprKind::MethodCall(name, recv, [], _) = expr.kind - && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && name.ident.name == sym::into_iter + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) { Some(recv) } else { @@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(name, recv, [], _) => { - if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) && name.ident.name == sym::into { + if name.ident.name == sym::into && cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_modulo_regions(a, b) { @@ -393,8 +393,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) - && name.ident.name == sym::try_into + if name.ident.name == sym::try_into + && cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) && let a = cx.typeck_results().expr_ty(e) && let b = cx.typeck_results().expr_ty(recv) && a.is_diag_item(cx, sym::Result) From 49bb992983166ac8940dcca691d81fd3ec449ca2 Mon Sep 17 00:00:00 2001 From: xmakro Date: Fri, 12 Jun 2026 15:07:34 -0700 Subject: [PATCH 047/278] perf: check the method name first in or_fun_call --- clippy_lints/src/methods/or_fun_call.rs | 29 ++++++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index a07cd5a8925ae..0d2f57ce334e7 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -30,6 +30,23 @@ pub(super) fn check<'tcx>( args: &'tcx [hir::Expr<'_>], msrv: Msrv, ) { + // Bail out early unless the method is one that `check_unwrap_or_default` or + // `check_or_fn_call` can lint, to avoid walking the argument of every method call. + if !matches!( + name, + sym::unwrap_or + | sym::unwrap_or_else + | sym::or_insert + | sym::or_insert_with + | sym::get_or_insert + | sym::map_or + | sym::ok_or + | sym::or + | sym::and + ) { + return; + } + if let [arg] = args { let inner_arg = peel_blocks(arg); for_each_expr(cx, inner_arg, |ex| { @@ -112,6 +129,12 @@ fn check_unwrap_or_default( method_span: Span, msrv: Msrv, ) -> bool { + let sugg = match (name, call_expr.is_some()) { + (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, + (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, + _ => return false, + }; + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); // Check MSRV, but only for `Result::unwrap_or_default` @@ -150,12 +173,6 @@ fn check_unwrap_or_default( } }; - let sugg = match (name, call_expr.is_some()) { - (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, - (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, - _ => return false, - }; - let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { cx.tcx .inherent_impls(adt_def.did()) From a972a8856bfb3618efe63beadada579d933af279 Mon Sep 17 00:00:00 2001 From: xmakro Date: Fri, 12 Jun 2026 15:44:22 -0700 Subject: [PATCH 048/278] perf: skip single_component_path_imports module walk when nothing to lint --- .../src/single_component_path_imports.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index aba51114c533a..67c8710b80ec8 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -146,16 +146,24 @@ impl SingleComponentPathImports { // ``` let mut macros = Vec::new(); - let mut import_usage_visitor = ImportUsageVisitor::default(); for item in items { self.track_uses(item, &mut imports_reused_with_self, &mut single_use_usages, &mut macros); + } + + // Only walk the module's AST in search of `self::xxx` paths when there are single + // component imports left to lint, as the visitor recurses into every nested item. + single_use_usages.retain(|usage| !imports_reused_with_self.contains(&usage.name)); + if single_use_usages.is_empty() { + return; + } + + let mut import_usage_visitor = ImportUsageVisitor::default(); + for item in items { import_usage_visitor.visit_item(item); } for usage in single_use_usages { - if !imports_reused_with_self.contains(&usage.name) - && !import_usage_visitor.imports_referenced_with_self.contains(&usage.name) - { + if !import_usage_visitor.imports_referenced_with_self.contains(&usage.name) { self.found.entry(usage.item_id).or_default().push(usage); } } From 2027f8ad957061d69858ecf453579f9edd2276b8 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 12 Jun 2026 21:56:43 -0700 Subject: [PATCH 049/278] Address nits - make the comments more specific and useful - add return-type ascription even if there are params that still prevent this from being a machine applicable lint --- .../src/methods/unnecessary_lazy_eval.rs | 57 ++++++++++--------- tests/ui/unnecessary_lazy_eval_unfixable.rs | 2 + .../ui/unnecessary_lazy_eval_unfixable.stderr | 18 +++++- 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 8db54a0a28b8b..0b3c8146b9f76 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -47,38 +47,43 @@ pub(super) fn check<'tcx>( } else { "unnecessary closure used with `bool::then`" }; + let mut applicability = Applicability::MachineApplicable; - let (ascription, turbofish) = if body + if body .params .iter() // bindings are checked to be unused above - .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) + .any(|param| !matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) { - match fn_decl.output { - FnRetTy::DefaultReturn(_) - | FnRetTy::Return(hir::Ty { - kind: hir::TyKind::Infer(()), - .. - }) => { - // type ascription is definitely not needed here - (String::new(), String::new()) - }, - FnRetTy::Return(ty) => { - // explicit type was given on the closure - // try to use turbofish for this, since it's less dangerous, - // but, failing that, use `as` - let ty = snippet_with_applicability(cx, ty.span, "_", &mut applicability); - if use_turbofish { - (String::new(), format!("::<{ty}>")) - } else { - (format!(" as {ty}"), String::new()) - } - }, - } - } else { - // can't infer the actual type + // If the closure parameters have a pattern, + // it might be required for type inferrence. applicability = Applicability::MaybeIncorrect; - (String::new(), String::new()) + } + let (ascription, turbofish) = match fn_decl.output { + FnRetTy::DefaultReturn(_) + | FnRetTy::Return(hir::Ty { + kind: hir::TyKind::Infer(()), + .. + }) => { + // if the closure has no explicit return type, + // then there's nothing to preserve + (String::new(), String::new()) + }, + FnRetTy::Return(ty) => { + // explicit return type was given on the closure + // + // we can preserve this information using `as`, but `as` is + // a somewhat dangerous feature, because it can be used to + // truncate integers + // + // if possible, use turbofish to preserve the type information + let ty = snippet_with_applicability(cx, ty.span, "_", &mut applicability); + if use_turbofish { + (String::new(), format!("::<{ty}>")) + } else { + (format!(" as {ty}"), String::new()) + } + }, }; // This is a duplicate of what's happening in clippy_lints::methods::method_call, diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.rs b/tests/ui/unnecessary_lazy_eval_unfixable.rs index adb386bd31623..7ceda40597942 100644 --- a/tests/ui/unnecessary_lazy_eval_unfixable.rs +++ b/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -12,6 +12,8 @@ fn main() { // fix will break type inference let _ = Ok(1).unwrap_or_else(|()| 2); //~^ unnecessary_lazy_evaluations + let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); + //~^ unnecessary_lazy_evaluations mod e { pub struct E; diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/tests/ui/unnecessary_lazy_eval_unfixable.stderr index 35e68957ca394..97e9ef621cee0 100644 --- a/tests/ui/unnecessary_lazy_eval_unfixable.stderr +++ b/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -13,7 +13,19 @@ LL + let _ = Ok(1).unwrap_or(2); | error: unnecessary closure used to substitute value for `Result::Err` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:19:13 + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:15:13 + | +LL | let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL - let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); +LL + let _ = Ok(1).unwrap_or({ 2 } as i32); + | + +error: unnecessary closure used to substitute value for `Result::Err` + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:21:13 | LL | let _ = Ok(1).unwrap_or_else(|e::E| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +37,7 @@ LL + let _ = Ok(1).unwrap_or(2); | error: unnecessary closure used to substitute value for `Result::Err` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:22:13 + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:24:13 | LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,5 +48,5 @@ LL - let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); LL + let _ = Ok(1).unwrap_or(2); | -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From c749c60a5428ca214cc46bbb30a9d19b95739ccf Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 13 Jun 2026 10:28:57 +0200 Subject: [PATCH 050/278] Be less assertive about the gains obtained through `mul_add` --- .../src/floating_point_arithmetic/mul_add.rs | 3 +- tests/ui/floating_point_mul_add.stderr | 41 ++++++++++--------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic/mul_add.rs b/clippy_lints/src/floating_point_arithmetic/mul_add.rs index 888d5b7b762a0..eb96e65638445 100644 --- a/clippy_lints/src/floating_point_arithmetic/mul_add.rs +++ b/clippy_lints/src/floating_point_arithmetic/mul_add.rs @@ -91,7 +91,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, SUBOPTIMAL_FLOPS, expr.span, - "multiply and add expressions can be calculated more efficiently and accurately", + "multiply and add expressions may be calculated more efficiently and accurately", |diag| { let maybe_neg_sugg = |expr, app: &mut _| { let sugg = Sugg::hir_with_applicability(cx, expr, "_", app); @@ -120,6 +120,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { }, app, ); + diag.note_once("the performance gain from `mul_add` may vary depending on the target architecture"); }, ); } diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr index 3f2ca76826f45..b14d0585f2c96 100644 --- a/tests/ui/floating_point_mul_add.stderr +++ b/tests/ui/floating_point_mul_add.stderr @@ -1,121 +1,122 @@ -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:19:13 | LL | let _ = a * b + c; | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` | + = note: the performance gain from `mul_add` may vary depending on the target architecture = note: `-D clippy::suboptimal-flops` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::suboptimal_flops)]` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:21:13 | LL | let _ = a * b - c; | ^^^^^^^^^ help: consider using: `a.mul_add(b, -c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:23:13 | LL | let _ = c + a * b; | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:25:13 | LL | let _ = c - a * b; | ^^^^^^^^^ help: consider using: `a.mul_add(-b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:27:13 | LL | let _ = a + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:29:13 | LL | let _ = a + 2. * 4.; | ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:32:13 | LL | let _ = (a * b) + c; | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:34:13 | LL | let _ = c + (a * b); | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:36:13 | LL | let _ = a * b * c + d; | ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:39:13 | LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:41:13 | LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:44:13 | LL | let _ = (a * a + b).sqrt(); | ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:48:13 | LL | let _ = a - (b * u as f64); | ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:102:13 | LL | let _ = 0.5 + 2.0 * x; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:104:13 | LL | let _ = 2.0 * x + 0.5; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:107:13 | LL | let _ = x + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, x)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:111:13 | LL | let _ = y * 2.0 + 0.5; | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(2.0, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:113:13 | LL | let _ = 1.0 * 2.0 + 0.5; | ^^^^^^^^^^^^^^^ help: consider using: `1.0f64.mul_add(2.0, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:122:5 | LL | a += b * c; | ^^^^^^^^^^ help: consider using: `a = b.mul_add(c, a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:125:5 | LL | a -= b * c; From 97057829063d6be372b29dfee719057f83cd9f45 Mon Sep 17 00:00:00 2001 From: cyphercodes Date: Sat, 13 Jun 2026 22:46:17 +0300 Subject: [PATCH 051/278] fix: defer adjusted unwrap_or type lookup --- clippy_lints/src/methods/map_unwrap_or.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index a12687c63736c..ec6dccd4fe543 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -35,7 +35,6 @@ pub(super) fn check<'tcx>( }; let unwrap_arg_ty = cx.typeck_results().expr_ty(unwrap_arg); - let unwrap_arg_ty_adjusted = cx.typeck_results().expr_ty_adjusted(unwrap_arg); if !is_copy(cx, unwrap_arg_ty) { // Replacing `.map().unwrap_or()` with `.map_or(, )` can sometimes lead to // borrowck errors, see #10579 for one such instance. @@ -129,7 +128,7 @@ pub(super) fn check<'tcx>( (SuggestedKind::AndThen, _) => "and_then", (SuggestedKind::IsVariantAnd, sym::Result) => "is_ok_and", (SuggestedKind::IsVariantAnd, sym::Option) => "is_some_and", - (SuggestedKind::Other, _) if unwrap_arg_ty != unwrap_arg_ty_adjusted => { + (SuggestedKind::Other, _) if unwrap_arg_ty != cx.typeck_results().expr_ty_adjusted(unwrap_arg) => { // If the `unwrap_or` argument needs an adjustment, moving it into `map_or`'s // first argument can make type inference pick the unadjusted type and reject // the closure return type. Keep the lint, but don't emit a rustfix. From 3d5b7a6cb5f142b952d1299b545b2072dd27302b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 1 May 2025 22:59:40 -0400 Subject: [PATCH 052/278] Change `HasSession` to `HasSourceMap` --- clippy_lints/src/collapsible_if.rs | 6 +- clippy_lints/src/double_parens.rs | 4 +- clippy_lints/src/if_not_else.rs | 23 ++- clippy_lints/src/inline_trait_bounds.rs | 4 +- clippy_lints/src/large_stack_frames.rs | 6 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 3 +- clippy_lints/src/redundant_pub_crate.rs | 3 +- .../missing_transmute_annotations.rs | 6 +- clippy_lints/src/unused_async.rs | 4 +- clippy_utils/src/consts.rs | 2 +- clippy_utils/src/lib.rs | 18 +- clippy_utils/src/source.rs | 179 ++++++++++-------- 12 files changed, 135 insertions(+), 123 deletions(-) diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index baa13b2133277..c7aa5f02ef1aa 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::source::{HasSession, IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; +use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; use clippy_utils::{can_use_if_let_chains, span_contains_cfg, span_contains_non_whitespace, sym, tokenize_with_text}; use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; @@ -319,7 +319,7 @@ pub(super) fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { } } -fn span_extract_keyword(cx: &impl HasSession, span: Span, keyword: &str) -> Option { +fn span_extract_keyword(cx: &LateContext<'_>, span: Span, keyword: &str) -> Option { span.with_source_text(cx, |snippet| { tokenize_with_text(snippet) .filter(|(t, s, _)| matches!(t, TokenKind::Ident if *s == keyword)) @@ -335,7 +335,7 @@ fn span_extract_keyword(cx: &impl HasSession, span: Span, keyword: &str) -> Opti } /// Peel the parentheses from an `if` expression, e.g. `((if true {} else {}))`. -pub(super) fn peel_parens(cx: &impl HasSession, mut span: Span) -> (Span, Span, Span) { +pub(super) fn peel_parens(cx: &LateContext<'_>, mut span: Span) -> (Span, Span, Span) { use crate::rustc_span::Pos; let start = span.shrink_to_lo(); diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index acc3e4936e440..cc897f41ca230 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{HasSession, SpanRangeExt, snippet_with_applicability, snippet_with_context}; +use clippy_utils::source::{SpanRangeExt, snippet_with_applicability, snippet_with_context}; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; declare_clippy_lint! { diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index ff22ba4fcd0d6..ea1992d471312 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::is_zero_integer_const; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_else_clause; -use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet_with_context}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_context}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -104,28 +104,27 @@ impl LateLintPass<'_> for IfNotElse { } } -fn make_sugg<'a>( - sess: &impl HasSession, +fn make_sugg( + cx: &LateContext<'_>, expr_span: Span, - cond_kind: &'a ExprKind<'a>, + cond_kind: &ExprKind<'_>, cond_inner: Span, els_span: Span, - default: &'a str, + default: &str, applicability: &mut Applicability, ) -> String { - let (cond_inner_snip, _) = snippet_with_context(sess, cond_inner, expr_span.ctxt(), default, applicability); - let (els_snip, _) = snippet_with_context(sess, els_span, expr_span.ctxt(), default, applicability); - let indent = indent_of(sess, expr_span); - + let (cond_inner_snip, _) = snippet_with_context(cx, cond_inner, expr_span.ctxt(), default, applicability); + let (els_snip, _) = snippet_with_context(cx, els_span, expr_span.ctxt(), default, applicability); + let indent = indent_of(cx, expr_span); let suggestion = match cond_kind { ExprKind::Unary(UnOp::Not, cond_rest) => { let (cond_rest_snip, _) = - snippet_with_context(sess, cond_rest.span, expr_span.ctxt(), default, applicability); + snippet_with_context(cx, cond_rest.span, expr_span.ctxt(), default, applicability); format!("if {cond_rest_snip} {els_snip} else {cond_inner_snip}") }, ExprKind::Binary(_, lhs, rhs) => { - let (lhs_snip, _) = snippet_with_context(sess, lhs.span, expr_span.ctxt(), default, applicability); - let (rhs_snip, _) = snippet_with_context(sess, rhs.span, expr_span.ctxt(), default, applicability); + let (lhs_snip, _) = snippet_with_context(cx, lhs.span, expr_span.ctxt(), default, applicability); + let (rhs_snip, _) = snippet_with_context(cx, rhs.span, expr_span.ctxt(), default, applicability); format!("if {lhs_snip} == {rhs_snip} {els_snip} else {cond_inner_snip}") }, diff --git a/clippy_lints/src/inline_trait_bounds.rs b/clippy_lints/src/inline_trait_bounds.rs index 3ffa23dcd6046..87e63e8732fe8 100644 --- a/clippy_lints/src/inline_trait_bounds.rs +++ b/clippy_lints/src/inline_trait_bounds.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, snippet}; +use clippy_utils::source::snippet; use clippy_utils::sym; use rustc_ast::ast::{Fn, FnRetTy, GenericParam, GenericParamKind}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{HasAttrs as _, NodeId}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::Span; diff --git a/clippy_lints/src/large_stack_frames.rs b/clippy_lints/src/large_stack_frames.rs index 7cf594384b5a2..10377584ef690 100644 --- a/clippy_lints/src/large_stack_frames.rs +++ b/clippy_lints/src/large_stack_frames.rs @@ -2,7 +2,7 @@ use std::{fmt, ops}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, SpanRangeExt}; +use clippy_utils::source::SpanRangeExt; use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_in_test}; use rustc_errors::Diag; use rustc_hir::def_id::LocalDefId; @@ -228,11 +228,11 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { if fn_span.from_expansion() { // Don't lint on the main function generated by `--test` target - if cx.sess().is_test_crate() && is_entrypoint_fn(cx, local_def_id.to_def_id()) { + if cx.tcx.sess.is_test_crate() && is_entrypoint_fn(cx, local_def_id.to_def_id()) { return; } - let is_from_external_macro = fn_span.in_external_macro(cx.sess().source_map()); + let is_from_external_macro = fn_span.in_external_macro(cx.tcx.sess.source_map()); span_lint_and_then( cx, LARGE_STACK_FRAMES, diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 91358ef77fa1e..dea88e592f2a5 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,7 +1,6 @@ use super::needless_pass_by_value::requires_exact_signature; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::HasSession as _; use clippy_utils::visitors::for_each_expr; use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self}; use core::ops::ControlFlow; @@ -269,7 +268,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // If the argument is never used mutably, we emit the warning. let sp = input.span; if let rustc_hir::TyKind::Ref(_, inner_ty) = input.kind { - let Some(after_mut_span) = cx.sess().source_map().span_extend_to_prev_str( + let Some(after_mut_span) = cx.tcx.sess.source_map().span_extend_to_prev_str( inner_ty.ty.span.shrink_to_lo(), "mut", true, diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 7ebf7ccb7571e..c7f7b47403376 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::HasSession; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind, UseKind}; @@ -49,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { && !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false) && !is_ignorable_export(item) - && !item.span.in_external_macro(cx.sess().source_map()) + && !item.span.in_external_macro(cx.tcx.sess.source_map()) { let span = item .kind diff --git a/clippy_lints/src/transmute/missing_transmute_annotations.rs b/clippy_lints/src/transmute/missing_transmute_annotations.rs index 42f3e06b7d6f5..ec1c34fec1780 100644 --- a/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, SpanRangeExt as _}; +use clippy_utils::source::SpanRangeExt as _; use rustc_errors::Applicability; use rustc_hir::{Expr, GenericArg, HirId, LetStmt, Node, Path, TyKind}; use rustc_lint::LateContext; @@ -114,8 +114,8 @@ fn ty_cannot_be_named(ty: Ty<'_>) -> bool { ) } -fn maybe_name_by_expr<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { - span.with_source_text(sess, |name| { +fn maybe_name_by_expr<'a>(cx: &LateContext<'_>, span: Span, default: &'a str) -> Cow<'a, str> { + span.with_source_text(cx, |name| { (name.len() + 9 < default.len()).then_some(format!("`{name}`'s type").into()) }) .flatten() diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index bfbfd0903bbe8..0a6f3f5992ece 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::is_def_id_trait_method; -use clippy_utils::source::{HasSession, snippet_with_applicability, walk_span_to_context}; +use clippy_utils::source::{snippet_with_applicability, walk_span_to_context}; use clippy_utils::usage::is_todo_unimplemented_stub; use rustc_errors::Applicability; use rustc_hir::def::DefKind; @@ -303,7 +303,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { // evaluate the expression, to immediately evaluate the expression. let mut app = Applicability::MaybeIncorrect; - let async_span = cx.sess().source_map().span_extend_while_whitespace(async_span); + let async_span = cx.tcx.sess.source_map().span_extend_while_whitespace(async_span); let signature_snippet = snippet_with_applicability(cx, signature_span, "_", &mut app); let tail_snippet = snippet_with_applicability(cx, tail_span, "_", &mut app).to_string(); diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 098be98f3672c..3cb9febb5003c 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -934,7 +934,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) && let expr_lo = expr_span.lo() && expr_lo >= span.lo - && let Some(src) = (span.lo..expr_lo).get_source_range(&self.tcx) + && let Some(src) = (span.lo..expr_lo).get_source_range(self.tcx) && let Some(src) = src.as_str() { use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace}; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index c208b8b97506f..203962c05d86e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -124,6 +124,7 @@ use crate::consts::ConstEvalCtxt; use crate::higher::Range; use crate::msrvs::Msrv; use crate::res::{MaybeDef, MaybeQPath, MaybeResPath}; +use crate::source::HasSourceMap; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -2880,8 +2881,8 @@ pub fn tokenize_with_text(s: &str) -> impl Iterator bool { - span.check_source_text(cx, |snippet| { +pub fn span_contains_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool { + span.check_source_text(sm, |snippet| { tokenize(snippet, FrontmatterAllowed::No).any(|token| { matches!( token.kind, @@ -2895,8 +2896,8 @@ pub fn span_contains_comment(cx: &impl source::HasSession, span: Span) -> bool { /// token, including comments unless `skip_comments` is set. /// This is useful to determine if there are any actual code tokens in the span that are omitted in /// the late pass, such as platform-specific code. -pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool { - span.check_source_text(cx, |snippet| { +pub fn span_contains_non_whitespace<'sm>(sm: impl HasSourceMap<'sm>, span: Span, skip_comments: bool) -> bool { + span.check_source_text(sm, |snippet| { tokenize_with_text(snippet).any(|(token, _, _)| match token { TokenKind::Whitespace => false, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments, @@ -2904,18 +2905,19 @@ pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, sk }) }) } + /// Returns all the comments a given span contains /// /// Comments are returned wrapped with their relevant delimiters -pub fn span_extract_comment(cx: &impl source::HasSession, span: Span) -> String { - span_extract_comments(cx, span).join("\n") +pub fn span_extract_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> String { + span_extract_comments(sm, span).join("\n") } /// Returns all the comments a given span contains. /// /// Comments are returned wrapped with their relevant delimiters. -pub fn span_extract_comments(cx: &impl source::HasSession, span: Span) -> Vec { - span.with_source_text(cx, |snippet| { +pub fn span_extract_comments<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Vec { + span.with_source_text(sm, |snippet| { tokenize_with_text(snippet) .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) .map(|(_, s, _)| s.to_string()) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 02d502f8b67f8..923d3a9cdb0d1 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -20,27 +20,38 @@ use std::borrow::Cow; use std::fmt; use std::ops::{Deref, Index, Range}; -pub trait HasSession { - fn sess(&self) -> &Session; +pub trait HasSourceMap<'sm>: Copy { + #[must_use] + fn source_map(self) -> &'sm SourceMap; } -impl HasSession for Session { - fn sess(&self) -> &Session { +impl<'sm> HasSourceMap<'sm> for &'sm SourceMap { + #[inline] + fn source_map(self) -> &'sm SourceMap { self } } -impl HasSession for TyCtxt<'_> { - fn sess(&self) -> &Session { - self.sess +impl<'sm> HasSourceMap<'sm> for &'sm Session { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.source_map() } } -impl HasSession for EarlyContext<'_> { - fn sess(&self) -> &Session { - ::rustc_lint::LintContext::sess(self) +impl<'sm> HasSourceMap<'sm> for TyCtxt<'sm> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.sess.source_map() } } -impl HasSession for LateContext<'_> { - fn sess(&self) -> &Session { - self.tcx.sess() +impl<'sm> HasSourceMap<'sm> for &'sm EarlyContext<'_> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + ::rustc_lint::LintContext::sess(self).source_map() + } +} +impl<'sm> HasSourceMap<'sm> for &LateContext<'sm> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.tcx.sess.source_map() } } @@ -98,36 +109,36 @@ impl IntoSpan for Range { pub trait SpanRangeExt: SpanRange { /// Attempts to get a handle to the source text. Returns `None` if either the span is malformed, /// or the source text is not accessible. - fn get_source_text(self, cx: &impl HasSession) -> Option { - get_source_range(cx.sess().source_map(), self.into_range()).and_then(SourceText::new) + fn get_source_text<'sm>(self, sm: impl HasSourceMap<'sm>) -> Option { + get_source_range(sm.source_map(), self.into_range()).and_then(SourceText::new) } /// Gets the source file, and range in the file, of the given span. Returns `None` if the span /// extends through multiple files, or is malformed. - fn get_source_range(self, cx: &impl HasSession) -> Option { - get_source_range(cx.sess().source_map(), self.into_range()) + fn get_source_range<'sm>(self, sm: impl HasSourceMap<'sm>) -> Option { + get_source_range(sm.source_map(), self.into_range()) } /// Calls the given function with the source text referenced and returns the value. Returns /// `None` if the source text cannot be retrieved. - fn with_source_text(self, cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str) -> T) -> Option { - with_source_text(cx.sess().source_map(), self.into_range(), f) + fn with_source_text<'sm, T>(self, sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option { + with_source_text(sm.source_map(), self.into_range(), f) } /// Checks if the referenced source text satisfies the given predicate. Returns `false` if the /// source text cannot be retrieved. - fn check_source_text(self, cx: &impl HasSession, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { - self.with_source_text(cx, pred).unwrap_or(false) + fn check_source_text<'sm>(self, sm: impl HasSourceMap<'sm>, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { + self.with_source_text(sm, pred).unwrap_or(false) } /// Calls the given function with the both the text of the source file and the referenced range, /// and returns the value. Returns `None` if the source text cannot be retrieved. - fn with_source_text_and_range( + fn with_source_text_and_range<'sm, T>( self, - cx: &impl HasSession, + sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a str, Range) -> T, ) -> Option { - with_source_text_and_range(cx.sess().source_map(), self.into_range(), f) + with_source_text_and_range(sm.source_map(), self.into_range(), f) } /// Calls the given function with the both the text of the source file and the referenced range, @@ -135,12 +146,12 @@ pub trait SpanRangeExt: SpanRange { /// retrieved, or no result is returned. /// /// The new range must reside within the same source file. - fn map_range( + fn map_range<'sm>( self, - cx: &impl HasSession, + sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a SourceFile, &'a str, Range) -> Option>, ) -> Option> { - map_range(cx.sess().source_map(), self.into_range(), f) + map_range(sm.source_map(), self.into_range(), f) } /// Extends the range to include all preceding whitespace characters. @@ -156,13 +167,13 @@ pub trait SpanRangeExt: SpanRange { /// /// When the range points to `foo`, suggesting to remove the range after it's been extended will /// cause the `)` to be placed inside the line comment as `( // Some comment)`. - fn with_leading_whitespace(self, cx: &impl HasSession) -> Range { - with_leading_whitespace(cx.sess().source_map(), self.into_range()) + fn with_leading_whitespace<'sm>(self, sm: impl HasSourceMap<'sm>) -> Range { + with_leading_whitespace(sm.source_map(), self.into_range()) } /// Trims the leading whitespace from the range. - fn trim_start(self, cx: &impl HasSession) -> Range { - trim_start(cx.sess().source_map(), self.into_range()) + fn trim_start<'sm>(self, sm: impl HasSourceMap<'sm>) -> Range { + trim_start(sm.source_map(), self.into_range()) } } impl SpanRangeExt for T {} @@ -353,15 +364,15 @@ impl SourceFileRange { } /// Like [`snippet_block`], but add braces if the expr is not an `ExprKind::Block` with no label. -pub fn expr_block( - sess: &impl HasSession, +pub fn expr_block<'sm>( + sm: impl HasSourceMap<'sm>, expr: &Expr<'_>, outer: SyntaxContext, default: &str, indent_relative_to: Option, app: &mut Applicability, ) -> String { - let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app); + let (code, from_macro) = snippet_block_with_context(sm, expr.span, outer, default, indent_relative_to, app); if !from_macro && let ExprKind::Block(block, None) = expr.kind && block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) @@ -386,13 +397,13 @@ pub fn expr_block( /// let x = (); /// // ^^^^^^^^^^ /// ``` -pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span { - first_char_in_first_line(sess, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) +pub fn first_line_of_span<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { + first_char_in_first_line(sm, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) } -fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option { - let line_span = line_span(sess, span); - snippet_opt(sess, line_span).and_then(|snip| { +fn first_char_in_first_line<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + let line_span = line_span(sm, span); + snippet_opt(sm, line_span).and_then(|snip| { snip.find(|c: char| !c.is_whitespace()) .map(|pos| line_span.lo() + BytePos::from_usize(pos)) }) @@ -407,9 +418,9 @@ fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option Span { +fn line_span<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { let span = original_sp(span, DUMMY_SP); - let SourceFileAndLine { sf, line } = sess.sess().source_map().lookup_line(span.lo()).unwrap(); + let SourceFileAndLine { sf, line } = sm.source_map().lookup_line(span.lo()).unwrap(); let line_start = sf.lines()[line]; let line_start = sf.absolute_position(line_start); span.with_lo(line_start) @@ -423,13 +434,13 @@ fn line_span(sess: &impl HasSession, span: Span) -> Span { /// let x = (); /// // ^^ -- will return 4 /// ``` -pub fn indent_of(sess: &impl HasSession, span: Span) -> Option { - snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) +pub fn indent_of<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + snippet_opt(sm, line_span(sm, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } /// Gets a snippet of the indentation of the line of a span -pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { - snippet_opt(sess, line_span(sess, span)).map(|mut s| { +pub fn snippet_indent<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + snippet_opt(sm, line_span(sm, span)).map(|mut s| { let len = s.len() - s.trim_start().len(); s.truncate(len); s @@ -441,8 +452,8 @@ pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { // sources that the user has no control over. // For some reason these attributes don't have any expansion info on them, so // we have to check it this way until there is a better way. -pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool { - if let Some(snippet) = snippet_opt(sess, span) +pub fn is_present_in_source<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool { + if let Some(snippet) = snippet_opt(sm, span) && snippet.is_empty() { return false; @@ -534,8 +545,8 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, /// snippet(cx, span1, "..") // -> "value" /// snippet(cx, span2, "..") // -> "Vec::new()" /// ``` -pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { - snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from) +pub fn snippet<'a, 'sm>(sm: impl HasSourceMap<'sm>, span: Span, default: &'a str) -> Cow<'a, str> { + snippet_opt(sm, span).map_or_else(|| Cow::Borrowed(default), From::from) } /// Same as [`snippet`], but it adapts the applicability level by following rules: @@ -547,17 +558,17 @@ pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow< /// /// If the span might realistically contain a macro call (e.g. `vec![]`), consider using /// [`snippet_with_context`] instead. -pub fn snippet_with_applicability<'a>( - sess: &impl HasSession, +pub fn snippet_with_applicability<'a, 'sm>( + sm: impl HasSourceMap<'sm>, span: Span, default: &'a str, applicability: &mut Applicability, ) -> Cow<'a, str> { - snippet_with_applicability_sess(sess.sess(), span, default, applicability) + snippet_with_applicability_sm(sm.source_map(), span, default, applicability) } -fn snippet_with_applicability_sess<'a>( - sess: &Session, +fn snippet_with_applicability_sm<'a>( + sm: &SourceMap, span: Span, default: &'a str, applicability: &mut Applicability, @@ -565,7 +576,7 @@ fn snippet_with_applicability_sess<'a>( if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } - if let Some(t) = snippet_opt(sess, span) { + if let Some(t) = snippet_opt(sm, span) { Cow::Owned(t) } else { if *applicability == Applicability::MachineApplicable { @@ -576,8 +587,8 @@ fn snippet_with_applicability_sess<'a>( } /// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option { - sess.sess().source_map().span_to_snippet(span).ok() +pub fn snippet_opt<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + sm.source_map().span_to_snippet(span).ok() } /// Converts a span (from a block) to a code snippet if available, otherwise use default. @@ -614,37 +625,42 @@ pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option { /// } // aligned with `if` /// ``` /// Note that the first line of the snippet always has 0 indentation. -pub fn snippet_block(sess: &impl HasSession, span: Span, default: &str, indent_relative_to: Option) -> String { - let snip = snippet(sess, span, default); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); +pub fn snippet_block<'sm>( + sm: impl HasSourceMap<'sm>, + span: Span, + default: &str, + indent_relative_to: Option, +) -> String { + let snip = snippet(sm, span, default); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); reindent_multiline(&snip, true, indent) } /// Same as [`snippet_block`], but adapts the applicability level by the rules of /// [`snippet_with_applicability`]. -pub fn snippet_block_with_applicability( - sess: &impl HasSession, +pub fn snippet_block_with_applicability<'sm>( + sm: impl HasSourceMap<'sm>, span: Span, default: &str, indent_relative_to: Option, applicability: &mut Applicability, ) -> String { - let snip = snippet_with_applicability(sess, span, default, applicability); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); + let snip = snippet_with_applicability(sm, span, default, applicability); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); reindent_multiline(&snip, true, indent) } /// Combination of [`snippet_block`] and [`snippet_with_context`]. -pub fn snippet_block_with_context( - sess: &impl HasSession, +pub fn snippet_block_with_context<'sm>( + sm: impl HasSourceMap<'sm>, span: Span, outer: SyntaxContext, default: &str, indent_relative_to: Option, app: &mut Applicability, ) -> (String, bool) { - let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); + let (snip, from_macro) = snippet_with_context(sm, span, outer, default, app); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); (reindent_multiline(&snip, true, indent), from_macro) } @@ -658,18 +674,18 @@ pub fn snippet_block_with_context( /// correctly get a snippet of `vec![]`. /// /// This will also return whether or not the snippet is a macro call. -pub fn snippet_with_context<'a>( - sess: &impl HasSession, +pub fn snippet_with_context<'a, 'sm>( + sm: impl HasSourceMap<'sm>, span: Span, outer: SyntaxContext, default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { - snippet_with_context_sess(sess.sess(), span, outer, default, applicability) + snippet_with_context_sm(sm.source_map(), span, outer, default, applicability) } -fn snippet_with_context_sess<'a>( - sess: &Session, +fn snippet_with_context_sm<'a>( + sm: &SourceMap, span: Span, outer: SyntaxContext, default: &'a str, @@ -677,10 +693,7 @@ fn snippet_with_context_sess<'a>( ) -> (Cow<'a, str>, bool) { // If it is just range desugaring, use the desugaring span since it may include parenthesis. if span.desugaring_kind() == Some(DesugaringKind::RangeExpr) && span.parent_callsite().unwrap().ctxt() == outer { - return ( - snippet_with_applicability_sess(sess, span, default, applicability), - false, - ); + return (snippet_with_applicability_sm(sm, span, default, applicability), false); } let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else( @@ -696,7 +709,7 @@ fn snippet_with_context_sess<'a>( ); ( - snippet_with_applicability_sess(sess, span, default, applicability), + snippet_with_applicability_sm(sm, span, default, applicability), is_macro_call, ) } @@ -759,15 +772,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span { /// writeln!(o, "") -> writeln!(o, "") /// ^^ ^^^^ /// ``` -pub fn expand_past_previous_comma(sess: &impl HasSession, span: Span) -> Span { - let extended = sess.sess().source_map().span_extend_to_prev_char(span, ',', true); +pub fn expand_past_previous_comma<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { + let extended = sm.source_map().span_extend_to_prev_char(span, ',', true); extended.with_lo(extended.lo() - BytePos(1)) } /// Converts `expr` to a `char` literal if it's a `str` literal containing a single /// character (or a single byte with `ascii_only`) -pub fn str_literal_to_char_literal( - sess: &impl HasSession, +pub fn str_literal_to_char_literal<'sm>( + sm: impl HasSourceMap<'sm>, expr: &Expr<'_>, applicability: &mut Applicability, ascii_only: bool, @@ -782,7 +795,7 @@ pub fn str_literal_to_char_literal( } && len == 1 { - let snip = snippet_with_applicability(sess, expr.span, string, applicability); + let snip = snippet_with_applicability(sm, expr.span, string, applicability); let ch = if let StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## From c5f215e7eea9ff2dd72f0fadf528e16d285b7265 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 1 May 2025 23:53:16 -0400 Subject: [PATCH 053/278] Rename `SpanRangeExt` to `SpanExt` --- clippy_lints/src/attrs/non_minimal_cfg.rs | 2 +- clippy_lints/src/attrs/unnecessary_clippy_cfg.rs | 2 +- clippy_lints/src/attrs/useless_attribute.rs | 2 +- clippy_lints/src/booleans.rs | 2 +- clippy_lints/src/borrow_deref_ref.rs | 2 +- clippy_lints/src/casts/as_ptr_cast_mut.rs | 2 +- clippy_lints/src/casts/cast_lossless.rs | 2 +- clippy_lints/src/casts/manual_dangling_ptr.rs | 2 +- clippy_lints/src/casts/unnecessary_cast.rs | 2 +- clippy_lints/src/casts/zero_ptr.rs | 2 +- clippy_lints/src/cognitive_complexity.rs | 2 +- clippy_lints/src/collapsible_if.rs | 2 +- clippy_lints/src/default_constructed_unit_structs.rs | 2 +- clippy_lints/src/double_parens.rs | 2 +- clippy_lints/src/empty_line_after.rs | 2 +- clippy_lints/src/empty_with_brackets.rs | 4 ++-- clippy_lints/src/format.rs | 2 +- clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/four_forward_slashes.rs | 2 +- clippy_lints/src/from_over_into.rs | 2 +- clippy_lints/src/functions/too_many_lines.rs | 2 +- clippy_lints/src/ifs/branches_sharing_code.rs | 2 +- clippy_lints/src/implicit_hasher.rs | 2 +- clippy_lints/src/ineffective_open_options.rs | 2 +- clippy_lints/src/items_after_test_module.rs | 2 +- clippy_lints/src/large_stack_frames.rs | 2 +- clippy_lints/src/legacy_numeric_constants.rs | 2 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/let_with_type_underscore.rs | 2 +- clippy_lints/src/literal_representation.rs | 2 +- clippy_lints/src/loops/unused_enumerate_index.rs | 2 +- clippy_lints/src/manual_async_fn.rs | 2 +- clippy_lints/src/manual_float_methods.rs | 2 +- clippy_lints/src/manual_hash_one.rs | 2 +- clippy_lints/src/manual_range_patterns.rs | 2 +- clippy_lints/src/matches/collapsible_match.rs | 2 +- clippy_lints/src/matches/manual_unwrap_or.rs | 2 +- clippy_lints/src/matches/match_same_arms.rs | 2 +- clippy_lints/src/matches/match_wild_enum.rs | 2 +- clippy_lints/src/matches/single_match.rs | 2 +- .../src/methods/case_sensitive_file_extension_comparisons.rs | 2 +- clippy_lints/src/methods/filter_map_bool_then.rs | 2 +- clippy_lints/src/methods/manual_inspect.rs | 2 +- clippy_lints/src/methods/manual_ok_or.rs | 2 +- clippy_lints/src/methods/manual_try_fold.rs | 2 +- clippy_lints/src/methods/map_all_any_identity.rs | 2 +- clippy_lints/src/methods/needless_character_iteration.rs | 2 +- clippy_lints/src/methods/needless_option_as_deref.rs | 2 +- clippy_lints/src/methods/ptr_offset_by_literal.rs | 2 +- clippy_lints/src/methods/range_zip_with_len.rs | 2 +- clippy_lints/src/methods/string_lit_chars_any.rs | 2 +- clippy_lints/src/methods/unnecessary_first_then_check.rs | 2 +- clippy_lints/src/methods/unnecessary_get_then_check.rs | 2 +- clippy_lints/src/methods/unnecessary_iter_cloned.rs | 2 +- clippy_lints/src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/misc_early/unneeded_field_pattern.rs | 2 +- clippy_lints/src/missing_enforced_import_rename.rs | 2 +- clippy_lints/src/multiple_bound_locations.rs | 2 +- clippy_lints/src/mutex_atomic.rs | 2 +- clippy_lints/src/needless_else.rs | 2 +- clippy_lints/src/needless_ifs.rs | 2 +- clippy_lints/src/needless_late_init.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/no_effect.rs | 2 +- clippy_lints/src/non_octal_unix_permissions.rs | 2 +- clippy_lints/src/nonstandard_macro_braces.rs | 2 +- clippy_lints/src/octal_escapes.rs | 2 +- clippy_lints/src/operators/assign_op_pattern.rs | 2 +- clippy_lints/src/operators/decimal_bitwise_operands.rs | 2 +- clippy_lints/src/operators/misrefactored_assign_op.rs | 2 +- clippy_lints/src/pathbuf_init_then_push.rs | 2 +- clippy_lints/src/ptr/ptr_arg.rs | 2 +- clippy_lints/src/ranges.rs | 2 +- clippy_lints/src/raw_strings.rs | 2 +- clippy_lints/src/redundant_clone.rs | 2 +- clippy_lints/src/regex.rs | 2 +- clippy_lints/src/semicolon_block.rs | 2 +- clippy_lints/src/single_range_in_vec_init.rs | 2 +- clippy_lints/src/trait_bounds.rs | 2 +- clippy_lints/src/transmute/missing_transmute_annotations.rs | 2 +- clippy_lints/src/unit_types/unit_arg.rs | 2 +- clippy_lints/src/unnecessary_mut_passed.rs | 2 +- clippy_lints/src/unused_unit.rs | 2 +- clippy_lints/src/useless_vec.rs | 2 +- clippy_lints/src/utils/format_args_collector.rs | 2 +- clippy_lints/src/visibility.rs | 2 +- clippy_lints/src/write/literal.rs | 2 +- clippy_lints/src/write/with_newline.rs | 2 +- clippy_utils/src/attrs.rs | 2 +- clippy_utils/src/consts.rs | 2 +- clippy_utils/src/hir_utils.rs | 2 +- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/source.rs | 4 ++-- 93 files changed, 95 insertions(+), 95 deletions(-) diff --git a/clippy_lints/src/attrs/non_minimal_cfg.rs b/clippy_lints/src/attrs/non_minimal_cfg.rs index 7eff5eccfa138..a76bdcfdfb3e5 100644 --- a/clippy_lints/src/attrs/non_minimal_cfg.rs +++ b/clippy_lints/src/attrs/non_minimal_cfg.rs @@ -1,6 +1,6 @@ use super::{Attribute, NON_MINIMAL_CFG}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::{MetaItemInner, MetaItemKind}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; diff --git a/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index 6efc931df2429..b4742e4da7503 100644 --- a/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -1,6 +1,6 @@ use super::{Attribute, UNNECESSARY_CLIPPY_CFG}; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::AttrStyle; use rustc_errors::Applicability; diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 2d56086a96024..7c0bfd9edb415 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -1,7 +1,7 @@ use super::USELESS_ATTRIBUTE; use super::utils::{is_lint_level, is_word, namespace_and_lint}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, first_line_of_span}; +use clippy_utils::source::{SpanExt, first_line_of_span}; use clippy_utils::sym; use rustc_ast::{Attribute, Item, ItemKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 2f2382d9a50ed..4a3ff6c63705e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher::has_let_expr; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{eq_expr_value, sym}; diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index 947b99696bb1f..1d5ea5ae88f72 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -1,6 +1,6 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use clippy_utils::{ get_enclosing_closure, get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, is_mutable, diff --git a/clippy_lints/src/casts/as_ptr_cast_mut.rs b/clippy_lints/src/casts/as_ptr_cast_mut.rs index adc43e282fc5c..0222dd96a6904 100644 --- a/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index c924fba6b5c85..bcfb59028201e 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_const_context; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index 55b0945f0962b..fe31de69a1545 100644 --- a/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{expr_or_init, std_or_core, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 333f31ba00eac..c320da98b4d61 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::res::MaybeResPath as _; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, sym}; diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs index f4738e7b0d51d..d33d08230d1a3 100644 --- a/clippy_lints/src/casts/zero_ptr.rs +++ b/clippy_lints/src/casts/zero_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 95b99c1d2435f..c330ed3d5a0a4 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index c7aa5f02ef1aa..6def41b775d1d 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; +use clippy_utils::source::{IntoSpan as _, SpanExt, snippet, snippet_block_with_applicability}; use clippy_utils::{can_use_if_let_chains, span_contains_cfg, span_contains_non_whitespace, sym, tokenize_with_text}; use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index c831f96443c60..dc930e83dfa50 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_ty_alias; -use clippy_utils::source::SpanRangeExt as _; +use clippy_utils::source::SpanExt as _; use hir::ExprKind; use hir::def::Res; use rustc_errors::Applicability; diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index cc897f41ca230..daf65e090f6ac 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, snippet_with_applicability, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_applicability, snippet_with_context}; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; diff --git a/clippy_lints/src/empty_line_after.rs b/clippy_lints/src/empty_line_after.rs index 9df1919867e2e..3978ba65f59b8 100644 --- a/clippy_lints/src/empty_line_after.rs +++ b/clippy_lints/src/empty_line_after.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, snippet_indent}; +use clippy_utils::source::{SpanExt, snippet_indent}; use clippy_utils::tokenize_with_text; use itertools::Itertools; use rustc_ast::token::CommentKind; diff --git a/clippy_lints/src/empty_with_brackets.rs b/clippy_lints/src/empty_with_brackets.rs index ee1be48417222..b01745008510c 100644 --- a/clippy_lints/src/empty_with_brackets.rs +++ b/clippy_lints/src/empty_with_brackets.rs @@ -1,6 +1,6 @@ use clippy_utils::attrs::span_contains_cfg; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::span_contains_non_whitespace; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_errors::Applicability; @@ -192,7 +192,7 @@ impl LateLintPass<'_> for EmptyWithBrackets { // Span of the parentheses in variant definition let span = variant.span.with_lo(variant.ident.span.hi()); let span_inner = span - .with_lo(SpanRangeExt::trim_start(span, cx).start + BytePos(1)) + .with_lo(SpanExt::trim_start(span, cx).start + BytePos(1)) .with_hi(span.hi() - BytePos(1)); if span_contains_non_whitespace(cx, span_inner, false) { continue; diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 2691fb4766397..18fefd4b5a550 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, first_node_in_macro, matching_root_macro_call}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::Sugg; use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 05145456071c0..bfc9c41c9dbd6 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -10,7 +10,7 @@ use clippy_utils::macros::{ }; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_opt}; +use clippy_utils::source::{SpanExt, snippet, snippet_opt}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_from_proc_macro, is_in_test, peel_hir_expr_while, sym, trait_ref_of_method}; use itertools::Itertools; diff --git a/clippy_lints/src/four_forward_slashes.rs b/clippy_lints/src/four_forward_slashes.rs index 57c1dd2eb72b0..f05626268fc21 100644 --- a/clippy_lints/src/four_forward_slashes.rs +++ b/clippy_lints/src/four_forward_slashes.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt as _; +use clippy_utils::source::SpanExt as _; use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::Item; diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 433d591418798..73250c2952bc1 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_path}; use rustc_hir::{ diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index 33eede8e65ac9..da3ad2287c319 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; diff --git a/clippy_lints/src/ifs/branches_sharing_code.rs b/clippy_lints/src/ifs/branches_sharing_code.rs index 06ebd3ac7f5fd..2701cdaa394da 100644 --- a/clippy_lints/src/ifs/branches_sharing_code.rs +++ b/clippy_lints/src/ifs/branches_sharing_code.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; +use clippy_utils::source::{IntoSpan, SpanExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 70176c62772b7..bf2c7a007644c 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -13,7 +13,7 @@ use rustc_session::declare_lint_pass; use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet, snippet_with_context}; +use clippy_utils::source::{IntoSpan, SpanExt, snippet, snippet_with_context}; use clippy_utils::sym; declare_clippy_lint! { diff --git a/clippy_lints/src/ineffective_open_options.rs b/clippy_lints/src/ineffective_open_options.rs index bc57d9e85478a..aae847031bf64 100644 --- a/clippy_lints/src/ineffective_open_options.rs +++ b/clippy_lints/src/ineffective_open_options.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{peel_blocks, peel_hir_expr_while, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/items_after_test_module.rs b/clippy_lints/src/items_after_test_module.rs index dd63de288b87f..3ab4be1442515 100644 --- a/clippy_lints/src/items_after_test_module.rs +++ b/clippy_lints/src/items_after_test_module.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro}; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::{HirId, Item, ItemKind, Mod}; diff --git a/clippy_lints/src/large_stack_frames.rs b/clippy_lints/src/large_stack_frames.rs index 10377584ef690..88216d188f8e5 100644 --- a/clippy_lints/src/large_stack_frames.rs +++ b/clippy_lints/src/large_stack_frames.rs @@ -2,7 +2,7 @@ use std::{fmt, ops}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_in_test}; use rustc_errors::Diag; use rustc_hir::def_id::LocalDefId; diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index 838abc81ce449..b53355150f6ab 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_from_proc_macro, sym}; use hir::def_id::DefId; use rustc_errors::Applicability; diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 53ee157dd6d70..4d1a8bacbfff2 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; use clippy_utils::{parent_item_name, peel_ref_operators, sym}; diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 7f2687664c686..3f4b59382ca15 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use rustc_ast::{Local, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index db99f601c8932..6b908e32ee3e0 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_ast::token; use rustc_errors::Applicability; diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs index 4ecfadbf9d494..b80efdae9294c 100644 --- a/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, walk_span_to_context}; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use clippy_utils::{expr_or_init, pat_is_wild, sym}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Pat, PatKind, TyKind}; diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index bee3b19b597c0..dff324d132a0e 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, position_before_rarrow, snippet_block}; +use clippy_utils::source::{SpanExt, position_before_rarrow, snippet_block}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 4a2784caf4c74..87fc28b39ce7e 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index 96d69003934fc..475ee2191a850 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use clippy_utils::visitors::{is_local_used, local_used_once}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs index f60b2a1a6729f..e1b353b4911ad 100644 --- a/clippy_lints/src/manual_range_patterns.rs +++ b/clippy_lints/src/manual_range_patterns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 67685f803841c..2eb15928f25aa 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::{If, IfLetOrMatch}; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; +use clippy_utils::source::{IntoSpan, SpanExt, snippet}; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::is_local_used; use clippy_utils::{SpanlessEq, get_ref_operators, is_unit_expr, peel_blocks_with_stmt, peel_ref_operators}; diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index e3a112d1f7802..38017335f5a11 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; use rustc_hir::def::Res; diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index caef6a0b36377..35b9e5617ca1b 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, search_same}; use core::cmp::Ordering; use core::{iter, slice}; diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 6b46791eb117a..b175ed2acf9af 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index b96c869b5151a..414932199d2e4 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{ - SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, + SpanExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, }; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; use clippy_utils::{is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, sym}; diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 15f4d91e4bd9e..43508edd42630 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use clippy_utils::sym; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index 8e5b271212e67..2c16a444adea3 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,7 +1,7 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::ty::is_copy; use clippy_utils::{CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, peel_blocks, sym}; use rustc_ast::Mutability; diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index a89a656a6bc7e..bcda19e32e097 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; use clippy_utils::{ExprUseNode, get_expr_use_site, sym}; diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index 48f2c10f97cc6..a50da461719e0 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; diff --git a/clippy_lints/src/methods/manual_try_fold.rs b/clippy_lints/src/methods/manual_try_fold.rs index 5f5944d5d4230..4ec4f41d2b8e3 100644 --- a/clippy_lints/src/methods/manual_try_fold.rs +++ b/clippy_lints/src/methods/manual_try_fold.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; diff --git a/clippy_lints/src/methods/map_all_any_identity.rs b/clippy_lints/src/methods/map_all_any_identity.rs index ad950f75f8130..91118c6fb1c47 100644 --- a/clippy_lints/src/methods/map_all_any_identity.rs +++ b/clippy_lints/src/methods/map_all_any_identity.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_expr_identity_function; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index 948ed8a25746e..ba62382da6b0c 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs index 06e6a3c70b87d..0e510a88015ec 100644 --- a/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use clippy_utils::usage::local_used_after_expr; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/ptr_offset_by_literal.rs b/clippy_lints/src/methods/ptr_offset_by_literal.rs index b5d2add65cf19..129d39c1e9f46 100644 --- a/clippy_lints/src/methods/ptr_offset_by_literal.rs +++ b/clippy_lints/src/methods/ptr_offset_by_literal.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index 5fec0c5f2cf7d..51b3db52bd27f 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; +use clippy_utils::source::{SpanExt as _, snippet_with_applicability}; use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; diff --git a/clippy_lints/src/methods/string_lit_chars_any.rs b/clippy_lints/src/methods/string_lit_chars_any.rs index 48e89c2998efa..27c324f863285 100644 --- a/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/clippy_lints/src/methods/string_lit_chars_any.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/unnecessary_first_then_check.rs b/clippy_lints/src/methods/unnecessary_first_then_check.rs index d322909bef359..9c5768dfb6f53 100644 --- a/clippy_lints/src/methods/unnecessary_first_then_check.rs +++ b/clippy_lints/src/methods/unnecessary_first_then_check.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/methods/unnecessary_get_then_check.rs b/clippy_lints/src/methods/unnecessary_get_then_check.rs index b2413bb77aa73..f0054bd3a897a 100644 --- a/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_errors::Applicability; diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 444d0a1d72422..9232547355f68 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr}; diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index a56dcd894b6aa..d26d8f5b42e0d 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,7 +3,7 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_context}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{fn_def_id, get_parent_expr, is_expr_temporary_value, return_ty, sym}; diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs index 33ab94e00a5c6..b2ee3f0c24f10 100644 --- a/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::ast::{Pat, PatKind}; use rustc_lint::EarlyContext; diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 7dbe39bb099d6..2b99ab81816b6 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::{PathNS, lookup_path_str}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdMap; diff --git a/clippy_lints/src/multiple_bound_locations.rs b/clippy_lints/src/multiple_bound_locations.rs index 8b02c4865d138..a78f53188ba8b 100644 --- a/clippy_lints/src/multiple_bound_locations.rs +++ b/clippy_lints/src/multiple_bound_locations.rs @@ -6,7 +6,7 @@ use rustc_session::declare_lint_pass; use rustc_span::Span; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index ad44d65b4d663..6d9950579fb24 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::ty_from_hir_ty; use rustc_errors::{Applicability, Diag}; diff --git a/clippy_lints/src/needless_else.rs b/clippy_lints/src/needless_else.rs index 807c4cf5da8e1..0a25ab37603e2 100644 --- a/clippy_lints/src/needless_else.rs +++ b/clippy_lints/src/needless_else.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/clippy_lints/src/needless_ifs.rs b/clippy_lints/src/needless_ifs.rs index cdf9bd91339c6..0356fc2696215 100644 --- a/clippy_lints/src/needless_ifs.rs +++ b/clippy_lints/src/needless_ifs.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::If; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{SpanRangeExt, walk_span_to_context}; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index bc4f6ca401710..e4dcaad69026c 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{SourceText, SpanRangeExt, snippet}; +use clippy_utils::source::{SourceText, SpanExt, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; use core::ops::ControlFlow; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 4ff5b0b0b3c39..b531eb00edc68 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::source::{SpanExt, snippet}; use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_self, peel_hir_ty_options, strip_pat_refs, sym}; diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index f2a54b99170e9..af64f71ba3e24 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{expr_type_is_certain, has_drop}; use clippy_utils::{in_automatically_derived, is_inside_always_const_context, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index cb934466bd890..72ebe13562be4 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet_with_applicability}; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index b449681ae2e14..eae2c22ef1eb4 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::MacroMatcher; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SourceText, SpanRangeExt}; +use clippy_utils::source::{SourceText, SpanExt}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index 6e7ee727965db..2ac9e010c69cf 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::token::LitKind; use rustc_ast::{Expr, ExprKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 5695779425f4a..8064a77d43486 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::qualify_min_const_fn::is_stable_const_fn; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{binop_traits, eq_expr_value, is_in_const_context, trait_ref_of_method}; diff --git a/clippy_lints/src/operators/decimal_bitwise_operands.rs b/clippy_lints/src/operators/decimal_bitwise_operands.rs index 8511f2151342d..c5050ca9d4fd5 100644 --- a/clippy_lints/src/operators/decimal_bitwise_operands.rs +++ b/clippy_lints/src/operators/decimal_bitwise_operands.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::numeric_literal; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_hir::{BinOpKind, Expr, ExprKind}; diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index f0b6407a141bd..610f252332f52 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{eq_expr_value, sugg}; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index a5e57d97301e3..d26aa25778165 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::source::{SpanExt, snippet}; use clippy_utils::sym; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/ptr/ptr_arg.rs b/clippy_lints/src/ptr/ptr_arg.rs index 40bc42dcdcb36..4f2105b8dc147 100644 --- a/clippy_lints/src/ptr/ptr_arg.rs +++ b/clippy_lints/src/ptr/ptr_arg.rs @@ -1,7 +1,7 @@ use super::PTR_ARG; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{VEC_METHODS_SHADOWING_SLICE_METHODS, get_expr_use_or_unification_node, is_lint_allowed, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 71965ee1e29f9..48456f809c42c 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{ diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index d8aa88d48b09d..5916828b2e8a9 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, snippet_opt}; +use clippy_utils::source::{SpanExt, snippet_opt}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::LitKind; use rustc_errors::Applicability; diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index bfb704dd21719..f04e3480a9c63 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{has_drop, is_copy, peel_and_count_ty_refs}; use clippy_utils::{fn_has_unsatisfiable_preds, sym}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 80696f0a81b15..53f8a453d275f 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::paths; use clippy_utils::paths::PathLookup; use clippy_utils::res::MaybeQPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index e806123596b85..cde9d16fc6066 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs index fc5702a358bbc..eaef8d111a991 100644 --- a/clippy_lints/src/single_range_in_vec_init.rs +++ b/clippy_lints/src/single_range_in_vec_init.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::{Range, VecArgs}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_no_std_crate, sym}; use rustc_ast::{LitIntType, LitKind, RangeLimits, UintTy}; diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 7b690b7eb136c..b3a63ed814cb5 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::{SpanlessEq, SpanlessHash, is_from_proc_macro}; use core::hash::{Hash, Hasher}; use itertools::Itertools; diff --git a/clippy_lints/src/transmute/missing_transmute_annotations.rs b/clippy_lints/src/transmute/missing_transmute_annotations.rs index ec1c34fec1780..d121c5385b3c7 100644 --- a/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt as _; +use clippy_utils::source::SpanExt as _; use rustc_errors::Applicability; use rustc_hir::{Expr, GenericArg, HirId, LetStmt, Node, Path, TyKind}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index 6df3e7baa1bca..ac86a457b5374 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -1,7 +1,7 @@ use std::iter; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::expr_type_is_certain; use clippy_utils::{is_empty_block, is_expr_default, is_from_proc_macro}; diff --git a/clippy_lints/src/unnecessary_mut_passed.rs b/clippy_lints/src/unnecessary_mut_passed.rs index 75b0b7b10687a..60a6688927ab5 100644 --- a/clippy_lints/src/unnecessary_mut_passed.rs +++ b/clippy_lints/src/unnecessary_mut_passed.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index d503bd3379b08..bb2ad9f92157c 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, position_before_rarrow}; +use clippy_utils::source::{SpanExt, position_before_rarrow}; use clippy_utils::{is_never_expr, is_unit_expr}; use rustc_ast::{Block, StmtKind}; use rustc_errors::Applicability; diff --git a/clippy_lints/src/useless_vec.rs b/clippy_lints/src/useless_vec.rs index 7b6122213d608..0cd7426cfe0f6 100644 --- a/clippy_lints/src/useless_vec.rs +++ b/clippy_lints/src/useless_vec.rs @@ -7,7 +7,7 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{VEC_METHODS_SHADOWING_SLICE_METHODS, get_parent_expr, higher, is_in_test, span_contains_comment}; diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs index 6629a67f78bd4..8f5014d1e3add 100644 --- a/clippy_lints/src/utils/format_args_collector.rs +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -1,5 +1,5 @@ use clippy_utils::macros::FormatArgsStorage; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; use rustc_data_structures::fx::FxHashMap; diff --git a/clippy_lints/src/visibility.rs b/clippy_lints/src/visibility.rs index 74ffeb629d2be..875041da780c1 100644 --- a/clippy_lints/src/visibility.rs +++ b/clippy_lints/src/visibility.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{Item, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; diff --git a/clippy_lints/src/write/literal.rs b/clippy_lints/src/write/literal.rs index 699ac7ea7a5cd..9711410ef8853 100644 --- a/clippy_lints/src/write/literal.rs +++ b/clippy_lints/src/write/literal.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::format_arg_removal_span; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_ast::token::LitKind; use rustc_ast::{ diff --git a/clippy_lints/src/write/with_newline.rs b/clippy_lints/src/write/with_newline.rs index e4b51da3cadcf..53109f4014c81 100644 --- a/clippy_lints/src/write/with_newline.rs +++ b/clippy_lints/src/write/with_newline.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::MacroCall; -use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; +use clippy_utils::source::{SpanExt, expand_past_previous_comma}; use clippy_utils::sym; use rustc_ast::{FormatArgs, FormatArgsPiece}; use rustc_errors::Applicability; diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 8df82489d6a3f..119836fda46c3 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -1,6 +1,6 @@ //! Utility functions for attributes, including Clippy's built-in ones -use crate::source::SpanRangeExt; +use crate::source::SpanExt; use crate::{sym, tokenize_with_text}; use rustc_ast::attr::AttributeExt; use rustc_errors::Applicability; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 3cb9febb5003c..26aef9c1fc910 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -5,7 +5,7 @@ #![expect(clippy::float_cmp)] use crate::res::MaybeDef; -use crate::source::{SpanRangeExt, walk_span_to_context}; +use crate::source::{SpanExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, sym, unsext}; use rustc_abi::Size; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 38724a1053f65..53352ddc7f83a 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,6 +1,6 @@ use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; -use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context}; +use crate::source::{SpanExt, SpanRange, walk_span_to_context}; use crate::{sym, tokenize_with_text}; use core::mem; use rustc_ast::ast; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 203962c05d86e..366ace799612c 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -116,7 +116,7 @@ use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::{InnerSpan, Span, SyntaxContext}; -use source::{SpanRangeExt, walk_span_to_context}; +use source::{SpanExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::ast_utils::unordered_over; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 923d3a9cdb0d1..036f1346ce625 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -106,7 +106,7 @@ impl IntoSpan for Range { } } -pub trait SpanRangeExt: SpanRange { +pub trait SpanExt: SpanRange { /// Attempts to get a handle to the source text. Returns `None` if either the span is malformed, /// or the source text is not accessible. fn get_source_text<'sm>(self, sm: impl HasSourceMap<'sm>) -> Option { @@ -176,7 +176,7 @@ pub trait SpanRangeExt: SpanRange { trim_start(sm.source_map(), self.into_range()) } } -impl SpanRangeExt for T {} +impl SpanExt for T {} /// Handle to a range of text in a source file. pub struct SourceText(SourceFileRange); From 9c88e494d50f6a5f878e691327aa0c34922fb9f5 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 18 Nov 2025 23:49:16 -0500 Subject: [PATCH 054/278] Rename `get_source_text` and `check_source_text` to `get_text` and `check_text`. --- clippy_lints/src/attrs/non_minimal_cfg.rs | 2 +- .../src/attrs/unnecessary_clippy_cfg.rs | 4 +-- clippy_lints/src/attrs/useless_attribute.rs | 2 +- clippy_lints/src/booleans.rs | 29 +++++++------------ clippy_lints/src/borrow_deref_ref.rs | 2 +- clippy_lints/src/casts/as_ptr_cast_mut.rs | 2 +- clippy_lints/src/casts/cast_lossless.rs | 2 +- clippy_lints/src/casts/manual_dangling_ptr.rs | 2 +- clippy_lints/src/casts/unnecessary_cast.rs | 4 +-- clippy_lints/src/casts/zero_ptr.rs | 2 +- .../src/default_constructed_unit_structs.rs | 2 +- clippy_lints/src/empty_line_after.rs | 2 +- clippy_lints/src/format.rs | 2 +- clippy_lints/src/format_args.rs | 4 +-- clippy_lints/src/four_forward_slashes.rs | 2 +- clippy_lints/src/from_over_into.rs | 4 +-- clippy_lints/src/functions/too_many_lines.rs | 2 +- clippy_lints/src/items_after_test_module.rs | 2 +- clippy_lints/src/large_stack_frames.rs | 2 +- clippy_lints/src/legacy_numeric_constants.rs | 2 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/literal_representation.rs | 4 +-- clippy_lints/src/manual_async_fn.rs | 6 ++-- clippy_lints/src/manual_float_methods.rs | 2 +- clippy_lints/src/manual_hash_one.rs | 4 +-- clippy_lints/src/manual_range_patterns.rs | 4 +-- clippy_lints/src/matches/manual_unwrap_or.rs | 2 +- clippy_lints/src/matches/match_same_arms.rs | 2 +- clippy_lints/src/matches/match_wild_enum.rs | 2 +- clippy_lints/src/matches/single_match.rs | 2 +- ...se_sensitive_file_extension_comparisons.rs | 2 +- .../src/methods/filter_map_bool_then.rs | 2 +- clippy_lints/src/methods/manual_ok_or.rs | 4 +-- clippy_lints/src/methods/manual_try_fold.rs | 6 ++-- .../src/methods/map_all_any_identity.rs | 2 +- .../methods/needless_character_iteration.rs | 4 +-- .../src/methods/needless_option_as_deref.rs | 2 +- .../src/methods/ptr_offset_by_literal.rs | 2 +- .../src/methods/range_zip_with_len.rs | 4 +-- .../src/methods/string_lit_chars_any.rs | 2 +- .../methods/unnecessary_first_then_check.rs | 4 +-- .../src/methods/unnecessary_get_then_check.rs | 8 ++--- .../src/methods/unnecessary_iter_cloned.rs | 4 +-- .../src/methods/unnecessary_to_owned.rs | 8 ++--- .../src/misc_early/unneeded_field_pattern.rs | 2 +- .../src/missing_enforced_import_rename.rs | 2 +- clippy_lints/src/needless_else.rs | 2 +- clippy_lints/src/needless_ifs.rs | 4 +-- clippy_lints/src/needless_late_init.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 4 +-- clippy_lints/src/no_effect.rs | 6 ++-- .../src/non_octal_unix_permissions.rs | 4 +-- clippy_lints/src/nonstandard_macro_braces.rs | 2 +- clippy_lints/src/octal_escapes.rs | 2 +- .../src/operators/assign_op_pattern.rs | 4 +-- .../src/operators/decimal_bitwise_operands.rs | 2 +- .../src/operators/misrefactored_assign_op.rs | 4 +-- clippy_lints/src/pathbuf_init_then_push.rs | 6 ++-- clippy_lints/src/ptr/ptr_arg.rs | 6 ++-- clippy_lints/src/ranges.rs | 2 +- clippy_lints/src/raw_strings.rs | 4 +-- clippy_lints/src/redundant_clone.rs | 2 +- clippy_lints/src/regex.rs | 2 +- clippy_lints/src/semicolon_block.rs | 2 +- clippy_lints/src/single_range_in_vec_init.rs | 2 +- clippy_lints/src/trait_bounds.rs | 7 ++--- clippy_lints/src/unit_types/unit_arg.rs | 4 +-- clippy_lints/src/useless_vec.rs | 4 +-- .../src/utils/format_args_collector.rs | 2 +- clippy_lints/src/visibility.rs | 2 +- clippy_lints/src/write/literal.rs | 4 +-- clippy_lints/src/write/with_newline.rs | 2 +- clippy_utils/src/attrs.rs | 2 +- clippy_utils/src/hir_utils.rs | 2 +- clippy_utils/src/lib.rs | 4 +-- clippy_utils/src/source.rs | 4 +-- 76 files changed, 125 insertions(+), 137 deletions(-) diff --git a/clippy_lints/src/attrs/non_minimal_cfg.rs b/clippy_lints/src/attrs/non_minimal_cfg.rs index a76bdcfdfb3e5..91e2689a14731 100644 --- a/clippy_lints/src/attrs/non_minimal_cfg.rs +++ b/clippy_lints/src/attrs/non_minimal_cfg.rs @@ -29,7 +29,7 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[MetaItemInner]) { meta.span, "unneeded sub `cfg` when there is only one condition", |diag| { - if let Some(snippet) = list[0].span().get_source_text(cx) { + if let Some(snippet) = list[0].span().get_text(cx) { diag.span_suggestion( meta.span, "try", diff --git a/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index b4742e4da7503..4c97c03471aa9 100644 --- a/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -32,7 +32,7 @@ pub(super) fn check( return; } if nb_items == clippy_lints.len() { - if let Some(snippet) = behind_cfg_attr.span.get_source_text(cx) { + if let Some(snippet) = behind_cfg_attr.span.get_text(cx) { span_lint_and_sugg( cx, UNNECESSARY_CLIPPY_CFG, @@ -48,7 +48,7 @@ pub(super) fn check( ); } } else { - let snippet = clippy_lints.iter().filter_map(|sp| sp.get_source_text(cx)).join(","); + let snippet = clippy_lints.iter().filter_map(|sp| sp.get_text(cx)).join(","); span_lint_and_note( cx, UNNECESSARY_CLIPPY_CFG, diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 7c0bfd9edb415..60cdcd4a85817 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -74,7 +74,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { } let line_span = first_line_of_span(cx, attr.span); - if let Some(src) = line_span.get_source_text(cx) + if let Some(src) = line_span.get_text(cx) && src.contains("#[") { #[expect(clippy::collapsible_span_lint_calls)] diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 4a3ff6c63705e..efd453b3276cc 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -157,30 +157,30 @@ fn check_inverted_bool_in_condition( let suggestion = match (left.kind, right.kind) { (ExprKind::Unary(UnOp::Not, left_sub), ExprKind::Unary(UnOp::Not, right_sub)) => { - let Some(left) = left_sub.span.get_source_text(cx) else { + let Some(left) = left_sub.span.get_text(cx) else { return; }; - let Some(right) = right_sub.span.get_source_text(cx) else { + let Some(right) = right_sub.span.get_text(cx) else { return; }; let Some(op) = bin_op_eq_str(op) else { return }; format!("{left} {op} {right}") }, (ExprKind::Unary(UnOp::Not, left_sub), _) => { - let Some(left) = left_sub.span.get_source_text(cx) else { + let Some(left) = left_sub.span.get_text(cx) else { return; }; - let Some(right) = right.span.get_source_text(cx) else { + let Some(right) = right.span.get_text(cx) else { return; }; let Some(op) = inverted_bin_op_eq_str(op) else { return }; format!("{left} {op} {right}") }, (_, ExprKind::Unary(UnOp::Not, right_sub)) => { - let Some(left) = left.span.get_source_text(cx) else { + let Some(left) = left.span.get_text(cx) else { return; }; - let Some(right) = right_sub.span.get_source_text(cx) else { + let Some(right) = right_sub.span.get_text(cx) else { return; }; let Some(op) = inverted_bin_op_eq_str(op) else { return }; @@ -392,12 +392,8 @@ impl SuggestContext<'_, '_, '_> { } }, &Term(n) => { - self.output.push_str( - &self.terminals[n as usize] - .span - .source_callsite() - .get_source_text(self.cx)?, - ); + self.output + .push_str(&self.terminals[n as usize].span.source_callsite().get_text(self.cx)?); }, } Some(()) @@ -452,10 +448,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio .map(|arg| simplify_not(cx, curr_msrv, arg)) .collect::>>()? .join(", "); - Some(format!( - "{}.{neg_method}({negated_args})", - receiver.span.get_source_text(cx)? - )) + Some(format!("{}.{neg_method}({negated_args})", receiver.span.get_text(cx)?)) }) }, ExprKind::Closure(closure) => { @@ -463,13 +456,13 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio let params = body .params .iter() - .map(|param| param.span.get_source_text(cx).map(|t| t.to_string())) + .map(|param| param.span.get_text(cx).map(|t| t.to_string())) .collect::>>()? .join(", "); let negated = simplify_not(cx, curr_msrv, body.value)?; Some(format!("|{params}| {negated}")) }, - ExprKind::Unary(UnOp::Not, expr) => expr.span.get_source_text(cx).map(|t| t.to_string()), + ExprKind::Unary(UnOp::Not, expr) => expr.span.get_text(cx).map(|t| t.to_string()), _ => None, } } diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index 1d5ea5ae88f72..13ae80868d9b5 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { // If the new borrow might be itself borrowed mutably and the original reference is not a temporary // value, do not propose to use it directly. && (is_expr_temporary_value(cx, deref_target) || !potentially_bound_to_mutable_ref(cx, e)) - && let Some(deref_text) = deref_target.span.get_source_text(cx) + && let Some(deref_text) = deref_target.span.get_text(cx) { // `&*x` can be needed to shorten the borrow of `x`. Replacing it with `x` can be // incorrect when `x` is a closure-captured upvar (e.g. a closure returning another diff --git a/clippy_lints/src/casts/as_ptr_cast_mut.rs b/clippy_lints/src/casts/as_ptr_cast_mut.rs index 0222dd96a6904..f23e789cf5d64 100644 --- a/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity().skip_norm_wip() && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next() && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() - && let Some(recv) = receiver.span.get_source_text(cx) + && let Some(recv) = receiver.span.get_text(cx) { // `as_mut_ptr` might not exist let applicability = Applicability::MaybeIncorrect; diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index bcfb59028201e..d2bf6db751320 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -40,7 +40,7 @@ pub(super) fn check( diag.help("an `as` cast can become silently lossy if the types change in the future"); let mut applicability = Applicability::MachineApplicable; let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "", &mut applicability); - let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_source_text(cx) else { + let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_text(cx) else { return; }; match cast_to_hir.kind { diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index fe31de69a1545..36d63ce63195b 100644 --- a/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -25,7 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind { format!("{std_or_core}::{sugg_fn}()") - } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) { + } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") } else { return; diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index c320da98b4d61..83a6cab01f9d9 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -113,7 +113,7 @@ pub(super) fn check<'tcx>( let literal_str = &cast_str; if let LitKind::Int(n, _) = lit.node - && let Some(src) = cast_expr.span.get_source_text(cx) + && let Some(src) = cast_expr.span.get_text(cx) && cast_to.is_floating_point() && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) && let from_nbits = 128 - n.get().leading_zeros() @@ -140,7 +140,7 @@ pub(super) fn check<'tcx>( | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => { - if let Some(src) = cast_expr.span.get_source_text(cx) + if let Some(src) = cast_expr.span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs index d33d08230d1a3..90290caf10bf7 100644 --- a/clippy_lints/src/casts/zero_ptr.rs +++ b/clippy_lints/src/casts/zero_ptr.rs @@ -21,7 +21,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_> let sugg = if let TyKind::Infer(()) = mut_ty.ty.kind { format!("{std_or_core}::{sugg_fn}()") - } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) { + } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") } else { return; diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index dc930e83dfa50..9e141bd945db0 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -78,7 +78,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { && !base.is_suggestable_infer_ty() { let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())]; - if expr.span.check_source_text(cx, |s| s.starts_with('<')) { + if expr.span.check_text(cx, |s| s.starts_with('<')) { // Remove `<`, '>` has already been removed by the existing removal expression. removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new())); } diff --git a/clippy_lints/src/empty_line_after.rs b/clippy_lints/src/empty_line_after.rs index 3978ba65f59b8..f076c6d29138d 100644 --- a/clippy_lints/src/empty_line_after.rs +++ b/clippy_lints/src/empty_line_after.rs @@ -288,7 +288,7 @@ impl<'a> Gap<'a> { let prev_stop = prev_chunk.last()?; let next_stop = next_chunk.first()?; let gap_span = prev_stop.span.between(next_stop.span); - let gap_snippet = gap_span.get_source_text(cx)?; + let gap_snippet = gap_span.get_text(cx)?; let mut has_comment = false; let mut empty_lines = Vec::new(); diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 18fefd4b5a550..f13278d91ce7f 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { ([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability), ([], [_]) => { // Simulate macro expansion, converting {{ and }} to { and }. - let Some(snippet) = format_args.span.get_source_text(cx) else { + let Some(snippet) = format_args.span.get_text(cx) else { return; }; let s_expand = snippet.replace("{{", "{").replace("}}", "}"); diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index bfc9c41c9dbd6..9014302fb8ea6 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -380,7 +380,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { /// Check if there is a comma after the last format macro arg. fn check_trailing_comma(&self) { let span = self.macro_call.span; - if let Some(src) = span.get_source_text(self.cx) + if let Some(src) = span.get_text(self.cx) && let Some(src) = src.strip_suffix([')', ']', '}']) && let src = src.trim_end_matches(|c: char| c.is_whitespace() && c != '\n') && let Some(src) = src.strip_suffix(',') @@ -694,7 +694,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()) && implements_trait(cx, target, display_trait_id, &[]) && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait() - && let Some(receiver_snippet) = receiver.span.source_callsite().get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.source_callsite().get_text(cx) { let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); if n_needed_derefs == 0 && !needs_ref { diff --git a/clippy_lints/src/four_forward_slashes.rs b/clippy_lints/src/four_forward_slashes.rs index f05626268fc21..c64e742592080 100644 --- a/clippy_lints/src/four_forward_slashes.rs +++ b/clippy_lints/src/four_forward_slashes.rs @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { // 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) { + if span.check_text(cx, contains_bare_cr) { diag.help(msg) .note("bare CR characters are not allowed in doc comments"); return; diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 73250c2952bc1..fb09cac1fe702 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -197,8 +197,8 @@ fn convert_to_from( return None; }; - let from = self_ty.span.get_source_text(cx)?; - let into = target_ty.span.get_source_text(cx)?; + let from = self_ty.span.get_text(cx)?; + let into = target_ty.span.get_text(cx)?; let mut suggestions = vec![ // impl Into for U -> impl From for U diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index da3ad2287c319..a5fed9f03a620 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -23,7 +23,7 @@ pub(super) fn check_fn( } let mut line_count: u64 = 0; - let too_many = body.value.span.check_source_text(cx, |src| { + let too_many = body.value.span.check_text(cx, |src| { let mut in_comment = false; let mut code_in_line; diff --git a/clippy_lints/src/items_after_test_module.rs b/clippy_lints/src/items_after_test_module.rs index 3ab4be1442515..f83bcb3cf4076 100644 --- a/clippy_lints/src/items_after_test_module.rs +++ b/clippy_lints/src/items_after_test_module.rs @@ -99,7 +99,7 @@ impl LateLintPass<'_> for ItemsAfterTestModule { if let Some(prev) = mod_pos.checked_sub(1) && let prev = cx.tcx.hir_item(module.item_ids[prev]) && let items_span = last.span.with_lo(test_mod.span.hi()) - && let Some(items) = items_span.get_source_text(cx) + && let Some(items) = items_span.get_text(cx) { diag.multipart_suggestion_with_style( "move the items to before the test module was defined", diff --git a/clippy_lints/src/large_stack_frames.rs b/clippy_lints/src/large_stack_frames.rs index 88216d188f8e5..dec3f3bcae895 100644 --- a/clippy_lints/src/large_stack_frames.rs +++ b/clippy_lints/src/large_stack_frames.rs @@ -197,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { // TODO: Is there a cleaner, robust way to ask this question? // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data", // and that doesn't get us the true name in scope rather than the span text either. - if let Some(name) = local_span.get_source_text(cx) + if let Some(name) = local_span.get_text(cx) && is_ident(&name) { // If the local is an ordinary named variable, diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index b53355150f6ab..48e1414defadf 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { && let QPath::TypeRelative(ty, last_segment) = qpath && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && is_integer_method(cx, def_id) - && let Some(mod_name) = ty.span.get_source_text(cx) + && let Some(mod_name) = ty.span.get_text(cx) && ty.span.eq_ctxt(last_segment.ident.span) { let name = last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(); diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 4d1a8bacbfff2..a84db8929713e 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -261,7 +261,7 @@ impl LenZero { } fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span { - let Some(snippet) = span.get_source_text(cx) else { + let Some(snippet) = span.get_text(cx) else { return span; }; if has_enclosing_paren(snippet) { diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 6b908e32ee3e0..4e7ff879ae590 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -228,7 +228,7 @@ impl LiteralDigitGrouping { } fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { - if let Some(src) = span.get_source_text(cx) + if let Some(src) = span.get_text(cx) && let Ok(lit_kind) = LitKind::from_token_lit(lit) && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) { @@ -441,7 +441,7 @@ impl DecimalLiteralRepresentation { if let Ok(lit_kind) = LitKind::from_token_lit(lit) && let LitKind::Int(val, _) = lit_kind && val >= u128::from(self.threshold) - && let Some(src) = span.get_source_text(cx) + && let Some(src) = span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) && num_lit.radix == Radix::Decimal { diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index dff324d132a0e..161834914b309 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -76,8 +76,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { "this function can be simplified using the `async fn` syntax", |diag| { if let Some(vis_span) = vis_span_opt - && let Some(vis_snip) = vis_span.get_source_text(cx) - && let Some(header_snip) = header_span.get_source_text(cx) + && let Some(vis_snip) = vis_span.get_text(cx) + && let Some(header_snip) = header_span.get_text(cx) && let Some(ret_pos) = position_before_rarrow(&header_snip) && let Some((_, ret_snip)) = suggested_ret(cx, output) { @@ -185,6 +185,6 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, Some((sugg, String::new())) } else { let sugg = "return the output of the future directly"; - output.span.get_source_text(cx).map(|src| (sugg, format!(" -> {src}"))) + output.span.get_text(cx).map(|src| (sugg, format!(" -> {src}"))) } } diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 87fc28b39ce7e..acd47fcd279db 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -157,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { // case somebody does that for some reason && (const_1.is_pos_infinity() && const_2.is_neg_infinity() || const_1.is_neg_infinity() && const_2.is_pos_infinity()) - && let Some(local_snippet) = first.span.get_source_text(cx) + && let Some(local_snippet) = first.span.get_text(cx) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite, diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index 475ee2191a850..33370194d4e7c 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -105,8 +105,8 @@ impl LateLintPass<'_> for ManualHashOne { finish_expr.span, "manual implementation of `BuildHasher::hash_one`", |diag| { - if let Some(build_hasher) = build_hasher.span.get_source_text(cx) - && let Some(hashed_value) = hashed_value.span.get_source_text(cx) + if let Some(build_hasher) = build_hasher.span.get_text(cx) + && let Some(hashed_value) = hashed_value.span.get_text(cx) { diag.multipart_suggestion( "try", diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs index e1b353b4911ad..94a3f2b1f15f9 100644 --- a/clippy_lints/src/manual_range_patterns.rs +++ b/clippy_lints/src/manual_range_patterns.rs @@ -143,8 +143,8 @@ impl LateLintPass<'_> for ManualRangePatterns { pat.span, "this OR pattern can be rewritten using a range", |diag| { - if let Some(min) = min.span.get_source_text(cx) - && let Some(max) = max.span.get_source_text(cx) + if let Some(min) = min.span.get_text(cx) + && let Some(max) = max.span.get_text(cx) { diag.span_suggestion( pat.span, diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 38017335f5a11..e317056e29809 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -160,7 +160,7 @@ fn handle( ); } else if let Some(ty_name) = find_type_name(cx, cx.typeck_results().expr_ty(condition)) && cx.typeck_results().expr_adjustments(body_some).is_empty() - && let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx) + && let Some(or_body_snippet) = peel_blocks(body_none).span.get_text(cx) && let Some(indent) = indent_of(cx, expr.span) && ConstEvalCtxt::new(cx).eval_local(body_none, expr.span.ctxt()).is_some() { diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 35b9e5617ca1b..f500f21246bdb 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -152,7 +152,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { if let Some(((_, dest), src)) = split && let Some(pat_snippets) = group .iter() - .map(|(_, arm)| arm.pat.span.get_source_text(cx)) + .map(|(_, arm)| arm.pat.span.get_text(cx)) .collect::>>() { let suggs = src diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index b175ed2acf9af..98530b7808076 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -119,7 +119,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { wildcard_ident.map_or(String::new(), |ident| { ident .span - .get_source_text(cx) + .get_text(cx) .map_or_else(|| format!("{} @ ", ident.name), |s| format!("{s} @ ")) }), if let CommonPrefixSearcher::Path(path_prefix) = path_prefix { diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 414932199d2e4..7c740ababb060 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -22,7 +22,7 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty /// match arms. fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { - span.check_source_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) + span.check_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) } pub(crate) fn check<'tcx>( diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 43508edd42630..b6694ad5148ba 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -51,7 +51,7 @@ pub(super) fn check<'tcx>( "case-sensitive file extension comparison", |diag| { diag.help("consider using a case-insensitive comparison instead"); - if let Some(recv_source) = recv.span.get_source_text(cx) { + if let Some(recv_source) = recv.span.get_text(cx) { let recv_source = if cx.typeck_results().expr_ty(recv).is_ref() { recv_source.to_owned() } else { diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index 2c16a444adea3..419a7e0ed877d 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & .iter() .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) .count() - && let Some(param_snippet) = param.span.get_source_text(cx) + && let Some(param_snippet) = param.span.get_text(cx) { let mut applicability = Applicability::MachineApplicable; let (filter, _) = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut applicability); diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index a50da461719e0..050b28b3babd7 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -27,8 +27,8 @@ pub(super) fn check<'tcx>( && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind && err_path.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && is_ok_wrapping(cx, map_expr) - && let Some(recv_snippet) = recv.span.get_source_text(cx) - && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx) + && let Some(recv_snippet) = recv.span.get_text(cx) + && let Some(err_arg_snippet) = err_arg.span.get_text(cx) && let Some(indent) = indent_of(cx, expr.span) { let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str(), true, Some(indent + 4)); diff --git a/clippy_lints/src/methods/manual_try_fold.rs b/clippy_lints/src/methods/manual_try_fold.rs index 4ec4f41d2b8e3..6ef7abffb0b2f 100644 --- a/clippy_lints/src/methods/manual_try_fold.rs +++ b/clippy_lints/src/methods/manual_try_fold.rs @@ -31,14 +31,12 @@ pub(super) fn check<'tcx>( && let ExprKind::Closure(closure) = acc.kind && msrv.meets(cx, msrvs::ITERATOR_TRY_FOLD) && !is_from_proc_macro(cx, expr) - && let Some(args_snip) = closure - .fn_arg_span - .and_then(|fn_arg_span| fn_arg_span.get_source_text(cx)) + && let Some(args_snip) = closure.fn_arg_span.and_then(|fn_arg_span| fn_arg_span.get_text(cx)) { let init_snip = rest .is_empty() .then_some(first.span) - .and_then(|span| span.get_source_text(cx)) + .and_then(|span| span.get_text(cx)) .map_or_else(|| "...".to_owned(), |src| src.to_owned()); span_lint_and_sugg( diff --git a/clippy_lints/src/methods/map_all_any_identity.rs b/clippy_lints/src/methods/map_all_any_identity.rs index 91118c6fb1c47..14a9752d3bded 100644 --- a/clippy_lints/src/methods/map_all_any_identity.rs +++ b/clippy_lints/src/methods/map_all_any_identity.rs @@ -24,7 +24,7 @@ pub(super) fn check( && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) && is_expr_identity_function(cx, any_arg) && let map_any_call_span = map_call_span.with_hi(any_call_span.hi()) - && let Some(map_arg) = map_arg.span.get_source_text(cx) + && let Some(map_arg) = map_arg.span.get_text(cx) { span_lint_and_then( cx, diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index ba62382da6b0c..20468784aae7a 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -36,7 +36,7 @@ fn handle_expr( && receiver.res_local_id() == Some(first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char - && let Some(snippet) = before_chars.get_source_text(cx) + && let Some(snippet) = before_chars.get_text(cx) { span_lint_and_sugg( cx, @@ -78,7 +78,7 @@ fn handle_expr( if revert != is_all && fn_path.ty_rel_def(cx).is_diag_item(cx, sym::char_is_ascii) && peels_expr_ref(arg).res_local_id() == Some(first_param) - && let Some(snippet) = before_chars.get_source_text(cx) + && let Some(snippet) = before_chars.get_text(cx) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs index 0e510a88015ec..7626cdb5a5fe8 100644 --- a/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name expr.span, "derefed type is same as origin", "try", - recv.span.get_source_text(cx).unwrap().to_owned(), + recv.span.get_text(cx).unwrap().to_owned(), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/methods/ptr_offset_by_literal.rs b/clippy_lints/src/methods/ptr_offset_by_literal.rs index 129d39c1e9f46..17f81f547aeb0 100644 --- a/clippy_lints/src/methods/ptr_offset_by_literal.rs +++ b/clippy_lints/src/methods/ptr_offset_by_literal.rs @@ -92,7 +92,7 @@ fn expr_as_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti } fn format_isize_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { - let text = expr.span.get_source_text(cx)?; + let text = expr.span.get_text(cx)?; let text = peel_parens_str(&text); Some(text.trim_end_matches("isize").trim_end_matches('_').to_string()) } diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index 51b3db52bd27f..b01adbf4d58af 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -49,8 +49,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' ), )]; if let Some((left, right)) = invert_bindings - && let Some(snip_left) = left.get_source_text(cx) - && let Some(snip_right) = right.get_source_text(cx) + && let Some(snip_left) = left.get_text(cx) + && let Some(snip_right) = right.get_text(cx) { suggestions.extend([(left, snip_right.to_string()), (right, snip_left.to_string())]); } else { diff --git a/clippy_lints/src/methods/string_lit_chars_any.rs b/clippy_lints/src/methods/string_lit_chars_any.rs index 27c324f863285..53c695e378476 100644 --- a/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/clippy_lints/src/methods/string_lit_chars_any.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( } && msrv.meets(cx, msrvs::MATCHES_MACRO) && !is_from_proc_macro(cx, expr) - && let Some(scrutinee_snip) = scrutinee.span.get_source_text(cx) + && let Some(scrutinee_snip) = scrutinee.span.get_text(cx) { // Normalize the char using `map` so `join` doesn't use `Display`, if we don't then // something like `r"\"` will become `'\'`, which is of course invalid diff --git a/clippy_lints/src/methods/unnecessary_first_then_check.rs b/clippy_lints/src/methods/unnecessary_first_then_check.rs index 9c5768dfb6f53..43dac249f7440 100644 --- a/clippy_lints/src/methods/unnecessary_first_then_check.rs +++ b/clippy_lints/src/methods/unnecessary_first_then_check.rs @@ -29,8 +29,8 @@ pub(super) fn check( }; let both_calls_span = first_call_span.with_hi(call_span.hi()); - if let Some(both_calls_snippet) = both_calls_span.get_source_text(cx) - && let Some(first_caller_snippet) = first_caller.span.get_source_text(cx) + if let Some(both_calls_snippet) = both_calls_span.get_text(cx) + && let Some(first_caller_snippet) = first_caller.span.get_text(cx) { let (sugg_span, suggestion) = if is_some { ( diff --git a/clippy_lints/src/methods/unnecessary_get_then_check.rs b/clippy_lints/src/methods/unnecessary_get_then_check.rs index f0054bd3a897a..b622a219f35dc 100644 --- a/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -39,11 +39,11 @@ pub(super) fn check( return; }; let both_calls_span = get_call_span.with_hi(call_span.hi()); - if let Some(snippet) = both_calls_span.get_source_text(cx) - && let Some(arg_snippet) = arg.span.get_source_text(cx) + if let Some(snippet) = both_calls_span.get_text(cx) + && let Some(arg_snippet) = arg.span.get_text(cx) { let generics_snippet = if let Some(generics) = path.args - && let Some(generics_snippet) = generics.span_ext.get_source_text(cx) + && let Some(generics_snippet) = generics.span_ext.get_text(cx) { format!("::{generics_snippet}") } else { @@ -64,7 +64,7 @@ pub(super) fn check( suggestion, Applicability::MaybeIncorrect, ); - } else if let Some(caller_snippet) = get_caller.span.get_source_text(cx) { + } else if let Some(caller_snippet) = get_caller.span.get_text(cx) { let full_span = get_caller.span.with_hi(call_span.hi()); span_lint_and_then( diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 9232547355f68..5b19c43f1b32f 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -42,7 +42,7 @@ pub fn check_for_loop_iter( && let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent) && let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body) && !clone_or_copy_needed - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) { // Issue 12098 // https://github.com/rust-lang/rust-clippy/issues/12098 @@ -102,7 +102,7 @@ pub fn check_for_loop_iter( && implements_trait(cx, collection_ty, into_iterator_trait_id, &[]) && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, sym::Item) && iter_item_ty == into_iter_item_ty - && let Some(collection_snippet) = collection.span.get_source_text(cx) + && let Some(collection_snippet) = collection.span.get_text(cx) { collection_snippet } else { diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index d26d8f5b42e0d..35ae44591c188 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -217,7 +217,7 @@ fn check_into_iter_call_arg( && let parent_ty = cx.typeck_results().expr_ty(parent) && implements_trait(cx, parent_ty, iterator_trait_id, &[]) && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. && !cx.typeck_results().expr_ty(receiver).is_diag_item(cx, sym::Cow) // Calling `iter()` on a temporary object can lead to false positives. #14242 @@ -313,8 +313,8 @@ fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if let Some(parent) = get_parent_expr(cx, expr) && let Some((sym::split, argument_expr)) = get_fn_name_and_arg(cx, parent) - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) - && let Some(arg_snippet) = argument_expr.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) + && let Some(arg_snippet) = argument_expr.span.get_text(cx) { // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x` // implements `AsRef` but does not implement `Deref`. In this case, we have to @@ -712,7 +712,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx && let arg_ty = arg_ty.peel_refs() // For now we limit this lint to `String` and `Vec`. && (is_str_and_string(cx, arg_ty, original_arg_ty) || is_slice_and_vec(cx, arg_ty, original_arg_ty)) - && let Some(snippet) = caller.span.get_source_text(cx) + && let Some(snippet) = caller.span.get_text(cx) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs index b2ee3f0c24f10..5bc437cf24f4c 100644 --- a/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -59,7 +59,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { .iter() .filter_map(|f| match f.pat.kind { PatKind::Wild => None, - _ => f.span.get_source_text(cx), + _ => f.span.get_text(cx), }) .format(", "), )); diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 2b99ab81816b6..36a2a7f4b406c 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -78,7 +78,7 @@ impl LateLintPass<'_> for ImportRename { && let Some(name) = self.renames.get(&id) // Remove semicolon since it is not present for nested imports && let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';') - && let Some(snip) = span_without_semi.get_source_text(cx) + && let Some(snip) = span_without_semi.get_text(cx) && let Some(import) = match snip.split_once(" as ") { None => Some(snip.as_str()), Some((import, rename)) => { diff --git a/clippy_lints/src/needless_else.rs b/clippy_lints/src/needless_else.rs index 0a25ab37603e2..299860ac1e037 100644 --- a/clippy_lints/src/needless_else.rs +++ b/clippy_lints/src/needless_else.rs @@ -43,7 +43,7 @@ impl EarlyLintPass for NeedlessElse { && !else_clause.span.from_expansion() && block.stmts.is_empty() && let range = (then_block.span.hi()..expr.span.hi()).trim_start(cx) - && range.clone().check_source_text(cx, |src| { + && range.clone().check_text(cx, |src| { // Ignore else blocks that contain comments or #[cfg]s !src.contains(['/', '#']) }) diff --git a/clippy_lints/src/needless_ifs.rs b/clippy_lints/src/needless_ifs.rs index 0356fc2696215..48d15056f7291 100644 --- a/clippy_lints/src/needless_ifs.rs +++ b/clippy_lints/src/needless_ifs.rs @@ -48,7 +48,7 @@ impl LateLintPass<'_> for NeedlessIfs { && block.stmts.is_empty() && block.expr.is_none() && !expr.span.in_external_macro(cx.sess().source_map()) - && then.span.check_source_text(cx, |src| { + && then.span.check_text(cx, |src| { // Ignore // - empty macro expansions // - empty reptitions in macro expansions @@ -58,7 +58,7 @@ impl LateLintPass<'_> for NeedlessIfs { .all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace() || ch == b'\x0b') }) && let Some(cond_span) = walk_span_to_context(cond.span, expr.span.ctxt()) - && let Some(cond_snippet) = cond_span.get_source_text(cx) + && let Some(cond_snippet) = cond_span.get_text(cx) && !is_from_proc_macro(cx, expr) { span_lint_and_sugg( diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index e4dcaad69026c..a2206102f053d 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -249,7 +249,7 @@ fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> None => local.pat.span.hi(), }); - span.get_source_text(cx) + span.get_text(cx) } fn check<'tcx>( diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index b531eb00edc68..a2ad531751288 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - span.get_source_text(cx).map_or_else( + span.get_text(cx).map_or_else( || "change the call to".to_owned(), |src| format!("change `{src}` to"), ), @@ -275,7 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - span.get_source_text(cx).map_or_else( + span.get_text(cx).map_or_else( || "change the call to".to_owned(), |src| format!("change `{src}` to"), ), diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index af64f71ba3e24..99ab839f28eda 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -285,8 +285,8 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if let ExprKind::Index(..) = &expr.kind { if !is_inside_always_const_context(cx.tcx, expr.hir_id) && let [arr, func] = &*reduced - && let Some(arr) = arr.span.get_source_text(cx) - && let Some(func) = func.span.get_source_text(cx) + && let Some(arr) = arr.span.get_text(cx) + && let Some(func) = func.span.get_text(cx) { span_lint_hir_and_then( cx, @@ -307,7 +307,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } else { let mut snippet = String::new(); for e in reduced { - if let Some(snip) = e.span.get_source_text(cx) { + if let Some(snip) = e.span.get_text(cx) { snippet.push_str(&snip); snippet.push_str("; "); } else { diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 72ebe13562be4..cf19cd9fc81d5 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { && param.span.eq_ctxt(expr.span) && param .span - .check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) + .check_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) { show_error(cx, param); } @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { && param.span.eq_ctxt(expr.span) && param .span - .check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) + .check_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) { show_error(cx, param); } diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index eae2c22ef1eb4..e601d54cc4eda 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -129,7 +129,7 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace if let ExpnKind::Macro(MacroKind::Bang, mac_name) = expn_data.kind && let name = mac_name.as_str() && let Some(&braces) = mac_braces.macro_braces.get(name) - && let Some(snip) = expn_data.call_site.get_source_text(cx) + && let Some(snip) = expn_data.call_site.get_text(cx) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 && let Some(macro_args_str) = snip.strip_prefix(name).and_then(|snip| snip.strip_prefix('!')) diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index 2ac9e010c69cf..5735c23a62ac8 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -86,7 +86,7 @@ impl EarlyLintPass for OctalEscapes { // Last check to make sure the source text matches what we read from the string. // Macros are involved somehow if this doesn't match. - if span.check_source_text(cx, |src| match *src.as_bytes() { + if span.check_text(cx, |src| match *src.as_bytes() { [b'\\', b'0', lo] => lo == c_lo, [b'\\', b'0', hi, lo] => hi == c_hi && lo == c_lo, _ => false, diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 8064a77d43486..fa391fad589fe 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -74,8 +74,8 @@ pub(super) fn check<'tcx>( expr.span, "manual implementation of an assign operation", |diag| { - if let Some(snip_a) = assignee.span.get_source_text(cx) - && let Some(snip_r) = rhs.span.get_source_text(cx) + if let Some(snip_a) = assignee.span.get_text(cx) + && let Some(snip_r) = rhs.span.get_text(cx) { diag.span_suggestion( expr.span, diff --git a/clippy_lints/src/operators/decimal_bitwise_operands.rs b/clippy_lints/src/operators/decimal_bitwise_operands.rs index c5050ca9d4fd5..08e56e4516c52 100644 --- a/clippy_lints/src/operators/decimal_bitwise_operands.rs +++ b/clippy_lints/src/operators/decimal_bitwise_operands.rs @@ -40,7 +40,7 @@ fn check_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { if let LitKind::Int(Pu128(val), _) = lit.node && !is_single_digit(val) && !is_power_of_twoish(val) - && let Some(src) = lit.span.get_source_text(cx) + && let Some(src) = lit.span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) && num_lit.is_decimal() { diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index 610f252332f52..8a2cdf430927b 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -43,8 +43,8 @@ fn lint_misrefactored_assign_op( expr.span, "variable appears on both sides of an assignment operation", |diag| { - if let Some(snip_a) = assignee.span.get_source_text(cx) - && let Some(snip_r) = rhs_other.span.get_source_text(cx) + if let Some(snip_a) = assignee.span.get_text(cx) + && let Some(snip_r) = rhs_other.span.get_text(cx) { let a = &sugg::Sugg::hir(cx, assignee, ".."); let r = &sugg::Sugg::hir(cx, rhs, ".."); diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index d26aa25778165..473718c5419f1 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -73,7 +73,7 @@ impl PathbufPushSearcher<'_> { && let Some(arg) = self.arg && let ExprKind::Lit(x) = arg.kind && let LitKind::Str(_, StrStyle::Cooked) = x.node - && let Some(s) = arg.span.get_source_text(cx) + && let Some(s) = arg.span.get_text(cx) { Some(format!(" = PathBuf::from({s});")) } else { @@ -83,8 +83,8 @@ impl PathbufPushSearcher<'_> { fn gen_pathbuf_join(&self, cx: &LateContext<'_>) -> Option { let arg = self.arg?; - let arg_str = arg.span.get_source_text(cx)?; - let init_val = self.init_val.span.get_source_text(cx)?; + let arg_str = arg.span.get_text(cx)?; + let init_val = self.init_val.span.get_text(cx)?; Some(format!(" = {init_val}.join({arg_str});")) } diff --git a/clippy_lints/src/ptr/ptr_arg.rs b/clippy_lints/src/ptr/ptr_arg.rs index 4f2105b8dc147..200ab8f8418f1 100644 --- a/clippy_lints/src/ptr/ptr_arg.rs +++ b/clippy_lints/src/ptr/ptr_arg.rs @@ -55,7 +55,7 @@ pub(super) fn check_body<'tcx>( .chain(result.replacements.iter().map(|r| { ( r.expr_span, - format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement), + format!("{}{}", r.self_span.get_text(cx).unwrap(), r.replacement), ) })) .collect(), @@ -156,7 +156,7 @@ impl fmt::Display for DerefTyDisplay<'_, '_> { DerefTy::Path => f.write_str("Path"), DerefTy::Slice(hir_ty, ty) => { f.write_char('[')?; - match hir_ty.and_then(|s| s.get_source_text(self.0)) { + match hir_ty.and_then(|s| s.get_text(self.0)) { Some(s) => f.write_str(&s)?, None => ty.fmt(f)?, } @@ -279,7 +279,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( diag.span_suggestion( hir_ty.span, "change this to", - match ty.span().get_source_text(cx) { + match ty.span().get_text(cx) { Some(s) => format!("&{}{s}", mutability.prefix_str()), None => format!("&{}{}", mutability.prefix_str(), args.type_at(1)), }, diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 48456f809c42c..051e2894e2606 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -300,7 +300,7 @@ fn check_possible_range_contains( if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind && op == lhs_op.node && let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()) - && new_span.check_source_text(cx, |src| { + && new_span.check_text(cx, |src| { // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong src.matches('(').count() == src.matches(')').count() }) diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index 5916828b2e8a9..31c90c591b7c2 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -74,7 +74,7 @@ impl EarlyLintPass for RawStrings { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::FormatArgs(format_args) = &expr.kind && !format_args.span.in_external_macro(cx.sess().source_map()) - && format_args.span.check_source_text(cx, |src| src.starts_with('r')) + && format_args.span.check_text(cx, |src| src.starts_with('r')) && let Some(str) = snippet_opt(cx.sess(), format_args.span) && let count_hash = str.bytes().skip(1).take_while(|b| *b == b'#').count() && let Some(str) = str.get(count_hash + 2..str.len() - count_hash - 1) @@ -97,7 +97,7 @@ impl EarlyLintPass for RawStrings { _ => return, } && !expr.span.in_external_macro(cx.sess().source_map()) - && expr.span.check_source_text(cx, |src| src.starts_with(prefix)) + && expr.span.check_text(cx, |src| src.starts_with(prefix)) { self.check_raw_string(cx, lit.symbol.as_str(), expr.span, prefix, max, lit.kind.descr()); } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index f04e3480a9c63..db3be9e64f147 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -214,7 +214,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { .unwrap_crate_local() .lint_root; - if let Some(snip) = span.get_source_text(cx) + if let Some(snip) = span.get_text(cx) && let Some(dot) = snip.rfind('.') { let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap())); diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 53f8a453d275f..837c29c08851b 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -190,7 +190,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape }; if let Some((primary, auxiliary, kind)) = parts - && let Some(literal_snippet) = base.get_source_text(cx) + && let Some(literal_snippet) = base.get_text(cx) && let Some(inner) = literal_snippet.get(offset as usize..) // Only convert to native rustc spans if the parsed regex matches the // source snippet exactly, to ensure the span offsets are correct diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index cde9d16fc6066..7fdc03891a889 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -94,7 +94,7 @@ impl SemicolonBlock { // ({ 0 }); // if we remove this `;`, this will parse as a `({ 0 })(5);` function call // (5); // } - if remove_span.check_source_text(cx, |src| src.contains(')')) { + if remove_span.check_text(cx, |src| src.contains(')')) { return; } diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs index eaef8d111a991..3d5de785b790c 100644 --- a/clippy_lints/src/single_range_in_vec_init.rs +++ b/clippy_lints/src/single_range_in_vec_init.rs @@ -91,7 +91,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { return; }; - let Some(snippet) = span.get_source_text(cx) else { + let Some(snippet) = span.get_text(cx) else { return; }; // `is_from_proc_macro` will skip any `vec![]`. Let's not! diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index b3a63ed814cb5..16cf0ecbc0bc7 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -212,10 +212,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { bounds_span = bounds_span.to(bound.span); } - let fixed_trait_snippet = unique_traits - .iter() - .filter_map(|b| b.span.get_source_text(cx)) - .join(" + "); + let fixed_trait_snippet = unique_traits.iter().filter_map(|b| b.span.get_text(cx)).join(" + "); span_lint_and_sugg( cx, @@ -451,7 +448,7 @@ fn rollup_traits<'cx, 'tcx>( let traits = comparable_bounds .iter() - .filter_map(|&(_, span)| span.get_source_text(cx)) + .filter_map(|&(_, span)| span.get_text(cx)) .join(" + "); span_lint_and_sugg( diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index ac86a457b5374..b9d6c81a152b3 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -85,7 +85,7 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_ && block.expr.is_none() && let Some(last_stmt) = block.stmts.iter().last() && let StmtKind::Semi(last_expr) = last_stmt.kind - && let Some(snip) = last_expr.span.get_source_text(cx) + && let Some(snip) = last_expr.span.get_text(cx) { Some((last_stmt.span, snip)) } else { @@ -117,7 +117,7 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_ .filter_map(|arg| get_expr_snippet_with_type_certainty(cx, arg)) .collect(); - if let Some(call_snippet) = expr.span.get_source_text(cx) { + if let Some(call_snippet) = expr.span.get_text(cx) { if arg_snippets_without_redundant_exprs.is_empty() && let suggestions = args_to_recover .iter() diff --git a/clippy_lints/src/useless_vec.rs b/clippy_lints/src/useless_vec.rs index 0cd7426cfe0f6..cdb757ed89e23 100644 --- a/clippy_lints/src/useless_vec.rs +++ b/clippy_lints/src/useless_vec.rs @@ -285,10 +285,10 @@ impl SuggestedType { 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.map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")); + let maybe_args = args_span.map(|sp| sp.get_text(cx).expect("spans are always crate-local")); let maybe_args = maybe_args.as_deref().unwrap_or_default(); let maybe_len = len_span - .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) + .map(|sp| sp.get_text(cx).expect("spans are always crate-local")) .map(|st| format!("; {st}")) .unwrap_or_default(); diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs index 8f5014d1e3add..ee1729750f2e5 100644 --- a/clippy_lints/src/utils/format_args_collector.rs +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -80,7 +80,7 @@ fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool { .tuple_windows() .map(|(start, end)| start.between(end)) .all(|sp| { - sp.check_source_text(cx, |src| { + sp.check_text(cx, |src| { // text should be either `, name` or `, name =` let mut iter = tokenize(src, FrontmatterAllowed::No).filter(|t| { !matches!( diff --git a/clippy_lints/src/visibility.rs b/clippy_lints/src/visibility.rs index 875041da780c1..9cefc54598b78 100644 --- a/clippy_lints/src/visibility.rs +++ b/clippy_lints/src/visibility.rs @@ -152,5 +152,5 @@ impl EarlyLintPass for Visibility { } fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> bool { - !span.check_source_text(cx, |src| src.starts_with("pub")) + !span.check_text(cx, |src| src.starts_with("pub")) } diff --git a/clippy_lints/src/write/literal.rs b/clippy_lints/src/write/literal.rs index 9711410ef8853..df31fad8615ed 100644 --- a/clippy_lints/src/write/literal.rs +++ b/clippy_lints/src/write/literal.rs @@ -48,7 +48,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) && let Some(arg) = format_args.arguments.by_index(index) && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind && !arg.expr.span.from_expansion() - && let Some(value_string) = arg.expr.span.get_source_text(cx) + && let Some(value_string) = arg.expr.span.get_text(cx) { let (replacement, replace_raw) = match lit.kind { LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { @@ -71,7 +71,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) _ => continue, }; - let Some(format_string_snippet) = format_args.span.get_source_text(cx) else { + let Some(format_string_snippet) = format_args.span.get_text(cx) else { continue; }; let format_string_is_raw = format_string_snippet.starts_with('r'); diff --git a/clippy_lints/src/write/with_newline.rs b/clippy_lints/src/write/with_newline.rs index 53109f4014c81..15cc29818aec4 100644 --- a/clippy_lints/src/write/with_newline.rs +++ b/clippy_lints/src/write/with_newline.rs @@ -48,7 +48,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: format!("using `{name}!()` with a format string that ends in a single newline"), |diag| { let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); - let Some(format_snippet) = format_string_span.get_source_text(cx) else { + let Some(format_snippet) = format_string_span.get_text(cx) else { return; }; diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 119836fda46c3..2341007cd49e8 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -107,7 +107,7 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { /// Checks whether the given span contains a `#[cfg(..)]` attribute pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { - s.check_source_text(cx, |src| { + s.check_text(cx, |src| { let mut iter = tokenize_with_text(src); // Search for the token sequence [`#`, `[`, `cfg`] diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 53352ddc7f83a..050e43344d0c4 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1036,7 +1036,7 @@ fn reduce_exprkind<'hir>( // `{}` => `()` ([], None) if block.span.ctxt() != eval_ctxt - || block.span.check_source_text(cx, |src| { + || block.span.check_text(cx, |src| { tokenize(src, FrontmatterAllowed::No) .map(|t| t.kind) .filter(|t| { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 366ace799612c..a067e55595692 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2882,7 +2882,7 @@ pub fn tokenize_with_text(s: &str) -> impl Iterator(sm: impl HasSourceMap<'sm>, span: Span) -> bool { - span.check_source_text(sm, |snippet| { + span.check_text(sm, |snippet| { tokenize(snippet, FrontmatterAllowed::No).any(|token| { matches!( token.kind, @@ -2897,7 +2897,7 @@ pub fn span_contains_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> boo /// This is useful to determine if there are any actual code tokens in the span that are omitted in /// the late pass, such as platform-specific code. pub fn span_contains_non_whitespace<'sm>(sm: impl HasSourceMap<'sm>, span: Span, skip_comments: bool) -> bool { - span.check_source_text(sm, |snippet| { + span.check_text(sm, |snippet| { tokenize_with_text(snippet).any(|(token, _, _)| match token { TokenKind::Whitespace => false, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments, diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 036f1346ce625..171276f997d39 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -109,7 +109,7 @@ impl IntoSpan for Range { pub trait SpanExt: SpanRange { /// Attempts to get a handle to the source text. Returns `None` if either the span is malformed, /// or the source text is not accessible. - fn get_source_text<'sm>(self, sm: impl HasSourceMap<'sm>) -> Option { + fn get_text<'sm>(self, sm: impl HasSourceMap<'sm>) -> Option { get_source_range(sm.source_map(), self.into_range()).and_then(SourceText::new) } @@ -127,7 +127,7 @@ pub trait SpanExt: SpanRange { /// Checks if the referenced source text satisfies the given predicate. Returns `false` if the /// source text cannot be retrieved. - fn check_source_text<'sm>(self, sm: impl HasSourceMap<'sm>, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { + fn check_text<'sm>(self, sm: impl HasSourceMap<'sm>, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { self.with_source_text(sm, pred).unwrap_or(false) } From 345dafc68a37d1765f80b97e0e6de9bf5933e9b8 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 13 Jun 2026 23:58:44 +0200 Subject: [PATCH 055/278] extract the tests out of `methods.rs` Among other things, this duplicates the repro for issue10029 for `.next_back()` --- tests/ui/filter_next.fixed | 50 ++++++++++++++++++ tests/ui/filter_next.rs | 50 ++++++++++++++++++ ...hods_fixable.stderr => filter_next.stderr} | 6 +-- tests/ui/filter_next_unfixable.rs | 35 +++++++++++++ tests/ui/filter_next_unfixable.stderr | 51 +++++++++++++++++++ tests/ui/methods.rs | 47 ----------------- tests/ui/methods.stderr | 29 +---------- tests/ui/methods_fixable.fixed | 25 --------- tests/ui/methods_fixable.rs | 25 --------- tests/ui/methods_unfixable.rs | 9 ---- tests/ui/methods_unfixable.stderr | 16 ------ 11 files changed, 191 insertions(+), 152 deletions(-) create mode 100644 tests/ui/filter_next.fixed create mode 100644 tests/ui/filter_next.rs rename tests/ui/{methods_fixable.stderr => filter_next.stderr} (90%) create mode 100644 tests/ui/filter_next_unfixable.rs create mode 100644 tests/ui/filter_next_unfixable.stderr delete mode 100644 tests/ui/methods_fixable.fixed delete mode 100644 tests/ui/methods_fixable.rs delete mode 100644 tests/ui/methods_unfixable.rs delete mode 100644 tests/ui/methods_unfixable.stderr diff --git a/tests/ui/filter_next.fixed b/tests/ui/filter_next.fixed new file mode 100644 index 0000000000000..e982c9233d28e --- /dev/null +++ b/tests/ui/filter_next.fixed @@ -0,0 +1,50 @@ +//@aux-build:option_helpers.rs +#![warn(clippy::filter_next)] +#![expect(clippy::disallowed_names)] +#![allow(clippy::useless_vec)] + +extern crate option_helpers; + +use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; + +fn main() {} + +fn filter_next() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().find(|&x| *x < 0); + //~^ filter_next + + let _ = v.iter().rfind(|&x| *x < 0); + //~^ filter_next + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next(); +} + +fn filter_next_back() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().rfind(|&x| x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +} diff --git a/tests/ui/filter_next.rs b/tests/ui/filter_next.rs new file mode 100644 index 0000000000000..c6575de8f95c8 --- /dev/null +++ b/tests/ui/filter_next.rs @@ -0,0 +1,50 @@ +//@aux-build:option_helpers.rs +#![warn(clippy::filter_next)] +#![expect(clippy::disallowed_names)] +#![allow(clippy::useless_vec)] + +extern crate option_helpers; + +use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; + +fn main() {} + +fn filter_next() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().filter(|&x| *x < 0).next(); + //~^ filter_next + + let _ = v.iter().filter(|&x| *x < 0).next_back(); + //~^ filter_next + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next(); +} + +fn filter_next_back() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +} diff --git a/tests/ui/methods_fixable.stderr b/tests/ui/filter_next.stderr similarity index 90% rename from tests/ui/methods_fixable.stderr rename to tests/ui/filter_next.stderr index d26b5e9ac271e..3c5ef757b07fd 100644 --- a/tests/ui/methods_fixable.stderr +++ b/tests/ui/filter_next.stderr @@ -1,5 +1,5 @@ error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods_fixable.rs:9:13 + --> tests/ui/filter_next.rs:16:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().find(|&x| *x < 0)` @@ -8,13 +8,13 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods_fixable.rs:12:13 + --> tests/ui/filter_next.rs:19:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().rfind(|&x| *x < 0)` error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods_fixable.rs:18:13 + --> tests/ui/filter_next.rs:43:13 | LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec![1].into_iter().rfind(|&x| x < 0)` diff --git a/tests/ui/filter_next_unfixable.rs b/tests/ui/filter_next_unfixable.rs new file mode 100644 index 0000000000000..ae70931ddf3b5 --- /dev/null +++ b/tests/ui/filter_next_unfixable.rs @@ -0,0 +1,35 @@ +//@no-rustfix +#![warn(clippy::filter_next)] + +#[rustfmt::skip] +fn main() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Multi-line case -- only a note is emitted + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next(); + + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next_back(); +} + +// The fixed version doesn't compile, as `iter` isn't `mut`. +// We do emit a note suggesting adding it, but not an autofix +pub fn issue10029() { + { + let iter = (0..10); + let _ = iter.filter(|_| true).next(); + //~^ filter_next + } + { + let iter = (0..10); + let _ = iter.filter(|_| true).next_back(); + //~^ filter_next + } +} diff --git a/tests/ui/filter_next_unfixable.stderr b/tests/ui/filter_next_unfixable.stderr new file mode 100644 index 0000000000000..1a31f1f17ca1d --- /dev/null +++ b/tests/ui/filter_next_unfixable.stderr @@ -0,0 +1,51 @@ +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead + --> tests/ui/filter_next_unfixable.rs:9:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next(); + | |___________________________^ + | + = note: `-D clippy::filter-next` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` + +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/filter_next_unfixable.rs:15:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next_back(); + | |________________________________^ + +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead + --> tests/ui/filter_next_unfixable.rs:27:17 + | +LL | let _ = iter.filter(|_| true).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.find(|_| true)` + | +help: you will also need to make `iter` mutable, because `find` takes `&mut self` + --> tests/ui/filter_next_unfixable.rs:26:13 + | +LL | let iter = (0..10); + | ^^^^ + +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/filter_next_unfixable.rs:32:17 + | +LL | let _ = iter.filter(|_| true).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.rfind(|_| true)` + | +help: you will also need to make `iter` mutable, because `rfind` takes `&mut self` + --> tests/ui/filter_next_unfixable.rs:31:13 + | +LL | let iter = (0..10); + | ^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index b45b7dcd56a43..10f0934bede74 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -1,18 +1,10 @@ -//@aux-build:option_helpers.rs - #![warn(clippy::filter_next, clippy::new_ret_no_self)] -#![expect(clippy::disallowed_names, clippy::useless_vec)] - -#[macro_use] -extern crate option_helpers; use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; use std::ops::Mul; use std::rc::{self, Rc}; use std::sync::{self, Arc}; -use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; - struct Lt<'a> { foo: &'a u32, } @@ -97,43 +89,4 @@ impl Mul for T { } } -/// Checks implementation of `FILTER_NEXT` lint. -#[rustfmt::skip] -fn filter_next() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Multi-line case. - let _ = v.iter().filter(|&x| { - //~^ filter_next - *x < 0 - } - ).next(); - - // Check that we don't lint if the caller is not an `Iterator`. - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.filter().next(); - - let foo = IteratorMethodFalsePositives {}; - let _ = foo.filter(42).next(); -} - -#[rustfmt::skip] -fn filter_next_back() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Multi-line case. - let _ = v.iter().filter(|&x| { - //~^ filter_next - *x < 0 - } - ).next_back(); - - // Check that we don't lint if the caller is not an `Iterator`. - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.filter().next_back(); - - let foo = IteratorMethodFalsePositives {}; - let _ = foo.filter(42).next_back(); -} - fn main() {} diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index a637818cee48c..1136b33ea9104 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> tests/ui/methods.rs:84:5 + --> tests/ui/methods.rs:76:5 | LL | / fn new() -> i32 { LL | | @@ -10,30 +10,5 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::new_ret_no_self)]` -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods.rs:106:13 - | -LL | let _ = v.iter().filter(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).next(); - | |___________________________^ - | - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods.rs:125:13 - | -LL | let _ = v.iter().filter(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).next_back(); - | |________________________________^ - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/methods_fixable.fixed b/tests/ui/methods_fixable.fixed deleted file mode 100644 index 17a751eb79457..0000000000000 --- a/tests/ui/methods_fixable.fixed +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::filter_next)] -#![expect(clippy::useless_vec)] - -/// Checks implementation of `FILTER_NEXT` lint. -fn main() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Single-line case. - let _ = v.iter().find(|&x| *x < 0); - //~^ filter_next - - let _ = v.iter().rfind(|&x| *x < 0); - //~^ filter_next -} - -#[clippy::msrv = "1.27"] -fn msrv_1_27() { - let _ = vec![1].into_iter().rfind(|&x| x < 0); - //~^ filter_next -} - -#[clippy::msrv = "1.26"] -fn msrv_1_26() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); -} diff --git a/tests/ui/methods_fixable.rs b/tests/ui/methods_fixable.rs deleted file mode 100644 index b2520acc36385..0000000000000 --- a/tests/ui/methods_fixable.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::filter_next)] -#![expect(clippy::useless_vec)] - -/// Checks implementation of `FILTER_NEXT` lint. -fn main() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Single-line case. - let _ = v.iter().filter(|&x| *x < 0).next(); - //~^ filter_next - - let _ = v.iter().filter(|&x| *x < 0).next_back(); - //~^ filter_next -} - -#[clippy::msrv = "1.27"] -fn msrv_1_27() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); - //~^ filter_next -} - -#[clippy::msrv = "1.26"] -fn msrv_1_26() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); -} diff --git a/tests/ui/methods_unfixable.rs b/tests/ui/methods_unfixable.rs deleted file mode 100644 index 46a5d95eb1b72..0000000000000 --- a/tests/ui/methods_unfixable.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![warn(clippy::filter_next)] -//@no-rustfix -fn main() {} - -pub fn issue10029() { - let iter = (0..10); - let _ = iter.filter(|_| true).next(); - //~^ filter_next -} diff --git a/tests/ui/methods_unfixable.stderr b/tests/ui/methods_unfixable.stderr deleted file mode 100644 index 137112ae41795..0000000000000 --- a/tests/ui/methods_unfixable.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods_unfixable.rs:7:13 - | -LL | let _ = iter.filter(|_| true).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.find(|_| true)` - | -help: you will also need to make `iter` mutable, because `find` takes `&mut self` - --> tests/ui/methods_unfixable.rs:6:9 - | -LL | let iter = (0..10); - | ^^^^ - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: aborting due to 1 previous error - From f0974c97494e4297e43c527518332b79cf097f51 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 14 Jun 2026 00:32:21 +0200 Subject: [PATCH 056/278] misc improvements --- clippy_lints/src/methods/filter_next.rs | 27 +++++++++++-------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 9c6b16b88bfd1..435c825c8a628 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -4,12 +4,12 @@ use clippy_utils::ty::implements_trait; use clippy_utils::{path_to_local_with_projections, sym}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; use super::FILTER_NEXT; -#[derive(Copy, Clone)] +#[derive(Clone, Copy)] pub(super) enum Direction { Forward, Backward, @@ -17,15 +17,13 @@ pub(super) enum Direction { /// lint use of `filter().next()` for `Iterator` and `filter().next_back()` for /// `DoubleEndedIterator` -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - recv: &'tcx hir::Expr<'_>, - filter_arg: &'tcx hir::Expr<'_>, +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + filter_arg: &Expr<'_>, direction: Direction, ) { - // lint if caller of `.filter().next()` is an Iterator or `.filter().next_back()` is a - // DoubleEndedIterator let (required_trait, next_method, find_method) = match direction { Direction::Forward => (sym::Iterator, "next", "find"), Direction::Backward => (sym::DoubleEndedIterator, "next_back", "rfind"), @@ -38,18 +36,17 @@ pub(super) fn check<'tcx>( return; } let msg = format!( - "called `filter(..).{next_method}()` on an `{}`. This is more succinctly expressed by calling \ - `.{find_method}(..)` instead", - required_trait.as_str() + "called `filter(..).{next_method}()` on an `{required_trait}`. \ + This is more succinctly expressed by calling `.{find_method}(..)` instead", ); + // add note if not multi-line let filter_snippet = snippet(cx, filter_arg.span, ".."); if filter_snippet.lines().count() <= 1 { let iter_snippet = snippet(cx, recv.span, ".."); - // add note if not multi-line span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) - && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) - && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind + && let Node::Pat(pat) = cx.tcx.hir_node(id) + && let PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind { (Applicability::Unspecified, Some((pat.span, ident))) } else { From f6bc2c8a4308aac4e3370963845d408c332097be Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 14 Jun 2026 00:01:34 +0200 Subject: [PATCH 057/278] misc: wrap everything in `span_lint_and_then` ..and inline `msg`, to make next commit's diff a bit nicer --- clippy_lints/src/methods/filter_next.rs | 69 +++++++++++++------------ 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 435c825c8a628..af26058e58e5f 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::implements_trait; use clippy_utils::{path_to_local_with_projections, sym}; @@ -35,39 +35,44 @@ pub(super) fn check( { return; } - let msg = format!( - "called `filter(..).{next_method}()` on an `{required_trait}`. \ + span_lint_and_then( + cx, + FILTER_NEXT, + expr.span, + format!( + "called `filter(..).{next_method}()` on an `{required_trait}`. \ This is more succinctly expressed by calling `.{find_method}(..)` instead", - ); - // add note if not multi-line - let filter_snippet = snippet(cx, filter_arg.span, ".."); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, recv.span, ".."); - span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { - let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) - && let Node::Pat(pat) = cx.tcx.hir_node(id) - && let PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind - { - (Applicability::Unspecified, Some((pat.span, ident))) - } else { - (Applicability::MachineApplicable, None) - }; - - diag.span_suggestion( - expr.span, - "try", - format!("{iter_snippet}.{find_method}({filter_snippet})"), - applicability, - ); + ), + |diag| { + // add note if not multi-line + let filter_snippet = snippet(cx, filter_arg.span, ".."); + if filter_snippet.lines().count() <= 1 { + let iter_snippet = snippet(cx, recv.span, ".."); + let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) + && let Node::Pat(pat) = cx.tcx.hir_node(id) + && let PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind + { + (Applicability::Unspecified, Some((pat.span, ident))) + } else { + (Applicability::MachineApplicable, None) + }; - if let Some((pat_span, ident)) = pat { - diag.span_help( - pat_span, - format!("you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`"), + diag.span_suggestion( + expr.span, + "try", + format!("{iter_snippet}.{find_method}({filter_snippet})"), + applicability, ); + + if let Some((pat_span, ident)) = pat { + diag.span_help( + pat_span, + format!( + "you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`" + ), + ); + } } - }); - } else { - span_lint(cx, FILTER_NEXT, expr.span, msg); - } + }, + ); } From 67878aab71e97222d19bf0f07138a92e5d40093c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 9 Sep 2025 18:58:04 +0200 Subject: [PATCH 058/278] refactor(`unit_return_expecting_ord`): use one big `span_lint_and_then` --- clippy_lints/src/unit_return_expecting_ord.rs | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 735225a9e2cee..13baa378910d9 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -180,32 +180,21 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { let args = std::iter::once(receiver).chain(args.iter()).collect::>(); let arg_indices = get_args_to_check(cx, expr, args.len(), fn_mut_trait, ord_trait, partial_ord_trait); for (i, trait_name) in arg_indices { - match check_arg(cx, args[i]) { - Some((span, None)) => { - span_lint( - cx, - UNIT_RETURN_EXPECTING_ORD, - span, - format!( - "this closure returns \ + if let Some((span, last_semi)) = check_arg(cx, args[i]) { + span_lint_and_then( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + format!( + "this closure returns \ the unit type which also implements {trait_name}" - ), - ); - }, - Some((span, Some(last_semi))) => { - span_lint_and_help( - cx, - UNIT_RETURN_EXPECTING_ORD, - span, - format!( - "this closure returns \ - the unit type which also implements {trait_name}" - ), - Some(last_semi), - "probably caused by this trailing semicolon", - ); - }, - None => {}, + ), + |diag| { + if let Some(last_semi) = last_semi { + diag.span_help(last_semi, "probably caused by this trailing semicolon"); + } + }, + ); } } } From 72dc832a807682ca668039cd8a10ef29048af181 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 14 Jun 2026 00:23:35 +0200 Subject: [PATCH 059/278] extract suggestion out of the main message 1. In the singe-line case, we switch to verbose suggestions, otherwise the suggestion line gets too long due to the longer message. 2. In the multi-line case, we use span-less help, otherwise the whole multiline span gets highlighted the second time. 3. Also make the suggestion message more concise. --- clippy_lints/src/methods/filter_next.rs | 13 ++++++----- tests/ui/filter_next.stderr | 29 ++++++++++++++++++++----- tests/ui/filter_next_unfixable.stderr | 25 ++++++++++++++++----- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index af26058e58e5f..430f97e4a7b0f 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -39,11 +39,10 @@ pub(super) fn check( cx, FILTER_NEXT, expr.span, - format!( - "called `filter(..).{next_method}()` on an `{required_trait}`. \ - This is more succinctly expressed by calling `.{find_method}(..)` instead", - ), + format!("called `filter(..).{next_method}()` on an `{required_trait}`"), |diag| { + let sugg_msg = format!("use `.{find_method}(..)` instead"); + // add note if not multi-line let filter_snippet = snippet(cx, filter_arg.span, ".."); if filter_snippet.lines().count() <= 1 { @@ -57,9 +56,9 @@ pub(super) fn check( (Applicability::MachineApplicable, None) }; - diag.span_suggestion( + diag.span_suggestion_verbose( expr.span, - "try", + sugg_msg, format!("{iter_snippet}.{find_method}({filter_snippet})"), applicability, ); @@ -72,6 +71,8 @@ pub(super) fn check( ), ); } + } else { + diag.help(sugg_msg); } }, ); diff --git a/tests/ui/filter_next.stderr b/tests/ui/filter_next.stderr index 3c5ef757b07fd..3442edd43decb 100644 --- a/tests/ui/filter_next.stderr +++ b/tests/ui/filter_next.stderr @@ -1,23 +1,40 @@ -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead +error: called `filter(..).next()` on an `Iterator` --> tests/ui/filter_next.rs:16:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().find(|&x| *x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::filter-next` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` +help: use `.find(..)` instead + | +LL - let _ = v.iter().filter(|&x| *x < 0).next(); +LL + let _ = v.iter().find(|&x| *x < 0); + | -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead +error: called `filter(..).next_back()` on an `DoubleEndedIterator` --> tests/ui/filter_next.rs:19:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().rfind(|&x| *x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `.rfind(..)` instead + | +LL - let _ = v.iter().filter(|&x| *x < 0).next_back(); +LL + let _ = v.iter().rfind(|&x| *x < 0); + | -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead +error: called `filter(..).next_back()` on an `DoubleEndedIterator` --> tests/ui/filter_next.rs:43:13 | LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec![1].into_iter().rfind(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `.rfind(..)` instead + | +LL - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +LL + let _ = vec![1].into_iter().rfind(|&x| x < 0); + | error: aborting due to 3 previous errors diff --git a/tests/ui/filter_next_unfixable.stderr b/tests/ui/filter_next_unfixable.stderr index 1a31f1f17ca1d..ca607714d4ff1 100644 --- a/tests/ui/filter_next_unfixable.stderr +++ b/tests/ui/filter_next_unfixable.stderr @@ -1,4 +1,4 @@ -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead +error: called `filter(..).next()` on an `Iterator` --> tests/ui/filter_next_unfixable.rs:9:13 | LL | let _ = v.iter().filter(|&x| { @@ -9,10 +9,11 @@ LL | | } LL | | ).next(); | |___________________________^ | + = help: use `.find(..)` instead = note: `-D clippy::filter-next` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead +error: called `filter(..).next_back()` on an `DoubleEndedIterator` --> tests/ui/filter_next_unfixable.rs:15:13 | LL | let _ = v.iter().filter(|&x| { @@ -22,30 +23,42 @@ LL | | *x < 0 LL | | } LL | | ).next_back(); | |________________________________^ + | + = help: use `.rfind(..)` instead -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead +error: called `filter(..).next()` on an `Iterator` --> tests/ui/filter_next_unfixable.rs:27:17 | LL | let _ = iter.filter(|_| true).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.find(|_| true)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: you will also need to make `iter` mutable, because `find` takes `&mut self` --> tests/ui/filter_next_unfixable.rs:26:13 | LL | let iter = (0..10); | ^^^^ +help: use `.find(..)` instead + | +LL - let _ = iter.filter(|_| true).next(); +LL + let _ = iter.find(|_| true); + | -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead +error: called `filter(..).next_back()` on an `DoubleEndedIterator` --> tests/ui/filter_next_unfixable.rs:32:17 | LL | let _ = iter.filter(|_| true).next_back(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.rfind(|_| true)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: you will also need to make `iter` mutable, because `rfind` takes `&mut self` --> tests/ui/filter_next_unfixable.rs:31:13 | LL | let iter = (0..10); | ^^^^ +help: use `.rfind(..)` instead + | +LL - let _ = iter.filter(|_| true).next_back(); +LL + let _ = iter.rfind(|_| true); + | error: aborting due to 4 previous errors From 537443bf6a80f0a248ae770cd1feef0c7a34fe2b Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 14 Jun 2026 00:53:02 +0200 Subject: [PATCH 060/278] fix: respect reduced applicability --- clippy_lints/src/methods/filter_next.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 430f97e4a7b0f..67674d723860e 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::{path_to_local_with_projections, sym}; use rustc_ast::{BindingMode, Mutability}; @@ -43,24 +43,26 @@ pub(super) fn check( |diag| { let sugg_msg = format!("use `.{find_method}(..)` instead"); + let mut app = Applicability::MachineApplicable; // add note if not multi-line - let filter_snippet = snippet(cx, filter_arg.span, ".."); + let filter_snippet = snippet_with_applicability(cx, filter_arg.span, "..", &mut app); if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, recv.span, ".."); - let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) + let iter_snippet = snippet_with_applicability(cx, recv.span, "..", &mut app); + let pat = if let Some(id) = path_to_local_with_projections(recv) && let Node::Pat(pat) = cx.tcx.hir_node(id) && let PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind { - (Applicability::Unspecified, Some((pat.span, ident))) + app = Applicability::Unspecified; + Some((pat.span, ident)) } else { - (Applicability::MachineApplicable, None) + None }; diag.span_suggestion_verbose( expr.span, sugg_msg, format!("{iter_snippet}.{find_method}({filter_snippet})"), - applicability, + app, ); if let Some((pat_span, ident)) = pat { From bef6f41446798c9053828bafc093fca145924aa6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 14 Jun 2026 14:16:05 +0200 Subject: [PATCH 061/278] also suggest in the multi-line case This used to be a workaround for a rustc diagnostics issue which has since been resolved --- clippy_lints/src/methods/filter_next.rs | 52 +++++++++++-------------- tests/ui/filter_next.fixed | 13 +++++++ tests/ui/filter_next.rs | 15 +++++++ tests/ui/filter_next.stderr | 42 +++++++++++++++++++- tests/ui/filter_next_unfixable.rs | 18 +-------- tests/ui/filter_next_unfixable.stderr | 40 ++++--------------- 6 files changed, 98 insertions(+), 82 deletions(-) diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 67674d723860e..3584833bc61cd 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -41,40 +41,32 @@ pub(super) fn check( expr.span, format!("called `filter(..).{next_method}()` on an `{required_trait}`"), |diag| { - let sugg_msg = format!("use `.{find_method}(..)` instead"); - let mut app = Applicability::MachineApplicable; - // add note if not multi-line let filter_snippet = snippet_with_applicability(cx, filter_arg.span, "..", &mut app); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet_with_applicability(cx, recv.span, "..", &mut app); - let pat = if let Some(id) = path_to_local_with_projections(recv) - && let Node::Pat(pat) = cx.tcx.hir_node(id) - && let PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind - { - app = Applicability::Unspecified; - Some((pat.span, ident)) - } else { - None - }; - - diag.span_suggestion_verbose( - expr.span, - sugg_msg, - format!("{iter_snippet}.{find_method}({filter_snippet})"), - app, - ); + let iter_snippet = snippet_with_applicability(cx, recv.span, "..", &mut app); - if let Some((pat_span, ident)) = pat { - diag.span_help( - pat_span, - format!( - "you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`" - ), - ); - } + let pat = if let Some(id) = path_to_local_with_projections(recv) + && let Node::Pat(pat) = cx.tcx.hir_node(id) + && let PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind + { + app = Applicability::Unspecified; + Some((pat.span, ident)) } else { - diag.help(sugg_msg); + None + }; + + diag.span_suggestion_verbose( + expr.span, + format!("use `.{find_method}(..)` instead"), + format!("{iter_snippet}.{find_method}({filter_snippet})"), + app, + ); + + if let Some((pat_span, ident)) = pat { + diag.span_help( + pat_span, + format!("you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`"), + ); } }, ); diff --git a/tests/ui/filter_next.fixed b/tests/ui/filter_next.fixed index e982c9233d28e..5502fa1fb2733 100644 --- a/tests/ui/filter_next.fixed +++ b/tests/ui/filter_next.fixed @@ -19,6 +19,19 @@ fn filter_next() { let _ = v.iter().rfind(|&x| *x < 0); //~^ filter_next + // Multi-line case. + #[rustfmt::skip] + let _ = v.iter().find(|&x| { + //~^ filter_next + *x < 0 + }); + + #[rustfmt::skip] + let _ = v.iter().rfind(|&x| { + //~^ filter_next + *x < 0 + }); + // Check that we don't lint if the caller is not an `Iterator`. let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.filter().next(); diff --git a/tests/ui/filter_next.rs b/tests/ui/filter_next.rs index c6575de8f95c8..c8e9f060cd609 100644 --- a/tests/ui/filter_next.rs +++ b/tests/ui/filter_next.rs @@ -19,6 +19,21 @@ fn filter_next() { let _ = v.iter().filter(|&x| *x < 0).next_back(); //~^ filter_next + // Multi-line case. + #[rustfmt::skip] + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next(); + + #[rustfmt::skip] + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next_back(); + // Check that we don't lint if the caller is not an `Iterator`. let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.filter().next(); diff --git a/tests/ui/filter_next.stderr b/tests/ui/filter_next.stderr index 3442edd43decb..19c2428c3d17d 100644 --- a/tests/ui/filter_next.stderr +++ b/tests/ui/filter_next.stderr @@ -24,8 +24,46 @@ LL - let _ = v.iter().filter(|&x| *x < 0).next_back(); LL + let _ = v.iter().rfind(|&x| *x < 0); | +error: called `filter(..).next()` on an `Iterator` + --> tests/ui/filter_next.rs:24:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next(); + | |___________________________^ + | +help: use `.find(..)` instead + | +LL ~ let _ = v.iter().find(|&x| { +LL + +LL + *x < 0 +LL ~ }); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next.rs:31:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next_back(); + | |________________________________^ + | +help: use `.rfind(..)` instead + | +LL ~ let _ = v.iter().rfind(|&x| { +LL + +LL + *x < 0 +LL ~ }); + | + error: called `filter(..).next_back()` on an `DoubleEndedIterator` - --> tests/ui/filter_next.rs:43:13 + --> tests/ui/filter_next.rs:58:13 | LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,5 +74,5 @@ LL - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); LL + let _ = vec![1].into_iter().rfind(|&x| x < 0); | -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/filter_next_unfixable.rs b/tests/ui/filter_next_unfixable.rs index ae70931ddf3b5..07a0db1353500 100644 --- a/tests/ui/filter_next_unfixable.rs +++ b/tests/ui/filter_next_unfixable.rs @@ -1,23 +1,7 @@ //@no-rustfix #![warn(clippy::filter_next)] -#[rustfmt::skip] -fn main() { - let v = [3, 2, 1, 0, -1, -2, -3]; - - // Multi-line case -- only a note is emitted - let _ = v.iter().filter(|&x| { - //~^ filter_next - *x < 0 - } - ).next(); - - let _ = v.iter().filter(|&x| { - //~^ filter_next - *x < 0 - } - ).next_back(); -} +fn main() {} // The fixed version doesn't compile, as `iter` isn't `mut`. // We do emit a note suggesting adding it, but not an autofix diff --git a/tests/ui/filter_next_unfixable.stderr b/tests/ui/filter_next_unfixable.stderr index ca607714d4ff1..2c35badec4853 100644 --- a/tests/ui/filter_next_unfixable.stderr +++ b/tests/ui/filter_next_unfixable.stderr @@ -1,42 +1,16 @@ error: called `filter(..).next()` on an `Iterator` - --> tests/ui/filter_next_unfixable.rs:9:13 - | -LL | let _ = v.iter().filter(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).next(); - | |___________________________^ - | - = help: use `.find(..)` instead - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: called `filter(..).next_back()` on an `DoubleEndedIterator` - --> tests/ui/filter_next_unfixable.rs:15:13 - | -LL | let _ = v.iter().filter(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).next_back(); - | |________________________________^ - | - = help: use `.rfind(..)` instead - -error: called `filter(..).next()` on an `Iterator` - --> tests/ui/filter_next_unfixable.rs:27:17 + --> tests/ui/filter_next_unfixable.rs:11:17 | LL | let _ = iter.filter(|_| true).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: you will also need to make `iter` mutable, because `find` takes `&mut self` - --> tests/ui/filter_next_unfixable.rs:26:13 + --> tests/ui/filter_next_unfixable.rs:10:13 | LL | let iter = (0..10); | ^^^^ + = note: `-D clippy::filter-next` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` help: use `.find(..)` instead | LL - let _ = iter.filter(|_| true).next(); @@ -44,13 +18,13 @@ LL + let _ = iter.find(|_| true); | error: called `filter(..).next_back()` on an `DoubleEndedIterator` - --> tests/ui/filter_next_unfixable.rs:32:17 + --> tests/ui/filter_next_unfixable.rs:16:17 | LL | let _ = iter.filter(|_| true).next_back(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: you will also need to make `iter` mutable, because `rfind` takes `&mut self` - --> tests/ui/filter_next_unfixable.rs:31:13 + --> tests/ui/filter_next_unfixable.rs:15:13 | LL | let iter = (0..10); | ^^^^ @@ -60,5 +34,5 @@ LL - let _ = iter.filter(|_| true).next_back(); LL + let _ = iter.rfind(|_| true); | -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors From 4200d7fc3fe95d11b01e8dabc343ebbed5e7a579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 14 Jun 2026 14:57:07 +0200 Subject: [PATCH 062/278] Add funding links --- .github/FUNDING.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000000..16ac7b6eb9bc9 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: rustfoundation +custom: [ "rust-lang.org/funding" ] From 5a17348bad97e1ac11ab9a9df0601fffc9962040 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 14 Jun 2026 10:10:31 -0400 Subject: [PATCH 063/278] Merge comment and cfg checking in `matches` lint pass. --- clippy_lints/src/matches/mod.rs | 154 +++++++++++++++----------------- 1 file changed, 72 insertions(+), 82 deletions(-) diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index ae0f3ffa0edc9..02b2d11d8aa61 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -26,14 +26,13 @@ mod wild_in_or_pats; use clippy_config::Conf; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::walk_span_to_context; -use clippy_utils::{ - higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, sym, -}; +use clippy_utils::source::SpanExt; +use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, sym, tokenize_with_text}; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; +use rustc_lexer::{TokenKind, is_whitespace}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{SpanData, SyntaxContext}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -1084,7 +1083,29 @@ impl<'tcx> LateLintPass<'tcx> for Matches { try_err::check(cx, expr, ex); } - if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { + if !from_expansion + && let mut has_cfg = false + && let mut has_comments = false + && walk_intra_arm_text(cx, expr.span, ex.span, arms, |s| { + let mut iter = tokenize_with_text(s).filter(|(t, ..)| match t { + TokenKind::Whitespace => false, + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } => { + has_comments = true; + false + }, + _ => true, + }); + while let Some((t, ..)) = iter.next() { + if matches!(t, TokenKind::Pound) + && matches!(iter.next(), Some((TokenKind::OpenBracket, ..))) + && matches!(iter.next(), Some((TokenKind::Ident, "cfg", _))) + { + has_cfg = true; + } + } + }) + && !has_cfg + { if source == MatchSource::Normal { if !(self.msrv.meets(cx, msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) @@ -1093,25 +1114,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } redundant_pattern_match::check_match(cx, expr, ex, arms); - let mut match_comments = span_extract_comments(cx, expr.span); - // We remove comments from inside arms block. - if !match_comments.is_empty() { - for arm in arms { - for comment in span_extract_comments(cx, arm.body.span) { - if let Some(index) = match_comments - .iter() - .enumerate() - .find(|(_, cm)| **cm == comment) - .map(|(index, _)| index) - { - match_comments.remove(index); - } - } - } - } - // If there are still comments, it means they are outside of the arms. Tell the lint - // code about it. - single_match::check(cx, ex, arms, expr, !match_comments.is_empty()); + single_match::check(cx, ex, arms, expr, has_comments); match_bool::check(cx, ex, arms, expr); overlapping_arms::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms); @@ -1216,64 +1219,51 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } -/// Checks if there are any arms with a `#[cfg(..)]` attribute. -fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) -> bool { - let Some(scrutinee_span) = walk_span_to_context(scrutinee.span, SyntaxContext::root()) else { - // Shouldn't happen, but treat this as though a `cfg` attribute were found - return true; - }; - - let start = scrutinee_span.hi(); - let mut arm_spans = arms.iter().map(|arm| { - let data = arm.span.data(); - (data.ctxt == SyntaxContext::root()).then_some((data.lo, data.hi)) - }); - let end = e.span.hi(); - - // Walk through all the non-code space before each match arm. The space trailing the final arm is - // handled after the `try_fold` e.g. - // - // match foo { - // _________^- everything between the scrutinee and arm1 - //| arm1 => (), - //|---^___________^ everything before arm2 - //| #[cfg(feature = "enabled")] - //| arm2 => some_code(), - //|---^____________________^ everything before arm3 - //| // some comment about arm3 - //| arm3 => some_code(), - //|---^____________________^ everything after arm3 - //| #[cfg(feature = "disabled")] - //| arm4 = some_code(), - //|}; - //|^ - let found = arm_spans.try_fold(start, |start, range| { - let Some((end, next_start)) = range else { - // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute - // were found. - return Err(()); - }; - let span = SpanData { - lo: start, - hi: end, - ctxt: SyntaxContext::root(), - parent: None, - } - .span(); - (!span_contains_cfg(cx, span)).then_some(next_start).ok_or(()) - }); - match found { - Ok(start) => { - let span = SpanData { - lo: start, - hi: end, - ctxt: SyntaxContext::root(), - parent: None, +/// Calls the given function for each segment of the source text within the +/// match block which is not part of any arm. For the purposes of this function +/// attributes on an arm are not considered part of the arm. +/// +/// This will return whether all the relevant source text could be retrieved. If +/// all the source text cannot be retrieved it should be assumed that the match +/// originates from a macro. +#[must_use] +fn walk_intra_arm_text( + cx: &LateContext<'_>, + match_sp: Span, + scrutinee_sp: Span, + arms: &[Arm<'_>], + mut f: impl FnMut(&str), +) -> bool { + if let Some(src) = match_sp.get_source_range(cx) + && let scrutinee_sp = scrutinee_sp.source_callsite().data() + && let block_start = (scrutinee_sp.hi.0 - src.sf.start_pos.0) as usize + && let Some(src_text) = src.sf.src.as_ref().map(|x| &***x) + && let Some(block_text) = src_text.get(block_start..src.range.end) + && let Some(stripped_text) = block_text.trim_start_matches(is_whitespace).strip_prefix('{') + && let arms_start = block_start + (block_text.len() - stripped_text.len()) + && let Some(arms_end) = stripped_text + .trim_end_matches(|c| is_whitespace(c) || c == ')') + .strip_suffix('}') + .map(|s| src.range.end - (stripped_text.len() - s.len())) + && let Some(range) = arms.iter().try_fold(arms_start..arms_end, |range, arm| { + let arm_sp: rustc_span::SpanData = arm.span.source_callsite().data(); + let arm_range = (arm_sp.lo.0 - src.sf.start_pos.0) as usize..(arm_sp.hi.0 - src.sf.start_pos.0) as usize; + if range.start <= arm_range.start + && arm_range.end <= range.end + && let Some(src) = src_text.get(range.start..arm_range.start) + { + f(src); + Some(arm_range.end..range.end) + } else { + None } - .span(); - span_contains_cfg(cx, span) - }, - Err(()) => true, + }) + && let Some(src) = src_text.get(range) + { + f(src); + true + } else { + false } } From 15025dcd2f0e47ec150dde47854a798714122550 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 14 Jun 2026 22:58:44 +0200 Subject: [PATCH 064/278] Fix ICE when the `clippy::author` attribute is applied to an item --- clippy_lints/src/utils/author.rs | 34 +++++++++++++++++++++++------- tests/ui/author/issue_17240.rs | 8 +++++++ tests/ui/author/issue_17240.stdout | 20 ++++++++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 tests/ui/author/issue_17240.rs create mode 100644 tests/ui/author/issue_17240.stdout diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index c60d84785a229..2bcf369fb3334 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -6,11 +6,11 @@ use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_hir::{ - self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, + self as hir, BindingMode, Body, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{FloatTy, IntTy, UintTy}; +use rustc_middle::ty::{FloatTy, IntTy, TypeckResults, UintTy}; use rustc_session::declare_lint_pass; use rustc_span::symbol::{Ident, Symbol}; use std::cell::Cell; @@ -137,15 +137,31 @@ impl<'tcx> LateLintPass<'tcx> for Author { fn check_item(cx: &LateContext<'_>, hir_id: HirId) { if let Some(body) = cx.tcx.hir_maybe_body_owned_by(hir_id.expect_owner().def_id) { - check_node(cx, hir_id, |v| { - v.expr(&v.bind("expr", body.value)); - }); + check_node_with_body( + cx, + hir_id, + |v| { + v.expr(&v.bind("expr", body.value)); + }, + Some(body), + ); } } fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { + check_node_with_body(cx, hir_id, f, None); +} + +/// Check the node at `hir_id`, in the context of `body` or the default from `cx` if none is given. +fn check_node_with_body( + cx: &LateContext<'_>, + hir_id: HirId, + f: impl Fn(&PrintVisitor<'_, '_>), + body: Option<&Body<'_>>, +) { if has_attr(cx, hir_id) { - f(&PrintVisitor::new(cx)); + let typeck_results = body.map_or_else(|| cx.typeck_results(), |body| cx.tcx.typeck_body(body.id())); + f(&PrintVisitor::new(cx, typeck_results)); println!("{{"); println!(" // report your lint here"); println!("}}"); @@ -199,6 +215,7 @@ impl Display for OptionPat { struct PrintVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, + typeck_results: &'tcx TypeckResults<'tcx>, /// Fields are the current index that needs to be appended to pattern /// binding names ids: Cell>, @@ -207,9 +224,10 @@ struct PrintVisitor<'a, 'tcx> { } impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>) -> Self { Self { cx, + typeck_results, ids: Cell::default(), first: Cell::new(true), } @@ -291,7 +309,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn maybe_path<'p>(&self, path: &Binding>) { - if let Some(id) = path.value.res(self.cx).opt_def_id() + if let Some(id) = path.value.res(self.typeck_results).opt_def_id() && !id.is_local() { if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { diff --git a/tests/ui/author/issue_17240.rs b/tests/ui/author/issue_17240.rs new file mode 100644 index 0000000000000..591a19907720a --- /dev/null +++ b/tests/ui/author/issue_17240.rs @@ -0,0 +1,8 @@ +//@ check-pass +// Ensure that a proper body is used when printing paths (`x`) +// if the attribute is placed on an item. +#[clippy::author] +fn main() { + let x = 42i32; + _ = -x; +} diff --git a/tests/ui/author/issue_17240.stdout b/tests/ui/author/issue_17240.stdout new file mode 100644 index 0000000000000..81c927180ce1c --- /dev/null +++ b/tests/ui/author/issue_17240.stdout @@ -0,0 +1,20 @@ +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 2 + && let StmtKind::Let(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Lit(ref lit) = init.kind + && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node + && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" + && let StmtKind::Semi(e) = block.stmts[1].kind + && let ExprKind::Block(block1, None) = e.kind + && block1.stmts.len() == 1 + && let StmtKind::Let(local1) = block1.stmts[0].kind + && let Some(init1) = local1.init + && let ExprKind::Unary(UnOp::Neg, inner) = init1.kind + && let PatKind::Wild = local1.pat.kind + && block1.expr.is_none() + && block.expr.is_none() +{ + // report your lint here +} From d9517a64c9b57531e0dd0cee34a0987c01024b42 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 5 May 2026 16:00:56 +1000 Subject: [PATCH 065/278] fix: [std_instead_of_core] false positive for core::io Using suggestion proposed in: https://github.com/rust-lang/rust-clippy/pull/16964#issuecomment-4699031740 --- clippy_lints/src/std_instead_of_core.rs | 29 ++++++++++--------- tests/ui/std_instead_of_core.fixed | 20 +++++++++++++ tests/ui/std_instead_of_core.rs | 20 +++++++++++++ tests/ui/std_instead_of_core.stderr | 14 ++++++++- tests/ui/std_instead_of_core_unfixable.rs | 11 +++++++ tests/ui/std_instead_of_core_unfixable.stderr | 18 +++++++++++- 6 files changed, 96 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index d519afba61b53..0f7cbd503bcd8 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -238,20 +238,21 @@ fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> /// Does not catch individually moved items fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: Msrv) -> bool { loop { - if let Some(stability) = cx.tcx.lookup_stability(def_id) - && let StabilityLevel::Stable { - since, - allowed_through_unstable_modules: None, - } = stability.level - { - let stable = match since { - StableSince::Version(v) => msrv.meets(cx, v), - StableSince::Current => msrv.current(cx).is_none(), - StableSince::Err(_) => false, - }; - - if !stable { - return false; + if let Some(stability) = cx.tcx.lookup_stability(def_id) { + match stability.level { + // Workaround for items from `core::intrinsics` with a stable export in a different module. + // Not that we ignore the `since` field as we are already accessing the item in question. + StabilityLevel::Stable { + allowed_through_unstable_modules: Some(_), + .. + } => return true, + StabilityLevel::Stable { since, .. } => match since { + StableSince::Version(v) if !msrv.meets(cx, v) => return false, + StableSince::Current if msrv.current(cx).is_none() => return false, + StableSince::Err(_) => return false, + StableSince::Version(_) | StableSince::Current => {}, + }, + StabilityLevel::Unstable { .. } => return false, } } diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed index c27cec558242d..3020ed5c6f39d 100644 --- a/tests/ui/std_instead_of_core.fixed +++ b/tests/ui/std_instead_of_core.fixed @@ -96,3 +96,23 @@ fn issue15579() { let layout = alloc::Layout::new::(); } + +#[warn(clippy::std_instead_of_core)] +fn issue13158_core_io() { + // items moved from std::io into core::io are stable in an unstable module. + use std::io::ErrorKind; +} + +#[clippy::msrv = "1.40"] +fn issue13158_msrv_1_40(_: &dyn std::panic::UnwindSafe) {} + +#[clippy::msrv = "1.41"] +fn issue13158_msrv_1_41(_: &dyn core::panic::UnwindSafe) {} +//~^ std_instead_of_core + +#[clippy::msrv = "1.80"] +fn issue13158_msrv_1_80(_: &dyn std::error::Error) {} + +#[clippy::msrv = "1.81"] +fn issue13158_msrv_1_81(_: &dyn core::error::Error) {} +//~^ std_instead_of_core diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index 7d53f7fc3072b..49b4218aa8983 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -96,3 +96,23 @@ fn issue15579() { let layout = alloc::Layout::new::(); } + +#[warn(clippy::std_instead_of_core)] +fn issue13158_core_io() { + // items moved from std::io into core::io are stable in an unstable module. + use std::io::ErrorKind; +} + +#[clippy::msrv = "1.40"] +fn issue13158_msrv_1_40(_: &dyn std::panic::UnwindSafe) {} + +#[clippy::msrv = "1.41"] +fn issue13158_msrv_1_41(_: &dyn std::panic::UnwindSafe) {} +//~^ std_instead_of_core + +#[clippy::msrv = "1.80"] +fn issue13158_msrv_1_80(_: &dyn std::error::Error) {} + +#[clippy::msrv = "1.81"] +fn issue13158_msrv_1_81(_: &dyn std::error::Error) {} +//~^ std_instead_of_core diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index a5f8fbbe37cb8..0363852cf8d49 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -97,5 +97,17 @@ error: used import from `std` instead of `core` LL | fn msrv_1_77(_: std::net::IpAddr) {} | ^^^ help: consider importing the item from `core`: `core` -error: aborting due to 15 previous errors +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:110:33 + | +LL | fn issue13158_msrv_1_41(_: &dyn std::panic::UnwindSafe) {} + | ^^^ help: consider importing the item from `core`: `core` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:117:33 + | +LL | fn issue13158_msrv_1_81(_: &dyn std::error::Error) {} + | ^^^ help: consider importing the item from `core`: `core` + +error: aborting due to 17 previous errors diff --git a/tests/ui/std_instead_of_core_unfixable.rs b/tests/ui/std_instead_of_core_unfixable.rs index 5e71159ac6c5d..66d834b5e4279 100644 --- a/tests/ui/std_instead_of_core_unfixable.rs +++ b/tests/ui/std_instead_of_core_unfixable.rs @@ -14,3 +14,14 @@ fn issue15143() { //~^ std_instead_of_core //~| std_instead_of_alloc } + +#[rustfmt::skip] +fn pr16964() { + use std::{ + borrow::Cow, + //~^ std_instead_of_alloc + collections::BTreeSet, + //~^ std_instead_of_alloc + ffi::OsString, + }; +} diff --git a/tests/ui/std_instead_of_core_unfixable.stderr b/tests/ui/std_instead_of_core_unfixable.stderr index 147b46022126b..6fa8f47a4d6f4 100644 --- a/tests/ui/std_instead_of_core_unfixable.stderr +++ b/tests/ui/std_instead_of_core_unfixable.stderr @@ -26,5 +26,21 @@ LL | use std::{error::Error, vec::Vec, fs::File}; = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` -error: aborting due to 3 previous errors +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:21:17 + | +LL | borrow::Cow, + | ^^^ + | + = help: consider importing the item from `alloc` + +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:23:22 + | +LL | collections::BTreeSet, + | ^^^^^^^^ + | + = help: consider importing the item from `alloc` + +error: aborting due to 5 previous errors From 6667ea1ebb038edb7bcc14c53ed4a1e193a971c1 Mon Sep 17 00:00:00 2001 From: mistaste Date: Mon, 15 Jun 2026 04:19:48 +0300 Subject: [PATCH 066/278] Don't trigger `unnecessary_box_returns` when the size depends on generics The lint fired on return types such as `Box<[T; N]>`, suggesting to return the array unboxed. The size of such a type depends on generic parameters and cannot be computed here: `approx_ty_size` falls back to `0` for an array with a const- generic length or a generic element type, so the size check always passed. Use the type's layout directly and only lint when it can actually be computed. This keeps the existing behaviour for concrete types (and for generic types with a known layout such as `Box>`) while no longer suggesting to unbox a value whose size is unknown and potentially large. --- clippy_lints/src/unnecessary_box_returns.rs | 11 ++++++++--- tests/ui/unnecessary_box_returns.rs | 10 ++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/unnecessary_box_returns.rs b/clippy_lints/src/unnecessary_box_returns.rs index 8d4f1a153def8..4112e1b6ca63f 100644 --- a/clippy_lints/src/unnecessary_box_returns.rs +++ b/clippy_lints/src/unnecessary_box_returns.rs @@ -1,10 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::approx_ty_size; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::{FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; use rustc_span::Symbol; @@ -81,8 +81,13 @@ impl UnnecessaryBoxReturns { // It's sometimes useful to return Box if T is unsized, so don't lint those. // Also, don't lint if we know that T is very large, in which case returning - // a Box may be beneficial. - if boxed_ty.is_sized(cx.tcx, cx.typing_env()) && approx_ty_size(cx, boxed_ty) <= self.maximum_size { + // a Box may be beneficial. When the size depends on generic parameters + // (e.g. `[T; N]`) it cannot be determined here, so don't lint that either, as + // the `Box` may be a deliberate choice to avoid copying a large value. + if boxed_ty.is_sized(cx.tcx, cx.typing_env()) + && let Ok(layout) = cx.layout_of(boxed_ty) + && layout.size.bytes() <= self.maximum_size + { span_lint_and_then( cx, UNNECESSARY_BOX_RETURNS, diff --git a/tests/ui/unnecessary_box_returns.rs b/tests/ui/unnecessary_box_returns.rs index a7ab7e90be06b..b0a892e2e2157 100644 --- a/tests/ui/unnecessary_box_returns.rs +++ b/tests/ui/unnecessary_box_returns.rs @@ -71,6 +71,16 @@ impl HasHuge { } } +// don't lint (issue #17202): the size of `[T; N]` depends on a const generic parameter +fn const_generic_array(value: Box<[T; N]>) -> Box<[T; N]> { + value +} + +// don't lint (issue #17202): the size of `[T; 10]` depends on the generic element type +fn generic_element_array(value: Box<[T; 10]>) -> Box<[T; 10]> { + value +} + fn main() { // don't lint: this is a closure let a = || -> Box { Box::new(5) }; From 3054d6fbad391ba906de3537d1fd7855740e5e4c Mon Sep 17 00:00:00 2001 From: mistaste Date: Mon, 15 Jun 2026 14:14:14 +0300 Subject: [PATCH 067/278] Add test for Box> with known layout --- tests/ui/unnecessary_box_returns.rs | 7 +++++++ tests/ui/unnecessary_box_returns.stderr | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/ui/unnecessary_box_returns.rs b/tests/ui/unnecessary_box_returns.rs index b0a892e2e2157..e550996256f44 100644 --- a/tests/ui/unnecessary_box_returns.rs +++ b/tests/ui/unnecessary_box_returns.rs @@ -81,6 +81,13 @@ fn generic_element_array(value: Box<[T; 10]>) -> Box<[T; 10]> { value } +// lint: the size of `Vec` is known regardless of the generic element type +#[expect(clippy::box_collection)] +fn generic_vec(value: Box>) -> Box> { + //~^ unnecessary_box_returns + value +} + fn main() { // don't lint: this is a closure let a = || -> Box { Box::new(5) }; diff --git a/tests/ui/unnecessary_box_returns.stderr b/tests/ui/unnecessary_box_returns.stderr index ab1d90f13b922..8eb39fca5680b 100644 --- a/tests/ui/unnecessary_box_returns.stderr +++ b/tests/ui/unnecessary_box_returns.stderr @@ -32,5 +32,13 @@ LL | fn _bxed_foo() -> Box { | = help: changing this also requires a change to the return expressions in this function -error: aborting due to 4 previous errors +error: boxed return of the sized type `std::vec::Vec` + --> tests/ui/unnecessary_box_returns.rs:86:42 + | +LL | fn generic_vec(value: Box>) -> Box> { + | ^^^^^^^^^^^ help: try: `std::vec::Vec` + | + = help: changing this also requires a change to the return expressions in this function + +error: aborting due to 5 previous errors From cabc5a616a967e4b42f0a4ed25a1cfea058a548a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandra=20Gonz=C3=A1lez?= Date: Mon, 15 Jun 2026 13:47:24 +0200 Subject: [PATCH 068/278] Revert "feat: Add profile-specific configuration for disallowed methods and types" --- CHANGELOG.md | 1 - book/src/lint_configuration.md | 22 -- clippy_config/src/conf.rs | 211 +----------------- clippy_config/src/types.rs | 9 - clippy_lints/src/disallowed_methods.rs | 125 +---------- clippy_lints/src/disallowed_types.rs | 167 +++----------- clippy_utils/src/attrs.rs | 2 - clippy_utils/src/disallowed_profiles.rs | 180 --------------- clippy_utils/src/lib.rs | 1 - clippy_utils/src/sym.rs | 2 - .../disallowed_profiles_methods/clippy.toml | 13 -- .../disallowed_profiles_methods/main.rs | 78 ------- .../disallowed_profiles_methods/main.stderr | 72 ------ .../disallowed_profiles_types/clippy.toml | 13 -- .../ui-toml/disallowed_profiles_types/main.rs | 61 ----- .../disallowed_profiles_types/main.stderr | 78 ------- .../toml_unknown_key/conf_unknown_key.stderr | 3 - 17 files changed, 39 insertions(+), 999 deletions(-) delete mode 100644 clippy_utils/src/disallowed_profiles.rs delete mode 100644 tests/ui-toml/disallowed_profiles_methods/clippy.toml delete mode 100644 tests/ui-toml/disallowed_profiles_methods/main.rs delete mode 100644 tests/ui-toml/disallowed_profiles_methods/main.stderr delete mode 100644 tests/ui-toml/disallowed_profiles_types/clippy.toml delete mode 100644 tests/ui-toml/disallowed_profiles_types/main.rs delete mode 100644 tests/ui-toml/disallowed_profiles_types/main.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index d06a72d9c4155..8fb6356ff8e87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7609,7 +7609,6 @@ Released 2018-09-13 [`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit -[`profiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#profiles [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior [`recursive-self-in-type-definitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#recursive-self-in-type-definitions [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index e123d7ba5704d..dd10ef0538c25 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -986,28 +986,6 @@ The minimum size (in bytes) to consider a type for passing by reference instead * [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) -## `profiles` -Named profiles of disallowed items (unrelated to Cargo build profiles). - -#### Example - -```toml -[profiles.persistent] -disallowed-methods = [{ path = "std::env::temp_dir" }] -disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] - -[profiles.single_threaded] -disallowed-methods = [{ path = "std::thread::spawn" }] -``` - -**Default Value:** `{}` - ---- -**Affected lints:** -* [`disallowed_methods`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) -* [`disallowed_types`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) - - ## `pub-underscore-fields-behavior` Lint "public" fields in a struct that are prefixed with an underscore based on their exported visibility, or whether they are marked as "pub". diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 47bc7b572c9d0..5a06cb477c304 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -1,13 +1,12 @@ use crate::ClippyConfiguration; use crate::types::{ - DisallowedPath, DisallowedPathWithoutReplacement, DisallowedProfile, InherentImplLintScope, MacroMatcher, - MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, + DisallowedPath, DisallowedPathWithoutReplacement, InherentImplLintScope, MacroMatcher, MatchLintBehaviour, + PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; use itertools::Itertools; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; @@ -223,74 +222,12 @@ macro_rules! deserialize { }}; } -macro_rules! parse_conf_value { - ( - $map:expr, - $ty:ty, - $errors:expr, - $file:expr, - $field_span:expr, - profiles @[$($profiles:expr)?], - disallowed @[$($disallowed:expr)?] - ) => { - parse_conf_value_impl!( - $map, - $ty, - $errors, - $file, - $field_span, - ($($profiles)?), - ($($disallowed)?) - ) - }; -} - -macro_rules! parse_conf_value_impl { - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ()) => {{ - let _ = &$field_span; - deserialize!($map, $ty, $errors, $file) - }}; - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, ($profiles:expr), ()) => {{ - let raw_value = $map.next_value::()?; - let value_span = $field_span.clone(); - let toml::Value::Table(table) = raw_value else { - $errors.push(ConfError::spanned( - $file, - "expected table with named profiles", - None, - value_span.clone(), - )); - continue; - }; - - let map = parse_profiles(table, $file, value_span.clone(), &mut $errors); - - (map, value_span) - }}; - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ($disallowed:expr)) => {{ - let _ = &$field_span; - deserialize!($map, $ty, $errors, $file, $disallowed) - }}; - ( - $map:expr, - $ty:ty, - $errors:expr, - $file:expr, - $field_span:expr, - ($profiles:expr), - ($disallowed:expr) - ) => { - compile_error!("field cannot specify both profiles and disallowed-paths attributes") - }; -} - macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? $(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])? - $(#[profiles = $profiles:expr])? $(#[lints($($for_lints:ident),* $(,)?)])? $name:ident: $ty:ty = $default:expr, )*) => { @@ -345,20 +282,10 @@ macro_rules! define_Conf { match field { $(Field::$name => { - let field_span = name.span(); // Is this a deprecated field, i.e., is `$dep` set? If so, push a warning. $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)? - let (value, value_span) = parse_conf_value!( - map, - $ty, - errors, - self.0, - field_span, - // Disallowed-profile table parsing is special-cased to preserve spans for - // diagnostics in disallowed-path entries. - profiles @[$($profiles)?], - disallowed @[$($replacements_allowed)?] - ); + let (value, value_span) = + deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?); // Was this field set previously? if $name.is_some() { errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); @@ -415,121 +342,6 @@ fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { ) } -fn parse_profiles( - table: toml::value::Table, - file: &SourceFile, - value_span: Range, - errors: &mut Vec, -) -> FxHashMap { - let mut profiles = FxHashMap::default(); - let config_span = span_from_toml_range(file, value_span.clone()); - - for (profile_name, profile_value) in table { - let toml::Value::Table(mut profile_table) = profile_value else { - errors.push(ConfError::spanned( - file, - format!("invalid profile `{profile_name}`: expected table"), - None, - value_span.clone(), - )); - continue; - }; - - let disallowed_methods = match profile_table - .remove("disallowed-methods") - .or_else(|| profile_table.remove("disallowed_methods")) - { - Some(value) => parse_profile_list( - file, - &profile_name, - "disallowed-methods", - value, - value_span.clone(), - config_span, - errors, - ), - None => Vec::new(), - }; - - let disallowed_types = match profile_table - .remove("disallowed-types") - .or_else(|| profile_table.remove("disallowed_types")) - { - Some(value) => parse_profile_list( - file, - &profile_name, - "disallowed-types", - value, - value_span.clone(), - config_span, - errors, - ), - None => Vec::new(), - }; - - if !profile_table.is_empty() { - let keys = profile_table.keys().map(String::as_str).collect::>().join(", "); - errors.push(ConfError::spanned( - file, - format!("profile `{profile_name}` has unknown keys: {keys}"), - None, - value_span.clone(), - )); - } - - profiles.insert( - profile_name, - DisallowedProfile { - disallowed_methods, - disallowed_types, - }, - ); - } - - profiles -} - -fn parse_profile_list( - file: &SourceFile, - profile_name: &str, - key_name: &str, - value: toml::Value, - value_span: Range, - config_span: Span, - errors: &mut Vec, -) -> Vec { - let toml::Value::Array(entries) = value else { - errors.push(ConfError::spanned( - file, - format!("profile `{profile_name}`: `{key_name}` must be an array"), - None, - value_span, - )); - return Vec::new(); - }; - - let mut disallowed = Vec::with_capacity(entries.len()); - for entry in entries { - match DisallowedPath::deserialize(entry.clone()) { - Ok(mut path) => { - path.set_span(config_span); - disallowed.push(path); - }, - Err(err) => errors.push(ConfError::spanned( - file, - format!( - "profile `{profile_name}`: {}", - err.to_string().replace('\n', " ").trim() - ), - None, - value_span.clone(), - )), - } - } - - disallowed -} - define_Conf! { /// Which crates to allow absolute paths from #[lints(absolute_paths)] @@ -1029,21 +841,6 @@ define_Conf! { /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. #[lints(large_types_passed_by_value)] pass_by_value_size_limit: u64 = 256, - /// Named profiles of disallowed items (unrelated to Cargo build profiles). - /// - /// #### Example - /// - /// ```toml - /// [profiles.persistent] - /// disallowed-methods = [{ path = "std::env::temp_dir" }] - /// disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] - /// - /// [profiles.single_threaded] - /// disallowed-methods = [{ path = "std::thread::spawn" }] - /// ``` - #[profiles = true] - #[lints(disallowed_methods, disallowed_types)] - profiles: FxHashMap = FxHashMap::default(), /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". #[lints(pub_underscore_fields)] diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index 5eaa44ddd51d7..8d9326a904b1e 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -57,15 +57,6 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath, - #[serde(default, alias = "disallowed_types")] - pub disallowed_types: Vec, -} - // `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just // above. `DisallowedPathEnum` is not meant to be used outside of this file. #[derive(Debug, Deserialize, Serialize)] diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 58cc318d57ccd..e2fd71b7d990f 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,18 +1,13 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::PathNS; -use clippy_utils::sym; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::smallvec::SmallVec; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -60,19 +55,6 @@ declare_clippy_lint! { /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config. /// xs.push(123); // Vec::push is _not_ disallowed in the config. /// ``` - /// - /// Disallowed profiles allow scoping different disallow lists: - /// ```toml - /// [profiles.forward_pass] - /// disallowed-methods = [{ path = "crate::devices::Buffer::copy_to_host", reason = "Forward code must not touch host buffers" }] - /// ``` - /// - /// ```rust,ignore - /// #[clippy::disallowed_profile("forward_pass")] - /// fn evaluate() { - /// // Method calls in this function use the `forward_pass` profile. - /// } - /// ``` #[clippy::version = "1.49.0"] pub DISALLOWED_METHODS, style, @@ -82,22 +64,12 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); pub struct DisallowedMethods { - default: DefIdMap<(&'static str, &'static DisallowedPath)>, - /// Lookup per profile that declares a non-empty `disallowed_methods` list. Profiles - /// declared in `[profiles.*]` but without `disallowed_methods` entries are absent here. - profiles: FxHashMap>, - /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes - /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist - /// in config but only define entries for other lints (e.g. `disallowed_types`). - known_profiles: FxHashSet, - profile_cache: ProfileResolver, - warned_unknown_profiles: FxHashSet, + disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, } impl DisallowedMethods { - #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (default, _) = create_disallowed_map( + let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_methods, PathNS::Value, @@ -110,62 +82,7 @@ impl DisallowedMethods { "function", false, ); - - let mut profiles = FxHashMap::default(); - let mut known_profiles = FxHashSet::default(); - let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); - profile_entries.sort_by_key(|(a, _)| *a); - for (name, profile) in profile_entries { - let symbol = Symbol::intern(name.as_str()); - known_profiles.insert(symbol); - - let paths = profile.disallowed_methods.as_slice(); - if paths.is_empty() { - continue; - } - - let (map, _) = create_disallowed_map( - tcx, - paths, - PathNS::Value, - |def_kind| { - matches!( - def_kind, - DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn - ) - }, - "function", - false, - ); - profiles.insert(symbol, map); - } - - Self { - default, - profiles, - known_profiles, - profile_cache: ProfileResolver::default(), - warned_unknown_profiles: FxHashSet::default(), - } - } - - fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { - if self.warned_unknown_profiles.insert(entry.span) { - let attr_name = if entry.attr_name == sym::disallowed_profiles { - "clippy::disallowed_profiles" - } else { - "clippy::disallowed_profile" - }; - span_lint( - cx, - DISALLOWED_METHODS, - entry.span, - format!( - "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_methods`", - entry.name - ), - ); - } + Self { disallowed } } } @@ -181,43 +98,13 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }, _ => return, }; - let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); - // Copy entries out of the cache before iterating: `warn_unknown_profile` takes - // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. - let entries: SmallVec<[ProfileEntry; 2]> = self - .profile_cache - .active_profiles(cx, expr.hir_id) - .map(|selection| selection.iter().copied().collect()) - .unwrap_or_default(); - for entry in &entries { - if self.profiles.contains_key(&entry.name) { - active_profiles.push(entry.name); - } else if !self.known_profiles.contains(&entry.name) { - self.warn_unknown_profile(cx, entry); - } - } - - if let Some((profile, &(path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { - self.profiles - .get(symbol) - .and_then(|map| map.get(&id).map(|info| (*symbol, info))) - }) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_METHODS, - span, - format!("use of a disallowed method `{path}` (profile: {profile})"), - |diag| diag_amendment(diag), - ); - } else if let Some(&(path, disallowed_path)) = self.default.get(&id) { - let diag_amendment = disallowed_path.diag_amendment(span); + if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) { span_lint_and_then( cx, DISALLOWED_METHODS, span, format!("use of a disallowed method `{path}`"), - |diag| diag_amendment(diag), + disallowed_path.diag_amendment(span), ); } } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 37dd605617a7a..2c520d053f439 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,18 +1,15 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::PathNS; -use clippy_utils::sym; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::smallvec::SmallVec; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -54,17 +51,6 @@ declare_clippy_lint! { /// // A similar type that is allowed by the config /// use std::collections::HashMap; /// ``` - /// - /// Disallowed profiles can scope lists to specific modules: - /// ```toml - /// [profiles.forward_pass] - /// disallowed-types = [{ path = "crate::buffers::HostBuffer", reason = "Prefer device buffers in forward computations" }] - /// ``` - /// - /// ```rust,ignore - /// #[clippy::disallowed_profile("forward_pass")] - /// fn forward_step(buffer: crate::buffers::DeviceBuffer) { /* ... */ } - /// ``` #[clippy::version = "1.55.0"] pub DISALLOWED_TYPES, style, @@ -73,127 +59,37 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); -struct TypeLookup { +pub struct DisallowedTypes { def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>, prim_tys: FxHashMap, } -impl TypeLookup { - fn from_config(tcx: TyCtxt<'_>, paths: &'static [DisallowedPath]) -> Self { - let (def_ids, prim_tys) = create_disallowed_map(tcx, paths, PathNS::Type, def_kind_predicate, "type", true); - Self { def_ids, prim_tys } - } - - fn find(&self, res: &Res) -> Option<(&'static str, &'static DisallowedPath)> { - match res { - Res::Def(_, did) => self.def_ids.get(did).copied(), - Res::PrimTy(prim) => self.prim_tys.get(prim).copied(), - _ => None, - } - } -} - -pub struct DisallowedTypes { - default: TypeLookup, - /// Lookup per profile that declares a non-empty `disallowed_types` list. Profiles - /// declared in `[profiles.*]` but without `disallowed_types` entries are absent here. - profiles: FxHashMap, - /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes - /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist - /// in config but only define entries for other lints (e.g. `disallowed_methods`). - known_profiles: FxHashSet, - profile_cache: ProfileResolver, - warned_unknown_profiles: FxHashSet, -} - impl DisallowedTypes { - #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let default = TypeLookup::from_config(tcx, &conf.disallowed_types); - - let mut profiles = FxHashMap::default(); - let mut known_profiles = FxHashSet::default(); - let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); - profile_entries.sort_by_key(|(a, _)| *a); - for (name, profile) in profile_entries { - let symbol = Symbol::intern(name.as_str()); - known_profiles.insert(symbol); - - let paths = profile.disallowed_types.as_slice(); - if paths.is_empty() { - continue; - } - profiles.insert(symbol, TypeLookup::from_config(tcx, paths)); - } - - Self { - default, - profiles, - known_profiles, - profile_cache: ProfileResolver::default(), - warned_unknown_profiles: FxHashSet::default(), - } - } - - fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { - if self.warned_unknown_profiles.insert(entry.span) { - let attr_name = if entry.attr_name == sym::disallowed_profiles { - "clippy::disallowed_profiles" - } else { - "clippy::disallowed_profile" - }; - span_lint( - cx, - DISALLOWED_TYPES, - entry.span, - format!( - "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_types`", - entry.name - ), - ); - } + let (def_ids, prim_tys) = create_disallowed_map( + tcx, + &conf.disallowed_types, + PathNS::Type, + def_kind_predicate, + "type", + true, + ); + Self { def_ids, prim_tys } } - fn check_res_emit(&mut self, cx: &LateContext<'_>, hir_id: rustc_hir::HirId, res: &Res, span: Span) { - let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); - // Copy entries out of the cache before iterating: `warn_unknown_profile` takes - // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. - let entries: SmallVec<[ProfileEntry; 2]> = self - .profile_cache - .active_profiles(cx, hir_id) - .map(|selection| selection.iter().copied().collect()) - .unwrap_or_default(); - for entry in &entries { - if self.profiles.contains_key(&entry.name) { - active_profiles.push(entry.name); - } else if !self.known_profiles.contains(&entry.name) { - self.warn_unknown_profile(cx, entry); - } - } - - if let Some((profile, (path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { - self.profiles - .get(symbol) - .and_then(|lookup| lookup.find(res).map(|info| (*symbol, info))) - }) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("use of a disallowed type `{path}` (profile: {profile})"), - |diag| diag_amendment(diag), - ); - } else if let Some((path, disallowed_path)) = self.default.find(res) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("use of a disallowed type `{path}`"), - |diag| diag_amendment(diag), - ); - } + fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { + let (path, disallowed_path) = match res { + Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x, + Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x, + _ => return, + }; + span_lint_and_then( + cx, + DISALLOWED_TYPES, + span, + format!("use of a disallowed type `{path}`"), + disallowed_path.diag_amendment(span), + ); } } @@ -215,22 +111,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind && let Some(res) = path.res.type_ns { - self.check_res_emit(cx, item.hir_id(), &res, item.span); + self.check_res_emit(cx, &res, item.span); } } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Path(path) = &ty.kind { - self.check_res_emit(cx, ty.hir_id, &cx.qpath_res(path, ty.hir_id), ty.span); + self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span); } } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { - self.check_res_emit( - cx, - poly.trait_ref.hir_ref_id, - &poly.trait_ref.path.res, - poly.trait_ref.path.span, - ); + self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span); } } diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 2341007cd49e8..ca3ed73b84817 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -37,8 +37,6 @@ pub fn check_clippy_attr(sess: &Session, attr: &A) { | sym::version | sym::cognitive_complexity | sym::dump - | sym::disallowed_profile - | sym::disallowed_profiles | sym::msrv | sym::has_significant_drop | sym::format_args => {}, diff --git a/clippy_utils/src/disallowed_profiles.rs b/clippy_utils/src/disallowed_profiles.rs deleted file mode 100644 index 98a498981b3b5..0000000000000 --- a/clippy_utils/src/disallowed_profiles.rs +++ /dev/null @@ -1,180 +0,0 @@ -use crate::sym; -use rustc_ast::ast::{LitKind, MetaItemInner}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::smallvec::SmallVec; -use rustc_hir::{Attribute, HirId}; -use rustc_lint::LateContext; -use rustc_span::{Span, Symbol}; - -/// One profile name referenced by a `#[clippy::disallowed_profile(...)]` or -/// `#[clippy::disallowed_profiles(...)]` attribute on an item. -/// -/// A single attribute produces one `ProfileEntry` per string argument. The entry records which -/// attribute variant introduced it, the profile name, and the span of that string literal so -/// diagnostics (e.g. "unknown profile") can point at the exact argument. -#[derive(Copy, Clone)] -pub struct ProfileEntry { - pub attr_name: Symbol, - pub name: Symbol, - pub span: Span, -} - -/// The set of profiles active at some `HirId`, obtained by walking up the HIR from that id and -/// collecting the first ancestor that carries a `#[clippy::disallowed_profile(s)]` attribute. -/// -/// An empty selection is represented by `None` at the call site; a `ProfileSelection` is always -/// non-empty. -#[derive(Clone)] -pub struct ProfileSelection { - entries: SmallVec<[ProfileEntry; 2]>, -} - -impl ProfileSelection { - pub fn new(entries: SmallVec<[ProfileEntry; 2]>) -> Self { - Self { entries } - } - - pub fn is_empty(&self) -> bool { - self.entries.is_empty() - } - - pub fn iter(&self) -> impl Iterator { - self.entries.iter() - } -} - -#[derive(Default)] -pub struct ProfileResolver { - cache: FxHashMap>, -} - -impl ProfileResolver { - pub fn active_profiles(&mut self, cx: &LateContext<'_>, hir_id: HirId) -> Option<&ProfileSelection> { - // NOTE: The `contains_key`+`get` dance is intentional: using only `get` here triggers borrowck - // errors because we need to mutate `self.cache` on cache misses. - if self.cache.contains_key(&hir_id) { - return self.cache.get(&hir_id).and_then(|selection| selection.as_ref()); - } - - let (resolved, visited) = self.resolve(cx, hir_id); - - for id in visited { - self.cache.entry(id).or_insert_with(|| resolved.clone()); - } - self.cache.insert(hir_id, resolved); - - self.cache.get(&hir_id).and_then(|selection| selection.as_ref()) - } - - fn resolve(&self, cx: &LateContext<'_>, start: HirId) -> (Option, SmallVec<[HirId; 8]>) { - let mut visited = SmallVec::<[HirId; 8]>::new(); - let mut current = Some(start); - - while let Some(id) = current { - if let Some(cached) = self.cache.get(&id) { - return (cached.clone(), visited); - } - - visited.push(id); - - if let Some(selection) = profiles_from_attrs(cx, cx.tcx.hir_attrs(id)) { - return (Some(selection), visited); - } - - if id == rustc_hir::CRATE_HIR_ID { - current = None; - } else { - current = Some(cx.tcx.parent_hir_id(id)); - } - } - - (None, visited) - } -} - -fn profiles_from_attrs(cx: &LateContext<'_>, attrs: &[Attribute]) -> Option { - let mut entries = SmallVec::<[ProfileEntry; 2]>::new(); - - for attr in attrs { - let path = attr.path(); - if path.len() != 2 || path[0] != sym::clippy { - continue; - } - - let name = path[1]; - if name != sym::disallowed_profile && name != sym::disallowed_profiles { - continue; - } - - let attr_label = if name == sym::disallowed_profiles { - "`clippy::disallowed_profiles`" - } else { - "`clippy::disallowed_profile`" - }; - - let Some(items) = attr.meta_item_list() else { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), format!("{attr_label} expects string arguments")) - .emit(); - continue; - }; - - if items.is_empty() { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), format!("{attr_label} expects at least one profile name")) - .emit(); - continue; - } - - if name == sym::disallowed_profile && items.len() != 1 { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), "use `clippy::disallowed_profiles` for multiple profiles") - .emit(); - } - - for item in items { - match literal_symbol(&item) { - Some((symbol, span)) => entries.push(ProfileEntry { - attr_name: name, - name: symbol, - span, - }), - None => emit_string_error(cx, &item), - } - } - } - - if entries.is_empty() { - None - } else { - Some(ProfileSelection::new(entries)) - } -} - -fn literal_symbol(item: &MetaItemInner) -> Option<(Symbol, Span)> { - match item { - MetaItemInner::Lit(lit) => { - let LitKind::Str(symbol, _) = lit.kind else { return None }; - Some((symbol, lit.span)) - }, - MetaItemInner::MetaItem(_) => None, - } -} - -fn emit_string_error(cx: &LateContext<'_>, item: &MetaItemInner) { - let span = match item { - MetaItemInner::Lit(lit) => lit.span, - MetaItemInner::MetaItem(meta) => meta.span, - }; - cx.tcx - .sess - .dcx() - .struct_span_err(span, "expected string literal profile name") - .emit(); -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index a067e55595692..83acfb49e3556 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -46,7 +46,6 @@ mod check_proc_macro; pub mod comparisons; pub mod consts; pub mod diagnostics; -pub mod disallowed_profiles; pub mod eager_or_lazy; pub mod higher; mod hir_utils; diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index f414c0ac47fa4..fe8c62d3a0fa4 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -208,8 +208,6 @@ generate! { deprecated_in_future, deref_mut_method, diagnostics, - disallowed_profile, - disallowed_profiles, disallowed_types, drain, dump, diff --git a/tests/ui-toml/disallowed_profiles_methods/clippy.toml b/tests/ui-toml/disallowed_profiles_methods/clippy.toml deleted file mode 100644 index c77f5658ae69c..0000000000000 --- a/tests/ui-toml/disallowed_profiles_methods/clippy.toml +++ /dev/null @@ -1,13 +0,0 @@ -disallowed-methods = [ - { path = "std::mem::drop" } -] - -[profiles.forward_pass] -disallowed-methods = [ - { path = "alloc::vec::Vec::push", reason = "push is forbidden in forward profile" } -] - -[profiles.export] -disallowed-methods = [ - { path = "core::option::Option::unwrap" } -] diff --git a/tests/ui-toml/disallowed_profiles_methods/main.rs b/tests/ui-toml/disallowed_profiles_methods/main.rs deleted file mode 100644 index 07bb22e05d6f7..0000000000000 --- a/tests/ui-toml/disallowed_profiles_methods/main.rs +++ /dev/null @@ -1,78 +0,0 @@ -#![warn(clippy::disallowed_methods)] -#![allow( - unused, - clippy::no_effect, - clippy::needless_borrow, - clippy::vec_init_then_push, - clippy::unnecessary_literal_unwrap -)] - -fn default_violation() { - let value = String::from("test"); - std::mem::drop(value); //~ ERROR: use of a disallowed method `std::mem::drop` -} - -#[expect(clippy::disallowed_methods)] -fn expected_violation() { - let value = String::from("test"); - std::mem::drop(value); -} - -#[clippy::disallowed_profile("forward_pass")] -fn forward_profile() { - let mut values = Vec::new(); - values.push(1); //~ ERROR: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) -} - -#[clippy::disallowed_profile("export")] -fn export_profile() { - let value = Some(1); - value.unwrap(); //~ ERROR: use of a disallowed method `core::option::Option::unwrap` (profile: export) -} - -#[clippy::disallowed_profile("unknown_profile")] -//~^ ERROR: unknown profile `unknown_profile` for -//~| ERROR: unknown profile `unknown_profile` for -fn unknown_profile() { - let mut values = Vec::new(); - values.push(1); - // unknown profile falls back to the default list - std::mem::drop(values); //~ ERROR: use of a disallowed method `std::mem::drop` -} - -#[clippy::disallowed_profiles("forward_pass", "export")] -fn merged_profiles() { - let mut values = Vec::new(); - values.push(1); //~ ERROR: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - let value = Some(1); - value.unwrap(); //~ ERROR: use of a disallowed method `core::option::Option::unwrap` (profile: export) -} - -// `#[expect(clippy::disallowed_methods)]` silences the body warning and the unknown-profile -// warning tagged under `DISALLOWED_METHODS`, but not the one tagged under `DISALLOWED_TYPES`. -#[expect(clippy::disallowed_methods)] -#[clippy::disallowed_profile("unknown_profile_expect_before")] -//~^ ERROR: unknown profile `unknown_profile_expect_before` for `clippy::disallowed_types` -fn expect_before_unknown_profile() { - let value = String::from("test"); - std::mem::drop(value); -} - -#[clippy::disallowed_profile("unknown_profile_expect_after")] -//~^ ERROR: unknown profile `unknown_profile_expect_after` for `clippy::disallowed_types` -#[expect(clippy::disallowed_methods)] -fn expect_after_unknown_profile() { - let value = String::from("test"); - std::mem::drop(value); -} - -fn main() { - default_violation(); - expected_violation(); - forward_profile(); - export_profile(); - unknown_profile(); - merged_profiles(); - expect_before_unknown_profile(); - expect_after_unknown_profile(); -} diff --git a/tests/ui-toml/disallowed_profiles_methods/main.stderr b/tests/ui-toml/disallowed_profiles_methods/main.stderr deleted file mode 100644 index 5f64b1bf4499b..0000000000000 --- a/tests/ui-toml/disallowed_profiles_methods/main.stderr +++ /dev/null @@ -1,72 +0,0 @@ -error: use of a disallowed method `std::mem::drop` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:12:5 - | -LL | std::mem::drop(value); - | ^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-methods` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` - -error: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:24:12 - | -LL | values.push(1); - | ^^^^ - | - = note: push is forbidden in forward profile - -error: use of a disallowed method `core::option::Option::unwrap` (profile: export) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:30:11 - | -LL | value.unwrap(); - | ^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:33:30 - | -LL | #[clippy::disallowed_profile("unknown_profile")] - | ^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:33:30 - | -LL | #[clippy::disallowed_profile("unknown_profile")] - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-types` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_types)]` - -error: use of a disallowed method `std::mem::drop` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:40:5 - | -LL | std::mem::drop(values); - | ^^^^^^^^^^^^^^ - -error: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:46:12 - | -LL | values.push(1); - | ^^^^ - | - = note: push is forbidden in forward profile - -error: use of a disallowed method `core::option::Option::unwrap` (profile: export) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:48:11 - | -LL | value.unwrap(); - | ^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile_expect_before` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:54:30 - | -LL | #[clippy::disallowed_profile("unknown_profile_expect_before")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile_expect_after` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:61:30 - | -LL | #[clippy::disallowed_profile("unknown_profile_expect_after")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 10 previous errors - diff --git a/tests/ui-toml/disallowed_profiles_types/clippy.toml b/tests/ui-toml/disallowed_profiles_types/clippy.toml deleted file mode 100644 index 3c47b2afe26fe..0000000000000 --- a/tests/ui-toml/disallowed_profiles_types/clippy.toml +++ /dev/null @@ -1,13 +0,0 @@ -disallowed-types = [ - { path = "std::rc::Rc" } -] - -[profiles.forward_pass] -disallowed-types = [ - { path = "std::cell::RefCell", reason = "Prefer shared references" } -] - -[profiles.export] -disallowed-types = [ - { path = "std::sync::Mutex" } -] diff --git a/tests/ui-toml/disallowed_profiles_types/main.rs b/tests/ui-toml/disallowed_profiles_types/main.rs deleted file mode 100644 index 0161417dd6022..0000000000000 --- a/tests/ui-toml/disallowed_profiles_types/main.rs +++ /dev/null @@ -1,61 +0,0 @@ -#![warn(clippy::disallowed_types)] -#![allow(dead_code)] - -use std::rc::Rc; //~ ERROR: use of a disallowed type `std::rc::Rc` -use std::sync::Mutex; - -struct Wrapper; - -fn default_type() { - let _value: Rc = todo!(); //~ ERROR: use of a disallowed type `std::rc::Rc` -} - -#[clippy::disallowed_profile("forward_pass")] -fn forward_profile() { - let _value: std::cell::RefCell = todo!(); //~ ERROR: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) -} - -#[clippy::disallowed_profile("export")] -fn export_profile() { - let _value: Mutex = todo!(); //~ ERROR: use of a disallowed type `std::sync::Mutex` (profile: export) -} - -#[clippy::disallowed_profile("unknown_type_profile")] -//~^ ERROR: unknown profile `unknown_type_profile` for -//~| ERROR: unknown profile `unknown_type_profile` for -fn unknown_profile() { - let _other = 1u32; - let _fallback: Rc = todo!(); //~ ERROR: use of a disallowed type `std::rc::Rc` -} - -#[clippy::disallowed_profiles("forward_pass", "export")] -fn merged_profiles() { - let _value: std::cell::RefCell = todo!(); //~ ERROR: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - let _other: Mutex = todo!(); //~ ERROR: use of a disallowed type `std::sync::Mutex` (profile: export) -} - -// `#[expect(clippy::disallowed_types)]` silences the body warning and the unknown-profile -// warning tagged under `DISALLOWED_TYPES`, but not one tagged under `DISALLOWED_METHODS`. -#[expect(clippy::disallowed_types)] -#[clippy::disallowed_profile("unknown_type_profile_expect_before")] -//~^ ERROR: unknown profile `unknown_type_profile_expect_before` for `clippy::disallowed_methods` -fn expect_before_unknown_profile() { - let _value: Rc = todo!(); -} - -#[clippy::disallowed_profile("unknown_type_profile_expect_after")] -//~^ ERROR: unknown profile `unknown_type_profile_expect_after` for `clippy::disallowed_methods` -#[expect(clippy::disallowed_types)] -fn expect_after_unknown_profile() { - let _value: Rc = todo!(); -} - -fn main() { - default_type(); - forward_profile(); - export_profile(); - unknown_profile(); - merged_profiles(); - expect_before_unknown_profile(); - expect_after_unknown_profile(); -} diff --git a/tests/ui-toml/disallowed_profiles_types/main.stderr b/tests/ui-toml/disallowed_profiles_types/main.stderr deleted file mode 100644 index 68d63e218c0de..0000000000000 --- a/tests/ui-toml/disallowed_profiles_types/main.stderr +++ /dev/null @@ -1,78 +0,0 @@ -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:4:1 - | -LL | use std::rc::Rc; - | ^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-types` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_types)]` - -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:10:17 - | -LL | let _value: Rc = todo!(); - | ^^^^^^^ - -error: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_types/main.rs:15:17 - | -LL | let _value: std::cell::RefCell = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: Prefer shared references - -error: use of a disallowed type `std::sync::Mutex` (profile: export) - --> tests/ui-toml/disallowed_profiles_types/main.rs:20:17 - | -LL | let _value: Mutex = todo!(); - | ^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:23:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile")] - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-methods` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_types/main.rs:23:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile")] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:28:20 - | -LL | let _fallback: Rc = todo!(); - | ^^^^^^^ - -error: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_types/main.rs:33:17 - | -LL | let _value: std::cell::RefCell = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: Prefer shared references - -error: use of a disallowed type `std::sync::Mutex` (profile: export) - --> tests/ui-toml/disallowed_profiles_types/main.rs:34:17 - | -LL | let _other: Mutex = todo!(); - | ^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile_expect_before` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:40:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile_expect_before")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile_expect_after` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:46:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile_expect_after")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 11 previous errors - diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index ba930d094fc19..6bb3db8db67f0 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -70,7 +70,6 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline @@ -172,7 +171,6 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline @@ -274,7 +272,6 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline From 491d90b79dcaa40abcc5e33e05c9675dac585ecf Mon Sep 17 00:00:00 2001 From: Hamdan-Khan Date: Mon, 15 Jun 2026 16:49:25 +0500 Subject: [PATCH 069/278] Do not trigger `ref_patterns` lint on automatically derived code Code marked with `#[automatically_derived]` attribute is macro generated and its syntax is out of user's control. Since `ref_patterns` is a lint for clarity, it should not be triggered for auto derived code. Fixed by converting the lint into a late pass lint and checking the auto derived attribute using `in_automatically_derived` utility. --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/ref_patterns.rs | 12 +++++++----- tests/ui/ref_patterns.rs | 10 ++++++++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4a7659a9cc210..600b37c4204c0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -534,7 +534,6 @@ rustc_lint::early_lint_methods!( PartialPubFields: partial_pub_fields::PartialPubFields = partial_pub_fields::PartialPubFields, UnderscoreTyped: let_with_type_underscore::UnderscoreTyped = let_with_type_underscore::UnderscoreTyped, ExcessiveNesting: excessive_nesting::ExcessiveNesting = excessive_nesting::ExcessiveNesting::new(conf), - RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns, NeedlessElse: needless_else::NeedlessElse = needless_else::NeedlessElse, RawStrings: raw_strings::RawStrings = raw_strings::RawStrings::new(conf), Visibility: visibility::Visibility = visibility::Visibility, @@ -862,6 +861,7 @@ rustc_lint::late_lint_methods!( ByteCharSlice: byte_char_slices::ByteCharSlice = byte_char_slices::ByteCharSlice, ManualAssertEq: manual_assert_eq::ManualAssertEq = manual_assert_eq::ManualAssertEq, WithCapacityZero: with_capacity_zero::WithCapacityZero = with_capacity_zero::WithCapacityZero, + RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns, // add late passes here, used by `cargo dev new_lint` ]] ); diff --git a/clippy_lints/src/ref_patterns.rs b/clippy_lints/src/ref_patterns.rs index b6a28571230a6..22d1c21aa43ba 100644 --- a/clippy_lints/src/ref_patterns.rs +++ b/clippy_lints/src/ref_patterns.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast::{BindingMode, Pat, PatKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use clippy_utils::in_automatically_derived; +use rustc_hir::{BindingMode, Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -29,10 +30,11 @@ declare_clippy_lint! { declare_lint_pass!(RefPatterns => [REF_PATTERNS]); -impl EarlyLintPass for RefPatterns { - fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::Ident(BindingMode::REF, _, _) = pat.kind +impl<'tcx> LateLintPass<'tcx> for RefPatterns { + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'tcx>) { + if let PatKind::Binding(BindingMode::REF, _, _, _) = pat.kind && !pat.span.from_expansion() + && !in_automatically_derived(cx.tcx, pat.hir_id) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, REF_PATTERNS, pat.span, "usage of ref pattern", |diag| { diff --git a/tests/ui/ref_patterns.rs b/tests/ui/ref_patterns.rs index 2905af13ccb63..3628cc3d537c0 100644 --- a/tests/ui/ref_patterns.rs +++ b/tests/ui/ref_patterns.rs @@ -19,4 +19,14 @@ fn use_in_binding() { fn use_in_parameter(ref x: i32) {} //~^ ref_patterns +struct Foo {} + +// shouldn't trigger the lint +#[automatically_derived] +impl Foo { + fn foo() { + if let Some(ref x) = Some(1) {} + } +} + fn main() {} From e5c59b3016d8f3ae7b41e7e1c389b08c41df1881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Mon, 25 May 2026 21:36:18 +0200 Subject: [PATCH 070/278] libc: Minimal support for `mprotect` and `madvise` We still only support `PROT_READ|PROT_WRITE`, so `mprotect` is a no-op other than validating arguments. We only implement `madvise` for the hints that can be ignored without a change in semantic (e.g. no `MADV_DONTNEED`, so it is also a no-op other than validating arguments. --- .../miri/src/shims/unix/foreign_items.rs | 38 +++++- src/tools/miri/src/shims/unix/mem.rs | 125 +++++++++++++++--- .../fail-dep/libc/madvise_out_of_bounds.rs | 20 +++ .../libc/madvise_out_of_bounds.stderr | 13 ++ .../fail-dep/libc/mprotect_out_of_bounds.rs | 20 +++ .../libc/mprotect_out_of_bounds.stderr | 13 ++ src/tools/miri/tests/pass-dep/libc/mmap.rs | 74 +++++++++++ 7 files changed, 278 insertions(+), 25 deletions(-) create mode 100644 src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr create mode 100644 src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index fa2af98b9fc8d..e7aa80d279d15 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -816,18 +816,46 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "mmap" => { - let [addr, length, prot, flags, fd, offset] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [addr, length, prot, flags, fd, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32, i32, i32, libc::off_t) -> *mut _), + link_name, + abi, + args, + )?; let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?; let ptr = this.mmap(addr, length, prot, flags, fd, offset)?; this.write_scalar(ptr, dest)?; } "munmap" => { - let [addr, length] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [addr, length] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize) -> i32), + link_name, + abi, + args, + )?; let result = this.munmap(addr, length)?; this.write_scalar(result, dest)?; } + "mprotect" => { + let [addr, length, prot] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32), + link_name, + abi, + args, + )?; + let result = this.mprotect(addr, length, prot)?; + this.write_scalar(result, dest)?; + } + "madvise" => { + let [addr, length, advice] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32), + link_name, + abi, + args, + )?; + let result = this.madvise(addr, length, advice)?; + this.write_scalar(result, dest)?; + } "reallocarray" => { // Currently this function does not exist on all Unixes, e.g. on macOS. @@ -1386,7 +1414,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [_, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } - "sigaction" | "mprotect" if this.frame_in_std() => { + "sigaction" if this.frame_in_std() => { let [_, _, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index c2ad7c0e9d0ab..50be3c7ae15e6 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -53,9 +53,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(Scalar::from_maybe_pointer(Pointer::without_provenance(addr), this)); } - let prot_read = this.eval_libc_i32("PROT_READ"); - let prot_write = this.eval_libc_i32("PROT_WRITE"); - // First, we do some basic argument validation as required by mmap if (flags & (map_private | map_shared)).count_ones() != 1 { this.set_last_error(LibcError("EINVAL"))?; @@ -80,13 +77,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - // Miri doesn't support protections other than PROT_READ|PROT_WRITE. - if prot != prot_read | prot_write { - throw_unsup_format!( - "Miri does not support calls to mmap with protections other than \ - PROT_READ|PROT_WRITE", - ); - } + verify_prot(this, prot)?; // Miri does not support shared mappings, or any of the other extensions that for example // Linux has added to the flags arguments. @@ -103,14 +94,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let align = this.machine.page_align(); - let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else { + let Some(map_length) = round_up_to_page_size(this, length) else { this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); }; - if map_length > this.target_usize_max() { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(this.eval_libc("MAP_FAILED")); - } let ptr = this.allocate_ptr( Size::from_bytes(map_length), @@ -135,13 +122,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); } - let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else { + let Some(length) = round_up_to_page_size(this, length) else { return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); }; - if length > this.target_usize_max() { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(this.eval_libc("MAP_FAILED")); - } let length = Size::from_bytes(length); this.deallocate_ptr( @@ -152,4 +135,106 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(0)) } + + fn mprotect( + &mut self, + addr: &OpTy<'tcx>, + length: &OpTy<'tcx>, + prot: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let addr = this.read_pointer(addr)?; + let length = this.read_target_usize(length)?; + let prot = this.read_scalar(prot)?.to_i32()?; + + // addr must be a multiple of the page size. + if !addr.addr().bytes().is_multiple_of(this.machine.page_size) { + return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); + } + + verify_prot(this, prot)?; + + // The pages from `[addr, addr + length)` must be mapped, so length definitely must not overflow. + let Some(length) = round_up_to_page_size(this, length) else { + return this.set_errno_and_return_neg1_i32(LibcError("ENOMEM")); + }; + // Ensure this is actually allocated memory we can access. + this.check_ptr_access(addr, Size::from_bytes(length), CheckInAllocMsg::MemoryAccess) + .map_err_kind(|_| err_ub_format!("`mprotect` called on out-of-bounds memory"))?; + + // If the memory comes from memory the Rust program has allocated with mmap, we only support + // `PROT_READ|PROT_WRITE`, so this `mprotect` is a no-op. If the memory was mmaped by the + // runtime (e.g. if it's the stack, executable memory, or static memory), POSIX also allows + // us to remap it. In those cases, such a call to `PROT_READ|PROT_WRITE` might actually change the permissions, + // but treating them as the new permissions is still UB. Therefore, we just pretend that we + // did the permission change by returning success, and will still reject if you try to use + // it with the "new" permissions. + interp_ok(Scalar::from_i32(0)) + } + + fn madvise( + &mut self, + addr: &OpTy<'tcx>, + length: &OpTy<'tcx>, + advice: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let addr = this.read_pointer(addr)?; + let length = this.read_target_usize(length)?; + let advise = this.read_scalar(advice)?.to_i32()?; + + // addr must be a multiple of the page size. + if !addr.addr().bytes().is_multiple_of(this.machine.page_size) { + return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); + } + + // advise must be supported. + let madv_normal = this.eval_libc_i32("MADV_NORMAL"); + let madv_random = this.eval_libc_i32("MADV_RANDOM"); + let madv_sequential = this.eval_libc_i32("MADV_SEQUENTIAL"); + let madv_willneed = this.eval_libc_i32("MADV_WILLNEED"); + if advise != madv_normal + && advise != madv_random + && advise != madv_sequential + && advise != madv_willneed + { + throw_unsup_format!( + "Miri does not support calls to madvise with advice other than MADV_NORMAL, MADV_RANDOM, MADV_SEQUENTIAL, or MADV_WILLNEED", + ); + } + + // The pages from `[addr, addr + length)` must be mapped, so length definitely must not overflow. + let Some(length) = round_up_to_page_size(this, length) else { + return this.set_errno_and_return_neg1_i32(LibcError("ENOMEM")); + }; + // Ensure this is actually allocated memory we can access. + this.check_ptr_access(addr, Size::from_bytes(length), CheckInAllocMsg::MemoryAccess) + .map_err_kind(|_| err_ub_format!("`madvise` called on out-of-bounds memory"))?; + + // All advises we support are no-ops. + interp_ok(Scalar::from_i32(0)) + } +} + +fn round_up_to_page_size(this: &MiriInterpCx<'_>, length: u64) -> Option { + length + .checked_next_multiple_of(this.machine.page_size) + .filter(|length| *length <= this.target_isize_max().try_into().unwrap()) +} + +fn verify_prot<'tcx>(this: &mut MiriInterpCx<'tcx>, prot: i32) -> InterpResult<'tcx> { + let prot_read = this.eval_libc_i32("PROT_READ"); + let prot_write = this.eval_libc_i32("PROT_WRITE"); + + // Miri doesn't support protections other than PROT_READ|PROT_WRITE. + if prot != prot_read | prot_write { + throw_unsup_format!( + "Miri does not support calls to mmap/mprotect with protections other than \ + PROT_READ|PROT_WRITE", + ); + } + + interp_ok(()) } diff --git a/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs new file mode 100644 index 0000000000000..29d804fd0d99a --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs @@ -0,0 +1,20 @@ +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target: windows # No mmap on Windows +//@normalize-stderr-test: "only .*? bytes" -> "only SIZE bytes" + +fn main() { + unsafe { + let page_size = page_size::get(); + let ptr = libc::mmap( + std::ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + assert!(!ptr.is_null()); + + libc::madvise(ptr, page_size + 1, libc::MADV_NORMAL); //~ ERROR: `madvise` called on out-of-bounds memory + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr new file mode 100644 index 0000000000000..dbf38c7e4dd0f --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: `madvise` called on out-of-bounds memory + --> tests/fail-dep/libc/madvise_out_of_bounds.rs:LL:CC + | +LL | libc::madvise(ptr, page_size + 1, libc::MADV_NORMAL); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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: 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-dep/libc/mprotect_out_of_bounds.rs b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs new file mode 100644 index 0000000000000..1e6868b714a81 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs @@ -0,0 +1,20 @@ +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target: windows # No mmap on Windows +//@normalize-stderr-test: "only .*? bytes" -> "only SIZE bytes" + +fn main() { + unsafe { + let page_size = page_size::get(); + let ptr = libc::mmap( + std::ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + assert!(!ptr.is_null()); + + libc::mprotect(ptr, page_size + 1, libc::PROT_READ | libc::PROT_WRITE); //~ ERROR: `mprotect` called on out-of-bounds memory + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr new file mode 100644 index 0000000000000..ac48880150486 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: `mprotect` called on out-of-bounds memory + --> tests/fail-dep/libc/mprotect_out_of_bounds.rs:LL:CC + | +LL | libc::mprotect(ptr, page_size + 1, libc::PROT_READ | libc::PROT_WRITE); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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: 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/pass-dep/libc/mmap.rs b/src/tools/miri/tests/pass-dep/libc/mmap.rs index bfd840d2fb89d..692b59d443ae8 100644 --- a/src/tools/miri/tests/pass-dep/libc/mmap.rs +++ b/src/tools/miri/tests/pass-dep/libc/mmap.rs @@ -94,6 +94,78 @@ fn test_mmap( assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); } +fn test_mprotect() { + let page_size = page_size::get(); + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + 4 * page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + Default::default(), + ) + }; + assert!(!ptr.is_null()); + + // Protect part of it redundantly. + let res = unsafe { + libc::mprotect(ptr.byte_add(2 * page_size), 42, libc::PROT_READ | libc::PROT_WRITE) + }; + assert_eq!(res, 0i32); + + // Protect everything redundantly. + let res = unsafe { libc::mprotect(ptr, 4 * page_size, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, 0i32); + + // We report an error when the address is not a multiple of the page size. + let res = + unsafe { libc::mprotect(ptr.byte_add(11), page_size, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + // We report an error if the length cannot be rounded up to a multiple of the page size. + let res = unsafe { libc::mprotect(ptr, usize::MAX - 1, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOMEM); +} + +fn test_madvise() { + let page_size = page_size::get(); + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + 4 * page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + Default::default(), + ) + }; + assert!(!ptr.is_null()); + + for advice in [libc::MADV_NORMAL, libc::MADV_RANDOM, libc::MADV_SEQUENTIAL, libc::MADV_WILLNEED] + { + // Advise part of it redundantly. + let res = unsafe { libc::madvise(ptr.byte_add(2 * page_size), 42, advice) }; + assert_eq!(res, 0i32); + + // Protect everything redundantly. + let res = unsafe { libc::madvise(ptr, 4 * page_size, advice) }; + assert_eq!(res, 0i32); + } + + // We report an error when the address is not a multiple of the page size. + let res = unsafe { libc::madvise(ptr.byte_add(11), page_size, libc::MADV_NORMAL) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + // We report an error if the length cannot be rounded up to a multiple of the page size. + let res = unsafe { libc::madvise(ptr, usize::MAX - 1, libc::MADV_NORMAL) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOMEM); +} + #[cfg(target_os = "linux")] fn test_mremap() { let page_size = page_size::get(); @@ -145,6 +217,8 @@ fn main() { test_mmap(libc::mmap); #[cfg(target_os = "linux")] test_mmap(libc::mmap64); + test_mprotect(); + test_madvise(); #[cfg(target_os = "linux")] test_mremap(); } From 3f5f9818547d15991d589dc63248836ac2ee8cb5 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 1 Jun 2026 12:44:41 +0000 Subject: [PATCH 071/278] arm-intrinsics: `svget{2,3,4}` These intrinsics need `Arguments_Preparation` added so that the intrinsic-test tool knows to generate const arguments. --- .../intrinsics_data/arm_intrinsics.json | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/library/stdarch/intrinsics_data/arm_intrinsics.json b/library/stdarch/intrinsics_data/arm_intrinsics.json index fab6da7f2c16c..582de29741587 100644 --- a/library/stdarch/intrinsics_data/arm_intrinsics.json +++ b/library/stdarch/intrinsics_data/arm_intrinsics.json @@ -51174,6 +51174,12 @@ "svfloat16x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat16_t" }, @@ -51188,6 +51194,12 @@ "svfloat32x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat32_t" }, @@ -51202,6 +51214,12 @@ "svfloat64x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat64_t" }, @@ -51216,6 +51234,12 @@ "svint16x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint16_t" }, @@ -51230,6 +51254,12 @@ "svint32x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint32_t" }, @@ -51244,6 +51274,12 @@ "svint64x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint64_t" }, @@ -51258,6 +51294,12 @@ "svint8x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint8_t" }, @@ -51272,6 +51314,12 @@ "svuint16x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint16_t" }, @@ -51286,6 +51334,12 @@ "svuint32x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint32_t" }, @@ -51300,6 +51354,12 @@ "svuint64x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint64_t" }, @@ -51314,6 +51374,12 @@ "svuint8x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint8_t" }, @@ -51328,6 +51394,12 @@ "svfloat16x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat16_t" }, @@ -51342,6 +51414,12 @@ "svfloat32x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat32_t" }, @@ -51356,6 +51434,12 @@ "svfloat64x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat64_t" }, @@ -51370,6 +51454,12 @@ "svint16x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint16_t" }, @@ -51384,6 +51474,12 @@ "svint32x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint32_t" }, @@ -51398,6 +51494,12 @@ "svint64x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint64_t" }, @@ -51412,6 +51514,12 @@ "svint8x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint8_t" }, @@ -51426,6 +51534,12 @@ "svuint16x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint16_t" }, @@ -51440,6 +51554,12 @@ "svuint32x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint32_t" }, @@ -51454,6 +51574,12 @@ "svuint64x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint64_t" }, @@ -51468,6 +51594,12 @@ "svuint8x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint8_t" }, @@ -51482,6 +51614,12 @@ "svfloat16x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat16_t" }, @@ -51496,6 +51634,12 @@ "svfloat32x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat32_t" }, @@ -51510,6 +51654,12 @@ "svfloat64x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat64_t" }, @@ -51524,6 +51674,12 @@ "svint16x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint16_t" }, @@ -51538,6 +51694,12 @@ "svint32x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint32_t" }, @@ -51552,6 +51714,12 @@ "svint64x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint64_t" }, @@ -51566,6 +51734,12 @@ "svint8x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint8_t" }, @@ -51580,6 +51754,12 @@ "svuint16x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint16_t" }, @@ -51594,6 +51774,12 @@ "svuint32x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint32_t" }, @@ -51608,6 +51794,12 @@ "svuint64x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint64_t" }, @@ -51622,6 +51814,12 @@ "svuint8x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint8_t" }, From 992cb1d9de25ea8181dc2ea2403b91d6306f4868 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 1 Jun 2026 12:44:41 +0000 Subject: [PATCH 072/278] arm-intrinsics: `svset{2,3,4}` These intrinsics need `Arguments_Preparation` added so that the intrinsic-test tool knows to generate const arguments. --- .../intrinsics_data/arm_intrinsics.json | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/library/stdarch/intrinsics_data/arm_intrinsics.json b/library/stdarch/intrinsics_data/arm_intrinsics.json index 582de29741587..7f749fe4d8842 100644 --- a/library/stdarch/intrinsics_data/arm_intrinsics.json +++ b/library/stdarch/intrinsics_data/arm_intrinsics.json @@ -163197,6 +163197,12 @@ "uint64_t imm_index", "svfloat16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat16x2_t" }, @@ -163212,6 +163218,12 @@ "uint64_t imm_index", "svfloat32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat32x2_t" }, @@ -163227,6 +163239,12 @@ "uint64_t imm_index", "svfloat64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat64x2_t" }, @@ -163242,6 +163260,12 @@ "uint64_t imm_index", "svint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint16x2_t" }, @@ -163257,6 +163281,12 @@ "uint64_t imm_index", "svint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint32x2_t" }, @@ -163272,6 +163302,12 @@ "uint64_t imm_index", "svint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint64x2_t" }, @@ -163287,6 +163323,12 @@ "uint64_t imm_index", "svint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint8x2_t" }, @@ -163302,6 +163344,12 @@ "uint64_t imm_index", "svuint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint16x2_t" }, @@ -163317,6 +163365,12 @@ "uint64_t imm_index", "svuint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint32x2_t" }, @@ -163332,6 +163386,12 @@ "uint64_t imm_index", "svuint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint64x2_t" }, @@ -163347,6 +163407,12 @@ "uint64_t imm_index", "svuint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint8x2_t" }, @@ -163362,6 +163428,12 @@ "uint64_t imm_index", "svfloat16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat16x3_t" }, @@ -163377,6 +163449,12 @@ "uint64_t imm_index", "svfloat32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat32x3_t" }, @@ -163392,6 +163470,12 @@ "uint64_t imm_index", "svfloat64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat64x3_t" }, @@ -163407,6 +163491,12 @@ "uint64_t imm_index", "svint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint16x3_t" }, @@ -163422,6 +163512,12 @@ "uint64_t imm_index", "svint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint32x3_t" }, @@ -163437,6 +163533,12 @@ "uint64_t imm_index", "svint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint64x3_t" }, @@ -163452,6 +163554,12 @@ "uint64_t imm_index", "svint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint8x3_t" }, @@ -163467,6 +163575,12 @@ "uint64_t imm_index", "svuint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint16x3_t" }, @@ -163482,6 +163596,12 @@ "uint64_t imm_index", "svuint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint32x3_t" }, @@ -163497,6 +163617,12 @@ "uint64_t imm_index", "svuint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint64x3_t" }, @@ -163512,6 +163638,12 @@ "uint64_t imm_index", "svuint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint8x3_t" }, @@ -163527,6 +163659,12 @@ "uint64_t imm_index", "svfloat16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat16x4_t" }, @@ -163542,6 +163680,12 @@ "uint64_t imm_index", "svfloat32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat32x4_t" }, @@ -163557,6 +163701,12 @@ "uint64_t imm_index", "svfloat64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat64x4_t" }, @@ -163572,6 +163722,12 @@ "uint64_t imm_index", "svint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint16x4_t" }, @@ -163587,6 +163743,12 @@ "uint64_t imm_index", "svint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint32x4_t" }, @@ -163602,6 +163764,12 @@ "uint64_t imm_index", "svint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint64x4_t" }, @@ -163617,6 +163785,12 @@ "uint64_t imm_index", "svint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint8x4_t" }, @@ -163632,6 +163806,12 @@ "uint64_t imm_index", "svuint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint16x4_t" }, @@ -163647,6 +163827,12 @@ "uint64_t imm_index", "svuint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint32x4_t" }, @@ -163662,6 +163848,12 @@ "uint64_t imm_index", "svuint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint64x4_t" }, @@ -163677,6 +163869,12 @@ "uint64_t imm_index", "svuint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint8x4_t" }, From 35e9bf6555e4d45a982e82c7eb67fa62b7713fa4 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 15 Jun 2026 10:20:39 +0000 Subject: [PATCH 073/278] core_arch: redefine `svrev_b{16,32,64}` Clang uses the `llvm.aarch64.sve.rev.bN` intrinsic for `svrev` with `b16`, `b32` and `b64`. This required small generator changes so it knew a bool-to-bool conversion was a no-op and a new blanket identity impl of `SveInto` so the calls generated compile. --- .../core_arch/src/aarch64/sve/generated.rs | 44 +++++++++---------- .../crates/core_arch/src/aarch64/sve/mod.rs | 8 ++++ .../stdarch-gen-arm/spec/sve/aarch64.spec.yml | 9 ++-- .../crates/stdarch-gen-arm/src/intrinsic.rs | 1 + 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs index 116adcf746802..42d2d1c5b899b 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs @@ -35226,19 +35226,6 @@ pub fn svreinterpret_u64_u64(op: svuint64_t) -> svuint64_t { unsafe { crate::intrinsics::transmute_unchecked(op) } } #[doc = "Reverse all elements"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(rev))] -pub fn svrev_b8(op: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv16i1")] - fn _svrev_b8(op: svbool_t) -> svbool_t; - } - unsafe { _svrev_b8(op) } -} -#[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -35246,10 +35233,10 @@ pub fn svrev_b8(op: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(rev))] pub fn svrev_b16(op: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv8i1")] - fn _svrev_b16(op: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.rev.b16")] + fn _svrev_b16(op: svbool_t) -> svbool_t; } - unsafe { _svrev_b16(op.sve_into()).sve_into() } + unsafe { _svrev_b16(op.sve_into()) } } #[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b32)"] @@ -35259,10 +35246,10 @@ pub fn svrev_b16(op: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(rev))] pub fn svrev_b32(op: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv4i1")] - fn _svrev_b32(op: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.rev.b32")] + fn _svrev_b32(op: svbool_t) -> svbool_t; } - unsafe { _svrev_b32(op.sve_into()).sve_into() } + unsafe { _svrev_b32(op.sve_into()) } } #[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b64)"] @@ -35272,10 +35259,10 @@ pub fn svrev_b32(op: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(rev))] pub fn svrev_b64(op: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv2i1")] - fn _svrev_b64(op: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.rev.b64")] + fn _svrev_b64(op: svbool_t) -> svbool_t; } - unsafe { _svrev_b64(op.sve_into()).sve_into() } + unsafe { _svrev_b64(op.sve_into()) } } #[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev[_f32])"] @@ -35391,6 +35378,19 @@ pub fn svrev_u32(op: svuint32_t) -> svuint32_t { pub fn svrev_u64(op: svuint64_t) -> svuint64_t { unsafe { svrev_s64(op.as_signed()).as_unsigned() } } +#[doc = "Reverse all elements"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(rev))] +pub fn svrev_b8(op: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv16i1")] + fn _svrev_b8(op: svbool_t) -> svbool_t; + } + unsafe { _svrev_b8(op) } +} #[doc = "Reverse bytes within elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrevb[_s16]_m)"] #[inline] diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs index c6d5d57aca869..f11ca660b15e2 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs @@ -28,6 +28,14 @@ pub(super) trait SveInto: Sized { unsafe fn sve_into(self) -> T; } +impl SveInto for T { + #[inline] + #[target_feature(enable = "sve")] + unsafe fn sve_into(self) -> T { + self + } +} + macro_rules! impl_sve_type { ($(($v:vis, $elem_type:ty, $name:ident, $elt:literal))*) => ($( #[doc = concat!("Scalable vector of type ", stringify!($elem_type))] diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml index 1f65732412241..057491d31c4e9 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml @@ -1207,7 +1207,7 @@ intrinsics: doc: Reverse all elements arguments: ["op: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [rev] compose: - LLVMLink: { name: "llvm.vector.reverse.{sve_type}" } @@ -1217,10 +1217,13 @@ intrinsics: doc: Reverse all elements arguments: ["op: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [rev] compose: - - LLVMLink: { name: "llvm.vector.reverse.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.rev.b{size}" + arguments: ["op: svbool_t"] + return_type: "svbool_t" - name: svrevb[_{type}]{_mxz} attr: [*sve-unstable] diff --git a/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs b/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs index 72fb97fee1f08..f96f05dfec680 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs @@ -1604,6 +1604,7 @@ impl Intrinsic { (Some(BaseTypeKind::Float), Some(BaseTypeKind::Float)) => ex, (Some(BaseTypeKind::UInt), Some(BaseTypeKind::UInt)) => ex, (Some(BaseTypeKind::Poly), Some(BaseTypeKind::Poly)) => ex, + (Some(BaseTypeKind::Bool), Some(BaseTypeKind::Bool)) => ex, (None, None) => ex, _ => unreachable!( From 6346107492ea71d30f52d7ce5294c6e7f8c73dbf Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 15 Jun 2026 10:20:39 +0000 Subject: [PATCH 074/278] core_arch: redefine `sv{zip,uzp}_b{16,32,64}` Clang uses the `llvm.aarch64.sve.zip.bN` intrinsic for `svzip` with `b16`, `b32` and `b64` and the `llvm.aarch64.sve.uzp.bN` intrinsic for `svuzp` with the same types. --- .../core_arch/src/aarch64/sve/generated.rs | 176 +++++++++--------- .../stdarch-gen-arm/spec/sve/aarch64.spec.yml | 36 ++-- 2 files changed, 112 insertions(+), 100 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs index 42d2d1c5b899b..ac3070918a025 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs @@ -43336,19 +43336,6 @@ pub fn svusmmla_s32(op1: svint32_t, op2: svuint8_t, op3: svint8_t) -> svint32_t unsafe { _svusmmla_s32(op1, op2.as_signed(), op3) } } #[doc = "Concatenate even elements from two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(uzp1))] -pub fn svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv16i1")] - fn _svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svuzp1_b8(op1, op2) } -} -#[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -43356,10 +43343,10 @@ pub fn svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp1))] pub fn svuzp1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv8i1")] - fn _svuzp1_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.b16")] + fn _svuzp1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp1_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp1_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b32)"] @@ -43369,10 +43356,10 @@ pub fn svuzp1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp1))] pub fn svuzp1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv4i1")] - fn _svuzp1_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.b32")] + fn _svuzp1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp1_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp1_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b64)"] @@ -43382,10 +43369,10 @@ pub fn svuzp1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp1))] pub fn svuzp1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv2i1")] - fn _svuzp1_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.b64")] + fn _svuzp1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp1_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp1_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1[_f32])"] @@ -43501,6 +43488,19 @@ pub fn svuzp1_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svuzp1_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svuzp1_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Concatenate even elements from two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(uzp1))] +pub fn svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv16i1")] + fn _svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svuzp1_b8(op1, op2) } +} #[doc = "Concatenate even quadwords from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1q[_f32])"] #[inline] @@ -43616,19 +43616,6 @@ pub fn svuzp1q_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svuzp1q_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } #[doc = "Concatenate odd elements from two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(uzp2))] -pub fn svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv16i1")] - fn _svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svuzp2_b8(op1, op2) } -} -#[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -43636,10 +43623,10 @@ pub fn svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp2))] pub fn svuzp2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv8i1")] - fn _svuzp2_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.b16")] + fn _svuzp2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp2_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp2_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b32)"] @@ -43649,10 +43636,10 @@ pub fn svuzp2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp2))] pub fn svuzp2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv4i1")] - fn _svuzp2_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.b32")] + fn _svuzp2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp2_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp2_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b64)"] @@ -43662,10 +43649,10 @@ pub fn svuzp2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp2))] pub fn svuzp2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv2i1")] - fn _svuzp2_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.b64")] + fn _svuzp2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp2_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp2_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2[_f32])"] @@ -43781,6 +43768,19 @@ pub fn svuzp2_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svuzp2_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svuzp2_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Concatenate odd elements from two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(uzp2))] +pub fn svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv16i1")] + fn _svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svuzp2_b8(op1, op2) } +} #[doc = "Concatenate odd quadwords from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2q[_f32])"] #[inline] @@ -44421,19 +44421,6 @@ pub fn svwrffr(op: svbool_t) { unsafe { _svwrffr(op) } } #[doc = "Interleave elements from low halves of two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(zip1))] -pub fn svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv16i1")] - fn _svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svzip1_b8(op1, op2) } -} -#[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -44441,10 +44428,10 @@ pub fn svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip1))] pub fn svzip1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv8i1")] - fn _svzip1_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.b16")] + fn _svzip1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip1_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip1_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b32)"] @@ -44454,10 +44441,10 @@ pub fn svzip1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip1))] pub fn svzip1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv4i1")] - fn _svzip1_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.b32")] + fn _svzip1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip1_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip1_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b64)"] @@ -44467,10 +44454,10 @@ pub fn svzip1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip1))] pub fn svzip1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv2i1")] - fn _svzip1_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.b64")] + fn _svzip1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip1_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip1_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1[_f32])"] @@ -44586,6 +44573,19 @@ pub fn svzip1_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svzip1_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svzip1_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Interleave elements from low halves of two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(zip1))] +pub fn svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv16i1")] + fn _svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svzip1_b8(op1, op2) } +} #[doc = "Interleave quadwords from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1q[_f32])"] #[inline] @@ -44701,19 +44701,6 @@ pub fn svzip1q_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svzip1q_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } #[doc = "Interleave elements from high halves of two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(zip2))] -pub fn svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv16i1")] - fn _svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svzip2_b8(op1, op2) } -} -#[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -44721,10 +44708,10 @@ pub fn svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip2))] pub fn svzip2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv8i1")] - fn _svzip2_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.b16")] + fn _svzip2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip2_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip2_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b32)"] @@ -44734,10 +44721,10 @@ pub fn svzip2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip2))] pub fn svzip2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv4i1")] - fn _svzip2_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.b32")] + fn _svzip2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip2_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip2_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b64)"] @@ -44747,10 +44734,10 @@ pub fn svzip2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip2))] pub fn svzip2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv2i1")] - fn _svzip2_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.b64")] + fn _svzip2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip2_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip2_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2[_f32])"] @@ -44866,6 +44853,19 @@ pub fn svzip2_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svzip2_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svzip2_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Interleave elements from high halves of two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(zip2))] +pub fn svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv16i1")] + fn _svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svzip2_b8(op1, op2) } +} #[doc = "Interleave quadwords from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2q[_f32])"] #[inline] diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml index 057491d31c4e9..138d5ba31175b 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml @@ -1021,7 +1021,7 @@ intrinsics: doc: Interleave elements from low halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [zip1] compose: - LLVMLink: { name: "zip1.{sve_type}" } @@ -1031,10 +1031,13 @@ intrinsics: doc: Interleave elements from low halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [zip1] compose: - - LLVMLink: { name: "zip1.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.zip1.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svzip1q[_{type}] attr: [*sve-unstable] @@ -1052,7 +1055,7 @@ intrinsics: doc: Interleave elements from high halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [zip2] compose: - LLVMLink: { name: "zip2.{sve_type}" } @@ -1062,10 +1065,13 @@ intrinsics: doc: Interleave elements from high halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [zip2] compose: - - LLVMLink: { name: "zip2.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.zip2.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svzip2q[_{type}] attr: [*sve-unstable] @@ -1083,7 +1089,7 @@ intrinsics: doc: Concatenate even elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [uzp1] compose: - LLVMLink: { name: "uzp1.{sve_type}" } @@ -1093,10 +1099,13 @@ intrinsics: doc: Concatenate even elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [uzp1] compose: - - LLVMLink: { name: "uzp1.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.uzp1.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svuzp1q[_{type}] attr: [*sve-unstable] @@ -1114,7 +1123,7 @@ intrinsics: doc: Concatenate odd elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [uzp2] compose: - LLVMLink: { name: "uzp2.{sve_type}" } @@ -1124,10 +1133,13 @@ intrinsics: doc: Concatenate odd elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [uzp2] compose: - - LLVMLink: { name: "uzp2.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.uzp2.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svuzp2q[_{type}] attr: [*sve-unstable] From ae20f1014f7306269b046f7927f463c3500fa9d5 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 15 Jun 2026 10:20:39 +0000 Subject: [PATCH 075/278] intrinsic-test: fwd args in `intrinsic-test.sh` Forward addl. arguments to `intrinsic-test.sh` to `cargo test` so that `--no-fail-fast` or a specific test name can be passed. --- library/stdarch/ci/intrinsic-test.sh | 31 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/library/stdarch/ci/intrinsic-test.sh b/library/stdarch/ci/intrinsic-test.sh index 0441611f38fd2..8de7a4cfa5221 100755 --- a/library/stdarch/ci/intrinsic-test.sh +++ b/library/stdarch/ci/intrinsic-test.sh @@ -1,13 +1,19 @@ #!/usr/bin/env sh -set -ex - if [ $# -lt 2 ]; then - >&2 echo "Usage: $0 " + >&2 echo "Usage: $0 <..args for \`cargo test\`..>" exit 1 fi -case ${2} in +set -ex + +# Pop both arguments and leave "$@" as containing args to be forwarded to `cargo test` +TARGET="$1" +shift +CC_KIND="$1" +shift + +case ${CC_KIND} in clang) export CC="${CLANG_PATH}" CC_ARG_STYLE=clang @@ -22,7 +28,7 @@ case ${2} in CC_ARG_STYLE=clang ;; *) - >&2 echo "Unknown compiler: ${2}" + >&2 echo "Unknown compiler: ${CC_KIND}" exit 1 ;; esac @@ -35,7 +41,7 @@ echo "PROFILE=${PROFILE}" INTRINSIC_TEST="--manifest-path=crates/intrinsic-test/Cargo.toml" -case ${1} in +case ${TARGET} in aarch64_be*) export CFLAGS="-I${AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc/usr/include --sysroot={AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc -Wno-nonportable-vector-initialization" ARCH=aarch64_be @@ -60,24 +66,25 @@ case ${1} in esac -case "${1}" in +case "${TARGET}" in x86_64-unknown-linux-gnu*) env -u CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER \ cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/x86-intel.xml \ --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ - --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ - --target "${1}" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${CC_KIND}.txt" \ + --target "${TARGET}" \ --cc-arg-style "${CC_ARG_STYLE}" ;; *) cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/arm_intrinsics.json \ --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ - --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ - --target "${1}" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${CC_KIND}.txt" \ + --target "${TARGET}" \ --cc-arg-style "${CC_ARG_STYLE}" ;; esac -cargo test --manifest-path=rust_programs/Cargo.toml --target "${1}" --profile "${PROFILE}" --tests +cargo test --manifest-path=rust_programs/Cargo.toml --target "${TARGET}" --profile "${PROFILE}" \ + --tests "$@" From e668e1be8439fbe641e716261c4780926c84a519 Mon Sep 17 00:00:00 2001 From: danieljofficial Date: Mon, 8 Jun 2026 15:05:46 +0100 Subject: [PATCH 076/278] move drop and dropck tests out of tests/ui/issues --- .../drop-with-trait-bound-calls-method-on-self.rs} | 0 .../drop-impl-for-type-param-with-trait-bound.rs} | 0 .../drop-impl-for-type-param-with-trait-bound.stderr} | 0 .../dropck-resolves-associated-type-in-field.rs} | 0 .../self-referential-struct-with-boxed-closure.rs} | 0 .../method-with-self-trait-bound-not-dyn-safe.rs} | 0 .../method-with-self-trait-bound-not-dyn-safe.stderr} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-4252.rs => drop/drop-with-trait-bound-calls-method-on-self.rs} (100%) rename tests/ui/{issues/issue-41974.rs => dropck/drop-impl-for-type-param-with-trait-bound.rs} (100%) rename tests/ui/{issues/issue-41974.stderr => dropck/drop-impl-for-type-param-with-trait-bound.stderr} (100%) rename tests/ui/{issues/issue-48132.rs => dropck/dropck-resolves-associated-type-in-field.rs} (100%) rename tests/ui/{issues/issue-26641.rs => dropck/self-referential-struct-with-boxed-closure.rs} (100%) rename tests/ui/{issues/issue-50781.rs => dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs} (100%) rename tests/ui/{issues/issue-50781.stderr => dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr} (100%) diff --git a/tests/ui/issues/issue-4252.rs b/tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs similarity index 100% rename from tests/ui/issues/issue-4252.rs rename to tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs diff --git a/tests/ui/issues/issue-41974.rs b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs similarity index 100% rename from tests/ui/issues/issue-41974.rs rename to tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs diff --git a/tests/ui/issues/issue-41974.stderr b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr similarity index 100% rename from tests/ui/issues/issue-41974.stderr rename to tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr diff --git a/tests/ui/issues/issue-48132.rs b/tests/ui/dropck/dropck-resolves-associated-type-in-field.rs similarity index 100% rename from tests/ui/issues/issue-48132.rs rename to tests/ui/dropck/dropck-resolves-associated-type-in-field.rs diff --git a/tests/ui/issues/issue-26641.rs b/tests/ui/dropck/self-referential-struct-with-boxed-closure.rs similarity index 100% rename from tests/ui/issues/issue-26641.rs rename to tests/ui/dropck/self-referential-struct-with-boxed-closure.rs diff --git a/tests/ui/issues/issue-50781.rs b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs similarity index 100% rename from tests/ui/issues/issue-50781.rs rename to tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs diff --git a/tests/ui/issues/issue-50781.stderr b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr similarity index 100% rename from tests/ui/issues/issue-50781.stderr rename to tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr From 2e4d8c9d49679f71d56d9e60f36e26b9acfc1534 Mon Sep 17 00:00:00 2001 From: danieljofficial Date: Mon, 8 Jun 2026 15:32:52 +0100 Subject: [PATCH 077/278] add issue links and bless --- .../ui/drop/drop-with-trait-bound-calls-method-on-self.rs | 1 + .../dropck/drop-impl-for-type-param-with-trait-bound.rs | 1 + .../drop-impl-for-type-param-with-trait-bound.stderr | 4 ++-- .../ui/dropck/dropck-resolves-associated-type-in-field.rs | 1 + .../dropck/self-referential-struct-with-boxed-closure.rs | 1 + .../method-with-self-trait-bound-not-dyn-safe.rs | 1 + .../method-with-self-trait-bound-not-dyn-safe.stderr | 8 ++++---- 7 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs b/tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs index 1939ad15f143a..7032a6bc5e759 100644 --- a/tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs +++ b/tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/4252 //@ run-pass trait X { fn call(&self, x: &T); diff --git a/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs index 10c363479a374..9e046dee42cf7 100644 --- a/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs +++ b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/41974 #[derive(Copy, Clone)] struct Flags; diff --git a/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr index 2ae073dd1ba82..cc3488aaca0cf 100644 --- a/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr +++ b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr @@ -1,5 +1,5 @@ error[E0210]: type parameter `T` must be used as an argument to some local type (e.g., `MyStruct`) - --> $DIR/issue-41974.rs:7:6 + --> $DIR/drop-impl-for-type-param-with-trait-bound.rs:8:6 | LL | impl Drop for T where T: A { | ^ uncovered type parameter @@ -8,7 +8,7 @@ LL | impl Drop for T where T: A { = note: only traits defined in the current crate can be implemented for a type parameter error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions - --> $DIR/issue-41974.rs:7:18 + --> $DIR/drop-impl-for-type-param-with-trait-bound.rs:8:18 | LL | impl Drop for T where T: A { | ^ must be a struct, enum, or union in the current crate diff --git a/tests/ui/dropck/dropck-resolves-associated-type-in-field.rs b/tests/ui/dropck/dropck-resolves-associated-type-in-field.rs index d8d7167a4ce82..16a44cd0d6de4 100644 --- a/tests/ui/dropck/dropck-resolves-associated-type-in-field.rs +++ b/tests/ui/dropck/dropck-resolves-associated-type-in-field.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/48132 // Regression test for #48132. This was failing due to problems around // the projection caching and dropck type enumeration. diff --git a/tests/ui/dropck/self-referential-struct-with-boxed-closure.rs b/tests/ui/dropck/self-referential-struct-with-boxed-closure.rs index 983bcbea92731..f78f3907de9a2 100644 --- a/tests/ui/dropck/self-referential-struct-with-boxed-closure.rs +++ b/tests/ui/dropck/self-referential-struct-with-boxed-closure.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/26641 //@ run-pass struct Parser<'a>(#[allow(dead_code)] Box); diff --git a/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs index d837b848591f2..39eee9129e1dd 100644 --- a/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs +++ b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/50781 trait Trait {} trait X { diff --git a/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr index 4ba3166b6c581..81f031a2123fd 100644 --- a/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr +++ b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr @@ -1,12 +1,12 @@ error[E0038]: the trait `X` is not dyn compatible - --> $DIR/issue-50781.rs:11:16 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:12:16 | LL | impl Trait for dyn X {} | ^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/issue-50781.rs:4:8 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:5:8 | LL | trait X { | - this trait is not dyn compatible... @@ -16,14 +16,14 @@ LL | fn foo(&self) where Self: Trait; = help: only type `()` implements `X`; consider using it directly instead. error[E0038]: the trait `X` is not dyn compatible - --> $DIR/issue-50781.rs:16:10 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:17:10 | LL | ::foo(&()); | ^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/issue-50781.rs:4:8 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:5:8 | LL | trait X { | - this trait is not dyn compatible... From 41306052d1e358256d5544e603ed08798cc0c64d Mon Sep 17 00:00:00 2001 From: Urgau <3616612+Urgau@users.noreply.github.com> Date: Wed, 17 Jun 2026 21:06:17 +0200 Subject: [PATCH 078/278] Enable triagebot `merge` and `delegate` commands --- triagebot.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 667468474b3c1..13ea2b250758c 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -60,6 +60,11 @@ labels = ["S-waiting-on-concerns"] [view-all-comments-link] threshold = 20 +# Allows `merge` and `delegate` commands for merging a PR +# Documentation at: https://forge.rust-lang.org/triagebot/merge.html +[merge] +type = "merge-queue" + [notify-zulip."lint-nominated"] zulip_stream = 577190 # #clippy/fcp topic = "FCP rust-clippy#{number}: {title}" From 830e6ebf1561209c73952f2099fe68a6cb2fb535 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 16 Jun 2026 08:48:40 +0200 Subject: [PATCH 079/278] Do not check for unused lifetimes in expanded code --- clippy_lints/src/lifetimes.rs | 4 +++- tests/ui/extra_unused_lifetimes.rs | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 2b4828872b202..3e58448af5266 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -161,7 +161,9 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly_trait_ref: &'tcx PolyTraitRef<'tcx>) { - report_extra_trait_object_lifetimes(cx, poly_trait_ref.bound_generic_params, &poly_trait_ref.trait_ref); + if !poly_trait_ref.span.from_expansion() { + report_extra_trait_object_lifetimes(cx, poly_trait_ref.bound_generic_params, &poly_trait_ref.trait_ref); + } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { diff --git a/tests/ui/extra_unused_lifetimes.rs b/tests/ui/extra_unused_lifetimes.rs index 2e062208935a2..fa2ebddfde3f2 100644 --- a/tests/ui/extra_unused_lifetimes.rs +++ b/tests/ui/extra_unused_lifetimes.rs @@ -211,4 +211,18 @@ mod proc_macro_generated { } } +mod issue17255 { + + trait AnotherSimpleTrait<'a> {} + + macro_rules! mac { + ($lt:lifetime, $t:ident, $tr:path) => { + impl<$t: for<'lt> $tr> AnotherSimpleTrait<'_> for $t {} + }; + } + + // Do not lint code expanded from macros + mac!('a, T, super::SimplerTrait); +} + fn main() {} From bfb63abdd5333de74b28b9e512dd418a8a8895c6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 29 Aug 2025 19:08:17 +0200 Subject: [PATCH 080/278] fix(same_type_modulo_regions): also ignore regions in references ..and add a bunch more cases --- clippy_utils/src/ty/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 056eb818c1ac3..0b394ce74c07c 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -33,7 +33,7 @@ use std::{debug_assert_matches, iter, mem}; use crate::paths::{PathNS, lookup_path_str}; use crate::res::{MaybeDef, MaybeQPath}; -use crate::sym; +use crate::{over, sym}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -485,8 +485,8 @@ pub fn peel_n_ty_refs(mut ty: Ty<'_>, n: usize) -> (Ty<'_>, Option) /// and `false` for: /// - `Result` and `Result` pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.kind(), &b.kind()) { - (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => { + match (a.kind(), b.kind()) { + (ty::Adt(did_a, args_a), ty::Adt(did_b, args_b)) => { if did_a != did_b { return false; } @@ -499,6 +499,9 @@ pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { _ => true, }) }, + (ty::Ref(_, a, mut_a), ty::Ref(_, b, mut_b)) => mut_a == mut_b && same_type_modulo_regions(*a, *b), + (ty::Tuple(as_), ty::Tuple(bs)) => over(as_, bs, |a, b| same_type_modulo_regions(*a, *b)), + (ty::Array(a, na), ty::Array(b, nb)) => na == nb && same_type_modulo_regions(*a, *b), _ => a == b, } } From ec2d840564455376940d1481eb5c0e7d666885b2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 19 Oct 2025 16:16:47 +0200 Subject: [PATCH 081/278] fix(extra_unused_type_parameters): don't suggest an autofix --- .../src/extra_unused_type_parameters.rs | 2 +- .../extra_unused_type_parameters_unfixable.rs | 15 ++++++++++++++ ...ra_unused_type_parameters_unfixable.stderr | 20 +++++++++++++++---- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index 93a6dc59993a6..a6f80da5dab9c 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -102,7 +102,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { fn emit_sugg(&self, spans: Vec, msg: String, help: &'static str) { let suggestions: Vec<(Span, String)> = spans.iter().copied().zip(std::iter::repeat(String::new())).collect(); span_lint_and_then(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, |diag| { - diag.multipart_suggestion(help, suggestions, Applicability::MachineApplicable); + diag.multipart_suggestion(help, suggestions, Applicability::MaybeIncorrect); }); } diff --git a/tests/ui/extra_unused_type_parameters_unfixable.rs b/tests/ui/extra_unused_type_parameters_unfixable.rs index 87ce517a0a451..6a20967b7fc50 100644 --- a/tests/ui/extra_unused_type_parameters_unfixable.rs +++ b/tests/ui/extra_unused_type_parameters_unfixable.rs @@ -1,3 +1,5 @@ +//@no-rustfix + #![warn(clippy::extra_unused_type_parameters)] fn unused_where_clause(x: U) @@ -24,4 +26,17 @@ where unimplemented!(); } +// The fix just removes the type parameter from the definition of `unused_ty`, but it doesn't adjust +// its callsites, leading to compilation errors. +mod issue15884 { + fn unused_ty(x: u8) { + //~^ extra_unused_type_parameters + unimplemented!() + } + + fn main() { + unused_ty::(0); + } +} + fn main() {} diff --git a/tests/ui/extra_unused_type_parameters_unfixable.stderr b/tests/ui/extra_unused_type_parameters_unfixable.stderr index 0765c41609368..2c7051886bf45 100644 --- a/tests/ui/extra_unused_type_parameters_unfixable.stderr +++ b/tests/ui/extra_unused_type_parameters_unfixable.stderr @@ -1,5 +1,5 @@ error: type parameter `T` goes unused in function definition - --> tests/ui/extra_unused_type_parameters_unfixable.rs:3:24 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:5:24 | LL | fn unused_where_clause(x: U) | ^ @@ -9,7 +9,7 @@ LL | fn unused_where_clause(x: U) = help: to override `-D warnings` add `#[allow(clippy::extra_unused_type_parameters)]` error: type parameters go unused in function definition: T, V - --> tests/ui/extra_unused_type_parameters_unfixable.rs:11:30 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:13:30 | LL | fn unused_multi_where_clause(x: U) | ^ ^^^^^^^^^^ @@ -17,12 +17,24 @@ LL | fn unused_multi_where_clause(x: U) = help: consider removing the parameters error: type parameters go unused in function definition: T, U, V - --> tests/ui/extra_unused_type_parameters_unfixable.rs:19:28 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:21:28 | LL | fn unused_all_where_clause() | ^ ^^^^^^^^^^ ^^^^^^^^^^ | = help: consider removing the parameters -error: aborting due to 3 previous errors +error: type parameter `T` goes unused in function definition + --> tests/ui/extra_unused_type_parameters_unfixable.rs:32:17 + | +LL | fn unused_ty(x: u8) { + | ^^^ + | +help: consider removing the parameter + | +LL - fn unused_ty(x: u8) { +LL + fn unused_ty(x: u8) { + | + +error: aborting due to 4 previous errors From dab03c8ce650af8319f8fe31ac6e331f06c48a41 Mon Sep 17 00:00:00 2001 From: Gri-ffin Date: Fri, 8 May 2026 12:28:02 +0100 Subject: [PATCH 082/278] fix: avoid ICE when evaluating constants containing unsized type args --- clippy_utils/src/consts.rs | 15 ++++++++++----- tests/ui/crashes/ice-16950.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 tests/ui/crashes/ice-16950.rs diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 26aef9c1fc910..5b74abdb69ff1 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -885,12 +885,17 @@ impl<'tcx> ConstEvalCtxt<'tcx> { _ => return None, }; + let args = self.typeck.node_args(id); + // We must use the const's own TypingEnv here. + // If we rely on the caller, a 'where Self: Sized' bound + // could trick us into thinking an unsized type is safe, triggering ICE later. + let const_typing_env = ty::TypingEnv::post_analysis(self.tcx, did); + if args.types().any(|ty| !ty.is_sized(self.tcx, const_typing_env)) { + return None; + } + self.tcx - .const_eval_resolve( - self.typing_env, - mir::UnevaluatedConst::new(did, self.typeck.node_args(id)), - qpath.span(), - ) + .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(did, args), qpath.span()) .ok() } diff --git a/tests/ui/crashes/ice-16950.rs b/tests/ui/crashes/ice-16950.rs new file mode 100644 index 0000000000000..7e3d979edea3e --- /dev/null +++ b/tests/ui/crashes/ice-16950.rs @@ -0,0 +1,28 @@ +//@check-pass +#![feature(trivial_bounds)] +#![allow(dead_code)] + +struct Helper(T); + +trait Unsized { + const SIZE: usize = usize::MAX; +} + +impl Unsized for T {} + +impl Helper { + const SIZE: usize = size_of::(); +} + +struct TrickClippy(str); + +impl TrickClippy { + fn trick_clippy() -> bool + where + Self: Sized, + { + Helper::::SIZE == str::SIZE + } +} + +fn main() {} From 6a534731ff042bbca3cdcb858cc5e7f6bae9e327 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 18 Jun 2026 12:20:35 +0200 Subject: [PATCH 083/278] add minimal NetBSD support --- src/tools/miri/ci/ci.sh | 1 + src/tools/miri/src/shims/unix/sync.rs | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index f7c0b739c1dd4..42107adfe566a 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -168,6 +168,7 @@ case $HOST_TARGET in MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests + MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-netbsd run_tests_minimal hello ;; armv7-unknown-linux-gnueabihf) # Host diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 4e351c1571218..a34314f3fbea6 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -35,7 +35,13 @@ const PTHREAD_INIT: u8 = 1; #[inline] fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { interp_ok(match &ecx.tcx.sess.target.os { - Os::Linux | Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android => 0, + Os::Linux + | Os::Illumos + | Os::Solaris + | Os::MacOs + | Os::FreeBsd + | Os::Android + | Os::NetBsd => 0, os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"), }) } @@ -135,8 +141,8 @@ impl SyncObj for PthreadMutex { fn mutex_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> { let offset = match &ecx.tcx.sess.target.os { Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0, - // macOS stores a signature in the first bytes, so we move to offset 4. - Os::MacOs => 4, + // macOS and NetBSD store a signature in the first bytes, so we move to offset 4. + Os::MacOs | Os::NetBsd => 4, os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), }; let offset = Size::from_bytes(offset); @@ -163,7 +169,7 @@ fn mutex_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> check_static_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP"); check_static_initializer("PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP"); } - Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android => { + Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android | Os::NetBsd => { // No non-standard initializers. } os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), From 4afa801d5e4317de07e35114610102b27cffa69c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 29 Aug 2025 19:08:17 +0200 Subject: [PATCH 084/278] feat(unnecessary_unwrap_unchecked): new lint Co-authored-by: Catherine Flores --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/mod.rs | 36 ++- .../methods/unnecessary_unwrap_unchecked.rs | 276 ++++++++++++++++++ clippy_utils/src/ty/mod.rs | 8 + .../unnecessary_unwrap_unchecked_helper.rs | 17 ++ tests/ui/unnecessary_unwrap_unchecked.fixed | 104 +++++++ tests/ui/unnecessary_unwrap_unchecked.rs | 104 +++++++ tests/ui/unnecessary_unwrap_unchecked.stderr | 88 ++++++ 9 files changed, 634 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs create mode 100644 tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs create mode 100644 tests/ui/unnecessary_unwrap_unchecked.fixed create mode 100644 tests/ui/unnecessary_unwrap_unchecked.rs create mode 100644 tests/ui/unnecessary_unwrap_unchecked.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aac684cd09bf..ee6b3c66e6ecb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7459,6 +7459,7 @@ Released 2018-09-13 [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned [`unnecessary_trailing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_trailing_comma [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap +[`unnecessary_unwrap_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap_unchecked [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 79429766c639f..51a848d022d80 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -509,6 +509,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_SORT_BY_INFO, crate::methods::UNNECESSARY_TO_OWNED_INFO, + crate::methods::UNNECESSARY_UNWRAP_UNCHECKED_INFO, crate::methods::UNWRAP_OR_DEFAULT_INFO, crate::methods::UNWRAP_USED_INFO, crate::methods::USELESS_ASREF_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 77baf2ce7a4d1..38a3b1705ad17 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -143,6 +143,7 @@ mod unnecessary_map_or_else; mod unnecessary_min_or_max; mod unnecessary_sort_by; mod unnecessary_to_owned; +mod unnecessary_unwrap_unchecked; mod unwrap_expect_used; mod useless_asref; mod useless_nonzero_new_unchecked; @@ -4602,6 +4603,34 @@ declare_clippy_lint! { "unnecessary calls to `to_owned`-like functions" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `unwrap_unchecked` when an `_unchecked` variant of the function exists. + /// + /// ### Why is this bad? + /// Calling the non-unchecked variant may result in checking that is then discarded + /// if `unwrap_unchecked` is called directly afterwards, whereas the unchecked + /// variant most likely avoids performing the check completely. + /// + /// ### Known problems + /// + /// The unchecked variant is only suggested if it's defined in the same `impl` block + /// as the non-unchecked one + /// + /// ### Example + /// ```rust + /// let s = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + /// ``` + /// Use instead: + /// ```rust + /// let s = unsafe { std::str::from_utf8_unchecked(&[]) }; + /// ``` + #[clippy::version = "1.98.0"] + pub UNNECESSARY_UNWRAP_UNCHECKED, + complexity, + "calling `unwrap_unchecked` on a function which has an `_unchecked` variant" +} + declare_clippy_lint! { /// ### What it does /// Checks for usages of the following functions with an argument that constructs a default value @@ -5042,6 +5071,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_RESULT_MAP_OR_ELSE, UNNECESSARY_SORT_BY, UNNECESSARY_TO_OWNED, + UNNECESSARY_UNWRAP_UNCHECKED, UNWRAP_OR_DEFAULT, UNWRAP_USED, USELESS_ASREF, @@ -5401,7 +5431,11 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - (sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_unchecked | sym::unwrap_err_unchecked, []) => { + (sym::unwrap_unchecked, []) => { + unnecessary_unwrap_unchecked::check(cx, expr, recv, call_span); + unnecessary_literal_unwrap::check(cx, expr, recv, name, args); + }, + (sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_err_unchecked, []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, (sym::extend, [arg]) => { diff --git a/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs b/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs new file mode 100644 index 0000000000000..3f7f146446361 --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs @@ -0,0 +1,276 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeQPath; +use clippy_utils::ty::{option_or_result_arg_ty, same_type_modulo_regions}; +use clippy_utils::{is_from_proc_macro, last_path_segment, over}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Body, Expr, ExprKind, PatKind, Safety}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; +use rustc_span::symbol::Ident; + +use super::UNNECESSARY_UNWRAP_UNCHECKED; + +#[derive(Clone, Copy, Debug)] +enum Variant { + /// Free `fn` in a module + Fn, + /// Associated item from an `impl` + Assoc(AssocKind), +} + +impl Variant { + fn msg(self) -> &'static str { + // Don't use `format!` instead -- it won't be optimized out. + match self { + Variant::Fn => "usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists", + Variant::Assoc(AssocKind::Fn) => { + "usage of `unwrap_unchecked` when an `_unchecked` variant of the associated function exists" + }, + Variant::Assoc(AssocKind::Method) => { + "usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists" + }, + } + } +} + +/// This only exists so the help message shows `associated function` or `method`, depending on +/// whether it has a `self` parameter. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum AssocKind { + /// No `self`: `fn new() -> Self` + Fn, + /// Has `self`: `fn ty<'tcx>(&self) -> Ty<'tcx>` + Method, +} + +impl AssocKind { + fn new(fn_has_self_parameter: bool) -> Self { + if fn_has_self_parameter { Self::Method } else { Self::Fn } + } +} + +fn unchecked_ident(checked_ident: Ident) -> Option { + let checked_ident = checked_ident.to_string(); + // Only add `_unchecked` if it doesn't already end with `_` + (!checked_ident.ends_with('_')).then(|| Ident::from_str(&(checked_ident + "_unchecked"))) +} + +/// Find a function called the same as `checked`, but with added `_unchecked`. +/// +/// This doesn't check if the methods are actually "similar" -- for that, see +/// [`same_functions_modulo_safety`] +fn find_unchecked_sibling_fn( + cx: &LateContext<'_>, + checked_def_id: DefId, + checked_ident: Ident, +) -> Option<(DefId, Ident)> { + // Don't use `parent_module`. We only want to lint if its first parent is a `Mod`, + // i.e. if this is a free-standing function + let parent = cx.tcx.parent(checked_def_id); + if cx.tcx.def_kind(parent) == DefKind::Mod + && let children = parent.as_local().map_or_else( + || cx.tcx.module_children(parent), + // We must use a !query for local modules to prevent an ICE. + |parent| cx.tcx.module_children_local(parent), + ) + // Make sure that there are other functions in this module + // (otherwise there couldn't be an unchecked version) + && children.len() > 1 + && let Some(unchecked_ident) = unchecked_ident(checked_ident) + && let Some(unchecked_def_id) = children.iter().find_map(|child| { + if child.ident == unchecked_ident + && let Res::Def(DefKind::Fn, def_id) = child.res + { + Some(def_id) + } else { + None + } + }) + { + Some((unchecked_def_id, unchecked_ident)) + } else { + None + } +} + +/// Find a method called the same as `checked`, but with added `_unchecked`. +/// +/// This doesn't check if the methods are actually "similar" -- for that, see +/// [`same_functions_modulo_safety`] +fn find_unchecked_sibling_method<'tcx>( + cx: &LateContext<'tcx>, + checked_def_id: DefId, + checked_ident: Ident, +) -> Option<(&'tcx ty::AssocItem, Ident)> { + // Don't use `parent_impl`. We only want to lint if its first parent is an `Impl` + let parent = cx.tcx.parent(checked_def_id); + if matches!(cx.tcx.def_kind(parent), DefKind::Impl { .. }) + && let Some(unchecked_ident) = unchecked_ident(checked_ident) + // Only look in the same impl (to avoid dealing with generics etc.) + && let Some(unchecked) = cx.tcx.associated_items(parent).find_by_ident_and_namespace( + cx.tcx, + unchecked_ident, + Namespace::ValueNS, + parent, + ) + { + Some((unchecked, unchecked_ident)) + } else { + None + } +} + +/// Checks that `checked_def_id` and `unchecked_def_id` refer to functions with: +/// - same visibility +/// - identical signatures, apart from unsafety +/// - "matching" return types: the checked version returns `Option`/`Result`, while the +/// unchecked one returns `T` +fn same_functions_modulo_safety<'tcx>( + cx: &LateContext<'tcx>, + checked_def_id: DefId, + unchecked_def_id: DefId, + unwrapped_ret_ty: Ty<'tcx>, +) -> bool { + let hir_body = |def_id: DefId| -> Option<&'tcx Body<'tcx>> { cx.tcx.hir_maybe_body_owned_by(def_id.as_local()?) }; + let fn_sig = |def_id| cx.tcx.fn_sig(def_id).skip_binder().skip_binder(); + + if match (hir_body(checked_def_id), hir_body(unchecked_def_id)) { + // For local functions, we can get the parameter names. In that case, we want to make sure + // that the latter are equal between the checked and unchecked versions. + (Some(checked_body), Some(unchecked_body)) => { + over(checked_body.params, unchecked_body.params, |p1, p2| { + // We only allow simple params (plain bindings) for now, to stay on the safer side. + if let PatKind::Binding(bm1, _, ident1, None) = p1.pat.kind + && let PatKind::Binding(bm2, _, ident2, None) = p2.pat.kind + { + bm1 == bm2 && ident1 == ident2 + } else { + false + } + }) + }, + // For non-local functions, parameter names are not accessible. Oh well, we'll let it slip + (None, None) => true, + // If only one of the versions is non-local, then something weird happened. Bail just in case + _ => false, + } { + // Check that the functions have identical signatures, apart from safety, and return type (see + // below) + let checked_fn_sig = fn_sig(checked_def_id); + let unchecked_fn_sig = fn_sig(unchecked_def_id); + + (checked_fn_sig.safety() == Safety::Safe && unchecked_fn_sig.safety() == Safety::Unsafe) + && checked_fn_sig.c_variadic() == unchecked_fn_sig.c_variadic() + && checked_fn_sig.abi() == unchecked_fn_sig.abi() + // NOTE: the reason we use `same_type_modulo_regions` all over the place here is that + // the regions of different functions will be distinct, even if they are called the same + && over(checked_fn_sig.inputs(), unchecked_fn_sig.inputs(), |ty1, ty2| { + same_type_modulo_regions(*ty1, *ty2) + }) + // The checked version should return `Option` or `Result`, + // and the unchecked version should return just `T` + && same_type_modulo_regions(unchecked_fn_sig.output(), unwrapped_ret_ty) + && option_or_result_arg_ty(cx, checked_fn_sig.output()) + .is_some_and(|wrapped_ty| same_type_modulo_regions(wrapped_ty, unwrapped_ret_ty)) + // Check that the visibilities are the same (for the purposes of replacing, it would be enough to have + // the former _at least as_ visible as the latter, but we don't bother) + && cx.tcx.visibility(unchecked_def_id) == cx.tcx.visibility(checked_def_id) + } else { + false + } +} + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, recv: &Expr<'_>, call_span: Span) { + if expr.span.from_expansion() { + return; + } + let expected_ret_ty = cx.typeck_results().expr_ty(expr); + let (variant, checked_span, unchecked_sugg, unchecked_full_path) = match recv.kind { + // Construct `Variant::Fn(_)`, if applicable. This is necessary for us to handle + // functions like `std::str::from_utf8_unchecked`. + ExprKind::Call(path, _) + if let ExprKind::Path(qpath) = path.kind + && let checked_ident = last_path_segment(&qpath).ident + && let checked_def_id = path.res(cx).def_id() + && let Some((unchecked_def_id, unchecked_ident)) = + find_unchecked_sibling_fn(cx, checked_def_id, checked_ident) + && same_functions_modulo_safety(cx, checked_def_id, unchecked_def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked_def_id); + ( + Variant::Fn, + checked_ident.span, + if checked_ident.span == path.span { + // replacing `bar(x)` with `bar_unchecked(x)` + // `bar_unchecked` might not be in scope, so suggest the full path + unchecked_full_path.clone() + } else { + // replacing `foo::bar(x)` with `foo::bar_unchecked(x)` + // since the path is qualified, we can just replace the final segment + unchecked_ident.to_string() + }, + unchecked_full_path, + ) + }, + // We unfortunately must handle `A::a(&a)` and `a.a()` separately, this handles the + // former + ExprKind::Call(path, _) + if let ExprKind::Path(qpath) = path.kind + && let checked_ident = last_path_segment(&qpath).ident + && let checked_def_id = path.res(cx).def_id() + && let Some((unchecked, unchecked_ident)) = + find_unchecked_sibling_method(cx, checked_def_id, checked_ident) + && let ty::AssocKind::Fn { has_self, .. } = unchecked.kind + && same_functions_modulo_safety(cx, checked_def_id, unchecked.def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked.def_id); + ( + Variant::Assoc(AssocKind::new(has_self)), + // since this is basically a method call, we only need to replace the method ident + checked_ident.span, + unchecked_ident.to_string(), + unchecked_full_path, + ) + }, + // ... And now the latter ^^ + ExprKind::MethodCall(segment, _, _, _) + if let checked_ident = segment.ident + && let Some(checked_def_id) = cx.typeck_results().type_dependent_def_id(recv.hir_id) + && let Some((unchecked, unchecked_ident)) = + find_unchecked_sibling_method(cx, checked_def_id, checked_ident) + && same_functions_modulo_safety(cx, checked_def_id, unchecked.def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked.def_id); + ( + Variant::Assoc(AssocKind::Method), + // since this is a method call, we only need to replace the method ident + checked_ident.span, + unchecked_ident.to_string(), + unchecked_full_path, + ) + }, + _ => return, + }; + + if !is_from_proc_macro(cx, expr) { + span_lint_and_then(cx, UNNECESSARY_UNWRAP_UNCHECKED, expr.span, variant.msg(), |diag| { + let sugg = vec![ + // replace the function with the unchecked version + (checked_span, unchecked_sugg), + // remove the call to `.unwrap_unchecked()` + (call_span.with_lo(recv.span.hi()), String::new()), + ]; + diag.multipart_suggestion( + format!("use `{unchecked_full_path}` instead, and remove the call to `.unwrap_unchecked()`"), + sugg, + // TODO: make this `MachineApplicable` when the function comes from std/alloc/core + // The reasoning is that, if the function comes from std/alloc/core, its checked and unchecked are + // pretty likely to have their semantics match. + Applicability::MaybeIncorrect, + ); + }); + } +} diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 0b394ce74c07c..e9ba7cfc4628a 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -1344,6 +1344,14 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option` or a `Result` and return its argument type (`T`) if it is. +pub fn option_or_result_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + match ty.kind() { + ty::Adt(adt, args) if matches!(adt.opt_diag_name(cx), Some(sym::Option | sym::Result)) => Some(args.type_at(0)), + _ => None, + } +} + /// Check if a Ty<'_> of `Iterator` contains any mutable access to non-owning types by checking if /// it contains fields of mutable references or pointers, or references/pointers to non-`Freeze` /// types, or `PhantomData` types containing any of the previous. This can be used to check whether diff --git a/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs b/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs new file mode 100644 index 0000000000000..4cc9ac80692d2 --- /dev/null +++ b/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs @@ -0,0 +1,17 @@ +#![allow(unused, clippy::missing_safety_doc)] + +pub fn lol() -> Option { + Some(0) +} + +pub unsafe fn lol_unchecked() -> u32 { + 0 +} + +pub fn kek() -> Option { + Some(0) +} + +unsafe fn kek_unchecked() -> u32 { + 0 +} diff --git a/tests/ui/unnecessary_unwrap_unchecked.fixed b/tests/ui/unnecessary_unwrap_unchecked.fixed new file mode 100644 index 0000000000000..274bb7a00bfb0 --- /dev/null +++ b/tests/ui/unnecessary_unwrap_unchecked.fixed @@ -0,0 +1,104 @@ +//@aux-build:unnecessary_unwrap_unchecked_helper.rs +//@aux-build:proc_macros.rs +#![warn(clippy::unnecessary_unwrap_unchecked)] + +use proc_macros::{external, with_span}; + +#[rustfmt::skip] +use unnecessary_unwrap_unchecked_helper::{lol, kek}; + +mod b { + pub fn test_fn() -> Option { + Some(0) + } + + pub unsafe fn test_fn_unchecked() -> u32 { + 0 + } +} + +fn test_fn() -> Option { + Some(0) +} + +unsafe fn test_fn_unchecked() -> u32 { + 0 +} + +struct A; + +impl A { + fn a(&self) -> Option { + Some(0) + } + + unsafe fn a_unchecked(&self) -> u32 { + 0 + } + + fn an_assoc_fn() -> Option { + Some(0) + } + + unsafe fn an_assoc_fn_unchecked() -> u32 { + 0 + } +} + +struct B; + +impl B { + fn b(&self) -> Option { + Some(0) + } +} + +impl B { + unsafe fn b_unchecked(&self) -> u32 { + 0 + } +} + +fn main() { + let string_slice = unsafe { std::str::from_utf8_unchecked(&[]) }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { A::a_unchecked(&A) }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { + let a = A; + a.a_unchecked() + }; + //~^^ unnecessary_unwrap_unchecked + let an_assoc_fn = unsafe { A::an_assoc_fn_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let local_fn = unsafe { b::test_fn_unchecked() }; + //~^ unnecessary_unwrap_unchecked + + // Don't lint: unlike `kek`, `kek_unchecked` isn't accessible from here + let extern_pub_fn = unsafe { kek().unwrap_unchecked() }; + + // Don't lint: `B::b` and `B::b_unchecked` come from distinct impl blocks + // (even though that wouldn't actually make the suggestion invalid in this case) + let b = unsafe { + let b = B; + b.b().unwrap_unchecked() + }; + + macro_rules! local { + () => {{ + unsafe { ::std::str::from_utf8(&[]).unwrap_unchecked() }; + }}; + } + local!(); + external! { + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } + with_span! { + span + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } +} diff --git a/tests/ui/unnecessary_unwrap_unchecked.rs b/tests/ui/unnecessary_unwrap_unchecked.rs new file mode 100644 index 0000000000000..006a541365a58 --- /dev/null +++ b/tests/ui/unnecessary_unwrap_unchecked.rs @@ -0,0 +1,104 @@ +//@aux-build:unnecessary_unwrap_unchecked_helper.rs +//@aux-build:proc_macros.rs +#![warn(clippy::unnecessary_unwrap_unchecked)] + +use proc_macros::{external, with_span}; + +#[rustfmt::skip] +use unnecessary_unwrap_unchecked_helper::{lol, kek}; + +mod b { + pub fn test_fn() -> Option { + Some(0) + } + + pub unsafe fn test_fn_unchecked() -> u32 { + 0 + } +} + +fn test_fn() -> Option { + Some(0) +} + +unsafe fn test_fn_unchecked() -> u32 { + 0 +} + +struct A; + +impl A { + fn a(&self) -> Option { + Some(0) + } + + unsafe fn a_unchecked(&self) -> u32 { + 0 + } + + fn an_assoc_fn() -> Option { + Some(0) + } + + unsafe fn an_assoc_fn_unchecked() -> u32 { + 0 + } +} + +struct B; + +impl B { + fn b(&self) -> Option { + Some(0) + } +} + +impl B { + unsafe fn b_unchecked(&self) -> u32 { + 0 + } +} + +fn main() { + let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { A::a(&A).unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { + let a = A; + a.a().unwrap_unchecked() + }; + //~^^ unnecessary_unwrap_unchecked + let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn = unsafe { lol().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + + // Don't lint: unlike `kek`, `kek_unchecked` isn't accessible from here + let extern_pub_fn = unsafe { kek().unwrap_unchecked() }; + + // Don't lint: `B::b` and `B::b_unchecked` come from distinct impl blocks + // (even though that wouldn't actually make the suggestion invalid in this case) + let b = unsafe { + let b = B; + b.b().unwrap_unchecked() + }; + + macro_rules! local { + () => {{ + unsafe { ::std::str::from_utf8(&[]).unwrap_unchecked() }; + }}; + } + local!(); + external! { + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } + with_span! { + span + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } +} diff --git a/tests/ui/unnecessary_unwrap_unchecked.stderr b/tests/ui/unnecessary_unwrap_unchecked.stderr new file mode 100644 index 0000000000000..359e50a33c9b7 --- /dev/null +++ b/tests/ui/unnecessary_unwrap_unchecked.stderr @@ -0,0 +1,88 @@ +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:63:33 + | +LL | let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-unwrap-unchecked` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_unwrap_unchecked)]` +help: use `std::str::from_utf8_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; +LL + let string_slice = unsafe { std::str::from_utf8_unchecked(&[]) }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:65:22 + | +LL | let a = unsafe { A::a(&A).unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::a_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let a = unsafe { A::a(&A).unwrap_unchecked() }; +LL + let a = unsafe { A::a_unchecked(&A) }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:69:9 + | +LL | a.a().unwrap_unchecked() + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::a_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - a.a().unwrap_unchecked() +LL + a.a_unchecked() + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the associated function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:72:32 + | +LL | let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::an_assoc_fn_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; +LL + let an_assoc_fn = unsafe { A::an_assoc_fn_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:74:30 + | +LL | let extern_fn = unsafe { lol().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unnecessary_unwrap_unchecked_helper::lol_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let extern_fn = unsafe { lol().unwrap_unchecked() }; +LL + let extern_fn = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:76:40 + | +LL | let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unnecessary_unwrap_unchecked_helper::lol_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; +LL + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:78:29 + | +LL | let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `b::test_fn_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; +LL + let local_fn = unsafe { b::test_fn_unchecked() }; + | + +error: aborting due to 7 previous errors + From 57f180d1bc9d3ed7bba9ef499e55363c2c0c113d Mon Sep 17 00:00:00 2001 From: Gri-ffin Date: Wed, 10 Jun 2026 20:32:26 +0100 Subject: [PATCH 085/278] prevent OOM/hang when checking validity of large types by changing to layout based check --- clippy_utils/src/ty/mod.rs | 106 +++++++++++++++++++++++++++----- tests/ui/uninit_vec.rs | 122 +++++++++++++++++++++++++++++++++++++ tests/ui/uninit_vec.stderr | 79 +++++++++++++++++++++++- 3 files changed, 290 insertions(+), 17 deletions(-) diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 056eb818c1ac3..dc8edb22c9f88 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -3,7 +3,7 @@ #![allow(clippy::module_name_repetitions)] use core::ops::ControlFlow; -use rustc_abi::VariantIdx; +use rustc_abi::{BackendRepr, FieldsShape, VariantIdx, Variants}; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; @@ -17,7 +17,7 @@ use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; -use rustc_middle::ty::layout::ValidityRequirement; +use rustc_middle::ty::layout::{LayoutError, LayoutOf, TyAndLayout}; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, GenericArgKind, GenericArgsRef, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, @@ -505,29 +505,103 @@ pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { /// Checks if a given type looks safe to be uninitialized. pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - let typing_env = cx.typing_env().with_post_analysis_normalized(cx.tcx); - cx.tcx - .check_validity_requirement((ValidityRequirement::Uninit, typing_env.as_query_input(ty))) - .unwrap_or_else(|_| is_uninit_value_valid_for_ty_fallback(cx, ty)) + match cx.layout_of(ty) { + Ok(layout) => is_uninit_value_valid_for_layout(cx, layout), + // The type layout is either not concrete enough yet or too large, fall back to structural check instead + Err(LayoutError::TooGeneric(_) | LayoutError::SizeOverflow(_)) => is_uninit_value_valid_for_ty_fallback(cx, ty), + Err(_) => false, + } +} + +fn is_uninit_value_valid_for_layout<'tcx>(cx: &LateContext<'tcx>, layout: TyAndLayout<'tcx>) -> bool { + // ZSTs contribute no bytes to the vector buffer + if layout.layout.is_zst() { + return true; + } + + match layout.layout.backend_repr { + BackendRepr::Scalar(s) => s.is_uninit_valid(), + BackendRepr::ScalarPair(a, b) => a.is_uninit_valid() && b.is_uninit_valid(), + BackendRepr::SimdVector { element, count } => count == 0 || element.is_uninit_valid(), + BackendRepr::SimdScalableVector { element, .. } => element.is_uninit_valid(), + // Here validity is determined by the structural fields instead. + BackendRepr::Memory { .. } => match &layout.layout.variants { + Variants::Single { .. } => match &layout.layout.fields { + FieldsShape::Primitive => { + debug_assert!(false, "Both Scalar primitives and ! should be handled above."); + false + }, + // Arrays are valid if empty, or if their elements are valid. + FieldsShape::Array { count, .. } => { + if *count == 0 { + true + } else { + is_uninit_value_valid_for_layout(cx, layout.field(cx, 0)) + } + }, + // Structs like types are valid only if all fields are valid. + FieldsShape::Arbitrary { offsets, .. } => { + (0..offsets.len()).all(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i))) + }, + // Unions are valid if at least one field is valid. + FieldsShape::Union(count) => { + (0..count.get()).any(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i))) + }, + }, + // Types with no valid variants must be uninhabited + Variants::Empty => true, + // Enum like with multiple inhabited variants have a discriminant, they cannot be uninitialized. + Variants::Multiple { .. } => false, + }, + } } -/// A fallback for polymorphic types, which are not supported by `check_validity_requirement`. +/// Fallback for polymorphic types where `layout_of` fails fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + let typing_env = cx.typing_env().with_post_analysis_normalized(cx.tcx); + match *ty.kind() { // The array length may be polymorphic, let's try the inner type. - ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component), + ty::Array(component, len) => { + // Zero-length arrays are always valid + if len.try_to_target_usize(cx.tcx) == Some(0) { + return true; + } + is_uninit_value_valid_for_ty(cx, component) + }, // Peek through tuples and try their fallbacks. ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)), - // Unions are always fine right now. - // This includes MaybeUninit, the main way people use uninitialized memory. - ty::Adt(adt, _) if adt.is_union() => true, + // For Unions, check if any field is uninit + ty::Adt(adt, args) if adt.is_union() => adt.all_fields().any(|field| { + let unnormalized_field_ty = field.ty(cx.tcx, args); + let Ok(field_ty) = cx.tcx.try_normalize_erasing_regions(typing_env, unnormalized_field_ty) else { + debug_assert!( + false, + "failed to normalize field type `{unnormalized_field_ty:?}`, ParamEnv is likely set incorrectly." + ); + return false; + }; + is_uninit_value_valid_for_ty(cx, field_ty) + }), // Types (e.g. `UnsafeCell>`) that recursively contain only types that can be uninit // can themselves be uninit too. - // This purposefully ignores enums as they may have a discriminant that can't be uninit. - ty::Adt(adt, args) if adt.is_struct() => adt - .all_fields() - .all(|field| is_uninit_value_valid_for_ty(cx, field.ty(cx.tcx, args).skip_norm_wip())), - // For the rest, conservatively assume that they cannot be uninit. + // This also applies for single variant enums, whose validity is determined by their fields. + ty::Adt(adt, args) if adt.is_struct() || adt.variants().len() == 1 => adt.all_fields().all(|field| { + let unnormalized_field_ty = field.ty(cx.tcx, args); + let Ok(field_ty) = cx.tcx.try_normalize_erasing_regions(typing_env, unnormalized_field_ty) else { + debug_assert!( + false, + "failed to normalize field type `{unnormalized_field_ty:?}`, ParamEnv is likely set incorrectly." + ); + return false; + }; + + is_uninit_value_valid_for_ty(cx, field_ty) + }), + // Without a usable whole type layout, + // conservatively reject remaining enum cases + ty::Adt(adt, _) if adt.is_enum() => false, + // Conservatively reject remaining types _ => false, } } diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index a0bac28e87292..ae5c685d89abd 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -205,3 +205,125 @@ fn main() { } } } + +mod issue_11715 { + use std::mem::MaybeUninit; + #[cfg(target_pointer_width = "64")] + const HUGE: usize = 4_294_967_296; + #[cfg(target_pointer_width = "32")] + const HUGE: usize = 268_435_455; + + fn large_maybeuninit_vec_ice() { + let mut v: Vec<[MaybeUninit; HUGE]> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + fn large_u8_vec_ice() { + let mut v: Vec<[u8; HUGE]> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + fn large_nested_maybeuninit_vec_ice() { + let mut v: Vec<[[MaybeUninit; HUGE]; 2]> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + struct HeavyWrapperSafe([MaybeUninit; HUGE]); + fn large_struct_maybeuninit_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + struct HeavyWrapperUnsafe([u8; HUGE]); + fn large_struct_u8_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + #[allow(clippy::large_enum_variant)] + enum HeavyEnum { + A([MaybeUninit; HUGE]), + B, + } + fn large_enum_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + enum Uninhabited {} + fn uninhabited_enum() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum SingleVariant { + OnlyOne, + } + fn single_variant_enum() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum OneVariantU8 { + ThisOne([u8; HUGE]), + } + fn one_variant_u8() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + enum OneVariantMaybeUninit { + ThisOne([MaybeUninit; HUGE]), + } + fn one_variant_maybe_uninit() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + fn generic_vec_lints() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + fn generic_vec_maybeuninit() { + let mut v: Vec> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + trait Assoc { + type Item; + } + fn projection_vec_lints() { + let mut v: Vec<::Item> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + struct Concrete; + impl Assoc for Concrete { + type Item = MaybeUninit; + } + fn normalized_projection_vec_ok() { + let mut v: Vec<::Item> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum E { + Foo(MaybeUninit), + Bar(U), + } + fn enum_uninhabited_zst() { + let mut v: Vec> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + fn enum_uninhabited_non_zst() { + let mut v: Vec> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } +} diff --git a/tests/ui/uninit_vec.stderr b/tests/ui/uninit_vec.stderr index 1b821ef004e6f..56a182614b64b 100644 --- a/tests/ui/uninit_vec.stderr +++ b/tests/ui/uninit_vec.stderr @@ -159,5 +159,82 @@ LL | vec.set_len(1); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 15 previous errors +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:222:9 + | +LL | let mut v: Vec<[u8; HUGE]> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:240:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:251:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:274:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:288:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:302:9 + | +LL | let mut v: Vec<::Item> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:325:9 + | +LL | let mut v: Vec> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: aborting due to 22 previous errors From 782225d25e06f2423e65aec3710d975b90090102 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 14 Jun 2026 14:23:30 -0400 Subject: [PATCH 086/278] Find a shared context for the format string and the `format!` call in `format_args_collector`. --- .../src/utils/format_args_collector.rs | 151 ++++++++++++------ tests/ui/unnecessary_trailing_comma.fixed | 2 +- tests/ui/unnecessary_trailing_comma.rs | 2 +- tests/ui/unnecessary_trailing_comma.stderr | 8 +- 4 files changed, 108 insertions(+), 55 deletions(-) diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs index ee1729750f2e5..6401b48f70b05 100644 --- a/clippy_lints/src/utils/format_args_collector.rs +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -1,18 +1,18 @@ use clippy_utils::macros::FormatArgsStorage; -use clippy_utils::source::SpanExt; -use itertools::Itertools; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; use rustc_data_structures::fx::FxHashMap; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, hygiene}; -use std::iter::once; +use rustc_span::source_map::SourceMap; +use rustc_span::{Span, SpanData}; use std::mem; /// Populates [`FormatArgsStorage`] with AST [`FormatArgs`] nodes pub struct FormatArgsCollector { format_args: FxHashMap, + parent_spans: Vec, storage: FormatArgsStorage, } @@ -20,6 +20,7 @@ impl FormatArgsCollector { pub fn new(storage: FormatArgsStorage) -> Self { Self { format_args: FxHashMap::default(), + parent_spans: Vec::new(), storage, } } @@ -30,7 +31,7 @@ impl_lint_pass!(FormatArgsCollector => []); impl EarlyLintPass for FormatArgsCollector { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::FormatArgs(args) = &expr.kind { - if has_span_from_proc_macro(cx, args) { + if self.has_span_from_external_macro(cx.sess().source_map(), expr.span, args) { return; } @@ -43,53 +44,99 @@ impl EarlyLintPass for FormatArgsCollector { } } -/// Detects if the format string or an argument has its span set by a proc macro to something inside -/// a macro callsite, e.g. -/// -/// ```ignore -/// println!(some_proc_macro!("input {}"), a); -/// ``` -/// -/// Where `some_proc_macro` expands to -/// -/// ```ignore -/// println!("output {}", a); -/// ``` -/// -/// But with the span of `"output {}"` set to the macro input -/// -/// ```ignore -/// println!(some_proc_macro!("input {}"), a); -/// // ^^^^^^^^^^ -/// ``` -fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool { - let ctxt = args.span.ctxt(); +impl FormatArgsCollector { + /// Detects if the format string or an argument has its span set by a proc macro to something + /// inside a macro callsite, e.g. + /// + /// ```ignore + /// println!(some_proc_macro!("input {}"), a); + /// ``` + /// + /// Where `some_proc_macro` expands to + /// + /// ```ignore + /// println!("output {}", a); + /// ``` + /// + /// But with the span of `"output {}"` set to the macro input + /// + /// ```ignore + /// println!(some_proc_macro!("input {}"), a); + /// // ^^^^^^^^^^ + /// ``` + fn has_span_from_external_macro(&mut self, sm: &SourceMap, fmt_sp: Span, args: &FormatArgs) -> bool { + let mut fmt_sp = fmt_sp.data(); + + // Find the first macro call that contains the format string. + let arg_sp = if let Some(arg_sp) = walk_span_to_context(args.span, fmt_sp.ctxt) { + arg_sp.data() + } else { + // Try to find a common parent for the format call and the format string. + self.parent_spans.clear(); + // `fmt_sp.ctxt` isn't a parent of the format string so don't add it to the + // search. The first iteration will always run since it can't be the root. + while !fmt_sp.ctxt.is_root() { + fmt_sp = fmt_sp.ctxt.outer_expn_data().call_site.data(); + self.parent_spans.push(fmt_sp); + } + let mut arg_sp = args.span.data(); + // Note: A parent span will always eventually be found since the root context + // is an ancestor of all contexts. + loop { + match self.parent_spans.iter().find(|s| s.ctxt == arg_sp.ctxt) { + Some(call_sp) if call_sp.lo <= arg_sp.lo && arg_sp.hi <= call_sp.hi => { + fmt_sp = *call_sp; + break arg_sp; + }, + // If the string isn't within the call span we some macro stuff we can't + // easily interpret. + Some(_) => return true, + None => arg_sp = arg_sp.ctxt.outer_expn_data().call_site.data(), + } + } + }; + if fmt_sp.ctxt.in_external_macro(sm) { + return true; + } + let Some(src) = arg_sp.get_source_range(sm) else { + return true; + }; + let Some(src_text) = src.sf.src.as_ref().map(|x| &***x) else { + return true; + }; - // `format!("{} {} {c}", "one", "two", c = "three")` - // ^^^^^ ^^^^^ ^^^^^^^ - let argument_span = args - .arguments - .explicit_args() - .iter() - .map(|argument| hygiene::walk_chain(argument.expr.span, ctxt)); + // Check the spans between the format string and the arguments and between each argument. + args.arguments + .explicit_args() + .iter() + .try_fold(src.range.end, |start, arg| { + let expr_sp = walk_span_to_context(arg.expr.span, fmt_sp.ctxt)?.data(); + let expr_start = (expr_sp.lo.0 - src.sf.start_pos.0) as usize; + let expr_end = (expr_sp.hi.0 - src.sf.start_pos.0) as usize; + let mut tks = tokenize(src_text.get(start..expr_start)?, FrontmatterAllowed::No) + .map(|x| x.kind) + .filter(|x| { + !matches!( + x, + TokenKind::LineComment { doc_style: None } + | TokenKind::BlockComment { + doc_style: None, + terminated: true + } + | TokenKind::Whitespace + ) + }); - // `format!("{} {} {c}", "one", "two", c = "three")` - // ^^ ^^ ^^^^^^ - !once(args.span) - .chain(argument_span) - .tuple_windows() - .map(|(start, end)| start.between(end)) - .all(|sp| { - sp.check_text(cx, |src| { - // text should be either `, name` or `, name =` - let mut iter = tokenize(src, FrontmatterAllowed::No).filter(|t| { - !matches!( - t.kind, - TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace - ) - }); - iter.next().is_some_and(|t| matches!(t.kind, TokenKind::Comma)) - && iter.all(|t| matches!(t.kind, TokenKind::Ident | TokenKind::Eq)) + // `,` or `, ident =` + let matches = matches!(tks.next(), Some(TokenKind::Comma)) + && match tks.next() { + Some(TokenKind::Ident) => matches!(tks.next(), Some(TokenKind::Eq)), + Some(_) => false, + None => true, + } + && tks.next().is_none(); + matches.then_some(expr_end) }) - }) + .is_none() + } } diff --git a/tests/ui/unnecessary_trailing_comma.fixed b/tests/ui/unnecessary_trailing_comma.fixed index 499d9ee1ca235..f418fe954d400 100644 --- a/tests/ui/unnecessary_trailing_comma.fixed +++ b/tests/ui/unnecessary_trailing_comma.fixed @@ -26,9 +26,9 @@ fn simple() { println!{"Foo{{,}}"}; //~ unnecessary_trailing_comma println!{"Foo(,"}; //~ unnecessary_trailing_comma println!{"Foo[,"}; //~ unnecessary_trailing_comma + println!(concat!("Foo", "=", "{}"), 1); //~ unnecessary_trailing_comma // This should eventually work, but requires more work - println!(concat!("Foo", "=", "{}"), 1,); println!("No params", /*"a,){ */); println!("No params" /* "a,){*/, /*"a,){ */); diff --git a/tests/ui/unnecessary_trailing_comma.rs b/tests/ui/unnecessary_trailing_comma.rs index 15dea27b887b6..26770ffaeee18 100644 --- a/tests/ui/unnecessary_trailing_comma.rs +++ b/tests/ui/unnecessary_trailing_comma.rs @@ -26,9 +26,9 @@ fn simple() { println!{"Foo{{,}}", }; //~ unnecessary_trailing_comma println!{"Foo(,", }; //~ unnecessary_trailing_comma println!{"Foo[,", }; //~ unnecessary_trailing_comma + println!(concat!("Foo", "=", "{}"), 1,); //~ unnecessary_trailing_comma // This should eventually work, but requires more work - println!(concat!("Foo", "=", "{}"), 1,); println!("No params", /*"a,){ */); println!("No params" /* "a,){*/, /*"a,){ */); diff --git a/tests/ui/unnecessary_trailing_comma.stderr b/tests/ui/unnecessary_trailing_comma.stderr index 06fd5b1861a55..8ff7195a147b0 100644 --- a/tests/ui/unnecessary_trailing_comma.stderr +++ b/tests/ui/unnecessary_trailing_comma.stderr @@ -115,5 +115,11 @@ error: unnecessary trailing comma LL | println!{"Foo[,", }; | ^^ help: remove the trailing comma -error: aborting due to 19 previous errors +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:29:42 + | +LL | println!(concat!("Foo", "=", "{}"), 1,); + | ^ help: remove the trailing comma + +error: aborting due to 20 previous errors From ad11a216f7b66abf7e0017108e0a13936ba2af6b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 12 Jun 2026 10:29:40 -0400 Subject: [PATCH 087/278] Take more precise arguments in `is_const_evaluatable`. --- clippy_lints/src/assertions_on_constants.rs | 4 +- clippy_lints/src/cloned_ref_to_slice_refs.rs | 2 +- clippy_lints/src/manual_clamp.rs | 8 +++- .../src/methods/chunks_exact_to_as_chunks.rs | 2 +- clippy_lints/src/methods/str_split.rs | 5 ++- clippy_utils/src/eager_or_lazy.rs | 4 +- clippy_utils/src/msrvs.rs | 40 ++++++++++++++----- clippy_utils/src/qualify_min_const_fn.rs | 22 +++++----- clippy_utils/src/visitors.rs | 40 +++++++++---------- 9 files changed, 79 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 6e57d0608bed8..118c3c23d727d 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -52,7 +52,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { _ => return, } && let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn) - && is_const_evaluatable(cx, condition) + // Check if the whole expression can be moved into a const context. + // Note that const eval can evaluate things which cannot be moved (e.g. `false && x`). + && is_const_evaluatable(cx.tcx, cx.typeck_results(), condition) && let Some((Constant::Bool(assert_val), const_src)) = ConstEvalCtxt::new(cx).eval_with_source(condition, macro_call.span.ctxt()) && let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id) diff --git a/clippy_lints/src/cloned_ref_to_slice_refs.rs b/clippy_lints/src/cloned_ref_to_slice_refs.rs index 980584e9a1eec..24955ac3c6258 100644 --- a/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { && let Some(adjustment) = is_needless_clone_or_equivalent(cx, recv, path.ident.name, item.hir_id) // check for immutability or purity - && (!is_mutable(cx, recv) || is_const_evaluatable(cx, recv)) + && (!is_mutable(cx, recv) || is_const_evaluatable(cx.tcx, cx.typeck_results(), recv)) // get appropriate crate for `slice::from_ref` && let Some(builtin_crate) = clippy_utils::std_or_core(cx) diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 6848af7748f3f..8cb60212f19da 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -367,12 +367,16 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) && let Some(inner_seg) = segment(cx, inner_fn) && let Some(outer_seg) = segment(cx, outer_fn) { - let (input, inner_arg) = match (is_const_evaluatable(cx, first), is_const_evaluatable(cx, second)) { + let typeck = cx.typeck_results(); + let (input, inner_arg) = match ( + is_const_evaluatable(cx.tcx, typeck, first), + is_const_evaluatable(cx.tcx, typeck, second), + ) { (true, false) => (second, first), (false, true) => (first, second), _ => return None, }; - let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); + let is_float = typeck.expr_ty_adjusted(input).is_floating_point(); let (min, max) = match (inner_seg, outer_seg) { (FunctionType::CmpMin, FunctionType::CmpMax) => (outer_arg, inner_arg), (FunctionType::CmpMax, FunctionType::CmpMin) => (inner_arg, outer_arg), diff --git a/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs b/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs index 235df80e46985..e151cf43a5044 100644 --- a/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs +++ b/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs @@ -25,7 +25,7 @@ pub(super) fn check<'tcx>( return; } - if is_const_evaluatable(cx, arg) { + if is_const_evaluatable(cx.tcx, cx.typeck_results(), arg) { if !msrv.meets(cx, msrvs::AS_CHUNKS) { return; } diff --git a/clippy_lints/src/methods/str_split.rs b/clippy_lints/src/methods/str_split.rs index 8641f7c0abec3..fbb3dd41c7a4e 100644 --- a/clippy_lints/src/methods/str_split.rs +++ b/clippy_lints/src/methods/str_split.rs @@ -22,8 +22,9 @@ pub(super) fn check<'a>( // basic ones: a `'\n'`, `"\n"`, and `"\r\n"`. if let ExprKind::MethodCall(trim_method_name, trim_recv, [], trim_span) = split_recv.kind && trim_method_name.ident.name == sym::trim - && cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str() - && !is_const_evaluatable(cx, trim_recv) + && let typeck = cx.typeck_results() + && typeck.expr_ty_adjusted(trim_recv).peel_refs().is_str() + && !is_const_evaluatable(cx.tcx, typeck, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind && matches!( split_lit.node, diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index cd609980b149b..68f1d0c2bb9fd 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -161,7 +161,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (), // No need to walk the arguments here, `is_const_evaluatable` already did - Res::Def(..) if is_const_evaluatable(self.cx, e) => { + Res::Def(..) if is_const_evaluatable(self.cx.tcx, self.cx.typeck_results(), e) => { self.eagerness |= NoChange; return; }, @@ -177,7 +177,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS _ => self.eagerness = Lazy, }, // No need to walk the arguments here, `is_const_evaluatable` already did - ExprKind::MethodCall(..) if is_const_evaluatable(self.cx, e) => { + ExprKind::MethodCall(..) if is_const_evaluatable(self.cx.tcx, self.cx.typeck_results(), e) => { self.eagerness |= NoChange; return; }, diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 6cdba7a03e637..daf9de05a2948 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -3,8 +3,9 @@ use rustc_ast::Attribute; use rustc_ast::attr::AttributeExt; use rustc_attr_parsing::parse_version; use rustc_data_structures::smallvec::SmallVec; -use rustc_hir::RustcVersion; +use rustc_hir::{HirId, RustcVersion}; use rustc_lint::LateContext; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::Symbol; use serde::Deserialize; @@ -118,16 +119,29 @@ impl Msrv { /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations pub fn current(self, cx: &LateContext<'_>) -> Option { if SEEN_MSRV_ATTR.load(Ordering::Relaxed) { - let start = cx.last_node_with_lint_attrs; - if let Some(msrv_attr) = once(start) - .chain(cx.tcx.hir_parent_id_iter(start)) - .find_map(|id| parse_attrs(cx.tcx.sess, cx.tcx.hir_attrs(id))) - { - return Some(msrv_attr); - } + self.from_attrs(cx.tcx, cx.last_node_with_lint_attrs) + } else { + self.0 } + } - self.0 + /// Returns the MSRV at the specified node + /// + /// If the crate being linted uses an `#[clippy::msrv]` attribute this will search the parent + /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations + pub fn at(self, tcx: TyCtxt<'_>, node: HirId) -> Option { + if SEEN_MSRV_ATTR.load(Ordering::Relaxed) { + self.from_attrs(tcx, node) + } else { + self.0 + } + } + + fn from_attrs(self, tcx: TyCtxt<'_>, node: HirId) -> Option { + once(node) + .chain(tcx.hir_parent_id_iter(node)) + .find_map(|id| parse_attrs(tcx.sess, tcx.hir_attrs(id))) + .or(self.0) } /// Checks if a required version from [this module](self) is met at the current node @@ -138,6 +152,14 @@ impl Msrv { self.current(cx).is_none_or(|msrv| msrv >= required) } + /// Checks if a required version from [this module](self) is met at the specified node + /// + /// If the crate being linted uses an `#[clippy::msrv]` attribute this will search the parent + /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations + pub fn meets_at(self, tcx: TyCtxt<'_>, node: HirId, required: RustcVersion) -> bool { + self.at(tcx, node).is_none_or(|msrv| msrv >= required) + } + pub fn read_cargo(&mut self, sess: &Session) { let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") .ok() diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index ddb3c2d6cb4a5..d0316a77c8ff4 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -6,9 +6,8 @@ use crate::msrvs::{self, Msrv}; use hir::LangItem; 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_hir::{self as hir, HirId, RustcVersion, StableSince}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::LateContext; @@ -420,14 +419,17 @@ fn check_terminator<'tcx>( /// Checks if the given `def_id` is a stable const fn, in respect to the given MSRV. pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool { - cx.tcx.is_const_fn(def_id) - && cx - .tcx + is_stable_const_fn_at(cx.tcx, cx.last_node_with_lint_attrs, def_id, msrv) +} + +/// Checks if the given `def_id` is a stable const fn, in respect to the given MSRV. +pub fn is_stable_const_fn_at(tcx: TyCtxt<'_>, node: HirId, def_id: DefId, msrv: Msrv) -> bool { + tcx.is_const_fn(def_id) + && tcx .lookup_const_stability(def_id) .or_else(|| { - cx.tcx - .trait_of_assoc(def_id) - .and_then(|trait_def_id| cx.tcx.lookup_const_stability(trait_def_id)) + tcx.trait_of_assoc(def_id) + .and_then(|trait_def_id| tcx.lookup_const_stability(trait_def_id)) }) .is_none_or(|const_stab| { if let rustc_hir::StabilityLevel::Stable { since, .. } = const_stab.level { @@ -441,10 +443,10 @@ pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bo StableSince::Err(_) => return false, }; - msrv.meets(cx, const_stab_rust_version) + msrv.meets_at(tcx, node, const_stab_rust_version) } else { // Unstable const fn, check if the feature is enabled. - cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none() + tcx.features().enabled(const_stab.feature) && msrv.at(tcx, node).is_none() } }) } diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 28449a75a8fc5..749c8c02a84b5 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,6 +1,6 @@ use crate::get_enclosing_block; use crate::msrvs::Msrv; -use crate::qualify_min_const_fn::is_stable_const_fn; +use crate::qualify_min_const_fn::is_stable_const_fn_at; use crate::res::MaybeResPath; use crate::ty::needs_ordered_drop; use core::ops::ControlFlow; @@ -8,8 +8,8 @@ use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr}; use rustc_hir::{ - self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, - ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource, + self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, CRATE_HIR_ID, Expr, ExprKind, HirId, + ItemId, ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -322,13 +322,14 @@ pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tc .is_some() } -/// Checks if the given expression is a constant. -pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { - struct V<'a, 'tcx> { - cx: &'a LateContext<'tcx>, +/// Checks if the given expression can be evaluated as a constant at the specified node +pub fn is_const_evaluatable<'tcx>(tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'tcx>, e: &'tcx Expr<'_>) -> bool { + struct V<'tcx> { + tcx: TyCtxt<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, } - impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'tcx> { type Result = ControlFlow<()>; type NestedFilter = intravisit::nested_filter::None; @@ -343,29 +344,28 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> }, _, ) if self - .cx + .typeck .qpath_res(p, hir_id) .opt_def_id() - .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, + .is_some_and(|id| is_stable_const_fn_at(self.tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, ExprKind::MethodCall(..) if self - .cx - .typeck_results() + .typeck .type_dependent_def_id(e.hir_id) - .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, + .is_some_and(|id| is_stable_const_fn_at(self.tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, ExprKind::Binary(_, lhs, rhs) - if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty() - && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, - ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (), - ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (), + if self.typeck.expr_ty(lhs).peel_refs().is_primitive_ty() + && self.typeck.expr_ty(rhs).peel_refs().is_primitive_ty() => {}, + ExprKind::Unary(UnOp::Deref, e) if self.typeck.expr_ty(e).is_raw_ptr() => (), + ExprKind::Unary(_, e) if self.typeck.expr_ty(e).peel_refs().is_primitive_ty() => (), ExprKind::Index(base, _, _) if matches!( - self.cx.typeck_results().expr_ty(base).peel_refs().kind(), + self.typeck.expr_ty(base).peel_refs().kind(), ty::Slice(_) | ty::Array(..) ) => {}, ExprKind::Path(ref p) if matches!( - self.cx.qpath_res(p, e.hir_id), + self.typeck.qpath_res(p, e.hir_id), Res::Def( DefKind::Const { .. } | DefKind::AssocConst { .. } @@ -403,7 +403,7 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> } } - let mut v = V { cx }; + let mut v = V { tcx, typeck }; v.visit_expr(e).is_continue() } From 29f1ca9e191c347caea9babdeb6b7a07be846ebe Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 12 Jun 2026 10:16:30 -0400 Subject: [PATCH 088/278] Take `TyCtxt` in `for_each_expr` --- clippy_lints/src/casts/needless_type_cast.rs | 2 +- clippy_lints/src/collection_is_never_read.rs | 2 +- clippy_lints/src/doc/missing_headers.rs | 2 +- clippy_lints/src/entry.rs | 2 +- clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs | 2 +- clippy_lints/src/loops/char_indices_as_byte_indices.rs | 4 ++-- clippy_lints/src/methods/expect_fun_call.rs | 2 +- clippy_lints/src/methods/manual_inspect.rs | 2 +- clippy_lints/src/methods/or_fun_call.rs | 4 ++-- clippy_lints/src/methods/str_splitn.rs | 2 +- clippy_lints/src/missing_fields_in_debug.rs | 4 ++-- clippy_lints/src/needless_late_init.rs | 2 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 2 +- clippy_lints/src/non_std_lazy_statics.rs | 2 +- clippy_lints/src/panic_in_result_fn.rs | 2 +- clippy_lints/src/redundant_test_prefix.rs | 2 +- clippy_lints/src/returns/let_and_return.rs | 2 +- clippy_lints/src/set_contains_or_insert.rs | 2 +- clippy_lints/src/shadow.rs | 2 +- clippy_lints/src/string_patterns.rs | 2 +- clippy_lints/src/undocumented_unsafe_blocks.rs | 2 +- .../src/repeated_is_diagnostic_item.rs | 4 ++-- clippy_utils/src/msrvs.rs | 6 +++--- clippy_utils/src/usage.rs | 4 ++-- clippy_utils/src/visitors.rs | 10 +++++----- 25 files changed, 36 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/casts/needless_type_cast.rs b/clippy_lints/src/casts/needless_type_cast.rs index 844d4c7acbe7f..5cfbffc6c3f66 100644 --- a/clippy_lints/src/casts/needless_type_cast.rs +++ b/clippy_lints/src/casts/needless_type_cast.rs @@ -249,7 +249,7 @@ fn can_coerce_to_target_type(expr: &Expr<'_>) -> bool { fn check_binding_usages<'a>(cx: &LateContext<'a>, body: &Body<'a>, hir_id: HirId, binding_info: &BindingInfo<'a>) { let mut usages = Vec::new(); - for_each_expr(cx, body.value, |expr| { + for_each_expr(cx.tcx, body.value, |expr| { if let ExprKind::Path(ref qpath) = expr.kind && !expr.span.from_expansion() && let Res::Local(id) = cx.qpath_res(qpath, expr.hir_id) diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index ddd3e8b805d19..4ddd396c50ce4 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -79,7 +79,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI let mut has_read_access = false; // Inspect all expressions and sub-expressions in the block. - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { // Ignore expressions that are not simply `id`. if expr.res_local_id() != Some(id) { return ControlFlow::Continue(()); diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index b164a9a99782b..4139df9cd7997 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -99,7 +99,7 @@ pub fn check( fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { let mut panic_span = None; let typeck = cx.tcx.typeck_body(body_id); - for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| { + for_each_expr(cx.tcx, cx.tcx.hir_body(body_id), |expr| { if is_inside_always_const_context(cx.tcx, expr.hir_id) { return ControlFlow::::Continue(()); } diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index a24cc957df070..cba40abf04e17 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -604,7 +604,7 @@ fn is_any_expr_in_map_used<'tcx>( map: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>, ) -> bool { - for_each_expr(cx, map, |e| { + for_each_expr(cx.tcx, map, |e| { if spanless_eq.eq_expr(ctxt, e, expr) { return ControlFlow::Break(()); } diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index e49dee4164b88..f3952b2687a56 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -50,7 +50,7 @@ fn check_raw_ptr<'tcx>( if !raw_ptrs.is_empty() { let typeck = cx.tcx.typeck_body(body.id()); - let _: Option = for_each_expr(cx, body.value, |e| { + let _: Option = for_each_expr(cx.tcx, body.value, |e| { match e.kind { hir::ExprKind::Call(f, args) if is_unsafe_fn(cx, typeck.expr_ty(f)) => { for arg in args { diff --git a/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/clippy_lints/src/loops/char_indices_as_byte_indices.rs index 41022e6a5321b..f4d71d0524b22 100644 --- a/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -48,7 +48,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr && let PatKind::Binding(_, binding_id, ..) = pat.kind { // Destructured iterator element `(idx, _)`, look for uses of the binding - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if expr.res_local_id() == Some(binding_id) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); } @@ -56,7 +56,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr }); } else if let PatKind::Binding(_, binding_id, ..) = pat.kind { // Bound as a tuple, look for `tup.0` - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Field(e, field) = expr.kind && e.res_local_id() == Some(binding_id) && field.name == sym::integer(0) diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 89b37d507b702..97a03f9868b1d 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -100,7 +100,7 @@ fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Ex } fn contains_call<'a>(cx: &LateContext<'a>, arg: &'a hir::Expr<'a>) -> bool { - for_each_expr(cx, arg, |expr| { + for_each_expr(cx.tcx, arg, |expr| { if matches!(expr.kind, hir::ExprKind::MethodCall { .. } | hir::ExprKind::Call { .. }) && !is_inside_always_const_context(cx.tcx, expr.hir_id) { diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index bcda19e32e097..5ef8450da93bc 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -47,7 +47,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: let can_lint = for_each_expr_without_closures(block.stmts, |e| { if let ExprKind::Closure(c) = e.kind { // Nested closures don't need to treat returns specially. - let _: Option = for_each_expr(cx, cx.tcx.hir_body(c.body).value, |e| { + let _: Option = for_each_expr(cx.tcx, cx.tcx.hir_body(c.body).value, |e| { if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, ctxt, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index a07cd5a8925ae..332411a00ede6 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( ) { if let [arg] = args { let inner_arg = peel_blocks(arg); - for_each_expr(cx, inner_arg, |ex| { + for_each_expr(cx.tcx, inner_arg, |ex| { // `or_fun_call` lint needs to take nested expr into account, // but `unwrap_or_default` lint doesn't, we don't want something like: // `opt.unwrap_or(Foo { inner: String::default(), other: 1 })` to get replaced by @@ -72,7 +72,7 @@ pub(super) fn check<'tcx>( // `map_or` takes two arguments if let [arg, lambda] = args { let inner_arg = peel_blocks(arg); - for_each_expr(cx, inner_arg, |ex| { + for_each_expr(cx.tcx, inner_arg, |ex| { let is_top_most_expr = ex.hir_id == inner_arg.hir_id; match ex.kind { hir::ExprKind::Call(fun, fun_args) => { diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index fff203296bce9..13782a492cbc5 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -214,7 +214,7 @@ fn indirect_usage<'tcx>( }) = stmt.kind { let mut path_to_binding = None; - let _: Option = for_each_expr(cx, init_expr, |e| { + let _: Option = for_each_expr(cx.tcx, init_expr, |e| { if e.res_local_id() == Some(binding) { path_to_binding = Some(e); } diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 332c19e140b3e..861105aa7317a 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -109,7 +109,7 @@ fn should_lint<'tcx>( // Is there a call to `DebugStruct::debug_struct`? Do lint if there is. let mut has_debug_struct = false; - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { let recv_ty = typeck_results.expr_ty(recv).peel_refs(); @@ -166,7 +166,7 @@ fn check_struct<'tcx>( let mut has_direct_field_access = false; let mut field_accesses = FxHashSet::default(); - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { if let ExprKind::Field(target, ident) = expr.kind && let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs() && target_ty == self_ty diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index a2206102f053d..82ab15578e064 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -64,7 +64,7 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]); fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool { - for_each_expr(cx, stmt, |e| { + for_each_expr(cx.tcx, stmt, |e| { if matches!(e.kind, ExprKind::Assign(..)) { ControlFlow::Break(()) } else { diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index dea88e592f2a5..0d5b2ee6ba29e 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // We retrieve all the closures declared in the function because they will not be found // by `euv::Delegate`. let mut closures: FxIndexSet = FxIndexSet::default(); - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Closure(closure) = expr.kind { closures.insert(closure.def_id); } diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index 487eaecf18890..2755e3add760e 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -198,7 +198,7 @@ impl LazyInfo { // visit body to collect `Lazy::new` calls let mut new_fn_calls = FxIndexMap::default(); - for_each_expr::<(), ()>(cx, body, |ex| { + for_each_expr::<(), ()>(cx.tcx, body, |ex| { if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id) && paths::ONCE_CELL_SYNC_LAZY_NEW.matches(cx, fn_did) { diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index cc147a4a84bdc..fa7a5d8feb380 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { let mut panics = Vec::new(); - let _: Option = for_each_expr(cx, body.value, |e| { + let _: Option = for_each_expr(cx.tcx, body.value, |e| { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return ControlFlow::Continue(Descend::Yes); }; diff --git a/clippy_lints/src/redundant_test_prefix.rs b/clippy_lints/src/redundant_test_prefix.rs index 602093259eaec..2dc03328fef86 100644 --- a/clippy_lints/src/redundant_test_prefix.rs +++ b/clippy_lints/src/redundant_test_prefix.rs @@ -144,7 +144,7 @@ fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: S // Also check that within the body of the function there is also no function call // with the same name (since it will result in recursion) - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Path(qpath) = &expr.kind && let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id() && let Some(name) = tcx.opt_item_name(def_id) diff --git a/clippy_lints/src/returns/let_and_return.rs b/clippy_lints/src/returns/let_and_return.rs index 2ec921ed21c7d..c1a909fdde4e1 100644 --- a/clippy_lints/src/returns/let_and_return.rs +++ b/clippy_lints/src/returns/let_and_return.rs @@ -67,7 +67,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) } } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - for_each_expr(cx, expr, |e| { + for_each_expr(cx.tcx, expr, |e| { if let Some(def_id) = fn_def_id(cx, e) && cx .tcx diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs index 4aba49071ee56..03acfb22c6c82 100644 --- a/clippy_lints/src/set_contains_or_insert.rs +++ b/clippy_lints/src/set_contains_or_insert.rs @@ -127,7 +127,7 @@ fn find_insert_calls<'tcx>( contains_expr: &OpExpr<'tcx>, expr: &'tcx Expr<'_>, ) -> Option> { - for_each_expr(cx, expr, |e| { + for_each_expr(cx.tcx, expr, |e| { if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym::insert) && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.receiver, insert_expr.receiver) && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.value, insert_expr.value) diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 4ddf82773d872..5072e39afb8c1 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -199,7 +199,7 @@ pub fn is_local_used_except<'tcx>( id: HirId, except: Option, ) -> bool { - for_each_expr(cx, visitable, |e| { + for_each_expr(cx.tcx, visitable, |e| { if except.is_some_and(|it| it == e.hir_id) { ControlFlow::Continue(Descend::No) } else if e.res_local_id() == Some(id) { diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index b4eb8977bf0fb..cf7ac4ae2e0f0 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -147,7 +147,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< // We want to retrieve all the comparisons done. // They are ordered in a nested way and so we need to traverse the AST to collect them all. - if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> { + if for_each_expr(cx.tcx, body.value, |sub_expr| -> ControlFlow<(), Descend> { match sub_expr.kind { ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => { if left.res_local_id() == Some(binding) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 2bf1d8be46536..d35139f2f6b9b 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -393,7 +393,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>( } // this should roughly be the reverse of `block_parents_have_safety_comment` - if for_each_expr(cx, expr, |expr| match expr.kind { + if for_each_expr(cx.tcx, expr, |expr| match expr.kind { hir::ExprKind::Block( Block { rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), diff --git a/clippy_lints_internal/src/repeated_is_diagnostic_item.rs b/clippy_lints_internal/src/repeated_is_diagnostic_item.rs index b300dfa27b0eb..e0148c79477c0 100644 --- a/clippy_lints_internal/src/repeated_is_diagnostic_item.rs +++ b/clippy_lints_internal/src/repeated_is_diagnostic_item.rs @@ -541,7 +541,7 @@ fn extract_nested_is_diag_item<'tcx>( cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, ) -> Option<(Span, (&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>))> { - for_each_expr(cx, cond, |cond_part| { + for_each_expr(cx.tcx, cond, |cond_part| { if let Some(res) = extract_is_diag_item(cx, cond_part) { ControlFlow::Break((cond_part.span, res)) } else { @@ -554,7 +554,7 @@ fn extract_nested_is_diagnostic_item<'tcx>( cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, ) -> Option<(Span, (&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>))> { - for_each_expr(cx, cond, |cond_part| { + for_each_expr(cx.tcx, cond, |cond_part| { if let Some(res) = extract_is_diagnostic_item(cx, cond_part) { ControlFlow::Break((cond_part.span, res)) } else { diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index daf9de05a2948..d9b34ebfe21e4 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -119,7 +119,7 @@ impl Msrv { /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations pub fn current(self, cx: &LateContext<'_>) -> Option { if SEEN_MSRV_ATTR.load(Ordering::Relaxed) { - self.from_attrs(cx.tcx, cx.last_node_with_lint_attrs) + self.for_attrs(cx.tcx, cx.last_node_with_lint_attrs) } else { self.0 } @@ -131,13 +131,13 @@ impl Msrv { /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations pub fn at(self, tcx: TyCtxt<'_>, node: HirId) -> Option { if SEEN_MSRV_ATTR.load(Ordering::Relaxed) { - self.from_attrs(tcx, node) + self.for_attrs(tcx, node) } else { self.0 } } - fn from_attrs(self, tcx: TyCtxt<'_>, node: HirId) -> Option { + fn for_attrs(self, tcx: TyCtxt<'_>, node: HirId) -> Option { once(node) .chain(tcx.hir_parent_id_iter(node)) .find_map(|id| parse_attrs(tcx.sess, tcx.hir_attrs(id))) diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 4a1d24024915b..d644cd5a4aace 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -195,7 +195,7 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { } pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool { - for_each_expr(cx, v, |e| { + for_each_expr(cx.tcx, v, |e| { if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { @@ -220,7 +220,7 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr let loop_start = get_enclosing_loop_or_multi_call_closure(cx, after).map(|e| e.hir_id); let mut past_expr = false; - for_each_expr(cx, block, |e| { + for_each_expr(cx.tcx, block, |e| { if past_expr { if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 749c8c02a84b5..ca34d4a831c91 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -145,7 +145,7 @@ pub fn for_each_expr_without_closures<'tcx, B, C: Continue>( /// Calls the given function once for each expression contained. This will enter bodies, but not /// nested items. pub fn for_each_expr<'tcx, B, C: Continue>( - cx: &LateContext<'tcx>, + tcx: TyCtxt<'tcx>, node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, ) -> Option { @@ -188,7 +188,7 @@ pub fn for_each_expr<'tcx, B, C: Continue>( ControlFlow::Continue(()) } } - let mut v = V { tcx: cx.tcx, f }; + let mut v = V { tcx, f }; node.visit(&mut v).break_value() } @@ -299,7 +299,7 @@ where /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { - for_each_expr(cx, cx.tcx.hir_body(body).value, |e| { + for_each_expr(cx.tcx, cx.tcx.hir_body(body).value, |e| { if let ExprKind::Path(p) = &e.kind && cx.qpath_res(p, e.hir_id) == res { @@ -312,7 +312,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { - for_each_expr(cx, visitable, |e| { + for_each_expr(cx.tcx, visitable, |e| { if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { @@ -785,7 +785,7 @@ pub fn local_used_once<'tcx>( ) -> Option<&'tcx Expr<'tcx>> { let mut expr = None; - let cf = for_each_expr(cx, visitable, |e| { + let cf = for_each_expr(cx.tcx, visitable, |e| { if e.res_local_id() == Some(id) && expr.replace(e).is_some() { ControlFlow::Break(()) } else { From 9deffbde3bc5861ca27406ad77d261bd24a1ade2 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 12 Jun 2026 10:29:16 -0400 Subject: [PATCH 089/278] Use `for_each_expr` in `is_const_evaluable`. --- clippy_utils/src/visitors.rs | 136 +++++++++++++++-------------------- 1 file changed, 58 insertions(+), 78 deletions(-) diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index ca34d4a831c91..ca1fa57cffc10 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -324,87 +324,67 @@ pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tc /// Checks if the given expression can be evaluated as a constant at the specified node pub fn is_const_evaluatable<'tcx>(tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'tcx>, e: &'tcx Expr<'_>) -> bool { - struct V<'tcx> { - tcx: TyCtxt<'tcx>, - typeck: &'tcx TypeckResults<'tcx>, - } - - impl<'tcx> Visitor<'tcx> for V<'tcx> { - type Result = ControlFlow<()>; - type NestedFilter = intravisit::nested_filter::None; - - fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { - match e.kind { - ExprKind::ConstBlock(_) => return ControlFlow::Continue(()), - ExprKind::Call( - &Expr { - kind: ExprKind::Path(ref p), - hir_id, - .. - }, - _, - ) if self - .typeck - .qpath_res(p, hir_id) - .opt_def_id() - .is_some_and(|id| is_stable_const_fn_at(self.tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, - ExprKind::MethodCall(..) - if self - .typeck - .type_dependent_def_id(e.hir_id) - .is_some_and(|id| is_stable_const_fn_at(self.tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, - ExprKind::Binary(_, lhs, rhs) - if self.typeck.expr_ty(lhs).peel_refs().is_primitive_ty() - && self.typeck.expr_ty(rhs).peel_refs().is_primitive_ty() => {}, - ExprKind::Unary(UnOp::Deref, e) if self.typeck.expr_ty(e).is_raw_ptr() => (), - ExprKind::Unary(_, e) if self.typeck.expr_ty(e).peel_refs().is_primitive_ty() => (), - ExprKind::Index(base, _, _) - if matches!( - self.typeck.expr_ty(base).peel_refs().kind(), - ty::Slice(_) | ty::Array(..) - ) => {}, - ExprKind::Path(ref p) - if matches!( - self.typeck.qpath_res(p, e.hir_id), - Res::Def( - DefKind::Const { .. } - | DefKind::AssocConst { .. } - | DefKind::AnonConst - | DefKind::ConstParam - | DefKind::Ctor(..) - | DefKind::Fn - | DefKind::AssocFn, - _ - ) | Res::SelfCtor(_) - ) => {}, - - ExprKind::AddrOf(..) - | ExprKind::Array(_) - | ExprKind::Block(..) - | ExprKind::Cast(..) - | ExprKind::DropTemps(_) - | ExprKind::Field(..) - | ExprKind::If(..) - | ExprKind::Let(..) - | ExprKind::Lit(_) - | ExprKind::Match(..) - | ExprKind::Repeat(..) - | ExprKind::Struct(..) - | ExprKind::Tup(_) - | ExprKind::Type(..) - | ExprKind::UnsafeBinderCast(..) => (), - - _ => { - return ControlFlow::Break(()); + for_each_expr(tcx, e, move |e| { + match e.kind { + ExprKind::ConstBlock(_) => return ControlFlow::Continue(Descend::No), + ExprKind::Call( + &Expr { + kind: ExprKind::Path(ref p), + hir_id, + .. }, - } + _, + ) if typeck + .qpath_res(p, hir_id) + .opt_def_id() + .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, + ExprKind::MethodCall(..) + if typeck + .type_dependent_def_id(e.hir_id) + .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, + ExprKind::Binary(_, lhs, rhs) + if typeck.expr_ty(lhs).peel_refs().is_primitive_ty() + && typeck.expr_ty(rhs).peel_refs().is_primitive_ty() => {}, + ExprKind::Unary(UnOp::Deref, e) if typeck.expr_ty(e).is_raw_ptr() => (), + ExprKind::Unary(_, e) if typeck.expr_ty(e).peel_refs().is_primitive_ty() => (), + ExprKind::Index(base, _, _) + if matches!(typeck.expr_ty(base).peel_refs().kind(), ty::Slice(_) | ty::Array(..)) => {}, + ExprKind::Path(ref p) + if matches!( + typeck.qpath_res(p, e.hir_id), + Res::Def( + DefKind::Const { .. } + | DefKind::AssocConst { .. } + | DefKind::AnonConst + | DefKind::ConstParam + | DefKind::Ctor(..) + | DefKind::Fn + | DefKind::AssocFn, + _ + ) | Res::SelfCtor(_) + ) => {}, + + ExprKind::AddrOf(..) + | ExprKind::Array(_) + | ExprKind::Block(..) + | ExprKind::Cast(..) + | ExprKind::DropTemps(_) + | ExprKind::Field(..) + | ExprKind::If(..) + | ExprKind::Let(..) + | ExprKind::Lit(_) + | ExprKind::Match(..) + | ExprKind::Repeat(..) + | ExprKind::Struct(..) + | ExprKind::Tup(_) + | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) => {}, - walk_expr(self, e) + _ => return ControlFlow::Break(()), } - } - - let mut v = V { tcx, typeck }; - v.visit_expr(e).is_continue() + ControlFlow::Continue(Descend::Yes) + }) + .is_none() } /// Checks if the given expression performs an unsafe operation outside of an unsafe block. From aac767264fea18545aaeff61103f9750aa24df46 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 12 Jun 2026 11:25:01 -0400 Subject: [PATCH 090/278] Store the `ConstEvalCtxt` inside the `expr_eagerness` visitor. --- clippy_utils/src/consts.rs | 6 +- clippy_utils/src/eager_or_lazy.rs | 105 ++++++++++++++---------------- 2 files changed, 51 insertions(+), 60 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 26aef9c1fc910..12e5adc6dfdaa 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -522,9 +522,9 @@ pub fn eval_int(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { /// /// See the module level documentation for some context. pub struct ConstEvalCtxt<'tcx> { - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - typeck: &'tcx TypeckResults<'tcx>, + pub tcx: TyCtxt<'tcx>, + pub typing_env: ty::TypingEnv<'tcx>, + pub typeck: &'tcx TypeckResults<'tcx>, source: Cell, ctxt: Cell, } diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 68f1d0c2bb9fd..1c133fccdb308 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -11,15 +11,15 @@ use crate::consts::{ConstEvalCtxt, FullInt}; use crate::sym; -use crate::ty::{all_predicates_of, is_copy}; +use crate::ty::all_predicates_of; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Symbol; use std::{cmp, ops}; @@ -48,17 +48,17 @@ impl ops::BitOrAssign for EagernessSuggestion { } /// Determine the eagerness of the given function call. -fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { +fn fn_eagerness(tcx: TyCtxt<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { use EagernessSuggestion::{Eager, Lazy, NoChange}; - let ty = match cx.tcx.impl_of_assoc(fn_id) { - Some(id) => cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(), + let ty = match tcx.impl_of_assoc(fn_id) { + Some(id) => tcx.type_of(id).instantiate_identity().skip_norm_wip(), None => return Lazy, }; if (matches!(name, sym::is_empty | sym::len) || name.as_str().starts_with("as_")) && have_one_arg { if matches!( - cx.tcx.crate_name(fn_id.krate), + tcx.crate_name(fn_id.krate), sym::std | sym::core | sym::alloc | sym::proc_macro ) { Eager @@ -71,22 +71,20 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: // Due to the limited operations on these types functions should be fairly cheap. if def.variants().iter().flat_map(|v| v.fields.iter()).any(|x| { matches!( - cx.tcx - .type_of(x.did) + tcx.type_of(x.did) .instantiate_identity() .skip_norm_wip() .peel_refs() .kind(), ty::Param(_) ) - }) && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { - ty::ClauseKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker, + }) && all_predicates_of(tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { + ty::ClauseKind::Trait(pred) => tcx.trait_def(pred.trait_ref.def_id).is_marker, _ => true, }) && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_))) { // Limit the function to either `(self) -> bool` or `(&self) -> bool` - match &**cx - .tcx + match &**tcx .fn_sig(fn_id) .instantiate_identity() .skip_norm_wip() @@ -104,14 +102,12 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: } } -fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool { +fn res_has_significant_drop(res: Res, ecx: &ConstEvalCtxt<'_>, e: &Expr<'_>) -> bool { if let Res::Def(DefKind::Ctor(..) | DefKind::Variant | DefKind::Enum | DefKind::Struct, _) | Res::SelfCtor(_) | Res::SelfTyAlias { .. } = res { - cx.typeck_results() - .expr_ty(e) - .has_significant_drop(cx.tcx, cx.typing_env()) + ecx.typeck.expr_ty(e).has_significant_drop(ecx.tcx, ecx.typing_env) } else { false } @@ -119,12 +115,12 @@ fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> boo #[expect(clippy::too_many_lines)] fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { - struct V<'cx, 'tcx> { - cx: &'cx LateContext<'tcx>, + struct V<'tcx> { + ecx: ConstEvalCtxt<'tcx>, eagerness: EagernessSuggestion, } - impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { use EagernessSuggestion::{ForceNoChange, Lazy, NoChange}; if self.eagerness == ForceNoChange { @@ -134,8 +130,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Autoderef through a user-defined `Deref` impl can have side-effects, // so don't suggest changing it. if self - .cx - .typeck_results() + .ecx + .typeck .expr_adjustments(e) .iter() .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) @@ -152,58 +148,62 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .. }, args, - ) => match self.cx.qpath_res(path, hir_id) { + ) => match self.ecx.typeck.qpath_res(path, hir_id) { res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => { - if res_has_significant_drop(res, self.cx, e) { + if res_has_significant_drop(res, &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, - Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (), + Res::Def(_, id) if self.ecx.tcx.is_promotable_const_fn(id) => (), // No need to walk the arguments here, `is_const_evaluatable` already did - Res::Def(..) if is_const_evaluatable(self.cx.tcx, self.cx.typeck_results(), e) => { + Res::Def(..) if is_const_evaluatable(self.ecx.tcx, self.ecx.typeck, e) => { self.eagerness |= NoChange; return; }, Res::Def(_, id) => match path { QPath::Resolved(_, p) => { - self.eagerness |= - fn_eagerness(self.cx, id, p.segments.last().unwrap().ident.name, !args.is_empty()); + self.eagerness |= fn_eagerness( + self.ecx.tcx, + id, + p.segments.last().unwrap().ident.name, + !args.is_empty(), + ); }, QPath::TypeRelative(_, name) => { - self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, !args.is_empty()); + self.eagerness |= fn_eagerness(self.ecx.tcx, id, name.ident.name, !args.is_empty()); }, }, _ => self.eagerness = Lazy, }, // No need to walk the arguments here, `is_const_evaluatable` already did - ExprKind::MethodCall(..) if is_const_evaluatable(self.cx.tcx, self.cx.typeck_results(), e) => { + ExprKind::MethodCall(..) if is_const_evaluatable(self.ecx.tcx, self.ecx.typeck, e) => { self.eagerness |= NoChange; return; }, #[expect(clippy::match_same_arms)] // arm pattern can't be merged due to `ref`, see rust#105778 ExprKind::Struct(path, ..) => { - if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + if res_has_significant_drop(self.ecx.typeck.qpath_res(path, e.hir_id), &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, ExprKind::Path(ref path) => { - if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + if res_has_significant_drop(self.ecx.typeck.qpath_res(path, e.hir_id), &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, ExprKind::MethodCall(name, ..) => { self.eagerness |= self - .cx - .typeck_results() + .ecx + .typeck .type_dependent_def_id(e.hir_id) - .map_or(Lazy, |id| fn_eagerness(self.cx, id, name.ident.name, true)); + .map_or(Lazy, |id| fn_eagerness(self.ecx.tcx, id, name.ident.name, true)); }, ExprKind::Index(_, e, _) => { - let ty = self.cx.typeck_results().expr_ty_adjusted(e); - if is_copy(self.cx, ty) && !ty.is_ref() { + let ty = self.ecx.typeck.expr_ty_adjusted(e); + if self.ecx.tcx.type_is_copy_modulo_regions(self.ecx.typing_env, ty) && !ty.is_ref() { self.eagerness |= NoChange; } else { self.eagerness = Lazy; @@ -211,24 +211,19 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, // `-i32::MIN` panics with overflow checks - ExprKind::Unary(UnOp::Neg, right) if ConstEvalCtxt::new(self.cx).eval(right).is_none() => { + ExprKind::Unary(UnOp::Neg, right) if self.ecx.eval(right).is_none() => { self.eagerness |= NoChange; }, // Custom `Deref` impl might have side effects - ExprKind::Unary(UnOp::Deref, e) - if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() => - { + ExprKind::Unary(UnOp::Deref, e) if self.ecx.typeck.expr_ty(e).builtin_deref(true).is_none() => { self.eagerness |= NoChange; }, // Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe. - ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (), + ExprKind::Unary(UnOp::Deref, e) if !self.ecx.typeck.expr_ty(e).is_raw_ptr() => (), ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange, ExprKind::Unary(_, e) - if matches!( - self.cx.typeck_results().expr_ty(e).kind(), - ty::Bool | ty::Int(_) | ty::Uint(_), - ) => {}, + if matches!(self.ecx.typeck.expr_ty(e).kind(), ty::Bool | ty::Int(_) | ty::Uint(_),) => {}, // `>>` and `<<` panic when the right-hand side is greater than or equal to the number of bits in the // type of the left-hand side, or is negative. @@ -236,18 +231,16 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // overflow with constants, the compiler emits an error for it and the programmer will have to fix it. // Thus, we would realistically only delay the lint. ExprKind::Binary(op, _, right) - if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) - && ConstEvalCtxt::new(self.cx).eval(right).is_none() => + if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) && self.ecx.eval(right).is_none() => { self.eagerness |= NoChange; }, ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Div | BinOpKind::Rem) - && let right_ty = self.cx.typeck_results().expr_ty(right) - && let ecx = ConstEvalCtxt::new(self.cx) - && let left = ecx.eval(left) - && let right = ecx.eval(right).and_then(|c| c.int_value(self.cx.tcx, right_ty)) + && let right_ty = self.ecx.typeck.expr_ty(right) + && let left = self.ecx.eval(left) + && let right = self.ecx.eval(right).and_then(|c| c.int_value(self.ecx.tcx, right_ty)) && matches!( (left, right), // `1 / x`: x might be zero @@ -265,16 +258,14 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // error and it's good to have the eagerness warning up front when the user fixes the logic error. ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul) - && !self.cx.typeck_results().expr_ty(e).is_floating_point() - && let ecx = ConstEvalCtxt::new(self.cx) - && (ecx.eval(left).is_none() || ecx.eval(right).is_none()) => + && !self.ecx.typeck.expr_ty(e).is_floating_point() + && (self.ecx.eval(left).is_none() || self.ecx.eval(right).is_none()) => { self.eagerness |= NoChange; }, ExprKind::Binary(_, lhs, rhs) - if self.cx.typeck_results().expr_ty(lhs).is_primitive() - && self.cx.typeck_results().expr_ty(rhs).is_primitive() => {}, + if self.ecx.typeck.expr_ty(lhs).is_primitive() && self.ecx.typeck.expr_ty(rhs).is_primitive() => {}, // Can't be moved into a closure ExprKind::Break(..) @@ -322,7 +313,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS } let mut v = V { - cx, + ecx: ConstEvalCtxt::new(cx), eagerness: EagernessSuggestion::Eager, }; v.visit_expr(e); From 2f89caf7dadb31ac43a4fd0141c6891ca9dc4ecb Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 12 Jun 2026 11:07:40 +1000 Subject: [PATCH 091/278] Put a box within `{Early,Late}LintPassFactory` That reflects how they're mostly used and avoids the need for some long signatures. And it matches `{Early,Late}LintPassObject` nicely. --- clippy_lints/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4a7659a9cc210..12d53e742f19a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -454,7 +454,9 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. // Due to the architecture of the compiler, currently `cfg_attr` attributes on crate // level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. - store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf))); + store.register_pre_expansion_pass( + Box::new(move || Box::new(attrs::EarlyAttributes::new(conf))) + ); let format_args_storage = FormatArgsStorage::default(); let attr_storage = AttrStorage::default(); From 0a1de1d8376f17b4462cd44275b3d30ef4924316 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 19 Jun 2026 08:20:45 +1000 Subject: [PATCH 092/278] Add `cfg(no_io_statics)` to `core` Allows disabling the use of a static `AtomicPtr` as an implementation detail of `core::io`, regardless of the platform. --- library/core/Cargo.toml | 3 +++ library/core/src/io/error.rs | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 8f435dd72d7a1..5421b1d2a2652 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -40,5 +40,8 @@ check-cfg = [ 'cfg(target_has_reliable_f128)', 'cfg(target_has_reliable_f128_math)', 'cfg(llvm_enzyme)', + # Prevents use of a static variable for providing platform specific RawOsError + # functionality + 'cfg(no_io_statics)', ] diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 032231ed05262..08c67bd5d8c3e 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -17,8 +17,14 @@ )] mod repr; -#[cfg_attr(target_has_atomic_load_store = "ptr", path = "error/os_functions_atomic.rs")] -#[cfg_attr(not(target_has_atomic_load_store = "ptr"), path = "error/os_functions.rs")] +#[cfg_attr( + all(target_has_atomic_load_store = "ptr", not(no_io_statics)), + path = "error/os_functions_atomic.rs" +)] +#[cfg_attr( + not(all(target_has_atomic_load_store = "ptr", not(no_io_statics))), + path = "error/os_functions.rs" +)] mod os_functions; use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; From d0cdf757bf925b025cada614369ea1b4f0ab8f94 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sat, 24 Jan 2026 13:37:53 +0100 Subject: [PATCH 093/278] ImproperCTypes: add recursion limit Simple change to stop irregular recursive types from causing infinitely-deep recursion in type checking. --- .../rustc_lint/src/types/improper_ctypes.rs | 39 ++++++++++++++----- tests/crashes/130310.rs | 15 ------- .../ice-irregular-recursive-types.rs | 21 ++++++++++ .../lint-non-recursion-limit.rs | 12 ++++-- .../lint-non-recursion-limit.stderr | 16 ++++++++ 5 files changed, 76 insertions(+), 27 deletions(-) delete mode 100644 tests/crashes/130310.rs create mode 100644 tests/ui/lint/improper-ctypes/ice-irregular-recursive-types.rs create mode 100644 tests/ui/lint/improper-ctypes/lint-non-recursion-limit.stderr diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index b086d7e1fedb9..95d75520f3f44 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -358,6 +358,8 @@ struct VisitorState { /// Flags describing both the immediate context in which the current Ty is, /// linked to how it relates to its parent Ty (or lack thereof). outer_ty_kind: OuterTyKind, + /// Type recursion depth, to prevent infinite recursion + depth: usize, } impl RootUseFlags { @@ -385,6 +387,7 @@ impl VisitorState { VisitorState { root_use_flags: self.root_use_flags, outer_ty_kind: OuterTyKind::from_ty(current_ty), + depth: self.depth + 1, } } @@ -399,6 +402,7 @@ impl VisitorState { FnPos::Arg => RootUseFlags::ARGUMENT_TY_IN_FNPTR, }, outer_ty_kind: OuterTyKind::from_ty(current_ty), + depth: self.depth + 1, } } @@ -410,12 +414,16 @@ impl VisitorState { (CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION, (CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION, }; - VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None } + VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None, depth: 0 } } /// Get the proper visitor state for a static variable's type fn static_entry_point() -> Self { - VisitorState { root_use_flags: RootUseFlags::STATIC_TY, outer_ty_kind: OuterTyKind::None } + VisitorState { + root_use_flags: RootUseFlags::STATIC_TY, + outer_ty_kind: OuterTyKind::None, + depth: 0, + } } /// Whether the type is used in a function. @@ -737,9 +745,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Protect against infinite recursion, for example // `struct S(*mut S);`. - // FIXME: A recursion limit is necessary as well, for irregular - // recursive types. - if !self.cache.insert(ty) { + if !(self.cache.insert(ty) && self.cx.tcx.recursion_limit().value_within_limit(state.depth)) + { return FfiSafe; } @@ -989,6 +996,8 @@ impl<'tcx> ImproperCTypesLint { fn_mode: CItemKind, ) { struct FnPtrFinder<'tcx> { + current_depth: usize, + depths: Vec, spans: Vec, tys: Vec>, } @@ -996,13 +1005,16 @@ impl<'tcx> ImproperCTypesLint { impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> { fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { debug!(?ty); + self.current_depth += 1; if let hir::TyKind::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind && !abi.is_rustic_abi() { + self.depths.push(self.current_depth); self.spans.push(ty.span); } hir::intravisit::walk_ty(self, ty); + self.current_depth -= 1; } } @@ -1020,16 +1032,25 @@ impl<'tcx> ImproperCTypesLint { } } - let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() }; + let mut visitor = FnPtrFinder { + spans: Vec::new(), + tys: Vec::new(), + depths: Vec::new(), + current_depth: 0, + }; ty.visit_with(&mut visitor); visitor.visit_ty_unambig(hir_ty); - let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)); - for (fn_ptr_ty, span) in all_types { + let all_types = iter::zip( + visitor.depths.drain(..), + iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)), + ); + for (depth, (fn_ptr_ty, span)) in all_types { let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty); let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode); + let bridge_state = VisitorState { depth, ..state }; // FIXME(ctypes): make a check_for_fnptr - let ffi_res = visitor.check_type(state, fn_ptr_ty); + let ffi_res = visitor.check_type(bridge_state, fn_ptr_ty); self.process_ffi_result(cx, span, ffi_res, fn_mode); } diff --git a/tests/crashes/130310.rs b/tests/crashes/130310.rs deleted file mode 100644 index d59dd39983c78..0000000000000 --- a/tests/crashes/130310.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: rust-lang/rust#130310 - -use std::marker::PhantomData; - -#[repr(C)] -struct A { - a: *const A>, - p: PhantomData, -} - -extern "C" { - fn f(a: *const A<()>); -} - -fn main() {} diff --git a/tests/ui/lint/improper-ctypes/ice-irregular-recursive-types.rs b/tests/ui/lint/improper-ctypes/ice-irregular-recursive-types.rs new file mode 100644 index 0000000000000..22975b45a3b4d --- /dev/null +++ b/tests/ui/lint/improper-ctypes/ice-irregular-recursive-types.rs @@ -0,0 +1,21 @@ +//@ check-pass + +//! this test checks that irregular recursive types do not cause stack overflow in ImproperCTypes +//! Issue: https://github.com/rust-lang/rust/issues/94223 + +#![deny(improper_ctypes, improper_ctypes_definitions)] + +use std::marker::PhantomData; + +#[repr(C)] +struct A { + a: *const A>, // without a recursion limit, checking this ends up creating checks for + // infinitely deep types the likes of `A>>>>>` + p: PhantomData, +} + +extern "C" { + fn f(a: *const A<()>); +} + +fn main() {} diff --git a/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs index 61e95dc5a464c..9aa9be052d761 100644 --- a/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs +++ b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs @@ -1,8 +1,10 @@ -//@ check-pass +//! This test checks that the depth limit of the ImproperCTypes lints counts the depth +//! of a type properly. +//! Issue: https://github.com/rust-lang/rust/issues/130757 #![recursion_limit = "5"] #![allow(unused)] -#![deny(improper_ctypes)] +#![deny(improper_ctypes_definitions)] #[repr(C)] struct F1(*const ()); @@ -15,7 +17,7 @@ struct F4(*const ()); #[repr(C)] struct F5(*const ()); #[repr(C)] -struct F6(*const ()); +struct F6([char;8]); //oops! #[repr(C)] struct B { @@ -24,9 +26,13 @@ struct B { f3: F3, f4: F4, f5: F5, + // If the depth counter misbehaves, it will believe `f6` is "too deep" without good reason. + // And in response, it will assume `B` is safe. + // so, the error linked to F6 means the test succeeds. f6: F6, } extern "C" fn foo(_: B) {} +//~^ ERROR: uses type `char` fn main() {} diff --git a/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.stderr b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.stderr new file mode 100644 index 0000000000000..7a47da8a788f6 --- /dev/null +++ b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.stderr @@ -0,0 +1,16 @@ +error: `extern` fn uses type `char`, which is not FFI-safe + --> $DIR/lint-non-recursion-limit.rs:35:22 + | +LL | extern "C" fn foo(_: B) {} + | ^ not FFI-safe + | + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent +note: the lint level is defined here + --> $DIR/lint-non-recursion-limit.rs:7:9 + | +LL | #![deny(improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 48e7567d86742f5513ea61ee81e47595a983bad6 Mon Sep 17 00:00:00 2001 From: xmakro Date: Fri, 12 Jun 2026 15:42:38 -0700 Subject: [PATCH 094/278] perf: skip tokenizing in span_contains_cfg when no '#' is present --- clippy_utils/src/attrs.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 2341007cd49e8..ac727bf817ab1 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -108,6 +108,11 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { /// Checks whether the given span contains a `#[cfg(..)]` attribute pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { s.check_text(cx, |src| { + // PERF: A `#[cfg]` needs a literal `#`, so skip the lexer when the source has none. + if !src.contains('#') { + return false; + } + let mut iter = tokenize_with_text(src); // Search for the token sequence [`#`, `[`, `cfg`] From 161d0e2f23b37650de412f708bf2a105193e77e7 Mon Sep 17 00:00:00 2001 From: Gri-ffin Date: Fri, 19 Jun 2026 09:31:45 +0100 Subject: [PATCH 095/278] avoid const eval in items with impossible bounds --- clippy_utils/src/consts.rs | 19 ++++++++++++------- tests/ui/crashes/ice-16950.rs | 1 - 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 5b74abdb69ff1..148f89d3e918a 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -885,17 +885,22 @@ impl<'tcx> ConstEvalCtxt<'tcx> { _ => return None, }; - let args = self.typeck.node_args(id); - // We must use the const's own TypingEnv here. - // If we rely on the caller, a 'where Self: Sized' bound - // could trick us into thinking an unsized type is safe, triggering ICE later. - let const_typing_env = ty::TypingEnv::post_analysis(self.tcx, did); - if args.types().any(|ty| !ty.is_sized(self.tcx, const_typing_env)) { + let owner_def_id = self.typeck.hir_owner.def_id.to_def_id(); + let identity_args = ty::GenericArgs::identity_for_item(self.tcx, owner_def_id); + // Don't try to fully evaluate consts inside code whose bounds can't be satisfied. + if self + .tcx + .instantiate_and_check_impossible_predicates((owner_def_id, identity_args)) + { return None; } self.tcx - .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(did, args), qpath.span()) + .const_eval_resolve( + self.typing_env, + mir::UnevaluatedConst::new(did, self.typeck.node_args(id)), + qpath.span(), + ) .ok() } diff --git a/tests/ui/crashes/ice-16950.rs b/tests/ui/crashes/ice-16950.rs index 7e3d979edea3e..414b34ebed348 100644 --- a/tests/ui/crashes/ice-16950.rs +++ b/tests/ui/crashes/ice-16950.rs @@ -1,6 +1,5 @@ //@check-pass #![feature(trivial_bounds)] -#![allow(dead_code)] struct Helper(T); From 9f27f6db2c9048a6ff9d24b3a0090f3d0bfd879e Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Wed, 17 Jun 2026 14:53:50 +0300 Subject: [PATCH 096/278] [Priroda] Add structured locals output --- src/tools/miri/priroda/src/main.rs | 69 ++++++++++++++++--- .../tests/ui/locals_in_function.stdout | 8 ++- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 2739b041b0b48..7ce46224a83e0 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -6,6 +6,7 @@ extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_hir; extern crate rustc_hir_analysis; +extern crate rustc_index; extern crate rustc_interface; extern crate rustc_log; extern crate rustc_middle; @@ -19,13 +20,15 @@ use std::path::PathBuf; use miri::*; use rustc_driver::Compilation; use rustc_hir::attrs::CrateType; +use rustc_index::IndexVec; use rustc_interface::interface; use rustc_middle::mir; +use rustc_middle::mir::{Local, VarDebugInfoContents}; use rustc_middle::ty::TyCtxt; use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; -use rustc_span::Span; use rustc_span::source_map::SourceMap; +use rustc_span::{Span, Symbol}; fn find_sysroot() -> String { std::env::var("MIRI_SYSROOT") @@ -129,6 +132,10 @@ struct PrirodaContext<'tcx> { last_location: Option, } +struct LocalDesc { + name: Option, + local: Local, +} /// Controls when execution returns to the frontend. enum ResumeMode { /// Stop at the next visible MIR instruction. @@ -336,15 +343,47 @@ impl<'tcx> PrirodaContext<'tcx> { } } - /// Returns the names of all user-visible locals in the innermost stack frame. + /// Returns structured descriptions for locals in the innermost stack frame. /// - /// Uses `var_debug_info` from the MIR body, which is the same source that - /// DWARF debug info is built from, so the names match what the user wrote. - fn list_locals(&self) -> Vec { + /// Starts from all MIR locals, then enriches them with source names from + /// `var_debug_info` when a debug entry maps directly to a whole local. + fn list_locals(&self) -> Vec { let Some(frame) = self.ecx.active_thread_stack().last() else { return Vec::new(); }; - frame.body().var_debug_info.iter().map(|info| info.name.to_string()).collect() + + self.local_desc_map(frame).into_iter().collect() + } + + fn local_desc_map( + &self, + frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>, + ) -> IndexVec { + // Initialize one description per MIR local so the table can be indexed by Local. + let mut locals: IndexVec = frame + .body() + .local_decls + .iter_enumerated() + .map(|(id, _local_decl)| LocalDesc { name: None, local: id }) + .collect(); + + // FIXME: Some debug-info entries do not have a backing MIR local, for example + // because the source variable was optimized out or is represented as a + // projection. This local-indexed table cannot represent those entries yet; + // the final locals list should become a `Vec` with `id : Option`, `id` + // could be renamed to `local`. + + // Attach source names from debug info when the debug entry maps directly to a whole MIR local. + for var_debug_info in &frame.body().var_debug_info { + if let VarDebugInfoContents::Place(place) = var_debug_info.value + && let Some(local) = place.as_local() + && locals[local].name.is_none() + { + locals[local].name = Some(var_debug_info.name); + } + } + + locals } } @@ -366,7 +405,7 @@ enum BreakpointSetResult { enum CommandResult { ExecutionStopped(StepResult), BreakpointResult(BreakpointSetResult), - Locals(Vec), + Locals(Vec), // FIXME: distinguish terminating the debugger session from disconnecting a // frontend and terminating the interpreted program once multiple frontends exist. TerminateSession, @@ -403,12 +442,20 @@ impl Cli { BreakpointSetResult::Duplicate => println!("Duplicate breakpoint"), }, - CommandResult::Locals(names) => - if names.is_empty() { + CommandResult::Locals(locals_desc) => + if locals_desc.is_empty() { println!("no locals"); } else { - for name in &names { - println!("{name}"); + for local_desc in &locals_desc { + let mut name_str = "None".to_string(); + if let Some(name) = local_desc.name { + name_str = name.to_string(); + } + println!( + "Name: {}, Id: _{}", + name_str, + local_desc.local.index(), + ); } }, CommandResult::TerminateSession => { diff --git a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout index ed5889f5836e7..b6ecc17bc56b3 100644 --- a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout @@ -1,6 +1,10 @@ (priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5 (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5 -(priroda) x -y +(priroda) Name: None, Id: _0 +Name: x, Id: _1 +Name: y, Id: _2 +Name: None, Id: _3 +Name: None, Id: _4 +Name: None, Id: _5 (priroda) quitting From e4181278f7bf63ede026af3538dc7aa02565b64a Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Fri, 19 Jun 2026 12:42:23 +0300 Subject: [PATCH 097/278] [Priroda] Add type strings to locals output --- src/tools/miri/priroda/src/main.rs | 8 ++++++-- .../miri/priroda/tests/ui/locals_in_function.stdout | 12 ++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 7ce46224a83e0..fe67b21d2c134 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -135,6 +135,7 @@ struct PrirodaContext<'tcx> { struct LocalDesc { name: Option, local: Local, + ty: String, } /// Controls when execution returns to the frontend. enum ResumeMode { @@ -364,7 +365,9 @@ impl<'tcx> PrirodaContext<'tcx> { .body() .local_decls .iter_enumerated() - .map(|(id, _local_decl)| LocalDesc { name: None, local: id }) + .map(|(id, local_decl)| { + LocalDesc { name: None, local: id, ty: local_decl.ty.to_string() } + }) .collect(); // FIXME: Some debug-info entries do not have a backing MIR local, for example @@ -452,9 +455,10 @@ impl Cli { name_str = name.to_string(); } println!( - "Name: {}, Id: _{}", + "Name: {}, Id: _{}, Ty: {}", name_str, local_desc.local.index(), + local_desc.ty, ); } }, diff --git a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout index b6ecc17bc56b3..4f2786d3e17b7 100644 --- a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout @@ -1,10 +1,10 @@ (priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5 (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5 -(priroda) Name: None, Id: _0 -Name: x, Id: _1 -Name: y, Id: _2 -Name: None, Id: _3 -Name: None, Id: _4 -Name: None, Id: _5 +(priroda) Name: None, Id: _0, Ty: () +Name: x, Id: _1, Ty: i32 +Name: y, Id: _2, Ty: bool +Name: None, Id: _3, Ty: (i32, bool) +Name: None, Id: _4, Ty: i32 +Name: None, Id: _5, Ty: bool (priroda) quitting From 37df0fcaf8206eeb873f3f47f8ed350e6232c185 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Fri, 19 Jun 2026 09:53:18 +0000 Subject: [PATCH 098/278] Prepare for merging from rust-lang/rust This updates the rust-version file to 8e150217bafcaaaa0c45bf685c55fd56cec48598. --- library/stdarch/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/stdarch/rust-version b/library/stdarch/rust-version index 387bd8edd2196..e8cec4e0ee9c9 100644 --- a/library/stdarch/rust-version +++ b/library/stdarch/rust-version @@ -1 +1 @@ -029c9e18dd1f4668e1d42bb187c1c263dfe20093 +8e150217bafcaaaa0c45bf685c55fd56cec48598 From 169a8e9e5954ec6759d611260330fbfd67098f9b Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 1 Jun 2026 12:44:41 +0000 Subject: [PATCH 099/278] intrinsic-test: skip unimplemented SVE intrinsics Various SVE intrinsics are not yet implemented in stdarch, but are present in the `arm_intrinsics.json` and so should be skipped. --- .../crates/intrinsic-test/src/arm/mod.rs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 378f23ba7c361..9f75e301ace2e 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -6,7 +6,7 @@ mod types; use crate::common::SupportedArchitectureTest; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; -use crate::common::intrinsic_helpers::TypeKind; +use crate::common::intrinsic_helpers::{SimdLen, TypeKind}; use intrinsic::ArmIntrinsicType; use json_parser::get_neon_intrinsics; @@ -54,6 +54,42 @@ impl SupportedArchitectureTest for ArmArchitectureTest { // Skip bfloat intrinsics - not currently supported .filter(|i| i.results.kind() != TypeKind::BFloat) .filter(|i| !i.arguments.iter().any(|a| a.ty.kind() == TypeKind::BFloat)) + // Skip SVE intrinsics that have `f16` - not yet implemented! + .filter(|i| { + let has_f16_arg = i + .arguments + .iter() + .any(|a| a.ty.kind() == TypeKind::Float && a.ty.bit_len == Some(16)); + let has_sve_arg = i + .arguments + .iter() + .any(|a| a.ty.simd_len == Some(SimdLen::Scalable)); + !(has_f16_arg && has_sve_arg) + }) + .filter(|i| { + let has_f16_ret = + i.results.kind() == TypeKind::Float && i.results.bit_len == Some(16); + let has_sve_ret = i.results.simd_len == Some(SimdLen::Scalable); + !(has_f16_ret && has_sve_ret) + }) + // Skip `svqcvtn{u,}n*_x2` intrinsics - not yet implemented! + .filter(|i| !(i.name.starts_with("svqcvtn") && i.name.ends_with("_x2"))) + // Skip `svqrshr{u,}n*_x2` intrinsics - not yet implemented! + .filter(|i| !(i.name.starts_with("svqrshrn") && i.name.ends_with("_x2"))) + .filter(|i| !(i.name.starts_with("svqrshrun") && i.name.ends_with("_x2"))) + // Skip `svclamp*` intrinsics - not yet implemented! + .filter(|i| !i.name.starts_with("svclamp")) + // Skip `svdot{_lane,}_{s,u}32_{s,u}16` intrinsics - not yet implemented! + .filter(|i| { + i.name != "svdot_lane_u32_u16" + && i.name != "svdot_lane_s32_s16" + && i.name != "svdot_u32_u16" + && i.name != "svdot_s32_s16" + }) + // Skip `svrevd*` intrinsics - not yet implemented! + .filter(|i| !i.name.starts_with("svrevd")) + // Skip `svpsel_lane_b*` intrinsics - not yet implemented! + .filter(|i| !i.name.starts_with("svpsel_lane_b")) // Skip pointers for now, we would probably need to look at the return // type to work out how many elements we need to point to. .filter(|i| !i.arguments.iter().any(|a| a.is_ptr())) From 937c582311ee6daaf60dd099b24bb49c2d882695 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 11 Jun 2026 10:23:23 +0000 Subject: [PATCH 100/278] intrinsic-test: skip SVE intrinsics on big endian SVE intrinsics aren't available on big endian --- library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs | 1 + library/stdarch/crates/intrinsic-test/src/arm/mod.rs | 3 +++ library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs | 3 +++ library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs | 1 + 4 files changed, 8 insertions(+) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index 06cf78a422285..6050cde84ffad 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -145,6 +145,7 @@ fn json_to_intrinsic( arguments, results: result_ty, arch_tags: intr.architectures, + extension: intr.simd_isa, }) } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 9f75e301ace2e..f8b193770a236 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -37,6 +37,7 @@ impl SupportedArchitectureTest for ArmArchitectureTest { } fn create(cli_options: &ProcessedCli) -> Self { + let big_endian = cli_options.target.starts_with("aarch64_be"); let a32 = cli_options.target.starts_with("armv7"); let mut intrinsics = get_neon_intrinsics(&cli_options.filename).expect("Error parsing input file"); @@ -99,6 +100,8 @@ impl SupportedArchitectureTest for ArmArchitectureTest { .filter(|i| !cli_options.skip.contains(&i.name)) // Skip A64-specific intrinsics on A32 .filter(|i| !(a32 && i.arch_tags == vec!["A64".to_string()])) + // Skip SVE intrinsics on big endian + .filter(|i| !(big_endian && (i.extension == "SVE" || i.extension == "SVE2"))) .take(sample_size) .collect::>(); diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index d69644388a830..7c9de818c1f48 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -17,6 +17,9 @@ pub struct Intrinsic { /// Any architecture-specific tags. pub arch_tags: Vec, + + /// Specific extension that the intrinsic is from + pub extension: String, } /// Invokes `f` for each combination of the values in the constraint ranges. diff --git a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs index 6006d7919f875..7815fba2a0a11 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs @@ -145,5 +145,6 @@ fn xml_to_intrinsic( arguments, results: result.unwrap(), arch_tags: intr.cpuid, + extension: intr.tech, }) } From 922330e68c214bf33c2df6a1528fb8d8fbc3bf20 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 15 Jun 2026 10:20:39 +0000 Subject: [PATCH 101/278] intrinsic-test: do not test `svundef*` The output of these cannot be compared. --- library/stdarch/crates/intrinsic-test/src/arm/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index f8b193770a236..ee5c3ebddf12b 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -91,6 +91,10 @@ impl SupportedArchitectureTest for ArmArchitectureTest { .filter(|i| !i.name.starts_with("svrevd")) // Skip `svpsel_lane_b*` intrinsics - not yet implemented! .filter(|i| !i.name.starts_with("svpsel_lane_b")) + // Skip `svundef*` intrinsics - to avoid undefined behaviour in Rust, these return + // zeroed vectors in Rust, which are inherently going to be different than the + // undefined vectors returned by the C intrinsics. + .filter(|i| !i.name.starts_with("svundef")) // Skip pointers for now, we would probably need to look at the return // type to work out how many elements we need to point to. .filter(|i| !i.arguments.iter().any(|a| a.is_ptr())) From dabb59e7b34756247966b44663e84246b80d9a3d Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 15 Jun 2026 10:20:39 +0000 Subject: [PATCH 102/278] intrinsic-test: skip `sveorv*` (llvm-project#203921) `sveorv` intrinsics trigger a miscompile in LLVM where the call to the Rust intrinsic is optimised out and replaced with a zero, which is incorrect. --- library/stdarch/crates/intrinsic-test/src/arm/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index ee5c3ebddf12b..dff0aee5940cf 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -95,6 +95,10 @@ impl SupportedArchitectureTest for ArmArchitectureTest { // zeroed vectors in Rust, which are inherently going to be different than the // undefined vectors returned by the C intrinsics. .filter(|i| !i.name.starts_with("svundef")) + // Skip `sveorv` intrinsics - the code produced by `intrinsic-test` for these + // miscompiles and the Rust intrinsic call gets replaced by a constant zero (see + // llvm/llvm-project#203921). + .filter(|i| !i.name.starts_with("sveorv")) // Skip pointers for now, we would probably need to look at the return // type to work out how many elements we need to point to. .filter(|i| !i.arguments.iter().any(|a| a.is_ptr())) From 6638cf5aa11231259d5f572e7eb2b3f800e74bb7 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 15 Jun 2026 10:20:39 +0000 Subject: [PATCH 103/278] intrinsic-test: skip `svld*_gather_*` These tests require that we generate test arrays with values that are valid when cast to a pointer, which we don't currently support. --- library/stdarch/crates/intrinsic-test/src/arm/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index dff0aee5940cf..ef4b330dd6fe7 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -99,6 +99,9 @@ impl SupportedArchitectureTest for ArmArchitectureTest { // miscompiles and the Rust intrinsic call gets replaced by a constant zero (see // llvm/llvm-project#203921). .filter(|i| !i.name.starts_with("sveorv")) + // These load intrinsics expect each element in the scalable vector `bases` argument to + // be able to be cast to a pointer, which we don't support generating tests for yet. + .filter(|i| !(i.name.starts_with("svld") && i.name.contains("_gather_"))) // Skip pointers for now, we would probably need to look at the return // type to work out how many elements we need to point to. .filter(|i| !i.arguments.iter().any(|a| a.is_ptr())) From dc2f96485ab31598445276db97266ebc95dfd687 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 15 Jun 2026 10:20:39 +0000 Subject: [PATCH 104/278] intrinsic-test: no SVE testing under GCC GCC quickly ICEs when asked to compile intrinsic-test's wrapper sources. --- library/stdarch/crates/intrinsic-test/src/arm/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index ef4b330dd6fe7..5dec050729754 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -32,6 +32,7 @@ impl SupportedArchitectureTest for ArmArchitectureTest { // GCC uses an extra `-` in the arch name match cli_options.cc_arg_style { CcArgStyle::Clang => vec!["-march=armv8.6a+crypto+crc+dotprod+fp16"], + // SVE tests aren't run under GCC so there are no target features added for SVE CcArgStyle::Gcc => vec!["-march=armv8.6-a+crypto+crc+dotprod+fp16+sha3+sm4"], } } @@ -113,6 +114,12 @@ impl SupportedArchitectureTest for ArmArchitectureTest { .filter(|i| !(a32 && i.arch_tags == vec!["A64".to_string()])) // Skip SVE intrinsics on big endian .filter(|i| !(big_endian && (i.extension == "SVE" || i.extension == "SVE2"))) + // Skip SVE intrinsics when testing against GCC as our wrappers run into ICEs + // See + .filter(|i| { + !(matches!(cli_options.cc_arg_style, CcArgStyle::Gcc) + && (i.extension == "SVE" || i.extension == "SVE2")) + }) .take(sample_size) .collect::>(); From e8ff32d6a97cd8e121a5f6bf4b4b5fe4b16ffb65 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 20 May 2026 12:12:58 +0000 Subject: [PATCH 105/278] intrinsic-test: remove `concatln!` This macro isn't necessary and just makes the generated code being written harder to read compared to multi-line strings. --- .../intrinsic-test/src/common/gen_rust.rs | 180 ++++++++---------- 1 file changed, 84 insertions(+), 96 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 02f6e40dc06a6..132ede8d9b710 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -32,12 +32,6 @@ macro_rules! wrap_partialeq { wrap_partialeq!(NanEqF16(f16), NanEqF32(f32), NanEqF64(f64)); "#; -macro_rules! concatln { - ($($lines:expr),* $(,)?) => { - concat!($( $lines, "\n" ),*) - }; -} - /// Run rustfmt on the generated source code pub fn run_rustfmt(source_path: &str) { let output = Command::new("rustfmt") @@ -65,11 +59,14 @@ pub fn write_bin_cargo_toml( w: &mut impl std::io::Write, module_count: usize, ) -> std::io::Result<()> { - write!(w, concatln!("[workspace]", "members = ["))?; - for i in 0..module_count { - writeln!(w, " \"mod_{i}\",")?; - } - writeln!(w, "]") + write!( + w, + r#" +[workspace] +members = [{members}] +"#, + members = (0..module_count).format_with(",", |i, fmt| fmt(&format_args!("\"mod_{i}\""))) + ) } /// Writes a `Cargo.toml` for a crate with name `name` to `w` that will contain a single Rust source @@ -77,21 +74,20 @@ pub fn write_bin_cargo_toml( pub fn write_lib_cargo_toml(w: &mut impl std::io::Write, name: &str) -> std::io::Result<()> { write!( w, - concatln!( - "[package]", - "name = \"{name}\"", - "version = \"{version}\"", - "authors = [{authors}]", - "license = \"{license}\"", - "edition = \"2018\"", - "", - "[dependencies]", - "core_arch = {{ path = \"../../crates/core_arch\" }}", - "", - "[build-dependencies]", - "cc = \"1\"" - ), - name = name, + r#" +[package] +name = "{name}" +version = "{version}" +authors = [{authors}] +license = "{license}" +edition = "2018" + +[dependencies] +core_arch = {{ path = "../../crates/core_arch" }} + +[build-dependencies] +cc = "1" +"#, version = env!("CARGO_PKG_VERSION"), authors = env!("CARGO_PKG_AUTHORS") .split(":") @@ -110,22 +106,25 @@ pub fn write_lib_rs( i: usize, intrinsics: &[Intrinsic], ) -> std::io::Result<()> { - write!(w, "{notice}")?; - - writeln!(w, "#![feature(simd_ffi)]")?; - writeln!(w, "#![feature(f16)]")?; - writeln!(w, "#![allow(unused)]")?; - - // Cargo will spam the logs if these warnings are not silenced. - writeln!(w, "#![allow(non_upper_case_globals)]")?; - writeln!(w, "#![allow(non_camel_case_types)]")?; - writeln!(w, "#![allow(non_snake_case)]")?; - - writeln!(w, "{cfg}")?; - - writeln!(w, "{}", COMMON_RUST_DEFINITIONS)?; - - writeln!(w, "{definitions}")?; + writeln!( + w, + r#" +{notice} +#![feature(simd_ffi)] +#![feature(f16)] +#![allow(unused)] + +// Cargo will spam the logs if these warnings are not silenced. +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +{cfg} +{COMMON_RUST_DEFINITIONS} + +{definitions} +"# + )?; let mut seen = std::collections::HashSet::new(); @@ -224,28 +223,25 @@ fn generate_rust_test_loop( write!( w, - concatln!( - " for (id, rust, c) in specializations {{", - " for i in 0..{passes} {{", - " unsafe {{", - "{loaded_args}", - " let __rust_return_value = rust({rust_args});", - "", - " let mut __c_return_value = std::mem::MaybeUninit::uninit();", - " c(__c_return_value.as_mut_ptr(){c_args});", - " let __c_return_value = __c_return_value.assume_init();", - "", - " assert_eq!({cast_prefix}__rust_return_value{cast_suffix}, {cast_prefix}__c_return_value{cast_suffix}, \"{{id}}\");", - " }}", - " }}", - " }}", - ), + r#" +for (id, rust, c) in specializations {{ + for i in 0..{PASSES} {{ + unsafe {{ + {loaded_args} + let __rust_return_value = rust({rust_args}); + + let mut __c_return_value = std::mem::MaybeUninit::uninit(); + c(__c_return_value.as_mut_ptr(){c_args}); + let __c_return_value = __c_return_value.assume_init(); + + assert_eq!({cast_prefix}__rust_return_value{cast_suffix}, {cast_prefix}__c_return_value{cast_suffix}, "{{id}}"); + }} + }} +}} +"#, loaded_args = intrinsic.arguments.load_values_rust(), rust_args = intrinsic.arguments.as_call_param_rust(), c_args = intrinsic.arguments.as_c_call_param_rust(), - passes = PASSES, - cast_prefix = cast_prefix, - cast_suffix = cast_suffix, ) } @@ -259,7 +255,10 @@ fn create_rust_test( write!( w, - concatln!("#[test]", "fn test_{intrinsic_name}() {{"), + r#" +#[test] +fn test_{intrinsic_name}() {{ +"#, intrinsic_name = intrinsic.name, )?; @@ -279,19 +278,18 @@ pub fn write_bindings_rust( ) -> std::io::Result<()> { write!( w, - concatln!( - "#[allow(improper_ctypes)]", - "#[link(name = \"wrapper_{i}\")]", - "unsafe extern \"C\" {{" - ), - i = i + r#" +#[allow(improper_ctypes)] +#[link(name = "wrapper_{i}")] +unsafe extern "C" {{ +"#, )?; for intrinsic in intrinsics { intrinsic.iter_specializations(|imm_values| { writeln!( w, - " fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", + "fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", return_ty = intrinsic.results.rust_type(), name = intrinsic.name, imm_arglist = imm_values @@ -326,32 +324,22 @@ pub fn write_build_rs( write!( w, - concatln!( - "fn main() {{", - " cc::Build::new()", - " .file(\"../../c_programs/wrapper_{i}.c\")", - " .opt_level(2)", - " .flags(&[", - ), - i = i - )?; - - let compiler_specific_flags = match cli_options.cc_arg_style { - CcArgStyle::Gcc => GCC_FLAGS, - CcArgStyle::Clang => CLANG_FLAGS, - }; - - for flag in COMMON_FLAGS - .iter() - .chain(compiler_specific_flags) - .chain(arch_flags) - { - writeln!(w, "\"{flag}\",")?; - } - - write!( - w, - concatln!(" ])", " .compile(\"wrapper_{i}\");", "}}"), - i = i + r#" +fn main() {{ + cc::Build::new() + .file("../../c_programs/wrapper_{i}.c") + .opt_level(2) + .flags(&[{flags}]) + .compile("wrapper_{i}"); +}} +"#, + flags = COMMON_FLAGS + .iter() + .chain(match cli_options.cc_arg_style { + CcArgStyle::Gcc => GCC_FLAGS, + CcArgStyle::Clang => CLANG_FLAGS, + }) + .chain(arch_flags) + .format_with(",", |flag, fmt| fmt(&format_args!("\"{flag}\""))), ) } From 92ca3a554c1922877ed0e50f8723713fa39a5ebb Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 9 Jun 2026 08:44:17 +0000 Subject: [PATCH 106/278] intrinsic-test: remove unnecessary newlines All of the generated output is run through rustfmt so these aren't necessary. --- library/stdarch/crates/intrinsic-test/src/common/argument.rs | 4 ++-- library/stdarch/crates/intrinsic-test/src/common/values.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index eaec5b71c47e3..f1d87f6dedd7e 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -179,14 +179,14 @@ where .map(|(idx, arg)| { if arg.is_simd() { format!( - "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);\n", + "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);", name = arg.generate_name(), vals_name = test_values_array_name(&arg.ty), load = arg.ty.get_load_function(), ) } else { format!( - "let {name} = {vals_name}[(i+{idx}) % {PASSES}];\n", + "let {name} = {vals_name}[(i+{idx}) % {PASSES}];", name = arg.generate_name(), vals_name = test_values_array_name(&arg.ty), ) diff --git a/library/stdarch/crates/intrinsic-test/src/common/values.rs b/library/stdarch/crates/intrinsic-test/src/common/values.rs index 4c3dd078e034f..f7bd4a4eab58d 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/values.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/values.rs @@ -24,7 +24,7 @@ pub fn test_values_array_static( ) -> std::io::Result<()> { writeln!( w, - "static {name}: [{ty}; {load_size}] = {values};\n", + "static {name}: [{ty}; {load_size}] = {values};", name = test_values_array_name(ty), ty = ty.rust_scalar_type(), load_size = test_values_array_length(&ty), From 7cfa29252b430c4a2349952673039ef56622e3d5 Mon Sep 17 00:00:00 2001 From: Gri-ffin Date: Fri, 19 Jun 2026 14:11:00 +0100 Subject: [PATCH 107/278] only check impossible bounds for generic const accesses --- clippy_utils/src/consts.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 148f89d3e918a..f1ea32f5569a8 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -885,22 +885,22 @@ impl<'tcx> ConstEvalCtxt<'tcx> { _ => return None, }; - let owner_def_id = self.typeck.hir_owner.def_id.to_def_id(); - let identity_args = ty::GenericArgs::identity_for_item(self.tcx, owner_def_id); - // Don't try to fully evaluate consts inside code whose bounds can't be satisfied. - if self - .tcx - .instantiate_and_check_impossible_predicates((owner_def_id, identity_args)) - { - return None; + let args = self.typeck.node_args(id); + + if !args.is_empty() { + let owner_def_id = self.typeck.hir_owner.def_id.to_def_id(); + let identity_args = ty::GenericArgs::identity_for_item(self.tcx, owner_def_id); + // Don't try to fully evaluate consts inside code whose bounds can't be satisfied. + if self + .tcx + .instantiate_and_check_impossible_predicates((owner_def_id, identity_args)) + { + return None; + } } self.tcx - .const_eval_resolve( - self.typing_env, - mir::UnevaluatedConst::new(did, self.typeck.node_args(id)), - qpath.span(), - ) + .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(did, args), qpath.span()) .ok() } From 642f0e3c89c40d64c1609b6cfc7b15518cfeb003 Mon Sep 17 00:00:00 2001 From: xmakro Date: Fri, 12 Jun 2026 15:07:55 -0700 Subject: [PATCH 108/278] perf: skip match_same_arms work when the lint is allowed --- clippy_lints/src/matches/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 02b2d11d8aa61..459c2bf1fef73 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -27,7 +27,9 @@ mod wild_in_or_pats; use clippy_config::Conf; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanExt; -use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, sym, tokenize_with_text}; +use clippy_utils::{ + higher, is_direct_expn_of, is_in_const_context, is_lint_allowed, is_span_match, sym, tokenize_with_text, +}; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; use rustc_lexer::{TokenKind, is_whitespace}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -1107,9 +1109,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches { && !has_cfg { if source == MatchSource::Normal { - if !(self.msrv.meets(cx, msrvs::MATCHES_MACRO) - && match_like_matches::check_match(cx, expr, ex, arms)) - { + let is_match_like_matches = self.msrv.meets(cx, msrvs::MATCHES_MACRO) + && match_like_matches::check_match(cx, expr, ex, arms); + if !(is_match_like_matches || is_lint_allowed(cx, MATCH_SAME_ARMS, expr.hir_id)) { match_same_arms::check(cx, arms); } From dbfa2c3637661d8e0c9f9ea8fa61a39bc184cc9a Mon Sep 17 00:00:00 2001 From: xmakro Date: Fri, 12 Jun 2026 15:21:41 -0700 Subject: [PATCH 109/278] perf: run structural checks before const context queries in question_mark, manual_clamp and ranges --- clippy_lints/src/manual_clamp.rs | 30 +++++++++++++---- clippy_lints/src/question_mark.rs | 9 +++++- clippy_lints/src/ranges.rs | 53 +++++++++++++++++-------------- 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 6848af7748f3f..60ef785177485 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -140,7 +140,13 @@ struct InputMinMax<'tcx> { impl<'tcx> LateLintPass<'tcx> for ManualClamp { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !expr.span.from_expansion() && !is_in_const_context(cx) { + // Cheap kind check before the costlier const context query. + if matches!( + expr.kind, + ExprKind::If(..) | ExprKind::Match(..) | ExprKind::MethodCall(..) | ExprKind::Call(..) + ) && !expr.span.from_expansion() + && !is_in_const_context(cx) + { let suggestion = is_if_elseif_else_pattern(cx, expr) .or_else(|| is_max_min_pattern(cx, expr)) .or_else(|| is_call_max_min_pattern(cx, expr)) @@ -155,6 +161,15 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { } fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + // Cheap `if`-statement check before the costlier const context query. + if !block + .stmts + .iter() + .any(|stmt| matches!(stmt.kind, StmtKind::Expr(e) if matches!(e.kind, ExprKind::If(..)))) + { + return; + } + if is_in_const_context(cx) || !self.msrv.meets(cx, msrvs::CLAMP) { return; } @@ -293,18 +308,19 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx /// ``` fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = expr.kind + && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind + // Match method names before the costlier type queries. + && let Some((min, max)) = match (seg_first.ident.name, seg_second.ident.name) { + (sym::min, sym::max) => Some((arg_second, arg_first)), + (sym::max, sym::min) => Some((arg_first, arg_second)), + _ => None, + } && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || cx.ty_based_def(expr).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) - && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || cx.ty_based_def(receiver).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) { let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); - let (min, max) = match (seg_first.ident.name, seg_second.ident.name) { - (sym::min, sym::max) => (arg_second, arg_first), - (sym::max, sym::min) => (arg_first, arg_second), - _ => return None, - }; Some(ClampSuggestion { params: InputMinMax { input, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index cbfd1af1907b7..7f95adba870d7 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -629,6 +629,11 @@ fn is_inferred_ret_closure(expr: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + // Cheap `let` check before the costlier lint level and const context queries. + if !matches!(stmt.kind, StmtKind::Let(..)) { + return; + } + if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) || !self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) { return; @@ -646,7 +651,9 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { return; } - if !self.inside_try_block() + // Cheap `if`/`match` check before the costlier lint level and const context queries. + if matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..)) + && !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) && self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 71965ee1e29f9..cd0b02940d068 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -191,14 +191,21 @@ impl Ranges { impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, l, r) = expr.kind + && matches!( + op.node, + BinOpKind::And | BinOpKind::BitAnd | BinOpKind::Or | BinOpKind::BitOr + ) && self.msrv.meets(cx, msrvs::RANGE_CONTAINS) + && !is_in_const_context(cx) { check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } - check_exclusive_range_plus_one(cx, expr); - check_inclusive_range_minus_one(cx, expr); - check_reversed_empty_range(cx, expr); + if let Some(range) = higher::Range::hir(cx, expr) { + check_exclusive_range_plus_one(cx, expr, &range); + check_inclusive_range_minus_one(cx, expr, &range); + check_reversed_empty_range(cx, expr, &range); + } } } @@ -210,10 +217,6 @@ fn check_possible_range_contains( expr: &Expr<'_>, span: Span, ) { - if is_in_const_context(cx) { - return; - } - let combine_and = match op { BinOpKind::And | BinOpKind::BitAnd => true, BinOpKind::Or | BinOpKind::BitOr => false, @@ -481,54 +484,58 @@ fn can_switch_ranges<'tcx>( } // exclusive range plus one: `x..(y+1)` -fn check_exclusive_range_plus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +fn check_exclusive_range_plus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, range: &higher::Range<'tcx>) { check_range_switch( cx, expr, + range, RangeLimits::HalfOpen, y_plus_one, RANGE_PLUS_ONE, "an inclusive range would be more readable", - "..=", ); } // inclusive range minus one: `x..=(y-1)` -fn check_inclusive_range_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +fn check_inclusive_range_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, range: &higher::Range<'tcx>) { check_range_switch( cx, expr, + range, RangeLimits::Closed, y_minus_one, RANGE_MINUS_ONE, "an exclusive range would be more readable", - "..", ); } /// Check for a `kind` of range in `expr`, check for `predicate` on the end, -/// and emit the `lint` with `msg` and the `operator`. +/// and emit the `lint` with `msg`, suggesting the opposite range limits. fn check_range_switch<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, + range: &higher::Range<'tcx>, kind: RangeLimits, predicate: impl for<'hir> FnOnce(&Expr<'hir>) -> Option<&'hir Expr<'hir>>, lint: &'static Lint, msg: &'static str, - operator: &str, ) { - if let Some(range) = higher::Range::hir(cx, expr) - && let higher::Range { - start, - end: Some(end), - limits, - span, - } = range + if let higher::Range { + start, + end: Some(end), + limits, + span, + } = *range && span.can_be_used_for_suggestions() && limits == kind && let Some(y) = predicate(end) && can_switch_ranges(cx, span.ctxt(), expr, kind, cx.typeck_results().expr_ty(y)) { + // Suggest the opposite range limits to the ones being checked. + let operator = match kind { + RangeLimits::HalfOpen => "..=", + RangeLimits::Closed => "..", + }; span_lint_and_then(cx, lint, span, msg, |diag| { let mut app = Applicability::MachineApplicable; let start = start.map_or(String::new(), |x| { @@ -550,7 +557,7 @@ fn check_range_switch<'tcx>( } } -fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { +fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>, range: &higher::Range<'_>) { fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!( get_parent_expr(cx, expr), @@ -580,12 +587,12 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { } } - if let Some(higher::Range { + if let higher::Range { start: Some(start), end: Some(end), limits, span, - }) = higher::Range::hir(cx, expr) + } = *range && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() && let ecx = ConstEvalCtxt::new(cx) From 6423f06385c3ea91614b536ddda156db8b56ff09 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 19 Jun 2026 17:12:37 -0700 Subject: [PATCH 110/278] Treat `!` the same as `-` in `unnecessary_cast` This change improves some of the suggestions, fixes the handling of precedence, and prevents it from removing the `as` cast when that's ambiguous. --- clippy_lints/src/casts/unnecessary_cast.rs | 4 +- tests/ui/unnecessary_cast-11882.fixed | 93 +++++++++++++ tests/ui/unnecessary_cast-11882.rs | 93 +++++++++++++ tests/ui/unnecessary_cast-11882.stderr | 149 +++++++++++++++++++++ 4 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 tests/ui/unnecessary_cast-11882.fixed create mode 100644 tests/ui/unnecessary_cast-11882.rs create mode 100644 tests/ui/unnecessary_cast-11882.stderr diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 83a6cab01f9d9..c14bd056de393 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -232,7 +232,7 @@ fn lint_unnecessary_cast( // (-1).foo() instead of -1.foo()) let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(..) = parent_expr.kind - && literal_str.starts_with('-') + && literal_str.starts_with(['-', '!']) { format!("({literal_str}_{cast_to})") } else { @@ -253,7 +253,7 @@ fn lint_unnecessary_cast( fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option { match expr.kind { ExprKind::Lit(lit) => Some(lit), - ExprKind::Unary(UnOp::Neg, e) => { + ExprKind::Unary(UnOp::Neg | UnOp::Not, e) => { if let ExprKind::Lit(lit) = e.kind { Some(lit) } else { diff --git a/tests/ui/unnecessary_cast-11882.fixed b/tests/ui/unnecessary_cast-11882.fixed new file mode 100644 index 0000000000000..6b2949248d8d1 --- /dev/null +++ b/tests/ui/unnecessary_cast-11882.fixed @@ -0,0 +1,93 @@ +//@ignore-bitwidth: 32 +#![feature(const_trait_impl, const_ops)] +#![warn(clippy::unnecessary_cast)] +#![allow(unused, clippy::identity_op)] + +const TEST: u64 = (!0_u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST2: u64 = (!0_u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST3: u64 = (not(0) as u64).overflowing_shr(1_u32).0; +const TEST4: u64 = not(0_u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST5: u64 = (!not(!0_u64)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST6: u64 = 0xff_ff_ff_ff_ff_ff_ff_ff_u64.overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST7: u64 = (!0_u64 + 0).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST8: u64 = (!(0_u64 + 0)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST9: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; + +const CHK1: u64 = not(!0_u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const CHK2: u64 = (not(!0) as u64).overflowing_shr(1_u32).0; +const CHK3: u64 = (!not(0) as u64).overflowing_shr(1_u32).0; +const CHK4: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const CHK5: u64 = not(!0_u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const CHK6: u64 = (!(not(0) as u64)).overflowing_shr(1_u32).0; +const CHK7: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const CHK8: u64 = not(!0_u64 + 0).overflowing_shr(1_u32).0; +//~^ unnecessary_cast + +fn main() { + // make sure that the calculated value doesn't change + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST2 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST3 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST4 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST5 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST6 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST7 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST8 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST9 as usize]; + + let _x: [(); 0] = [(); CHK1 as usize]; + let _x: [(); 0] = [(); CHK2 as usize]; + let _x: [(); 0] = [(); CHK3 as usize]; + let _x: [(); 0] = [(); CHK4 as usize]; + let _x: [(); 0] = [(); CHK5 as usize]; + let _x: [(); 0] = [(); CHK6 as usize]; + let _x: [(); 0] = [(); CHK7 as usize]; + let _x: [(); 0] = [(); CHK8 as usize]; + + // the non-const version of the tests + let test: u64 = (!0_u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test2: u64 = (!0_u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test3: u64 = (not(0) as u64).overflowing_shr(1_u32).0; + let test4: u64 = not(0_u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test5: u64 = (!not(!0_u64)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test6: u64 = 0xff_ff_ff_ff_ff_ff_ff_ff_u64.overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test7: u64 = (!0_u64 + 0).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test8: u64 = (!(0_u64 + 0)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test9: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; + + let chk1: u64 = not(!0_u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let chk2: u64 = (not(!0) as u64).overflowing_shr(1_u32).0; + let chk3: u64 = (!not(0) as u64).overflowing_shr(1_u32).0; + let chk4: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let chk5: u64 = not(!0_u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let chk6: u64 = (!(not(0) as u64)).overflowing_shr(1_u32).0; + let chk7: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let chk8: u64 = not(!0_u64 + 0).overflowing_shr(1_u32).0; + //~^ unnecessary_cast +} + +const fn not>(x: T) -> T { + !x +} diff --git a/tests/ui/unnecessary_cast-11882.rs b/tests/ui/unnecessary_cast-11882.rs new file mode 100644 index 0000000000000..d8028308755f0 --- /dev/null +++ b/tests/ui/unnecessary_cast-11882.rs @@ -0,0 +1,93 @@ +//@ignore-bitwidth: 32 +#![feature(const_trait_impl, const_ops)] +#![warn(clippy::unnecessary_cast)] +#![allow(unused, clippy::identity_op)] + +const TEST: u64 = (!0 as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST3: u64 = (not(0) as u64).overflowing_shr(1_u32).0; +const TEST4: u64 = (not(0_u64) as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST5: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST6: u64 = (0xff_ff_ff_ff_ff_ff_ff_ff as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST7: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST8: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const TEST9: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; + +const CHK1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const CHK2: u64 = (not(!0) as u64).overflowing_shr(1_u32).0; +const CHK3: u64 = (!not(0) as u64).overflowing_shr(1_u32).0; +const CHK4: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const CHK5: u64 = (not(!0_u64) as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const CHK6: u64 = (!(not(0) as u64)).overflowing_shr(1_u32).0; +const CHK7: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const CHK8: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; +//~^ unnecessary_cast + +fn main() { + // make sure that the calculated value doesn't change + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST2 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST3 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST4 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST5 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST6 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST7 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST8 as usize]; + let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST9 as usize]; + + let _x: [(); 0] = [(); CHK1 as usize]; + let _x: [(); 0] = [(); CHK2 as usize]; + let _x: [(); 0] = [(); CHK3 as usize]; + let _x: [(); 0] = [(); CHK4 as usize]; + let _x: [(); 0] = [(); CHK5 as usize]; + let _x: [(); 0] = [(); CHK6 as usize]; + let _x: [(); 0] = [(); CHK7 as usize]; + let _x: [(); 0] = [(); CHK8 as usize]; + + // the non-const version of the tests + let test: u64 = (!0 as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test3: u64 = (not(0) as u64).overflowing_shr(1_u32).0; + let test4: u64 = (not(0_u64) as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test5: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test6: u64 = (0xff_ff_ff_ff_ff_ff_ff_ff as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test7: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test8: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let test9: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; + + let chk1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let chk2: u64 = (not(!0) as u64).overflowing_shr(1_u32).0; + let chk3: u64 = (!not(0) as u64).overflowing_shr(1_u32).0; + let chk4: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let chk5: u64 = (not(!0_u64) as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let chk6: u64 = (!(not(0) as u64)).overflowing_shr(1_u32).0; + let chk7: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let chk8: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; + //~^ unnecessary_cast +} + +const fn not>(x: T) -> T { + !x +} diff --git a/tests/ui/unnecessary_cast-11882.stderr b/tests/ui/unnecessary_cast-11882.stderr new file mode 100644 index 0000000000000..056f04c65ed35 --- /dev/null +++ b/tests/ui/unnecessary_cast-11882.stderr @@ -0,0 +1,149 @@ +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:6:19 + | +LL | const TEST: u64 = (!0 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^ help: try: `(!0_u64)` + | + = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast-11882.rs:8:20 + | +LL | const TEST2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast-11882.rs:11:20 + | +LL | const TEST4: u64 = (not(0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^ help: try: `not(0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast-11882.rs:13:20 + | +LL | const TEST5: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(!0_u64))` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:15:20 + | +LL | const TEST6: u64 = (0xff_ff_ff_ff_ff_ff_ff_ff as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0xff_ff_ff_ff_ff_ff_ff_ff_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:17:21 + | +LL | const TEST7: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:19:23 + | +LL | const TEST8: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:23:23 + | +LL | const CHK1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast-11882.rs:27:19 + | +LL | const CHK4: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(0_u64))` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast-11882.rs:29:19 + | +LL | const CHK5: u64 = (not(!0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `not(!0_u64)` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:32:25 + | +LL | const CHK7: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:34:23 + | +LL | const CHK8: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:59:21 + | +LL | let test: u64 = (!0 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast-11882.rs:61:22 + | +LL | let test2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast-11882.rs:64:22 + | +LL | let test4: u64 = (not(0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^ help: try: `not(0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast-11882.rs:66:22 + | +LL | let test5: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(!0_u64))` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:68:22 + | +LL | let test6: u64 = (0xff_ff_ff_ff_ff_ff_ff_ff as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0xff_ff_ff_ff_ff_ff_ff_ff_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:70:23 + | +LL | let test7: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:72:25 + | +LL | let test8: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:76:25 + | +LL | let chk1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast-11882.rs:80:21 + | +LL | let chk4: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(0_u64))` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast-11882.rs:82:21 + | +LL | let chk5: u64 = (not(!0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `not(!0_u64)` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:85:27 + | +LL | let chk7: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast-11882.rs:87:25 + | +LL | let chk8: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: aborting due to 24 previous errors + From c2c6cb43e85a0c98585b74dcbf142ec42a7e38ef Mon Sep 17 00:00:00 2001 From: xmakro Date: Fri, 19 Jun 2026 19:27:51 -0700 Subject: [PATCH 111/278] Fix too-short variance slice in `variances_of` cycle recovery --- compiler/rustc_query_impl/src/handle_cycle_error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_query_impl/src/handle_cycle_error.rs b/compiler/rustc_query_impl/src/handle_cycle_error.rs index 79e7788cafe81..2beef6d6e3367 100644 --- a/compiler/rustc_query_impl/src/handle_cycle_error.rs +++ b/compiler/rustc_query_impl/src/handle_cycle_error.rs @@ -104,7 +104,7 @@ pub(crate) fn variances_of<'tcx>( err: Diag<'_>, ) -> &'tcx [ty::Variance] { let _guar = err.delay_as_bug(); - let n = tcx.generics_of(def_id).own_params.len(); + let n = tcx.generics_of(def_id).count(); tcx.arena.alloc_from_iter(iter::repeat_n(ty::Bivariant, n)) } From 77a83f264f995eb1956ab390623c2e321d4fa1d0 Mon Sep 17 00:00:00 2001 From: xonx <119700621+xonx4l@users.noreply.github.com> Date: Tue, 9 Jun 2026 17:43:11 +0000 Subject: [PATCH 112/278] Add stdarch-gen-common --- library/stdarch/.github/workflows/main.yml | 2 + library/stdarch/Cargo.lock | 62 +++ .../crates/core_arch/src/hexagon/v128.rs | 1 + .../crates/core_arch/src/hexagon/v64.rs | 1 + .../crates/stdarch-gen-common/Cargo.toml | 7 + .../crates/stdarch-gen-common/src/lib.rs | 352 ++++++++++++++++++ .../crates/stdarch-gen-hexagon/Cargo.toml | 1 + .../crates/stdarch-gen-hexagon/src/main.rs | 34 +- 8 files changed, 443 insertions(+), 17 deletions(-) create mode 100644 library/stdarch/crates/stdarch-gen-common/Cargo.toml create mode 100644 library/stdarch/crates/stdarch-gen-common/src/lib.rs diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 98f6b842d135f..6333b32ab8ad4 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -337,6 +337,8 @@ jobs: cargo run --bin=stdarch-gen-loongarch --release -- crates/stdarch-gen-loongarch/lasx.spec git diff --exit-code - name: Check hexagon + env: + STDARCH_GEN_MODE: check run: | cargo run -p stdarch-gen-hexagon --release git diff --exit-code diff --git a/library/stdarch/Cargo.lock b/library/stdarch/Cargo.lock index 804879c8fdc6a..c8cfc21a5a1a1 100644 --- a/library/stdarch/Cargo.lock +++ b/library/stdarch/Cargo.lock @@ -268,6 +268,22 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -446,6 +462,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "log" version = "0.4.29" @@ -458,6 +480,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + [[package]] name = "once_cell_polyfill" version = "1.70.2" @@ -654,6 +682,19 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.23" @@ -793,11 +834,19 @@ dependencies = [ "walkdir", ] +[[package]] +name = "stdarch-gen-common" +version = "0.1.0" +dependencies = [ + "tempfile", +] + [[package]] name = "stdarch-gen-hexagon" version = "0.1.0" dependencies = [ "regex", + "stdarch-gen-common", ] [[package]] @@ -870,6 +919,19 @@ version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe" +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "termcolor" version = "1.4.1" diff --git a/library/stdarch/crates/core_arch/src/hexagon/v128.rs b/library/stdarch/crates/core_arch/src/hexagon/v128.rs index 1f0566af78ef3..1a1826ddda316 100644 --- a/library/stdarch/crates/core_arch/src/hexagon/v128.rs +++ b/library/stdarch/crates/core_arch/src/hexagon/v128.rs @@ -1,3 +1,4 @@ +// This code is automatically generated. DO NOT MODIFY. //! Hexagon HVX 128-byte vector mode intrinsics //! //! This module provides intrinsics for the Hexagon Vector Extensions (HVX) diff --git a/library/stdarch/crates/core_arch/src/hexagon/v64.rs b/library/stdarch/crates/core_arch/src/hexagon/v64.rs index e9b18b2fd8efe..3ac8b47079333 100644 --- a/library/stdarch/crates/core_arch/src/hexagon/v64.rs +++ b/library/stdarch/crates/core_arch/src/hexagon/v64.rs @@ -1,3 +1,4 @@ +// This code is automatically generated. DO NOT MODIFY. //! Hexagon HVX 64-byte vector mode intrinsics //! //! This module provides intrinsics for the Hexagon Vector Extensions (HVX) diff --git a/library/stdarch/crates/stdarch-gen-common/Cargo.toml b/library/stdarch/crates/stdarch-gen-common/Cargo.toml new file mode 100644 index 0000000000000..691be14971a0d --- /dev/null +++ b/library/stdarch/crates/stdarch-gen-common/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "stdarch-gen-common" +version = "0.1.0" +edition = "2024" + +[dependencies] +tempfile = "3" \ No newline at end of file diff --git a/library/stdarch/crates/stdarch-gen-common/src/lib.rs b/library/stdarch/crates/stdarch-gen-common/src/lib.rs new file mode 100644 index 0000000000000..13d788594ca55 --- /dev/null +++ b/library/stdarch/crates/stdarch-gen-common/src/lib.rs @@ -0,0 +1,352 @@ +//! Shared check/bless harness for stdarch generators. + +use std::error::Error as StdError; +use std::fmt; +use std::fs; +use std::io; +use std::io::Read; +use std::path::{Path, PathBuf}; + +/// First-line marker identifying an auto-generated file. Generators emit this +/// as the first line of every file they produce; the harness uses it to +/// discover which files in `committed` are owned by the generator. +pub const GENERATED_MARKER: &str = "// This code is automatically generated. DO NOT MODIFY."; + +/// Controls what `run_generator` does with the generator's output. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Mode { + /// Verify that the `committed` matches the generator's output for owned files. + /// + /// Runs the generator into a temp directory, then compares each produced + /// file against the committed copy. Returns an error on the first mismatch. + Check, + /// Update the `committed` to match the generator's output for owned files. + /// + /// Runs the generator into a temp directory and copies each produced file + /// into `committed`. If the generator no longer produces an owned file, the + /// committed copy is deleted. Files in `committed` that are not owned + /// are left untouched. + Bless, +} + +impl Mode { + /// Read the mode from the `STDARCH_GEN_MODE` environment variable. + /// + /// Recognized values: + /// - `"check"` → [`Mode::Check`] + /// - `"bless"` → [`Mode::Bless`] + /// - unset → [`Mode::Bless`] + /// - any other value → panic + pub fn from_env() -> Self { + match std::env::var("STDARCH_GEN_MODE").as_deref() { + Ok("check") => Mode::Check, + Ok("bless") => Mode::Bless, + Ok(other) => panic!("unknown STDARCH_GEN_MODE value: {other:?}"), + Err(_) => Mode::Bless, + } + } +} + +#[derive(Debug)] +pub enum Error { + Io(io::Error), + Mismatch { path: PathBuf, kind: MismatchKind }, + Generator(Box), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MismatchKind { + /// Owned file produced by the generator but absent from the `committed`. + /// Means the `committed` needs to be regenerated. + MissingInCommitted, + /// Owned file present in the `committed` but the generator no longer + /// produces it. The file must be removed from the `committed` . + ExtraInCommitted, + /// Owned file exists on both sides but contents differ. + ContentsDiffer, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Io(e) => write!(f, "I/O error: {e}"), + Error::Mismatch { path, kind } => match kind { + MismatchKind::MissingInCommitted => { + write!(f, "{}: generated but not committed", path.display()) + } + MismatchKind::ExtraInCommitted => { + write!(f, "{}: committed but no longer generated", path.display()) + } + MismatchKind::ContentsDiffer => write!(f, "{}: contents differ", path.display()), + }, + Error::Generator(e) => write!(f, "generator failed: {e}"), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::Io(e) => Some(e), + Error::Generator(e) => Some(&**e), + _ => None, + } + } +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +pub type Result = std::result::Result; + +/// Run a generator under the chosen `mode`, reconciling its output with `committed`. +/// +/// Arguments: +/// - `committed` — the directory holding the in-tree (committed) source files. +/// Files inside `committed` whose first line begins with [`GENERATED_MARKER`] +/// are treated as owned by the generator. Anything else is treated as +/// hand-written and is left untouched by `Bless` and ignored by `Check`. +/// So generated files coexist with hand-written files in the same directory. +/// - `mode` — what to do with the generator's output. +/// - `generate` — closure that writes the generator's output into the +/// directory it is given. Its error is wrapped in [`Error::Generator`]. +/// +/// Behavior per mode: +/// - [`Mode::Check`]: runs the generator into a temp dir and compares owned +/// files against the committed copies. Mismatch returns [`Error::Mismatch`]. +/// - [`Mode::Bless`]: runs the generator into a temp dir and copies owned +/// files into `committed`, or removes `committed`'s copy if the generator no +/// longer produces them. +pub fn run_generator(committed: &Path, mode: Mode, generate: F) -> Result<()> +where + F: FnOnce(&Path) -> std::result::Result<(), E>, + E: Into>, +{ + let scratch = tempfile::tempdir()?; + generate(scratch.path()).map_err(|e| Error::Generator(e.into()))?; + + let owned = discover_owned(committed)?; + let produced = discover_all(scratch.path())?; + + let mut names: Vec<&String> = owned.iter().chain(produced.iter()).collect(); + names.sort(); + names.dedup(); + + for name in names { + match mode { + Mode::Check => compare(scratch.path(), committed, name)?, + Mode::Bless => apply_bless(scratch.path(), committed, name)?, + } + } + Ok(()) +} + +/// Returns the names of files in `dir` whose first line begins with +/// [`GENERATED_MARKER`]. Files without the marker are skipped. +fn discover_owned(dir: &Path) -> Result> { + let mut out = Vec::new(); + for entry in fs::read_dir(dir)? { + let entry = entry?; + if !entry.file_type()?.is_file() { + continue; + } + let Ok(mut file) = fs::File::open(entry.path()) else { + continue; + }; + let mut buf = [0u8; GENERATED_MARKER.len()]; + if file.read_exact(&mut buf).is_err() { + continue; + } + if buf == *GENERATED_MARKER.as_bytes() + && let Some(name) = entry.file_name().to_str() + { + out.push(name.to_owned()); + } + } + out.sort(); + Ok(out) +} + +/// Returns every file name in `dir` (scratch dir — all files are generator output). +fn discover_all(dir: &Path) -> Result> { + let mut out = Vec::new(); + for entry in fs::read_dir(dir)? { + let entry = entry?; + if entry.file_type()?.is_file() + && let Some(name) = entry.file_name().to_str() + { + out.push(name.to_string()); + } + } + Ok(out) +} + +fn compare(generated_dir: &Path, committed_dir: &Path, filename: &str) -> Result<()> { + let rel_path = PathBuf::from(filename); + let gen_path = generated_dir.join(&rel_path); + let comm_path = committed_dir.join(&rel_path); + match (gen_path.exists(), comm_path.exists()) { + (true, false) => Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::MissingInCommitted, + }), + (false, true) => Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::ExtraInCommitted, + }), + (false, false) => Ok(()), + (true, true) => { + if fs::read(&gen_path)? != fs::read(&comm_path)? { + Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::ContentsDiffer, + }) + } else { + Ok(()) + } + } + } +} + +fn apply_bless(generated_dir: &Path, committed_dir: &Path, filename: &str) -> Result<()> { + fs::create_dir_all(committed_dir)?; + let rel_path = PathBuf::from(filename); + let from = generated_dir.join(&rel_path); + let to = committed_dir.join(&rel_path); + if from.exists() { + if let Some(parent) = to.parent() { + fs::create_dir_all(parent)?; + } + fs::copy(&from, &to)?; + } else if to.exists() { + fs::remove_file(&to)?; + } + Ok(()) +} + +// Skipped on iOS because these tests fail on the `x86_64-apple-ios-macabi` CI runner +// with `Os { code: 17, kind: AlreadyExists, message: "File exists" }`. +#[cfg(all(test, not(target_os = "ios")))] +mod tests { + use super::*; + + fn write_marker(p: &Path, body: &[u8]) { + if let Some(d) = p.parent() { + fs::create_dir_all(d).unwrap(); + } + let mut bytes = Vec::new(); + bytes.extend_from_slice(GENERATED_MARKER.as_bytes()); + bytes.push(b'\n'); + bytes.extend_from_slice(body); + fs::write(p, bytes).unwrap(); + } + + #[test] + fn check_fails_on_byte_diff() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write_marker(&committed.join("a.txt"), b"hi"); + let e = run_generator( + &committed, + Mode::Check, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("a.txt"), b"HI"); + Ok(()) + }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::ContentsDiffer, + .. + } + )); + } + + #[test] + fn check_fails_when_owned_file_missing_from_generated() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write_marker(&committed.join("a.txt"), b"hi"); + let e = run_generator( + &committed, + Mode::Check, + |_| -> std::result::Result<(), io::Error> { Ok(()) }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::ExtraInCommitted, + .. + } + )); + } + + #[test] + fn check_fails_when_owned_file_missing_from_committed() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + fs::create_dir_all(&committed).unwrap(); + let e = run_generator( + &committed, + Mode::Check, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("a.txt"), b"hi"); + Ok(()) + }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::MissingInCommitted, + .. + } + )); + } + + #[test] + fn bless_deletes_files_no_longer_produced() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write_marker(&committed.join("keep.txt"), b""); + write_marker(&committed.join("stale.txt"), b""); + run_generator( + &committed, + Mode::Bless, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("keep.txt"), b""); + Ok(()) + }, + ) + .unwrap(); + assert!(committed.join("keep.txt").exists()); + assert!(!committed.join("stale.txt").exists()); + } + + #[test] + fn bless_preserves_unowned_files() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + fs::create_dir_all(&committed).unwrap(); + fs::write(committed.join("mod.rs"), b"hand-written").unwrap(); + fs::write(committed.join("old.txt"), b"old").unwrap(); + run_generator( + &committed, + Mode::Bless, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("new.txt"), b"new"); + Ok(()) + }, + ) + .unwrap(); + assert_eq!(fs::read(committed.join("mod.rs")).unwrap(), b"hand-written"); + assert_eq!(fs::read(committed.join("old.txt")).unwrap(), b"old"); + assert!(committed.join("new.txt").exists()); + } +} diff --git a/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml b/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml index 397c7816f8d1e..c7dfce2c0fd7b 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml +++ b/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] regex = "1.10" +stdarch-gen-common = { path = "../stdarch-gen-common" } diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs index 7a1c3030c0042..c3ad153ab0a7a 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs @@ -21,6 +21,7 @@ use std::collections::{HashMap, HashSet}; use std::fs::File; use std::io::Write; use std::path::Path; +use stdarch_gen_common::{run_generator, Mode, GENERATED_MARKER}; /// Mappings from HVX intrinsics to architecture-independent SIMD intrinsics. /// These intrinsics have equivalent semantics and can be lowered to the generic form. @@ -1609,6 +1610,7 @@ fn generate_module_file( let mut output = File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; + writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_module_doc(mode)).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_types(mode)).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_extern_block(intrinsics, mode)).map_err(|e| e.to_string())?; @@ -1691,23 +1693,21 @@ fn main() -> Result<(), String> { } // Generate output files - let hexagon_dir = std::env::args() - .nth(1) - .map(std::path::PathBuf::from) - .unwrap_or_else(|| crate_dir.join("../core_arch/src/hexagon")); - std::fs::create_dir_all(&hexagon_dir).map_err(|e| e.to_string())?; - - // Generate v64.rs (64-byte vector mode) - let v64_path = hexagon_dir.join("v64.rs"); - println!("\nStep 3: Generating v64.rs (64-byte mode)..."); - generate_module_file(&intrinsics, &v64_path, VectorMode::V64)?; - println!(" Output: {}", v64_path.display()); - - // Generate v128.rs (128-byte vector mode) - let v128_path = hexagon_dir.join("v128.rs"); - println!("\nStep 4: Generating v128.rs (128-byte mode)..."); - generate_module_file(&intrinsics, &v128_path, VectorMode::V128)?; - println!(" Output: {}", v128_path.display()); + let hexagon_dir = crate_dir.join("../core_arch/src/hexagon"); + + // Either "check" to check the output versus the committed output, or "bless" + // to update the output. + let mode = Mode::from_env(); + println!("\nStep 3: Generating v64.rs and v128.rs (mode: {mode:?})..."); + run_generator(&hexagon_dir, mode, |out_dir| -> Result<(), String> { + for (filename, vmode) in [("v64.rs", VectorMode::V64), ("v128.rs", VectorMode::V128)] { + let path = out_dir.join(filename); + generate_module_file(&intrinsics, &path, vmode)?; + println!(" Output: {}", hexagon_dir.join(filename).display()); + } + Ok(()) + }) + .map_err(|e| e.to_string())?; println!("\n=== Results ==="); println!( From 7c96471b6f608388a011efdb63abf4298a1afe5f Mon Sep 17 00:00:00 2001 From: xmakro Date: Sat, 20 Jun 2026 11:36:42 -0700 Subject: [PATCH 113/278] perf: Make stable_crate_ids reads lock-free after crate loading --- compiler/rustc_interface/src/passes.rs | 4 ++-- .../rustc_metadata/src/rmeta/decoder/cstore_impl.rs | 12 ++++++------ compiler/rustc_resolve/src/lib.rs | 4 ++-- compiler/rustc_session/src/cstore.rs | 10 ++++++++++ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index ce99b01637b04..41a4896d89e52 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -805,8 +805,8 @@ fn resolver_for_lowering_raw<'tcx>( ); let krate = configure_and_expand(krate, &pre_configured_attrs, &mut resolver); - // Make sure we don't mutate the cstore from here on. - tcx.untracked().cstore.freeze(); + // Don't mutate the cstore or stable crate id map from here on. + tcx.untracked().freeze_cstore(); let ResolverOutputs { global_ctxt: untracked_resolutions, diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index cbd6afd68473a..be856f01a9429 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -555,15 +555,15 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { ) }, crates: |tcx, ()| { - // The list of loaded crates is now frozen in query cache, - // so make sure cstore is not mutably accessed from here on. - tcx.untracked().cstore.freeze(); + // The loaded-crate list is now frozen in the query cache; stop + // mutating the cstore and stable crate id map from here on. + tcx.untracked().freeze_cstore(); tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).iter_crate_data().map(|(cnum, _)| cnum)) }, used_crates: |tcx, ()| { - // The list of loaded crates is now frozen in query cache, - // so make sure cstore is not mutably accessed from here on. - tcx.untracked().cstore.freeze(); + // The loaded-crate list is now frozen in the query cache; stop + // mutating the cstore and stable crate id map from here on. + tcx.untracked().freeze_cstore(); tcx.arena.alloc_from_iter( CStore::from_tcx(tcx) .iter_crate_data() diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 061471ccc97e0..fd60094e21558 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2091,8 +2091,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .time("resolve_postprocess", || self.cstore_mut().postprocess(self.tcx, krate)); }); - // Make sure we don't mutate the cstore from here on. - self.tcx.untracked().cstore.freeze(); + // Don't mutate the cstore or stable crate id map from here on. + self.tcx.untracked().freeze_cstore(); } fn traits_in_scope( diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index 39fe9c80923ec..94237132bd393 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -224,3 +224,13 @@ pub struct Untracked { /// The interned [StableCrateId]s. pub stable_crate_ids: FreezeLock, } + +impl Untracked { + /// Freezes the cstore and, with it, the `StableCrateId` map, making reads of + /// both lock-free. The cstore is frozen first so any in-flight crate loading + /// (which writes the map) finishes before the map is frozen. + pub fn freeze_cstore(&self) { + self.cstore.freeze(); + self.stable_crate_ids.freeze(); + } +} From 80844b8595e70aa46541c0e9e4ba99dd14994936 Mon Sep 17 00:00:00 2001 From: xmakro Date: Thu, 11 Jun 2026 10:16:40 -0700 Subject: [PATCH 114/278] perf: drop the full-crate AST walk in check_unused --- compiler/rustc_resolve/src/check_unused.rs | 13 ++++++----- compiler/rustc_resolve/src/late.rs | 25 +++++++++++++++------- compiler/rustc_resolve/src/lib.rs | 5 +++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 32e4bb9262fa8..4c5c591ad94e3 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -10,9 +10,9 @@ // // Checking for unused imports is split into three steps: // -// - `UnusedImportCheckVisitor` walks the AST to find all the unused imports -// inside of `UseTree`s, recording their `NodeId`s and grouping them by -// the parent `use` item +// - `UnusedImportCheckVisitor` visits the `use` items collected during late +// resolution to find all the unused imports inside of `UseTree`s, recording +// their `NodeId`s and grouping them by the parent `use` item // // - `calc_unused_spans` then walks over all the `use` items marked in the // previous step to collect the spans associated with the `NodeId`s and to @@ -410,7 +410,7 @@ fn calc_unused_spans( } impl Resolver<'_, '_> { - pub(crate) fn check_unused(&mut self, krate: &ast::Crate) { + pub(crate) fn check_unused(&mut self, use_items: Vec<&ast::Item>) { let tcx = self.tcx; let mut maybe_unused_extern_crates = FxHashMap::default(); @@ -465,7 +465,10 @@ impl Resolver<'_, '_> { base_id: ast::DUMMY_NODE_ID, item_span: DUMMY_SP, }; - visit::walk_crate(&mut visitor, krate); + // `use_items` is in crate DFS order, so diagnostics and side effects are unchanged. + for item in use_items { + visitor.visit_item(item); + } visitor.report_unused_extern_crate_items(maybe_unused_extern_crates); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 5ae2aaadffae7..bf9d6370092ff 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -5587,12 +5587,15 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } /// Walks the whole crate in DFS order, visiting each item, counting the declared number of -/// lifetime generic parameters and function parameters. -struct ItemInfoCollector<'a, 'ra, 'tcx> { +/// lifetime generic parameters and function parameters. Also collects all `use` and +/// `extern crate` items so that `check_unused` doesn't need to walk the crate again. +struct ItemInfoCollector<'a, 'ast, 'ra, 'tcx> { r: &'a mut Resolver<'ra, 'tcx>, + /// All `use` and `extern crate` items, in the order in which they are visited. + use_items: Vec<&'ast Item>, } -impl ItemInfoCollector<'_, '_, '_> { +impl ItemInfoCollector<'_, '_, '_, '_> { fn collect_fn_info(&mut self, decl: &FnDecl, id: NodeId) { self.r .delegation_fn_sigs @@ -5626,7 +5629,7 @@ fn required_generic_args_suggestion(generics: &ast::Generics) -> Option if required.is_empty() { None } else { Some(format!("<{}>", required.join(", "))) } } -impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { +impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, 'ast, '_, '_> { fn visit_item(&mut self, item: &'ast Item) { match &item.kind { ItemKind::TyAlias(TyAlias { generics, .. }) @@ -5659,11 +5662,13 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { } } + ItemKind::Use(..) | ItemKind::ExternCrate(..) => { + self.use_items.push(item); + } + ItemKind::Mod(..) | ItemKind::Static(..) | ItemKind::ConstBlock(..) - | ItemKind::Use(..) - | ItemKind::ExternCrate(..) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(..) | ItemKind::MacCall(..) @@ -5694,9 +5699,12 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { } impl<'ra, 'tcx> Resolver<'ra, 'tcx> { - pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) { + /// Returns the `use` and `extern crate` items of the crate, for use by `check_unused`. + pub(crate) fn late_resolve_crate<'ast>(&mut self, krate: &'ast Crate) -> Vec<&'ast Item> { with_owner(self, CRATE_NODE_ID, |this| { - visit::walk_crate(&mut ItemInfoCollector { r: this }, krate); + let mut info_collector = ItemInfoCollector { r: this, use_items: Vec::new() }; + visit::walk_crate(&mut info_collector, krate); + let use_items = info_collector.use_items; let mut late_resolution_visitor = LateResolutionVisitor::new(this); late_resolution_visitor .resolve_doc_links(&krate.attrs, MaybeExported::Ok(CRATE_NODE_ID)); @@ -5709,6 +5717,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { crate::diagnostics::UnusedLabel, ); } + use_items }) } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 061471ccc97e0..b8c9d42f6a6ab 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2082,9 +2082,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.tcx .sess .time("finalize_macro_resolutions", || self.finalize_macro_resolutions(krate)); - self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate)); + let use_items = + self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate)); self.tcx.sess.time("resolve_main", || self.resolve_main()); - self.tcx.sess.time("resolve_check_unused", || self.check_unused(krate)); + self.tcx.sess.time("resolve_check_unused", || self.check_unused(use_items)); self.tcx.sess.time("resolve_report_errors", || self.report_errors(krate)); self.tcx .sess From 55dc5523e535ac76223bfd0dfe1adcb823cca2bd Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 20 Jun 2026 21:35:03 -0700 Subject: [PATCH 115/278] Clean up unrelated tests --- tests/ui/unnecessary_cast-11882.rs | 83 ++++++++++-------------------- 1 file changed, 28 insertions(+), 55 deletions(-) diff --git a/tests/ui/unnecessary_cast-11882.rs b/tests/ui/unnecessary_cast-11882.rs index d8028308755f0..2c84b2f21635a 100644 --- a/tests/ui/unnecessary_cast-11882.rs +++ b/tests/ui/unnecessary_cast-11882.rs @@ -1,4 +1,3 @@ -//@ignore-bitwidth: 32 #![feature(const_trait_impl, const_ops)] #![warn(clippy::unnecessary_cast)] #![allow(unused, clippy::identity_op)] @@ -7,84 +6,58 @@ const TEST: u64 = (!0 as u64).overflowing_shr(1_u32).0; //~^ unnecessary_cast const TEST2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; //~^ unnecessary_cast -const TEST3: u64 = (not(0) as u64).overflowing_shr(1_u32).0; -const TEST4: u64 = (not(0_u64) as u64).overflowing_shr(1_u32).0; +const TEST3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; //~^ unnecessary_cast -const TEST5: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; +const TEST4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; //~^ unnecessary_cast -const TEST6: u64 = (0xff_ff_ff_ff_ff_ff_ff_ff as u64).overflowing_shr(1_u32).0; +const TEST5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; //~^ unnecessary_cast -const TEST7: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST8: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST9: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; +const TEST6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; const CHK1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; //~^ unnecessary_cast -const CHK2: u64 = (not(!0) as u64).overflowing_shr(1_u32).0; -const CHK3: u64 = (!not(0) as u64).overflowing_shr(1_u32).0; -const CHK4: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const CHK5: u64 = (not(!0_u64) as u64).overflowing_shr(1_u32).0; +const CHK2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; //~^ unnecessary_cast -const CHK6: u64 = (!(not(0) as u64)).overflowing_shr(1_u32).0; -const CHK7: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; +const CHK3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; //~^ unnecessary_cast -const CHK8: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; +const CHK4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; //~^ unnecessary_cast -fn main() { - // make sure that the calculated value doesn't change - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST2 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST3 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST4 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST5 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST6 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST7 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST8 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST9 as usize]; - - let _x: [(); 0] = [(); CHK1 as usize]; - let _x: [(); 0] = [(); CHK2 as usize]; - let _x: [(); 0] = [(); CHK3 as usize]; - let _x: [(); 0] = [(); CHK4 as usize]; - let _x: [(); 0] = [(); CHK5 as usize]; - let _x: [(); 0] = [(); CHK6 as usize]; - let _x: [(); 0] = [(); CHK7 as usize]; - let _x: [(); 0] = [(); CHK8 as usize]; +// Make sure that the calculated values aren't changed by the fixes. +const _: () = { + assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST); + assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST2); + assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST3); + assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST4); + assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST5); + assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST6); + assert_eq!(0, CHK1); + assert_eq!(0, CHK2); + assert_eq!(0, CHK3); + assert_eq!(0, CHK4); +}; +fn main() { // the non-const version of the tests let test: u64 = (!0 as u64).overflowing_shr(1_u32).0; //~^ unnecessary_cast let test2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; //~^ unnecessary_cast - let test3: u64 = (not(0) as u64).overflowing_shr(1_u32).0; - let test4: u64 = (not(0_u64) as u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test5: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; + let test3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; //~^ unnecessary_cast - let test6: u64 = (0xff_ff_ff_ff_ff_ff_ff_ff as u64).overflowing_shr(1_u32).0; + let test4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; //~^ unnecessary_cast - let test7: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; + let test5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; //~^ unnecessary_cast - let test8: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test9: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; + let test6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; let chk1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; //~^ unnecessary_cast - let chk2: u64 = (not(!0) as u64).overflowing_shr(1_u32).0; - let chk3: u64 = (!not(0) as u64).overflowing_shr(1_u32).0; - let chk4: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let chk5: u64 = (not(!0_u64) as u64).overflowing_shr(1_u32).0; + let chk2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; //~^ unnecessary_cast - let chk6: u64 = (!(not(0) as u64)).overflowing_shr(1_u32).0; - let chk7: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; + let chk3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; //~^ unnecessary_cast - let chk8: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; + let chk4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; //~^ unnecessary_cast } From 53e0ed0098b8217ad598d57d8dae512e4a7e4600 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 20 Jun 2026 21:39:50 -0700 Subject: [PATCH 116/278] Move issue 11882 tests into same unnecessary_cast --- tests/ui/unnecessary_cast-11882.fixed | 93 ---------- tests/ui/unnecessary_cast-11882.rs | 66 ------- tests/ui/unnecessary_cast-11882.stderr | 149 --------------- tests/ui/unnecessary_cast.fixed | 65 +++++++ tests/ui/unnecessary_cast.rs | 65 +++++++ tests/ui/unnecessary_cast.stderr | 240 ++++++++++++++++++------- 6 files changed, 304 insertions(+), 374 deletions(-) delete mode 100644 tests/ui/unnecessary_cast-11882.fixed delete mode 100644 tests/ui/unnecessary_cast-11882.rs delete mode 100644 tests/ui/unnecessary_cast-11882.stderr diff --git a/tests/ui/unnecessary_cast-11882.fixed b/tests/ui/unnecessary_cast-11882.fixed deleted file mode 100644 index 6b2949248d8d1..0000000000000 --- a/tests/ui/unnecessary_cast-11882.fixed +++ /dev/null @@ -1,93 +0,0 @@ -//@ignore-bitwidth: 32 -#![feature(const_trait_impl, const_ops)] -#![warn(clippy::unnecessary_cast)] -#![allow(unused, clippy::identity_op)] - -const TEST: u64 = (!0_u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST2: u64 = (!0_u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST3: u64 = (not(0) as u64).overflowing_shr(1_u32).0; -const TEST4: u64 = not(0_u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST5: u64 = (!not(!0_u64)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST6: u64 = 0xff_ff_ff_ff_ff_ff_ff_ff_u64.overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST7: u64 = (!0_u64 + 0).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST8: u64 = (!(0_u64 + 0)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST9: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; - -const CHK1: u64 = not(!0_u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const CHK2: u64 = (not(!0) as u64).overflowing_shr(1_u32).0; -const CHK3: u64 = (!not(0) as u64).overflowing_shr(1_u32).0; -const CHK4: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const CHK5: u64 = not(!0_u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const CHK6: u64 = (!(not(0) as u64)).overflowing_shr(1_u32).0; -const CHK7: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const CHK8: u64 = not(!0_u64 + 0).overflowing_shr(1_u32).0; -//~^ unnecessary_cast - -fn main() { - // make sure that the calculated value doesn't change - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST2 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST3 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST4 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST5 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST6 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST7 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST8 as usize]; - let _x: [(); 0x7f_ff_ff_ff_ff_ff_ff_ff] = [(); TEST9 as usize]; - - let _x: [(); 0] = [(); CHK1 as usize]; - let _x: [(); 0] = [(); CHK2 as usize]; - let _x: [(); 0] = [(); CHK3 as usize]; - let _x: [(); 0] = [(); CHK4 as usize]; - let _x: [(); 0] = [(); CHK5 as usize]; - let _x: [(); 0] = [(); CHK6 as usize]; - let _x: [(); 0] = [(); CHK7 as usize]; - let _x: [(); 0] = [(); CHK8 as usize]; - - // the non-const version of the tests - let test: u64 = (!0_u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test2: u64 = (!0_u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test3: u64 = (not(0) as u64).overflowing_shr(1_u32).0; - let test4: u64 = not(0_u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test5: u64 = (!not(!0_u64)).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test6: u64 = 0xff_ff_ff_ff_ff_ff_ff_ff_u64.overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test7: u64 = (!0_u64 + 0).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test8: u64 = (!(0_u64 + 0)).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test9: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; - - let chk1: u64 = not(!0_u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let chk2: u64 = (not(!0) as u64).overflowing_shr(1_u32).0; - let chk3: u64 = (!not(0) as u64).overflowing_shr(1_u32).0; - let chk4: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let chk5: u64 = not(!0_u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let chk6: u64 = (!(not(0) as u64)).overflowing_shr(1_u32).0; - let chk7: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let chk8: u64 = not(!0_u64 + 0).overflowing_shr(1_u32).0; - //~^ unnecessary_cast -} - -const fn not>(x: T) -> T { - !x -} diff --git a/tests/ui/unnecessary_cast-11882.rs b/tests/ui/unnecessary_cast-11882.rs deleted file mode 100644 index 2c84b2f21635a..0000000000000 --- a/tests/ui/unnecessary_cast-11882.rs +++ /dev/null @@ -1,66 +0,0 @@ -#![feature(const_trait_impl, const_ops)] -#![warn(clippy::unnecessary_cast)] -#![allow(unused, clippy::identity_op)] - -const TEST: u64 = (!0 as u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const TEST6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; - -const CHK1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const CHK2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const CHK3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const CHK4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; -//~^ unnecessary_cast - -// Make sure that the calculated values aren't changed by the fixes. -const _: () = { - assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST); - assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST2); - assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST3); - assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST4); - assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST5); - assert_eq!(0x7f_ff_ff_ff_ff_ff_ff_ffu64, TEST6); - assert_eq!(0, CHK1); - assert_eq!(0, CHK2); - assert_eq!(0, CHK3); - assert_eq!(0, CHK4); -}; - -fn main() { - // the non-const version of the tests - let test: u64 = (!0 as u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let test6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; - - let chk1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let chk2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let chk3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let chk4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; - //~^ unnecessary_cast -} - -const fn not>(x: T) -> T { - !x -} diff --git a/tests/ui/unnecessary_cast-11882.stderr b/tests/ui/unnecessary_cast-11882.stderr deleted file mode 100644 index 056f04c65ed35..0000000000000 --- a/tests/ui/unnecessary_cast-11882.stderr +++ /dev/null @@ -1,149 +0,0 @@ -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:6:19 - | -LL | const TEST: u64 = (!0 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^ help: try: `(!0_u64)` - | - = note: `-D clippy::unnecessary-cast` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast-11882.rs:8:20 - | -LL | const TEST2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast-11882.rs:11:20 - | -LL | const TEST4: u64 = (not(0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^ help: try: `not(0_u64)` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast-11882.rs:13:20 - | -LL | const TEST5: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(!0_u64))` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:15:20 - | -LL | const TEST6: u64 = (0xff_ff_ff_ff_ff_ff_ff_ff as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0xff_ff_ff_ff_ff_ff_ff_ff_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:17:21 - | -LL | const TEST7: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:19:23 - | -LL | const TEST8: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; - | ^^^^^^^^ help: try: `0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:23:23 - | -LL | const CHK1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast-11882.rs:27:19 - | -LL | const CHK4: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(0_u64))` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast-11882.rs:29:19 - | -LL | const CHK5: u64 = (not(!0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `not(!0_u64)` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:32:25 - | -LL | const CHK7: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; - | ^^^^^^^^ help: try: `0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:34:23 - | -LL | const CHK8: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:59:21 - | -LL | let test: u64 = (!0 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^ help: try: `(!0_u64)` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast-11882.rs:61:22 - | -LL | let test2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast-11882.rs:64:22 - | -LL | let test4: u64 = (not(0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^ help: try: `not(0_u64)` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast-11882.rs:66:22 - | -LL | let test5: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(!0_u64))` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:68:22 - | -LL | let test6: u64 = (0xff_ff_ff_ff_ff_ff_ff_ff as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0xff_ff_ff_ff_ff_ff_ff_ff_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:70:23 - | -LL | let test7: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:72:25 - | -LL | let test8: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; - | ^^^^^^^^ help: try: `0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:76:25 - | -LL | let chk1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast-11882.rs:80:21 - | -LL | let chk4: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(0_u64))` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast-11882.rs:82:21 - | -LL | let chk5: u64 = (not(!0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `not(!0_u64)` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:85:27 - | -LL | let chk7: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; - | ^^^^^^^^ help: try: `0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast-11882.rs:87:25 - | -LL | let chk8: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` - -error: aborting due to 24 previous errors - diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 1ecc3ecf57a00..5016ce3470672 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -1,7 +1,9 @@ //@aux-build:extern_fake_libc.rs +#![feature(const_trait_impl, const_ops)] #![warn(clippy::unnecessary_cast)] #![allow( clippy::borrow_as_ptr, + clippy::identity_op, clippy::multiple_bound_locations, clippy::no_effect, clippy::nonstandard_macro_braces, @@ -656,3 +658,66 @@ fn issue16475() -> *const u8 { //~^ unnecessary_cast } } + +const ISSUE_11882_TEST: u64 = (!0_u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_TEST2: u64 = (!0_u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_TEST3: u64 = (!not(!0_u64)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_TEST4: u64 = (!0_u64 + 0).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_TEST5: u64 = (!(0_u64 + 0)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_TEST6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; + +const ISSUE_11882_CHK1: u64 = not(!0_u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_CHK2: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_CHK3: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_CHK4: u64 = not(!0_u64 + 0).overflowing_shr(1_u32).0; +//~^ unnecessary_cast + +// Make sure that the calculated values aren't changed by the fixes. +const _: () = { + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST2); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST3); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST4); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST5); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST6); + assert!(0 == ISSUE_11882_CHK1); + assert!(0 == ISSUE_11882_CHK2); + assert!(0 == ISSUE_11882_CHK3); + assert!(0 == ISSUE_11882_CHK4); +}; + +fn issue_11882() { + // the non-const version of the tests + let issue_11882_test: u64 = (!0_u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_test2: u64 = (!0_u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_test3: u64 = (!not(!0_u64)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_test4: u64 = (!0_u64 + 0).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_test5: u64 = (!(0_u64 + 0)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_test6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; + + let issue_11882_chk1: u64 = not(!0_u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_chk2: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_chk3: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_chk4: u64 = not(!0_u64 + 0).overflowing_shr(1_u32).0; + //~^ unnecessary_cast +} + +const fn not>(x: T) -> T { + !x +} diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 1a6cc831aa4b8..00a2687387140 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -1,7 +1,9 @@ //@aux-build:extern_fake_libc.rs +#![feature(const_trait_impl, const_ops)] #![warn(clippy::unnecessary_cast)] #![allow( clippy::borrow_as_ptr, + clippy::identity_op, clippy::multiple_bound_locations, clippy::no_effect, clippy::nonstandard_macro_braces, @@ -656,3 +658,66 @@ fn issue16475() -> *const u8 { //~^ unnecessary_cast } } + +const ISSUE_11882_TEST: u64 = (!0 as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_TEST2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_TEST3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_TEST4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_TEST5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_TEST6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; + +const ISSUE_11882_CHK1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_CHK2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_CHK3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; +//~^ unnecessary_cast +const ISSUE_11882_CHK4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; +//~^ unnecessary_cast + +// Make sure that the calculated values aren't changed by the fixes. +const _: () = { + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST2); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST3); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST4); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST5); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST6); + assert!(0 == ISSUE_11882_CHK1); + assert!(0 == ISSUE_11882_CHK2); + assert!(0 == ISSUE_11882_CHK3); + assert!(0 == ISSUE_11882_CHK4); +}; + +fn issue_11882() { + // the non-const version of the tests + let issue_11882_test: u64 = (!0 as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_test2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_test3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_test4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_test5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_test6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; + + let issue_11882_chk1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_chk2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_chk3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; + //~^ unnecessary_cast + let issue_11882_chk4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; + //~^ unnecessary_cast +} + +const fn not>(x: T) -> T { + !x +} diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index 19d2afcb4f255..eb710ba1fee29 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -1,5 +1,5 @@ error: casting raw pointers to the same type and constness is unnecessary (`*const T` -> `*const T`) - --> tests/ui/unnecessary_cast.rs:19:5 + --> tests/ui/unnecessary_cast.rs:21:5 | LL | ptr as *const T | ^^^^^^^^^^^^^^^ help: try: `ptr` @@ -8,388 +8,496 @@ LL | ptr as *const T = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:55:5 + --> tests/ui/unnecessary_cast.rs:57:5 | LL | 1i32 as i32; | ^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:57:5 + --> tests/ui/unnecessary_cast.rs:59:5 | LL | 1f32 as f32; | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) - --> tests/ui/unnecessary_cast.rs:59:5 + --> tests/ui/unnecessary_cast.rs:61:5 | LL | false as bool; | ^^^^^^^^^^^^^ help: try: `false` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:63:5 + --> tests/ui/unnecessary_cast.rs:65:5 | LL | -1_i32 as i32; | ^^^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:65:5 + --> tests/ui/unnecessary_cast.rs:67:5 | LL | - 1_i32 as i32; | ^^^^^^^^^^^^^^ help: try: `- 1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:67:5 + --> tests/ui/unnecessary_cast.rs:69:5 | LL | -1f32 as f32; | ^^^^^^^^^^^^ help: try: `-1_f32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:69:5 + --> tests/ui/unnecessary_cast.rs:71:5 | LL | 1_i32 as i32; | ^^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:71:5 + --> tests/ui/unnecessary_cast.rs:73:5 | LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:74:22 + --> tests/ui/unnecessary_cast.rs:76:22 | LL | let _: *mut u8 = [1u8, 2].as_ptr() as *const u8 as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:77:5 + --> tests/ui/unnecessary_cast.rs:79:5 | LL | [1u8, 2].as_ptr() as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*mut u8` -> `*mut u8`) - --> tests/ui/unnecessary_cast.rs:80:5 + --> tests/ui/unnecessary_cast.rs:82:5 | LL | [1u8, 2].as_mut_ptr() as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_mut_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> tests/ui/unnecessary_cast.rs:92:5 + --> tests/ui/unnecessary_cast.rs:94:5 | LL | owo::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `owo::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:94:5 + --> tests/ui/unnecessary_cast.rs:96:5 | LL | uwu::([1u32].as_ptr()) as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> tests/ui/unnecessary_cast.rs:97:5 + --> tests/ui/unnecessary_cast.rs:99:5 | LL | uwu::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:133:5 + --> tests/ui/unnecessary_cast.rs:135:5 | LL | aaa() as u32; | ^^^^^^^^^^^^ help: try: `aaa()` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:136:5 + --> tests/ui/unnecessary_cast.rs:138:5 | LL | x as u32; | ^^^^^^^^ help: try: `x` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:138:5 + --> tests/ui/unnecessary_cast.rs:140:5 | LL | bbb() as u32; | ^^^^^^^^^^^^ help: try: `bbb()` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:141:5 + --> tests/ui/unnecessary_cast.rs:143:5 | LL | x as u32; | ^^^^^^^^ help: try: `x` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:174:9 + --> tests/ui/unnecessary_cast.rs:176:9 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:176:9 + --> tests/ui/unnecessary_cast.rs:178:9 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:178:9 + --> tests/ui/unnecessary_cast.rs:180:9 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:180:17 + --> tests/ui/unnecessary_cast.rs:182:17 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:182:17 + --> tests/ui/unnecessary_cast.rs:184:17 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:184:17 + --> tests/ui/unnecessary_cast.rs:186:17 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:186:9 + --> tests/ui/unnecessary_cast.rs:188:9 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:188:9 + --> tests/ui/unnecessary_cast.rs:190:9 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:201:9 + --> tests/ui/unnecessary_cast.rs:203:9 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:203:9 + --> tests/ui/unnecessary_cast.rs:205:9 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> tests/ui/unnecessary_cast.rs:205:9 + --> tests/ui/unnecessary_cast.rs:207:9 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> tests/ui/unnecessary_cast.rs:207:9 + --> tests/ui/unnecessary_cast.rs:209:9 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:209:9 + --> tests/ui/unnecessary_cast.rs:211:9 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:212:9 + --> tests/ui/unnecessary_cast.rs:214:9 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:214:9 + --> tests/ui/unnecessary_cast.rs:216:9 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:219:17 + --> tests/ui/unnecessary_cast.rs:221:17 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:221:17 + --> tests/ui/unnecessary_cast.rs:223:17 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` error: casting to the same type is unnecessary (`i32` -> `i32`) - --> tests/ui/unnecessary_cast.rs:228:18 + --> tests/ui/unnecessary_cast.rs:230:18 | LL | let _ = &(x as i32); | ^^^^^^^^^^ help: try: `{ x }` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:235:22 + --> tests/ui/unnecessary_cast.rs:237:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> tests/ui/unnecessary_cast.rs:238:22 + --> tests/ui/unnecessary_cast.rs:240:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:246:22 + --> tests/ui/unnecessary_cast.rs:248:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:249:23 + --> tests/ui/unnecessary_cast.rs:251:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> tests/ui/unnecessary_cast.rs:259:20 + --> tests/ui/unnecessary_cast.rs:261:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:270:9 + --> tests/ui/unnecessary_cast.rs:272:9 | LL | (*x as usize).pow(2) | ^^^^^^^^^^^^^ help: try: `(*x)` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:278:31 + --> tests/ui/unnecessary_cast.rs:280:31 | LL | assert_eq!(vec.len(), x as usize); | ^^^^^^^^^^ help: try: `x` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:281:17 + --> tests/ui/unnecessary_cast.rs:283:17 | LL | let _ = (5i32 as i64 as i64).abs(); | ^^^^^^^^^^^^^^^^^^^^ help: try: `(5i32 as i64)` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:284:17 + --> tests/ui/unnecessary_cast.rs:286:17 | LL | let _ = 5i32 as i64 as i64; | ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:563:24 + --> tests/ui/unnecessary_cast.rs:565:24 | LL | id::>(1.0_f64.pow_like(2) as f64 * &a).view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:567:26 + --> tests/ui/unnecessary_cast.rs:569:26 | LL | s.id::>(1.0_f64.pow_like(2) as f64 * &a).view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:571:26 + --> tests/ui/unnecessary_cast.rs:573:26 | LL | wrap::>(1.0_f64.pow_like(2) as f64 * &a).inner.view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:575:28 + --> tests/ui/unnecessary_cast.rs:577:28 | LL | s.wrap::>(1.0_f64.pow_like(2) as f64 * &a).inner.view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:591:31 + --> tests/ui/unnecessary_cast.rs:593:31 | LL | let _ = receiver.take(1.0_f64.pow_like_single_impl(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:601:20 + --> tests/ui/unnecessary_cast.rs:603:20 | LL | let _ = id(1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:604:22 + --> tests/ui/unnecessary_cast.rs:606:22 | LL | let _ = wrap(1.0_f64.powi(2) as f64).inner.abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:607:22 + --> tests/ui/unnecessary_cast.rs:609:22 | LL | let _ = s.id(1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:610:24 + --> tests/ui/unnecessary_cast.rs:612:24 | LL | let _ = s.wrap(1.0_f64.powi(2) as f64).inner.abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:613:20 + --> tests/ui/unnecessary_cast.rs:615:20 | LL | let _ = id(1.0_f64.powi(2) as f64 * &a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:616:22 + --> tests/ui/unnecessary_cast.rs:618:22 | LL | let _ = s.id(1.0_f64.powi(2) as f64 * &a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:629:17 + --> tests/ui/unnecessary_cast.rs:631:17 | LL | let _ = 1.0_f64.pow_like(0.5) as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(0.5)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:632:17 + --> tests/ui/unnecessary_cast.rs:634:17 | LL | let _ = 1.0_f64.pow_like(2) as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:635:17 + --> tests/ui/unnecessary_cast.rs:637:17 | LL | let _ = (1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:638:17 + --> tests/ui/unnecessary_cast.rs:640:17 | LL | let _ = ((Y + 2) as f64).abs(); | ^^^^^^^^^^^^^^^^ help: try: `((Y + 2))` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:641:18 + --> tests/ui/unnecessary_cast.rs:643:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + 1.0_f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:644:18 + --> tests/ui/unnecessary_cast.rs:646:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + ONE).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:647:18 + --> tests/ui/unnecessary_cast.rs:649:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + one).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting raw pointers to the same type and constness is unnecessary (`*const *const u8` -> `*const *const u8`) - --> tests/ui/unnecessary_cast.rs:655:10 + --> tests/ui/unnecessary_cast.rs:657:10 | LL | *(&NONE as *const _ as *const _ as *const *const u8 as *const *const u8) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&NONE as *const _ as *const _ as *const *const u8)` -error: aborting due to 65 previous errors +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:662:31 + | +LL | const ISSUE_11882_TEST: u64 = (!0 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast.rs:664:32 + | +LL | const ISSUE_11882_TEST2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast.rs:666:32 + | +LL | const ISSUE_11882_TEST3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(!0_u64))` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:668:33 + | +LL | const ISSUE_11882_TEST4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:670:35 + | +LL | const ISSUE_11882_TEST5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:674:35 + | +LL | const ISSUE_11882_CHK1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast.rs:676:31 + | +LL | const ISSUE_11882_CHK2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(0_u64))` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:678:37 + | +LL | const ISSUE_11882_CHK3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:680:35 + | +LL | const ISSUE_11882_CHK4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:699:33 + | +LL | let issue_11882_test: u64 = (!0 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast.rs:701:34 + | +LL | let issue_11882_test2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast.rs:703:34 + | +LL | let issue_11882_test3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(!0_u64))` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:705:35 + | +LL | let issue_11882_test4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:707:37 + | +LL | let issue_11882_test5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:711:37 + | +LL | let issue_11882_chk1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast.rs:713:33 + | +LL | let issue_11882_chk2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(0_u64))` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:715:39 + | +LL | let issue_11882_chk3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:717:37 + | +LL | let issue_11882_chk4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; + | ^^^^^^^^^ help: try: `!0_u64` + +error: aborting due to 83 previous errors From 01458a74a958e372ab6ed13d2deecf1b410ef4e4 Mon Sep 17 00:00:00 2001 From: Aliaksei Semianiuk Date: Sat, 20 Jun 2026 23:59:59 +0500 Subject: [PATCH 117/278] Update actions/checkout to v7 --- .github/workflows/clippy_dev.yml | 2 +- .github/workflows/clippy_mq.yml | 8 ++++---- .github/workflows/clippy_pr.yml | 2 +- .github/workflows/deploy.yml | 4 ++-- .github/workflows/lintcheck.yml | 6 +++--- .github/workflows/remark.yml | 2 +- book/src/continuous_integration/github_actions.md | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 3a99d65233d38..cd90bb0a25f5f 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -16,7 +16,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/.github/workflows/clippy_mq.yml b/.github/workflows/clippy_mq.yml index b612ea4611a9f..7d313ece05b80 100644 --- a/.github/workflows/clippy_mq.yml +++ b/.github/workflows/clippy_mq.yml @@ -34,7 +34,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -94,7 +94,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -112,7 +112,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -168,7 +168,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false diff --git a/.github/workflows/clippy_pr.yml b/.github/workflows/clippy_pr.yml index f9e882d9757ce..5658e041e03cf 100644 --- a/.github/workflows/clippy_pr.yml +++ b/.github/workflows/clippy_pr.yml @@ -24,7 +24,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 872931160c35f..b95cbbddd4b32 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -25,13 +25,13 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 981fecd17c988..210f10b7b8804 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 2 # Unsetting this would make so that any malicious package could get our Github Token @@ -80,7 +80,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false @@ -113,7 +113,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index d4dc80efe79de..532af9dff83f2 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -14,7 +14,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/book/src/continuous_integration/github_actions.md b/book/src/continuous_integration/github_actions.md index bed0f66bab33a..e198a13b38d24 100644 --- a/book/src/continuous_integration/github_actions.md +++ b/book/src/continuous_integration/github_actions.md @@ -15,7 +15,7 @@ jobs: clippy_check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Run Clippy run: cargo clippy --all-targets --all-features ``` From 0556e4cc0a227e14b361e9b5d60459ae05b6b075 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Sun, 21 Jun 2026 13:32:47 +0200 Subject: [PATCH 118/278] remove AliasTy::def_id() --- clippy_lints/src/functions/must_use.rs | 5 +--- clippy_lints/src/functions/result.rs | 9 +------ clippy_lints/src/len_without_is_empty.rs | 7 ++++-- clippy_lints/src/methods/needless_collect.rs | 6 +---- clippy_lints/src/no_effect.rs | 9 +------ clippy_utils/src/ty/mod.rs | 26 ++++++-------------- 6 files changed, 17 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index a3e982bb09430..39b53e02831c4 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -4,7 +4,6 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdSet; use rustc_hir::{self as hir, Attribute, QPath, find_attr}; -use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; @@ -16,7 +15,6 @@ use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{is_entrypoint_fn, return_ty, trait_ref_of_method}; use rustc_span::Symbol; -use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use core::ops::ControlFlow; @@ -166,8 +164,7 @@ fn check_needless_must_use( } else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { // Ignore async functions unless Future::Output type is a must_use type if sig.header.is_async() { - let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); - if let Some(future_ty) = infcx.err_ctxt().get_impl_future_output_ty(return_ty(cx, item_id)) + if let Some(future_ty) = cx.tcx.get_impl_future_output_ty(return_ty(cx, item_id)) && !is_must_use_ty(cx, future_ty) { return; diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 331c6e23bddd7..816f2cac8429b 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -2,12 +2,10 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; use rustc_errors::Diag; use rustc_hir as hir; -use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_span::def_id::DefIdSet; use rustc_span::{Span, sym}; -use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::ty::{AdtVariantInfo, approx_ty_size}; @@ -32,12 +30,7 @@ fn result_err_ty<'tcx>( // for async functions, peel through `impl Future` to get `T` if cx.tcx.ty_is_opaque_future(ty) - && let Some(future_output_ty) = cx - .tcx - .infer_ctxt() - .build(cx.typing_mode()) - .err_ctxt() - .get_impl_future_output_ty(ty) + && let Some(future_output_ty) = cx.tcx.get_impl_future_output_ty(ty) { ty = future_output_ty; } diff --git a/clippy_lints/src/len_without_is_empty.rs b/clippy_lints/src/len_without_is_empty.rs index 4b188709a8be3..34730dbb098c4 100644 --- a/clippy_lints/src/len_without_is_empty.rs +++ b/clippy_lints/src/len_without_is_empty.rs @@ -149,8 +149,11 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Iden } fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { - if let ty::Alias(alias_ty) = ty.kind() - && let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir_get_if_local(alias_ty.kind.def_id()) + if let ty::Alias(ty::AliasTy { + kind: ty::Opaque { def_id }, + .. + }) = *ty.kind() + && let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir_get_if_local(def_id) && let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin && let [GenericBound::Trait(trait_ref)] = &opaque.bounds && let Some(segment) = trait_ref.trait_ref.path.segments.last() diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 4f281d745a94e..62a192b7f770f 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -247,11 +247,7 @@ fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, sym::Item, [collect_ty]) && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions( cx.typing_env(), - Unnormalized::new_wip(Ty::new_projection_from_args( - cx.tcx, - into_iter_item_proj.kind.def_id(), - into_iter_item_proj.args, - )), + Unnormalized::new_wip(Ty::new_alias(cx.tcx, into_iter_item_proj)), ) { iter_item_ty == into_iter_item_ty diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index f2a54b99170e9..9e6fc68116d4f 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -9,11 +9,9 @@ use rustc_hir::{ BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, Stmt, StmtKind, StructTailExpr, UnsafeSource, is_range_literal, }; -use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::Span; -use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use std::ops::Deref; declare_clippy_lint! { @@ -162,12 +160,7 @@ impl NoEffect { // Remove `impl Future` to get `T` if cx.tcx.ty_is_opaque_future(ret_ty) - && let Some(true_ret_ty) = cx - .tcx - .infer_ctxt() - .build(cx.typing_mode()) - .err_ctxt() - .get_impl_future_output_ty(ret_ty) + && let Some(true_ret_ty) = cx.tcx.get_impl_future_output_ty(ret_ty) { ret_ty = true_ret_ty; } diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 60d19ecf68bb5..8cded245fac22 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -20,8 +20,9 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, - GenericArgKind, GenericArgsRef, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, UintTy, Unnormalized, Upcast, VariantDef, VariantDiscr, + GenericArgKind, GenericArgsRef, IntTy, ProjectionAliasTy, Region, RegionKind, TraitRef, Ty, TyCtxt, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Unnormalized, Upcast, VariantDef, + VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol}; @@ -669,12 +670,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option None, } }, - ty::Alias( - proj @ AliasTy { - kind: ty::Projection { .. }, - .. - }, - ) => match cx + ty::Alias(alias) if let Some(proj) = alias.try_to_projection() => match cx .tcx .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty)) { @@ -728,14 +724,14 @@ fn sig_from_bounds<'tcx>( inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id)) } -fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option> { +fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionAliasTy<'tcx>) -> Option> { let mut inputs = None; let mut output = None; let lang_items = cx.tcx.lang_items(); for (pred, _) in cx .tcx - .explicit_item_bounds(ty.kind.def_id()) + .explicit_item_bounds(ty.kind) .iter_instantiated_copied(cx.tcx, ty.args) .map(Unnormalized::skip_norm_wip) { @@ -1097,10 +1093,7 @@ pub fn make_normalized_projection<'tcx>( ); return None; } - match tcx.try_normalize_erasing_regions( - typing_env, - Unnormalized::new_wip(Ty::new_projection_from_args(tcx, ty.kind.def_id(), ty.args)), - ) { + match tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(Ty::new_alias(tcx, ty))) { Ok(ty) => Some(ty), Err(e) => { debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); @@ -1254,10 +1247,7 @@ pub fn make_normalized_projection_with_regions<'tcx>( } let cause = ObligationCause::dummy(); let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); - match infcx - .at(&cause, param_env) - .query_normalize(Ty::new_projection_from_args(tcx, ty.kind.def_id(), ty.args)) - { + match infcx.at(&cause, param_env).query_normalize(Ty::new_alias(tcx, ty)) { Ok(ty) => Some(ty.value), Err(e) => { debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); From 9558c381000c3d060872043746887ec29e1b4930 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 2 Jun 2026 12:39:02 +0000 Subject: [PATCH 119/278] intrinsic-test: shorten various type names This isn't strictly necessary but these type names were longer than they needed to be. --- .../crates/intrinsic-test/src/arm/intrinsic.rs | 6 +++--- .../intrinsic-test/src/arm/json_parser.rs | 18 +++++++----------- .../crates/intrinsic-test/src/arm/mod.rs | 18 ++++++++---------- .../crates/intrinsic-test/src/arm/types.rs | 8 +++----- .../intrinsic-test/src/common/argument.rs | 10 +++++----- .../crates/intrinsic-test/src/common/gen_c.rs | 4 ++-- .../intrinsic-test/src/common/gen_rust.rs | 10 +++++----- .../intrinsic-test/src/common/intrinsic.rs | 6 +++--- .../src/common/intrinsic_helpers.rs | 2 +- .../crates/intrinsic-test/src/common/mod.rs | 8 ++++---- .../crates/intrinsic-test/src/common/values.rs | 6 +++--- .../stdarch/crates/intrinsic-test/src/main.rs | 18 ++++++------------ .../crates/intrinsic-test/src/x86/mod.rs | 8 ++++---- .../crates/intrinsic-test/src/x86/types.rs | 6 ++---- 14 files changed, 56 insertions(+), 72 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index a54e5857192e0..bcbee3503b9ad 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -2,9 +2,9 @@ use crate::common::intrinsic_helpers::IntrinsicType; use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone, PartialEq)] -pub struct ArmIntrinsicType(pub IntrinsicType); +pub struct ArmType(pub IntrinsicType); -impl Deref for ArmIntrinsicType { +impl Deref for ArmType { type Target = IntrinsicType; fn deref(&self) -> &Self::Target { @@ -12,7 +12,7 @@ impl Deref for ArmIntrinsicType { } } -impl DerefMut for ArmIntrinsicType { +impl DerefMut for ArmType { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index 6050cde84ffad..ebbde115aa4bf 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -1,4 +1,4 @@ -use super::intrinsic::ArmIntrinsicType; +use super::intrinsic::ArmType; use crate::arm::types::parse_intrinsic_type; use crate::common::argument::{Argument, ArgumentList}; use crate::common::constraint::Constraint; @@ -59,7 +59,7 @@ struct JsonIntrinsic { pub fn get_neon_intrinsics( filename: &Path, -) -> Result>, Box> { +) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); let json: Vec = serde_json::from_reader(reader).expect("Couldn't parse JSON"); @@ -79,10 +79,10 @@ pub fn get_neon_intrinsics( fn json_to_intrinsic( mut intr: JsonIntrinsic, -) -> Result, Box> { +) -> Result, Box> { let name = intr.name.replace(['[', ']'], ""); - let result_ty = ArmIntrinsicType(parse_intrinsic_type(&intr.return_type.value)?); + let result_ty = ArmType(parse_intrinsic_type(&intr.return_type.value)?); let args = intr .arguments @@ -120,12 +120,8 @@ fn json_to_intrinsic( } }); - let mut arg = Argument::::new( - i, - String::from(arg_name), - ArmIntrinsicType(arg_ty), - constraint, - ); + let mut arg = + Argument::::new(i, String::from(arg_name), ArmType(arg_ty), constraint); // The JSON doesn't list immediates as const let IntrinsicType { @@ -138,7 +134,7 @@ fn json_to_intrinsic( }) .collect(); - let arguments = ArgumentList:: { args }; + let arguments = ArgumentList:: { args }; Ok(Intrinsic { name, diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 5dec050729754..0c5aa5da929de 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -3,22 +3,20 @@ mod intrinsic; mod json_parser; mod types; -use crate::common::SupportedArchitectureTest; +use crate::common::SupportedArchitecture; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::{SimdLen, TypeKind}; -use intrinsic::ArmIntrinsicType; +use intrinsic::ArmType; use json_parser::get_neon_intrinsics; -pub struct ArmArchitectureTest { - intrinsics: Vec>, -} +pub struct Arm(Vec>); -impl SupportedArchitectureTest for ArmArchitectureTest { - type IntrinsicImpl = ArmIntrinsicType; +impl SupportedArchitecture for Arm { + type Type = ArmType; - fn intrinsics(&self) -> &[Intrinsic] { - &self.intrinsics + fn intrinsics(&self) -> &[Intrinsic] { + &self.0 } const NOTICE: &str = config::NOTICE; @@ -123,6 +121,6 @@ impl SupportedArchitectureTest for ArmArchitectureTest { .take(sample_size) .collect::>(); - Self { intrinsics } + Self(intrinsics) } } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index cd420f10678fc..44af107eb96e5 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -1,9 +1,7 @@ -use super::intrinsic::ArmIntrinsicType; -use crate::common::intrinsic_helpers::{ - IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, -}; +use super::intrinsic::ArmType; +use crate::common::intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}; -impl IntrinsicTypeDefinition for ArmIntrinsicType { +impl TypeDefinition for ArmType { /// Gets a string containing the typename for this type in C format. fn c_type(&self) -> String { let prefix = self.kind.c_prefix(); diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index f1d87f6dedd7e..e91a9ab7735e1 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -5,11 +5,11 @@ use crate::common::values::test_values_array_name; use super::PASSES; use super::constraint::Constraint; -use super::intrinsic_helpers::IntrinsicTypeDefinition; +use super::intrinsic_helpers::TypeDefinition; /// An argument for the intrinsic. #[derive(Debug, PartialEq, Clone)] -pub struct Argument { +pub struct Argument { /// The argument's index in the intrinsic function call. pub pos: usize, /// The argument name. @@ -22,7 +22,7 @@ pub struct Argument { impl Argument where - T: IntrinsicTypeDefinition, + T: TypeDefinition, { pub fn new(pos: usize, name: String, ty: T, constraint: Option) -> Self { Argument { @@ -63,13 +63,13 @@ where /// Arguments of an intrinsic - including parameters that end up being const generics. #[derive(Debug, PartialEq, Clone)] -pub struct ArgumentList { +pub struct ArgumentList { pub args: Vec>, } impl ArgumentList where - T: IntrinsicTypeDefinition, + T: TypeDefinition, { /// Returns a string with the arguments in `self` as a parameter list for a wrapper fn /// definition in C (e.g. `$ty1 $arg1, $ty2 $arg2`). diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index 24756324c48e4..888f5b2805d08 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -2,7 +2,7 @@ use itertools::Itertools; use crate::common::intrinsic::Intrinsic; -use super::intrinsic_helpers::IntrinsicTypeDefinition; +use super::intrinsic_helpers::TypeDefinition; /// Generates a C source file containing wrapper functions around each specialisation of each /// intrinsic (that is, intrinsics with specific values for the the immediate arguments). Each @@ -14,7 +14,7 @@ use super::intrinsic_helpers::IntrinsicTypeDefinition; /// *__dst = __crc32cd(a, b); /// } /// ``` -pub fn write_wrapper_c( +pub fn write_wrapper_c( w: &mut impl std::io::Write, notice: &str, platform_headers: &[&str], diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 132ede8d9b710..36f5754367474 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -2,7 +2,7 @@ use std::process::Command; use itertools::Itertools; -use super::intrinsic_helpers::IntrinsicTypeDefinition; +use super::intrinsic_helpers::TypeDefinition; use crate::common::PASSES; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; @@ -98,7 +98,7 @@ cc = "1" /// Writes a Rust source file into `w` with common definitions, static arrays with test values, /// declarations of C wrapper functions for FFI and Rust test functions. -pub fn write_lib_rs( +pub fn write_lib_rs( w: &mut impl std::io::Write, notice: &str, cfg: &str, @@ -156,7 +156,7 @@ pub fn write_lib_rs( /// (first loop) `PASSES` number of times (second loop). For a given iteration of a given /// specialisation, test values are loaded for each argument and passed to the Rust intrinsic /// and the C wrapper function, and the results are compared. -fn generate_rust_test_loop( +fn generate_rust_test_loop( w: &mut impl std::io::Write, intrinsic: &Intrinsic, ) -> std::io::Result<()> { @@ -247,7 +247,7 @@ for (id, rust, c) in specializations {{ /// Writes a test function for an given intrinsic to `w`, with a body generated by /// `generate_rust_test_loop`. -fn create_rust_test( +fn create_rust_test( w: &mut impl std::io::Write, intrinsic: &Intrinsic, ) -> std::io::Result<()> { @@ -271,7 +271,7 @@ fn test_{intrinsic_name}() {{ /// Writes an `extern "C"` block with function declarations for each of the C wrapper functions into /// `w`. -pub fn write_bindings_rust( +pub fn write_bindings_rust( w: &mut impl std::io::Write, i: usize, intrinsics: &[Intrinsic], diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index 7c9de818c1f48..ca59385c3b4e2 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -1,11 +1,11 @@ use crate::common::constraint::Constraint; use super::argument::ArgumentList; -use super::intrinsic_helpers::IntrinsicTypeDefinition; +use super::intrinsic_helpers::TypeDefinition; /// An intrinsic #[derive(Debug, PartialEq, Clone)] -pub struct Intrinsic { +pub struct Intrinsic { /// The function name of this intrinsic. pub name: String, @@ -43,7 +43,7 @@ fn recurse_specializations<'a, E>( } } -impl Intrinsic { +impl Intrinsic { /// Invokes `f` for "specialisation" of the intrinsic - a specific instantiation of the /// constant generics of the intrinsic. `f` takes a slice where the `i`th element corresponds /// to the value of the `i`th const generic argument of the intrinsic. diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index ca5aeba86dab9..8e5d55ff3b39a 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -195,7 +195,7 @@ impl IntrinsicType { } } -pub trait IntrinsicTypeDefinition: Deref { +pub trait TypeDefinition: Deref { /// Determines the load function for this type. fn get_load_function(&self) -> String; diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index b577491454e44..78720d6bc5202 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -10,7 +10,7 @@ use crate::common::{ run_rustfmt, write_bin_cargo_toml, write_build_rs, write_lib_cargo_toml, write_lib_rs, }, intrinsic::Intrinsic, - intrinsic_helpers::IntrinsicTypeDefinition, + intrinsic_helpers::TypeDefinition, }; pub mod argument; @@ -29,10 +29,10 @@ pub(crate) const PASSES: u32 = 20; /// Architectures must support this trait /// to be successfully tested. -pub trait SupportedArchitectureTest { - type IntrinsicImpl: IntrinsicTypeDefinition + Sync; +pub trait SupportedArchitecture { + type Type: TypeDefinition + Sync; - fn intrinsics(&self) -> &[Intrinsic]; + fn intrinsics(&self) -> &[Intrinsic]; fn create(cli_options: &ProcessedCli) -> Self; diff --git a/library/stdarch/crates/intrinsic-test/src/common/values.rs b/library/stdarch/crates/intrinsic-test/src/common/values.rs index f7bd4a4eab58d..8c549346ce6ed 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/values.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/values.rs @@ -2,7 +2,7 @@ use itertools::Itertools as _; use crate::common::{ PASSES, - intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind}, + intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}, }; /// Maximum size of a SVE vector @@ -18,7 +18,7 @@ pub const MAX_SVE_BITS: u32 = 2048; /// 0x80, 0x3b, 0xff, /// ]; /// ``` -pub fn test_values_array_static( +pub fn test_values_array_static( w: &mut impl std::io::Write, ty: &T, ) -> std::io::Result<()> { @@ -34,7 +34,7 @@ pub fn test_values_array_static( /// Returns a string with the name of the static variable containing test values for intrinsic /// arguments of this type. -pub fn test_values_array_name(ty: &T) -> String { +pub fn test_values_array_name(ty: &T) -> String { format!( "{ty}_{load_size}", ty = ty.rust_scalar_type().to_uppercase(), diff --git a/library/stdarch/crates/intrinsic-test/src/main.rs b/library/stdarch/crates/intrinsic-test/src/main.rs index 4c0136041fc35..e25eb48a456d1 100644 --- a/library/stdarch/crates/intrinsic-test/src/main.rs +++ b/library/stdarch/crates/intrinsic-test/src/main.rs @@ -5,10 +5,10 @@ mod arm; mod common; mod x86; -use arm::ArmArchitectureTest; -use common::SupportedArchitectureTest; +use arm::Arm; +use common::SupportedArchitecture; use common::cli::{Cli, ProcessedCli}; -use x86::X86ArchitectureTest; +use x86::X86; fn main() { pretty_env_logger::init(); @@ -18,21 +18,15 @@ fn main() { if processed_cli_options.target.starts_with("arm") | processed_cli_options.target.starts_with("aarch64") { - run( - ArmArchitectureTest::create(&processed_cli_options), - processed_cli_options, - ) + run(Arm::create(&processed_cli_options), processed_cli_options) } else if processed_cli_options.target.starts_with("x86") { - run( - X86ArchitectureTest::create(&processed_cli_options), - processed_cli_options, - ) + run(X86::create(&processed_cli_options), processed_cli_options) } else { unimplemented!("Unsupported target {}", processed_cli_options.target) } } -fn run(test_environment: impl SupportedArchitectureTest, processed_cli_options: ProcessedCli) { +fn run(test_environment: impl SupportedArchitecture, processed_cli_options: ProcessedCli) { info!("building C binaries"); test_environment.generate_c_file(); diff --git a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs index 288bd8bdf8961..ae6d46080982b 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs @@ -4,19 +4,19 @@ mod intrinsic; mod types; mod xml_parser; -use crate::common::SupportedArchitectureTest; +use crate::common::SupportedArchitecture; use crate::common::cli::ProcessedCli; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; use intrinsic::X86IntrinsicType; use xml_parser::get_xml_intrinsics; -pub struct X86ArchitectureTest { +pub struct X86 { intrinsics: Vec>, } -impl SupportedArchitectureTest for X86ArchitectureTest { - type IntrinsicImpl = X86IntrinsicType; +impl SupportedArchitecture for X86 { + type Type = X86IntrinsicType; fn intrinsics(&self) -> &[Intrinsic] { &self.intrinsics diff --git a/library/stdarch/crates/intrinsic-test/src/x86/types.rs b/library/stdarch/crates/intrinsic-test/src/x86/types.rs index a0e14c77d6b5e..b7e063e22804d 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/types.rs @@ -3,12 +3,10 @@ use std::str::FromStr; use itertools::Itertools; use super::intrinsic::X86IntrinsicType; -use crate::common::intrinsic_helpers::{ - IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, -}; +use crate::common::intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}; use crate::x86::xml_parser::Parameter; -impl IntrinsicTypeDefinition for X86IntrinsicType { +impl TypeDefinition for X86IntrinsicType { /// Gets a string containing the type in C format. /// This function assumes that this value is present in the metadata hashmap. fn c_type(&self) -> String { From a7e203ab087688abc2a9ca25340d3eea83471ed4 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 2 Jun 2026 13:36:57 +0000 Subject: [PATCH 120/278] intrinsic-test: intrinsic generic over arch not type Refactoring enabling accessing architecture-specific behaviour that isn't associated with either of the return or argument types. --- .../intrinsic-test/src/arm/json_parser.rs | 5 ++-- .../crates/intrinsic-test/src/arm/mod.rs | 5 ++-- .../crates/intrinsic-test/src/common/gen_c.rs | 6 ++--- .../intrinsic-test/src/common/gen_rust.rs | 26 +++++++++---------- .../intrinsic-test/src/common/intrinsic.rs | 12 ++++----- .../crates/intrinsic-test/src/common/mod.rs | 13 +++------- .../crates/intrinsic-test/src/x86/mod.rs | 4 +-- .../intrinsic-test/src/x86/xml_parser.rs | 9 +++---- 8 files changed, 36 insertions(+), 44 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index ebbde115aa4bf..61e826957d065 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -1,4 +1,5 @@ use super::intrinsic::ArmType; +use crate::arm::Arm; use crate::arm::types::parse_intrinsic_type; use crate::common::argument::{Argument, ArgumentList}; use crate::common::constraint::Constraint; @@ -59,7 +60,7 @@ struct JsonIntrinsic { pub fn get_neon_intrinsics( filename: &Path, -) -> Result>, Box> { +) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); let json: Vec = serde_json::from_reader(reader).expect("Couldn't parse JSON"); @@ -79,7 +80,7 @@ pub fn get_neon_intrinsics( fn json_to_intrinsic( mut intr: JsonIntrinsic, -) -> Result, Box> { +) -> Result, Box> { let name = intr.name.replace(['[', ']'], ""); let result_ty = ArmType(parse_intrinsic_type(&intr.return_type.value)?); diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 0c5aa5da929de..6cd578fe43d53 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -10,12 +10,13 @@ use crate::common::intrinsic_helpers::{SimdLen, TypeKind}; use intrinsic::ArmType; use json_parser::get_neon_intrinsics; -pub struct Arm(Vec>); +#[derive(PartialEq)] +pub struct Arm(Vec>); impl SupportedArchitecture for Arm { type Type = ArmType; - fn intrinsics(&self) -> &[Intrinsic] { + fn intrinsics(&self) -> &[Intrinsic] { &self.0 } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index 888f5b2805d08..e4b6c9815c1ea 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -1,6 +1,6 @@ use itertools::Itertools; -use crate::common::intrinsic::Intrinsic; +use crate::common::{SupportedArchitecture, intrinsic::Intrinsic}; use super::intrinsic_helpers::TypeDefinition; @@ -14,11 +14,11 @@ use super::intrinsic_helpers::TypeDefinition; /// *__dst = __crc32cd(a, b); /// } /// ``` -pub fn write_wrapper_c( +pub fn write_wrapper_c( w: &mut impl std::io::Write, notice: &str, platform_headers: &[&str], - intrinsics: &[Intrinsic], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { write!(w, "{notice}")?; diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 36f5754367474..a2100e839a645 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -3,11 +3,11 @@ use std::process::Command; use itertools::Itertools; use super::intrinsic_helpers::TypeDefinition; -use crate::common::PASSES; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::{test_values_array_name, test_values_array_static}; +use crate::common::{PASSES, SupportedArchitecture}; /// Rust definitions that are included verbatim in the generated source. In particular, defines /// a wrapper around float types that defines `NaN`s to be equal reflexively to enable @@ -98,13 +98,10 @@ cc = "1" /// Writes a Rust source file into `w` with common definitions, static arrays with test values, /// declarations of C wrapper functions for FFI and Rust test functions. -pub fn write_lib_rs( +pub fn write_lib_rs( w: &mut impl std::io::Write, - notice: &str, - cfg: &str, - definitions: &str, i: usize, - intrinsics: &[Intrinsic], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { writeln!( w, @@ -123,7 +120,10 @@ pub fn write_lib_rs( {COMMON_RUST_DEFINITIONS} {definitions} -"# +"#, + notice = A::NOTICE, + cfg = A::PLATFORM_RUST_CFGS, + definitions = A::PLATFORM_RUST_DEFINITIONS, )?; let mut seen = std::collections::HashSet::new(); @@ -156,9 +156,9 @@ pub fn write_lib_rs( /// (first loop) `PASSES` number of times (second loop). For a given iteration of a given /// specialisation, test values are loaded for each argument and passed to the Rust intrinsic /// and the C wrapper function, and the results are compared. -fn generate_rust_test_loop( +fn generate_rust_test_loop( w: &mut impl std::io::Write, - intrinsic: &Intrinsic, + intrinsic: &Intrinsic, ) -> std::io::Result<()> { let intrinsic_name = &intrinsic.name; @@ -247,9 +247,9 @@ for (id, rust, c) in specializations {{ /// Writes a test function for an given intrinsic to `w`, with a body generated by /// `generate_rust_test_loop`. -fn create_rust_test( +fn create_rust_test( w: &mut impl std::io::Write, - intrinsic: &Intrinsic, + intrinsic: &Intrinsic, ) -> std::io::Result<()> { trace!("generating `{}`", intrinsic.name); @@ -271,10 +271,10 @@ fn test_{intrinsic_name}() {{ /// Writes an `extern "C"` block with function declarations for each of the C wrapper functions into /// `w`. -pub fn write_bindings_rust( +pub fn write_bindings_rust( w: &mut impl std::io::Write, i: usize, - intrinsics: &[Intrinsic], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { write!( w, diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index ca59385c3b4e2..904d1d6819f60 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -1,19 +1,17 @@ -use crate::common::constraint::Constraint; - use super::argument::ArgumentList; -use super::intrinsic_helpers::TypeDefinition; +use crate::common::{SupportedArchitecture, constraint::Constraint}; /// An intrinsic #[derive(Debug, PartialEq, Clone)] -pub struct Intrinsic { +pub struct Intrinsic { /// The function name of this intrinsic. pub name: String, /// Any arguments for this intrinsic. - pub arguments: ArgumentList, + pub arguments: ArgumentList, /// The return type of this intrinsic. - pub results: T, + pub results: A::Type, /// Any architecture-specific tags. pub arch_tags: Vec, @@ -43,7 +41,7 @@ fn recurse_specializations<'a, E>( } } -impl Intrinsic { +impl Intrinsic { /// Invokes `f` for "specialisation" of the intrinsic - a specific instantiation of the /// constant generics of the intrinsic. `f` takes a slice where the `i`th element corresponds /// to the value of the `i`th const generic argument of the intrinsic. diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 78720d6bc5202..aee2da630a7a4 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -29,10 +29,10 @@ pub(crate) const PASSES: u32 = 20; /// Architectures must support this trait /// to be successfully tested. -pub trait SupportedArchitecture { +pub trait SupportedArchitecture: Sized { type Type: TypeDefinition + Sync; - fn intrinsics(&self) -> &[Intrinsic]; + fn intrinsics(&self) -> &[Intrinsic]; fn create(cli_options: &ProcessedCli) -> Self; @@ -81,14 +81,7 @@ pub trait SupportedArchitecture { trace!("generating `{rust_filename}`"); let mut file = File::create(&rust_filename)?; - write_lib_rs( - &mut file, - Self::NOTICE, - Self::PLATFORM_RUST_CFGS, - Self::PLATFORM_RUST_DEFINITIONS, - i, - chunk, - )?; + write_lib_rs(&mut file, i, chunk)?; run_rustfmt(&rust_filename); let toml_filename = format!("rust_programs/mod_{i}/Cargo.toml"); diff --git a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs index ae6d46080982b..ec198d92188fa 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs @@ -12,13 +12,13 @@ use intrinsic::X86IntrinsicType; use xml_parser::get_xml_intrinsics; pub struct X86 { - intrinsics: Vec>, + intrinsics: Vec>, } impl SupportedArchitecture for X86 { type Type = X86IntrinsicType; - fn intrinsics(&self) -> &[Intrinsic] { + fn intrinsics(&self) -> &[Intrinsic] { &self.intrinsics } diff --git a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs index 7815fba2a0a11..f844d749ad32c 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs @@ -1,6 +1,7 @@ use crate::common::argument::{Argument, ArgumentList}; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; +use crate::x86::X86; use crate::x86::constraint::map_constraints; use regex::Regex; @@ -56,13 +57,13 @@ pub struct Parameter { pub fn get_xml_intrinsics( filename: &Path, -) -> Result>, Box> { +) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); let data: Data = quick_xml::de::from_reader(reader).expect("failed to deserialize the source XML file"); - let parsed_intrinsics: Vec> = data + let parsed_intrinsics: Vec> = data .intrinsics .into_iter() .filter(|intrinsic| { @@ -84,9 +85,7 @@ pub fn get_xml_intrinsics( Ok(parsed_intrinsics) } -fn xml_to_intrinsic( - intr: XMLIntrinsic, -) -> Result, Box> { +fn xml_to_intrinsic(intr: XMLIntrinsic) -> Result, Box> { let name = intr.name; let result = X86IntrinsicType::from_param(&intr.return_data); let args_check = intr.parameters.into_iter().enumerate().map(|(i, param)| { From 2a346c542322fc4659dbcfd4300ad15d005bcc03 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 9 Jun 2026 08:12:08 +0000 Subject: [PATCH 121/278] intrinsic-test: arg generic over arch not type Refactoring enabling accessing architecture-specific behaviour that isn't associated with the specific argument type. --- .../intrinsic-test/src/arm/json_parser.rs | 4 ++-- .../intrinsic-test/src/common/argument.rs | 21 ++++++++++--------- .../intrinsic-test/src/common/intrinsic.rs | 2 +- .../src/common/intrinsic_helpers.rs | 2 +- .../crates/intrinsic-test/src/common/mod.rs | 2 +- .../intrinsic-test/src/x86/xml_parser.rs | 13 ++++-------- 6 files changed, 20 insertions(+), 24 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index 61e826957d065..26f861ca64b62 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -122,7 +122,7 @@ fn json_to_intrinsic( }); let mut arg = - Argument::::new(i, String::from(arg_name), ArmType(arg_ty), constraint); + Argument::::new(i, String::from(arg_name), ArmType(arg_ty), constraint); // The JSON doesn't list immediates as const let IntrinsicType { @@ -135,7 +135,7 @@ fn json_to_intrinsic( }) .collect(); - let arguments = ArgumentList:: { args }; + let arguments = ArgumentList:: { args }; Ok(Intrinsic { name, diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index e91a9ab7735e1..0b42151d2862e 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -1,5 +1,6 @@ use itertools::Itertools; +use crate::common::SupportedArchitecture; use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::test_values_array_name; @@ -9,22 +10,22 @@ use super::intrinsic_helpers::TypeDefinition; /// An argument for the intrinsic. #[derive(Debug, PartialEq, Clone)] -pub struct Argument { +pub struct Argument { /// The argument's index in the intrinsic function call. pub pos: usize, /// The argument name. pub name: String, /// The type of the argument. - pub ty: T, + pub ty: A::Type, /// Any constraints that are on this argument pub constraint: Option, } -impl Argument +impl Argument where - T: TypeDefinition, + A: SupportedArchitecture, { - pub fn new(pos: usize, name: String, ty: T, constraint: Option) -> Self { + pub fn new(pos: usize, name: String, ty: A::Type, constraint: Option) -> Self { Argument { pos, name, @@ -63,13 +64,13 @@ where /// Arguments of an intrinsic - including parameters that end up being const generics. #[derive(Debug, PartialEq, Clone)] -pub struct ArgumentList { - pub args: Vec>, +pub struct ArgumentList { + pub args: Vec>, } -impl ArgumentList +impl ArgumentList where - T: TypeDefinition, + A: SupportedArchitecture, { /// Returns a string with the arguments in `self` as a parameter list for a wrapper fn /// definition in C (e.g. `$ty1 $arg1, $ty2 $arg2`). @@ -196,7 +197,7 @@ where } /// Returns an iterator over the contained arguments - pub fn iter(&self) -> std::slice::Iter<'_, Argument> { + pub fn iter(&self) -> std::slice::Iter<'_, Argument> { self.args.iter() } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index 904d1d6819f60..0c5bf43069d00 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -8,7 +8,7 @@ pub struct Intrinsic { pub name: String, /// Any arguments for this intrinsic. - pub arguments: ArgumentList, + pub arguments: ArgumentList, /// The return type of this intrinsic. pub results: A::Type, diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index 8e5d55ff3b39a..0e1b699293df9 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -195,7 +195,7 @@ impl IntrinsicType { } } -pub trait TypeDefinition: Deref { +pub trait TypeDefinition: Clone + Deref { /// Determines the load function for this type. fn get_load_function(&self) -> String; diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index aee2da630a7a4..ff0b6cbbafa47 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -30,7 +30,7 @@ pub(crate) const PASSES: u32 = 20; /// Architectures must support this trait /// to be successfully tested. pub trait SupportedArchitecture: Sized { - type Type: TypeDefinition + Sync; + type Type: TypeDefinition + std::fmt::Debug + PartialEq + Sync; fn intrinsics(&self) -> &[Intrinsic]; diff --git a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs index f844d749ad32c..52f6da786e3d7 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs @@ -99,12 +99,7 @@ fn xml_to_intrinsic(intr: XMLIntrinsic) -> Result, Box::new( - i, - param.var_name.clone(), - ty.unwrap(), - constraint, - ); + let arg = Argument::::new(i, param.var_name.clone(), ty.unwrap(), constraint); Some(arg) } }); @@ -124,8 +119,8 @@ fn xml_to_intrinsic(intr: XMLIntrinsic) -> Result, Box| arg.ty.param.etype.as_str() == "MASK"; - let is_vector = |arg: &Argument| re.is_match(arg.ty.param.type_data.as_str()); + let is_mask = |arg: &Argument| arg.ty.param.etype.as_str() == "MASK"; + let is_vector = |arg: &Argument| re.is_match(arg.ty.param.type_data.as_str()); let pos = args_test.position(|arg| is_mask(arg) && is_vector(arg)); if let Some(index) = pos { args[index].ty.bit_len = args[0].ty.bit_len; @@ -133,7 +128,7 @@ fn xml_to_intrinsic(intr: XMLIntrinsic) -> Result, Box { args }; + let arguments = ArgumentList:: { args }; if let Err(message) = result { return Err(Box::from(message)); From f4b49bc195fb12d8883dd42b045624fac59a7c48 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 11 Jun 2026 09:58:43 +0000 Subject: [PATCH 122/278] intrinsic-test: simplify architecture constants There doesn't need to be so many or other modules with the values. --- .../crates/intrinsic-test/src/arm/config.rs | 25 --- .../crates/intrinsic-test/src/arm/mod.rs | 38 ++++- .../crates/intrinsic-test/src/common/gen_c.rs | 9 +- .../intrinsic-test/src/common/gen_rust.rs | 7 +- .../crates/intrinsic-test/src/common/mod.rs | 12 +- .../crates/intrinsic-test/src/x86/config.rs | 138 ---------------- .../crates/intrinsic-test/src/x86/mod.rs | 148 +++++++++++++++++- 7 files changed, 181 insertions(+), 196 deletions(-) delete mode 100644 library/stdarch/crates/intrinsic-test/src/arm/config.rs delete mode 100644 library/stdarch/crates/intrinsic-test/src/x86/config.rs diff --git a/library/stdarch/crates/intrinsic-test/src/arm/config.rs b/library/stdarch/crates/intrinsic-test/src/arm/config.rs deleted file mode 100644 index 7c26143622e71..0000000000000 --- a/library/stdarch/crates/intrinsic-test/src/arm/config.rs +++ /dev/null @@ -1,25 +0,0 @@ -pub const NOTICE: &str = "\ -// This is a transient test file, not intended for distribution. Some aspects of the -// test are derived from a JSON specification, published under the same license as the -// `intrinsic-test` crate.\n"; - -pub const PLATFORM_RUST_DEFINITIONS: &str = ""; - -pub const PLATFORM_RUST_CFGS: &str = r#" -#![cfg_attr(target_arch = "arm", feature(stdarch_arm_neon_intrinsics))] -#![cfg_attr(target_arch = "arm", feature(stdarch_aarch32_crc32))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fcma))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_i8mm))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_sm4))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_feat_lut))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fp8))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(faminmax))] -#![feature(stdarch_neon_f16)] - -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] -use core_arch::arch::aarch64::*; - -#[cfg(target_arch = "arm")] -use core_arch::arch::arm::*; -"#; diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 6cd578fe43d53..a60e0ff1551a3 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -1,4 +1,3 @@ -mod config; mod intrinsic; mod json_parser; mod types; @@ -20,14 +19,20 @@ impl SupportedArchitecture for Arm { &self.0 } - const NOTICE: &str = config::NOTICE; + const NOTICE: &str = r#" +// This is a transient test file, not intended for distribution. Some aspects of the +// test are derived from a JSON specification, published under the same license as the +// `intrinsic-test` crate. +"#; - const PLATFORM_C_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; + const C_PRELUDE: &str = r#" +#include +#include +#include +"#; + const RUST_PRELUDE: &str = RUST_PRELUDE; - const PLATFORM_RUST_DEFINITIONS: &str = config::PLATFORM_RUST_DEFINITIONS; - const PLATFORM_RUST_CFGS: &str = config::PLATFORM_RUST_CFGS; - - fn arch_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { + fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { // GCC uses an extra `-` in the arch name match cli_options.cc_arg_style { CcArgStyle::Clang => vec!["-march=armv8.6a+crypto+crc+dotprod+fp16"], @@ -125,3 +130,22 @@ impl SupportedArchitecture for Arm { Self(intrinsics) } } + +const RUST_PRELUDE: &str = r#" +#![cfg_attr(target_arch = "arm", feature(stdarch_arm_neon_intrinsics))] +#![cfg_attr(target_arch = "arm", feature(stdarch_aarch32_crc32))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fcma))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_i8mm))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_sm4))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_feat_lut))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fp8))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(faminmax))] +#![feature(stdarch_neon_f16)] + +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] +use core_arch::arch::aarch64::*; + +#[cfg(target_arch = "arm")] +use core_arch::arch::arm::*; +"#; diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index e4b6c9815c1ea..21cc5cfa2e597 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -16,18 +16,13 @@ use super::intrinsic_helpers::TypeDefinition; /// ``` pub fn write_wrapper_c( w: &mut impl std::io::Write, - notice: &str, - platform_headers: &[&str], intrinsics: &[Intrinsic], ) -> std::io::Result<()> { - write!(w, "{notice}")?; + write!(w, "{}", A::NOTICE)?; writeln!(w, "#include ")?; writeln!(w, "#include ")?; - - for header in platform_headers { - writeln!(w, "#include <{header}>")?; - } + writeln!(w, "{}", A::C_PRELUDE)?; for intrinsic in intrinsics { intrinsic.iter_specializations(|imm_values| { diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index a2100e839a645..432cdde3d45af 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -116,14 +116,11 @@ pub fn write_lib_rs( #![allow(non_camel_case_types)] #![allow(non_snake_case)] -{cfg} +{prelude} {COMMON_RUST_DEFINITIONS} - -{definitions} "#, notice = A::NOTICE, - cfg = A::PLATFORM_RUST_CFGS, - definitions = A::PLATFORM_RUST_DEFINITIONS, + prelude = A::RUST_PRELUDE, )?; let mut seen = std::collections::HashSet::new(); diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index ff0b6cbbafa47..73daabbd6661a 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -38,12 +38,10 @@ pub trait SupportedArchitecture: Sized { const NOTICE: &str; - const PLATFORM_C_HEADERS: &[&str]; + const C_PRELUDE: &str; + const RUST_PRELUDE: &str; - const PLATFORM_RUST_CFGS: &str; - const PLATFORM_RUST_DEFINITIONS: &str; - - fn arch_flags(&self, cli_options: &ProcessedCli) -> Vec<&str>; + fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str>; fn generate_c_file(&self) { let (max_chunk_size, _chunk_count) = manual_chunk(self.intrinsics().len()); @@ -55,14 +53,14 @@ pub trait SupportedArchitecture: Sized { .map(|(i, chunk)| { let c_filename = format!("c_programs/wrapper_{i}.c"); let mut file = File::create(&c_filename).unwrap(); - write_wrapper_c(&mut file, Self::NOTICE, Self::PLATFORM_C_HEADERS, chunk) + write_wrapper_c(&mut file, chunk) }) .collect::>() .unwrap(); } fn generate_rust_file(&self, cli_options: &ProcessedCli) { - let arch_flags = self.arch_flags(cli_options); + let arch_flags = self.c_compiler_flags(cli_options); std::fs::create_dir_all("rust_programs").unwrap(); diff --git a/library/stdarch/crates/intrinsic-test/src/x86/config.rs b/library/stdarch/crates/intrinsic-test/src/x86/config.rs deleted file mode 100644 index 68737ab5ac4c8..0000000000000 --- a/library/stdarch/crates/intrinsic-test/src/x86/config.rs +++ /dev/null @@ -1,138 +0,0 @@ -pub const NOTICE: &str = "\ -// This is a transient test file, not intended for distribution. Some aspects of the -// test are derived from an XML specification, published under the same license as the -// `intrinsic-test` crate.\n"; - -pub const PLATFORM_RUST_DEFINITIONS: &str = r#" -use core_arch::arch::x86_64::*; - -#[inline] -unsafe fn _mm_loadu_ph_to___m128i(mem_addr: *const f16) -> __m128i { - _mm_castph_si128(_mm_loadu_ph(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_ph_to___m256i(mem_addr: *const f16) -> __m256i { - _mm256_castph_si256(_mm256_loadu_ph(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_ph_to___mm512i(mem_addr: *const f16) -> __m512i { - _mm512_castph_si512(_mm512_loadu_ph(mem_addr)) -} - - -#[inline] -unsafe fn _mm_loadu_ps_to___m128h(mem_addr: *const f32) -> __m128h { - _mm_castps_ph(_mm_loadu_ps(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_ps_to___m256h(mem_addr: *const f32) -> __m256h { - _mm256_castps_ph(_mm256_loadu_ps(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_ps_to___m512h(mem_addr: *const f32) -> __m512h { - _mm512_castps_ph(_mm512_loadu_ps(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi16_to___m128d(mem_addr: *const i16) -> __m128d { - _mm_castsi128_pd(_mm_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi16_to___m256d(mem_addr: *const i16) -> __m256d { - _mm256_castsi256_pd(_mm256_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi16_to___m512d(mem_addr: *const i16) -> __m512d { - _mm512_castsi512_pd(_mm512_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi32_to___m128d(mem_addr: *const i32) -> __m128d { - _mm_castsi128_pd(_mm_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi32_to___m256d(mem_addr: *const i32) -> __m256d { - _mm256_castsi256_pd(_mm256_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi32_to___m512d(mem_addr: *const i32) -> __m512d { - _mm512_castsi512_pd(_mm512_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi64_to___m128d(mem_addr: *const i64) -> __m128d { - _mm_castsi128_pd(_mm_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi64_to___m256d(mem_addr: *const i64) -> __m256d { - _mm256_castsi256_pd(_mm256_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi64_to___m512d(mem_addr: *const i64) -> __m512d { - _mm512_castsi512_pd(_mm512_loadu_epi64(mem_addr)) -} - -// === -#[inline] -unsafe fn _mm_loadu_epi16_to___m128(mem_addr: *const i16) -> __m128 { - _mm_castsi128_ps(_mm_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi16_to___m256(mem_addr: *const i16) -> __m256 { - _mm256_castsi256_ps(_mm256_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi16_to___m512(mem_addr: *const i16) -> __m512 { - _mm512_castsi512_ps(_mm512_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi32_to___m128(mem_addr: *const i32) -> __m128 { - _mm_castsi128_ps(_mm_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi32_to___m256(mem_addr: *const i32) -> __m256 { - _mm256_castsi256_ps(_mm256_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi32_to___m512(mem_addr: *const i32) -> __m512 { - _mm512_castsi512_ps(_mm512_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi64_to___m128(mem_addr: *const i64) -> __m128 { - _mm_castsi128_ps(_mm_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi64_to___m256(mem_addr: *const i64) -> __m256 { - _mm256_castsi256_ps(_mm256_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi64_to___m512(mem_addr: *const i64) -> __m512 { - _mm512_castsi512_ps(_mm512_loadu_epi64(mem_addr)) -} - -"#; - -pub const PLATFORM_RUST_CFGS: &str = r#" -#![feature(stdarch_x86_avx512_bf16)] -#![feature(stdarch_x86_avx512_f16)] -#![feature(stdarch_x86_rtm)] -#![feature(x86_amx_intrinsics)] -"#; diff --git a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs index ec198d92188fa..ce4955032916d 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs @@ -1,4 +1,3 @@ -mod config; mod constraint; mod intrinsic; mod types; @@ -22,14 +21,18 @@ impl SupportedArchitecture for X86 { &self.intrinsics } - const NOTICE: &str = config::NOTICE; + const NOTICE: &str = r#" +// This is a transient test file, not intended for distribution. Some aspects of the +// test are derived from an XML specification, published under the same license as the +// `intrinsic-test` crate. +"#; - const PLATFORM_C_HEADERS: &[&str] = &["immintrin.h"]; + const C_PRELUDE: &str = r#" +#include +"#; + const RUST_PRELUDE: &str = RUST_PRELUDE; - const PLATFORM_RUST_DEFINITIONS: &str = config::PLATFORM_RUST_DEFINITIONS; - const PLATFORM_RUST_CFGS: &str = config::PLATFORM_RUST_CFGS; - - fn arch_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { + fn c_compiler_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { vec![ "-maes", "-mf16c", @@ -97,3 +100,134 @@ impl SupportedArchitecture for X86 { Self { intrinsics } } } + +const RUST_PRELUDE: &str = r#" +#![feature(stdarch_x86_avx512_bf16)] +#![feature(stdarch_x86_avx512_f16)] +#![feature(stdarch_x86_rtm)] +#![feature(x86_amx_intrinsics)] + +use core_arch::arch::x86_64::*; + +#[inline] +unsafe fn _mm_loadu_ph_to___m128i(mem_addr: *const f16) -> __m128i { + _mm_castph_si128(_mm_loadu_ph(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_ph_to___m256i(mem_addr: *const f16) -> __m256i { + _mm256_castph_si256(_mm256_loadu_ph(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_ph_to___mm512i(mem_addr: *const f16) -> __m512i { + _mm512_castph_si512(_mm512_loadu_ph(mem_addr)) +} + + +#[inline] +unsafe fn _mm_loadu_ps_to___m128h(mem_addr: *const f32) -> __m128h { + _mm_castps_ph(_mm_loadu_ps(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_ps_to___m256h(mem_addr: *const f32) -> __m256h { + _mm256_castps_ph(_mm256_loadu_ps(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_ps_to___m512h(mem_addr: *const f32) -> __m512h { + _mm512_castps_ph(_mm512_loadu_ps(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi16_to___m128d(mem_addr: *const i16) -> __m128d { + _mm_castsi128_pd(_mm_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi16_to___m256d(mem_addr: *const i16) -> __m256d { + _mm256_castsi256_pd(_mm256_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi16_to___m512d(mem_addr: *const i16) -> __m512d { + _mm512_castsi512_pd(_mm512_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi32_to___m128d(mem_addr: *const i32) -> __m128d { + _mm_castsi128_pd(_mm_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi32_to___m256d(mem_addr: *const i32) -> __m256d { + _mm256_castsi256_pd(_mm256_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi32_to___m512d(mem_addr: *const i32) -> __m512d { + _mm512_castsi512_pd(_mm512_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi64_to___m128d(mem_addr: *const i64) -> __m128d { + _mm_castsi128_pd(_mm_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi64_to___m256d(mem_addr: *const i64) -> __m256d { + _mm256_castsi256_pd(_mm256_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi64_to___m512d(mem_addr: *const i64) -> __m512d { + _mm512_castsi512_pd(_mm512_loadu_epi64(mem_addr)) +} + +// === +#[inline] +unsafe fn _mm_loadu_epi16_to___m128(mem_addr: *const i16) -> __m128 { + _mm_castsi128_ps(_mm_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi16_to___m256(mem_addr: *const i16) -> __m256 { + _mm256_castsi256_ps(_mm256_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi16_to___m512(mem_addr: *const i16) -> __m512 { + _mm512_castsi512_ps(_mm512_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi32_to___m128(mem_addr: *const i32) -> __m128 { + _mm_castsi128_ps(_mm_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi32_to___m256(mem_addr: *const i32) -> __m256 { + _mm256_castsi256_ps(_mm256_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi32_to___m512(mem_addr: *const i32) -> __m512 { + _mm512_castsi512_ps(_mm512_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi64_to___m128(mem_addr: *const i64) -> __m128 { + _mm_castsi128_ps(_mm_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi64_to___m256(mem_addr: *const i64) -> __m256 { + _mm256_castsi256_ps(_mm256_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi64_to___m512(mem_addr: *const i64) -> __m512 { + _mm512_castsi512_ps(_mm512_loadu_epi64(mem_addr)) +} +"#; From 62ddde1d17c469813a045a0c97a4c6acea6fd8d9 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 1 Jun 2026 12:44:41 +0000 Subject: [PATCH 123/278] intrinsic-test: simplify type printing A small refactoring to make the type printing logic slightly cleaner and with greater code re-use. --- .../crates/intrinsic-test/src/arm/types.rs | 80 ++++++++++++------- .../src/common/intrinsic_helpers.rs | 25 +++--- 2 files changed, 64 insertions(+), 41 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index 44af107eb96e5..d27ae8069152c 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -6,21 +6,32 @@ impl TypeDefinition for ArmType { fn c_type(&self) -> String { let prefix = self.kind.c_prefix(); - if let Some(bit_len) = self.bit_len { - match (self.simd_len, self.vec_len) { - (None, None) => format!("{prefix}{bit_len}_t"), - (Some(SimdLen::Fixed(simd)), None) => format!("{prefix}{bit_len}x{simd}_t"), - (Some(SimdLen::Fixed(simd)), Some(vec)) => { - format!("{prefix}{bit_len}x{simd}x{vec}_t") - } - (Some(SimdLen::Scalable), None) => format!("sv{prefix}{bit_len}_t"), - (Some(SimdLen::Scalable), Some(vec)) => { - format!("sv{prefix}{bit_len}x{vec}_t") - } - (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case + match (self.bit_len, self.simd_len, self.vec_len) { + // e.g. `bool` + (Some(_), None, None) if matches!(self.kind, TypeKind::Bool) => { + format!("{prefix}") } - } else { - todo!("{self:#?}") + // e.g. `float32_t`, `int64_t` + (Some(bit_len), None, None) => format!("{prefix}{bit_len}_t"), + // e.g. `float32x2_t`, `int64x2_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), None) => { + format!("{prefix}{bit_len}x{simd}_t") + } + // e.g. `float32x2x3_t`, `int64x2x3_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{prefix}{bit_len}x{simd}x{vec}_t") + } + // e.g. `svbool_t` + (Some(_), Some(SimdLen::Scalable), None) if matches!(self.kind, TypeKind::Bool) => { + format!("sv{prefix}_t") + } + // e.g. `svfloat32_t`, `svint64_t` + (Some(bit_len), Some(SimdLen::Scalable), None) => format!("sv{prefix}{bit_len}_t"), + // e.g. `svfloat32x3_t`, `svint64x3_t` + (Some(bit_len), Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{prefix}{bit_len}x{vec}_t") + } + _ => todo!("{self:#?}"), } } @@ -28,21 +39,34 @@ impl TypeDefinition for ArmType { let rust_prefix = self.kind.rust_prefix(); let c_prefix = self.kind.c_prefix(); - if let Some(bit_len) = self.bit_len { - match (self.simd_len, self.vec_len) { - (None, None) => format!("{rust_prefix}{bit_len}"), - (Some(SimdLen::Fixed(simd)), None) => format!("{c_prefix}{bit_len}x{simd}_t"), - (Some(SimdLen::Fixed(simd)), Some(vec)) => { - format!("{c_prefix}{bit_len}x{simd}x{vec}_t") - } - (Some(SimdLen::Scalable), None) => format!("sv{c_prefix}{bit_len}_t"), - (Some(SimdLen::Scalable), Some(vec)) => { - format!("sv{c_prefix}{bit_len}x{vec}_t") - } - (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case + match (self.bit_len, self.simd_len, self.vec_len) { + // e.g. `svpattern` + (None, _, _) => format!("{rust_prefix}"), + // e.g. `bool` + (Some(_), None, None) if matches!(self.kind, TypeKind::Bool) => { + format!("{rust_prefix}") } - } else { - todo!("{self:#?}") + // e.g. `i32` + (Some(bit_len), None, None) => format!("{rust_prefix}{bit_len}"), + // e.g. `int32x2_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), None) => { + format!("{c_prefix}{bit_len}x{simd}_t") + } + // e.g. `int32x2x3_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{c_prefix}{bit_len}x{simd}x{vec}_t") + } + // e.g. `svbool_t` + (Some(_), Some(SimdLen::Scalable), None) if matches!(self.kind, TypeKind::Bool) => { + format!("sv{c_prefix}_t") + } + // e.g. `svint32_t` + (Some(bit_len), Some(SimdLen::Scalable), None) => format!("sv{c_prefix}{bit_len}_t"), + // e.g. `svint32x3_t` + (Some(bit_len), Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{c_prefix}{bit_len}x{vec}_t") + } + (Some(_), None, Some(_)) => todo!("{self:#?}"), } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index 0e1b699293df9..aae4a594df935 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -1,6 +1,6 @@ use std::cmp; use std::fmt; -use std::ops::Deref; +use std::ops::DerefMut; use std::str::FromStr; #[derive(Debug, PartialEq, Copy, Clone)] @@ -90,9 +90,13 @@ impl TypeKind { } } - /// Returns the Rust prefix for this type kind i.e. `i`, `u`, or `f`. + /// Returns the Rust prefix for this type kind (i.e. `i` for `i16`, or `u` for `u16`). For type + /// kinds without any bit length at the end (e.g. `bool`), returns the whole type name. pub fn rust_prefix(&self) -> &str { match self { + Self::Bool => "bool", + Self::SvPattern => "svpattern", + Self::SvPrefetchOp => "svprfop", Self::BFloat => "bf", Self::Float => "f", Self::Int(Sign::Signed) => "i", @@ -101,7 +105,7 @@ impl TypeKind { Self::Char(Sign::Unsigned) => "u", Self::Char(Sign::Signed) => "i", Self::Mask => "u", - _ => unreachable!("Unused type kind: {self:#?}"), + _ => unreachable!("type kind without Rust prefix: {self:#?}"), } } } @@ -195,7 +199,7 @@ impl IntrinsicType { } } -pub trait TypeDefinition: Clone + Deref { +pub trait TypeDefinition: Clone + DerefMut { /// Determines the load function for this type. fn get_load_function(&self) -> String; @@ -208,14 +212,9 @@ pub trait TypeDefinition: Clone + Deref { /// Gets a string containing the name of the scalar type corresponding to this type if it is a /// vector. fn rust_scalar_type(&self) -> String { - if self.is_simd() { - format!( - "{prefix}{bits}", - prefix = self.kind().rust_prefix(), - bits = self.inner_size() - ) - } else { - self.rust_type() - } + let mut ty = self.clone(); + ty.simd_len = None; + ty.vec_len = None; + ty.rust_type() } } From bae77b10221c92aa5d4f1e79a4b72d1c0c82f236 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 1 Jun 2026 12:44:41 +0000 Subject: [PATCH 124/278] intrinsic-test: introduce `get_comparison_function` Introduces a per-architecture abstraction over how intrinsic results are compared, so that later commits can implement Arm-specific comparison logic for SVE. --- .../intrinsic-test/src/common/gen_rust.rs | 27 +--------- .../src/common/intrinsic_helpers.rs | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 432cdde3d45af..f6a7c62a3cbd5 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -5,7 +5,6 @@ use itertools::Itertools; use super::intrinsic_helpers::TypeDefinition; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; -use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::{test_values_array_name, test_values_array_static}; use crate::common::{PASSES, SupportedArchitecture}; @@ -195,29 +194,6 @@ fn generate_rust_test_loop( writeln!(w, " ];")?; } - let (cast_prefix, cast_suffix) = if intrinsic.results.is_simd() { - ( - format!( - "std::mem::transmute::<_, [{}; {}]>(", - intrinsic.results.rust_scalar_type().replace("f", "NanEqF"), - intrinsic.results.num_lanes() * intrinsic.results.num_vectors() - ), - ")", - ) - } else if intrinsic.results.kind == TypeKind::Float { - ( - match intrinsic.results.inner_size() { - 16 => format!("NanEqF16("), - 32 => format!("NanEqF32("), - 64 => format!("NanEqF64("), - _ => unimplemented!(), - }, - ")", - ) - } else { - ("".to_string(), "") - }; - write!( w, r#" @@ -231,7 +207,7 @@ for (id, rust, c) in specializations {{ c(__c_return_value.as_mut_ptr(){c_args}); let __c_return_value = __c_return_value.assume_init(); - assert_eq!({cast_prefix}__rust_return_value{cast_suffix}, {cast_prefix}__c_return_value{cast_suffix}, "{{id}}"); + {comparison} }} }} }} @@ -239,6 +215,7 @@ for (id, rust, c) in specializations {{ loaded_args = intrinsic.arguments.load_values_rust(), rust_args = intrinsic.arguments.as_call_param_rust(), c_args = intrinsic.arguments.as_c_call_param_rust(), + comparison = intrinsic.results.get_comparison_function(), ) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index aae4a594df935..18ab3b745d513 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -203,6 +203,16 @@ pub trait TypeDefinition: Clone + DerefMut { /// Determines the load function for this type. fn get_load_function(&self) -> String; + /// Determines the comparison function for this type. + fn get_comparison_function(&self) -> String { + match self.simd_len { + Some(SimdLen::Scalable) => unimplemented!("architecture-specific"), + Some(SimdLen::Fixed(_)) | None => { + default_fixed_vector_comparison(self, self.num_lanes()) + } + } + } + /// Gets a string containing the typename for this type in C. fn c_type(&self) -> String; @@ -218,3 +228,43 @@ pub trait TypeDefinition: Clone + DerefMut { ty.rust_type() } } + +/// Returns the default comparison between results of an intrinsic - casting the vectors to arrays +/// and using `assert_eq` - using `NanEqF*` where required for floats. +pub(crate) fn default_fixed_vector_comparison( + ty: &Ty, + num_lanes: u32, +) -> String { + let (cast_prefix, cast_suffix) = if ty.is_simd() { + ( + format!( + "std::mem::transmute::<_, [{}; {}]>(", + ty.rust_scalar_type().replace("f", "NanEqF"), + num_lanes * ty.num_vectors() + ), + ")", + ) + } else if ty.kind == TypeKind::Float { + ( + match ty.inner_size() { + 16 => format!("NanEqF16("), + 32 => format!("NanEqF32("), + 64 => format!("NanEqF64("), + _ => unimplemented!(), + }, + ")", + ) + } else { + ("".to_string(), "") + }; + + format!( + r#" +assert_eq!( + {cast_prefix}__rust_return_value{cast_suffix}, + {cast_prefix}__c_return_value{cast_suffix}, + "{{id}}" +); +"#, + ) +} From 9686cccca9aee46a33283beb5fa744c162518a86 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 4 Jun 2026 12:48:11 +0000 Subject: [PATCH 125/278] intrinsic-test: rename `get_*_function` fns --- library/stdarch/crates/intrinsic-test/src/arm/types.rs | 4 ++-- library/stdarch/crates/intrinsic-test/src/common/argument.rs | 2 +- library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs | 2 +- .../crates/intrinsic-test/src/common/intrinsic_helpers.rs | 4 ++-- library/stdarch/crates/intrinsic-test/src/x86/types.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index d27ae8069152c..7754e9ec2d0dd 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -71,7 +71,7 @@ impl TypeDefinition for ArmType { } /// Determines the load function for this type. - fn get_load_function(&self) -> String { + fn load_function(&self) -> String { if let IntrinsicType { kind: k, bit_len: Some(bl), @@ -95,7 +95,7 @@ impl TypeDefinition for ArmType { len = vec_len.unwrap_or(1), ) } else { - todo!("get_load_function IntrinsicType: {self:#?}") + todo!("load_function IntrinsicType: {self:#?}") } } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index 0b42151d2862e..4d38bce327930 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -183,7 +183,7 @@ where "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);", name = arg.generate_name(), vals_name = test_values_array_name(&arg.ty), - load = arg.ty.get_load_function(), + load = arg.ty.load_function(), ) } else { format!( diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index f6a7c62a3cbd5..baa11511ec219 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -215,7 +215,7 @@ for (id, rust, c) in specializations {{ loaded_args = intrinsic.arguments.load_values_rust(), rust_args = intrinsic.arguments.as_call_param_rust(), c_args = intrinsic.arguments.as_c_call_param_rust(), - comparison = intrinsic.results.get_comparison_function(), + comparison = intrinsic.results.comparison_function(), ) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index 18ab3b745d513..b9f30af7dfd51 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -201,10 +201,10 @@ impl IntrinsicType { pub trait TypeDefinition: Clone + DerefMut { /// Determines the load function for this type. - fn get_load_function(&self) -> String; + fn load_function(&self) -> String; /// Determines the comparison function for this type. - fn get_comparison_function(&self) -> String { + fn comparison_function(&self) -> String { match self.simd_len { Some(SimdLen::Scalable) => unimplemented!("architecture-specific"), Some(SimdLen::Fixed(_)) | None => { diff --git a/library/stdarch/crates/intrinsic-test/src/x86/types.rs b/library/stdarch/crates/intrinsic-test/src/x86/types.rs index b7e063e22804d..2cba54a73f767 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/types.rs @@ -48,7 +48,7 @@ impl TypeDefinition for X86IntrinsicType { } /// Determines the load function for this type. - fn get_load_function(&self) -> String { + fn load_function(&self) -> String { let type_value = self.param.type_data.clone(); if type_value.len() == 0 { unimplemented!("the value for key 'type' is not present!"); From ca7ffdd0b148be35325976781c41c503563bf942 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sun, 21 Jun 2026 15:56:43 -0700 Subject: [PATCH 126/278] Move all test cases out of named constants --- tests/ui/unnecessary_cast.fixed | 65 ++++-------------- tests/ui/unnecessary_cast.rs | 65 ++++-------------- tests/ui/unnecessary_cast.stderr | 110 +++++++------------------------ 3 files changed, 46 insertions(+), 194 deletions(-) diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 5016ce3470672..9e07c52e82e67 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -659,65 +659,24 @@ fn issue16475() -> *const u8 { } } -const ISSUE_11882_TEST: u64 = (!0_u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_TEST2: u64 = (!0_u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_TEST3: u64 = (!not(!0_u64)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_TEST4: u64 = (!0_u64 + 0).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_TEST5: u64 = (!(0_u64 + 0)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_TEST6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; - -const ISSUE_11882_CHK1: u64 = not(!0_u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_CHK2: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_CHK3: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_CHK4: u64 = not(!0_u64 + 0).overflowing_shr(1_u32).0; -//~^ unnecessary_cast - // Make sure that the calculated values aren't changed by the fixes. const _: () = { - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST); - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST2); - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST3); - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST4); - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST5); - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST6); - assert!(0 == ISSUE_11882_CHK1); - assert!(0 == ISSUE_11882_CHK2); - assert!(0 == ISSUE_11882_CHK3); - assert!(0 == ISSUE_11882_CHK4); -}; - -fn issue_11882() { - // the non-const version of the tests - let issue_11882_test: u64 = (!0_u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let issue_11882_test2: u64 = (!0_u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let issue_11882_test3: u64 = (!not(!0_u64)).overflowing_shr(1_u32).0; + use std::convert::identity; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_test4: u64 = (!0_u64 + 0).overflowing_shr(1_u32).0; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_test5: u64 = (!(0_u64 + 0)).overflowing_shr(1_u32).0; + assert!(0 == (!identity(!0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 + 0).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_test6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; - - let issue_11882_chk1: u64 = not(!0_u64).overflowing_shr(1_u32).0; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0_u64 + 0)).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_chk2: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!((0 + 0) as u64)).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0_u64).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_chk3: u64 = (!not(0_u64)).overflowing_shr(1_u32).0; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64)).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_chk4: u64 = not(!0_u64 + 0).overflowing_shr(1_u32).0; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0_u64 + 0).overflowing_shr(1_u32).0); //~^ unnecessary_cast -} - -const fn not>(x: T) -> T { - !x -} +}; diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 00a2687387140..43d3e56e365a2 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -659,65 +659,24 @@ fn issue16475() -> *const u8 { } } -const ISSUE_11882_TEST: u64 = (!0 as u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_TEST2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_TEST3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_TEST4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_TEST5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_TEST6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; - -const ISSUE_11882_CHK1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_CHK2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_CHK3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; -//~^ unnecessary_cast -const ISSUE_11882_CHK4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; -//~^ unnecessary_cast - // Make sure that the calculated values aren't changed by the fixes. const _: () = { - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST); - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST2); - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST3); - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST4); - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST5); - assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == ISSUE_11882_TEST6); - assert!(0 == ISSUE_11882_CHK1); - assert!(0 == ISSUE_11882_CHK2); - assert!(0 == ISSUE_11882_CHK3); - assert!(0 == ISSUE_11882_CHK4); -}; - -fn issue_11882() { - // the non-const version of the tests - let issue_11882_test: u64 = (!0 as u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let issue_11882_test2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; - //~^ unnecessary_cast - let issue_11882_test3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; + use std::convert::identity; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_test4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 as u64).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_test5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; + assert!(0 == (!identity(!0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64 + 0).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_test6: u64 = (!((0 + 0) as u64)).overflowing_shr(1_u32).0; - - let issue_11882_chk1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0 as u64 + 0)).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_chk2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!((0 + 0) as u64)).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_chk3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0 as u64)).overflowing_shr(1_u32).0); //~^ unnecessary_cast - let issue_11882_chk4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64 + 0).overflowing_shr(1_u32).0); //~^ unnecessary_cast -} - -const fn not>(x: T) -> T { - !x -} +}; diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index eb710ba1fee29..9807ae0341421 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -392,112 +392,46 @@ LL | *(&NONE as *const _ as *const _ as *const *const u8 as *const *cons | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&NONE as *const _ as *const _ as *const *const u8)` error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:662:31 + --> tests/ui/unnecessary_cast.rs:665:45 | -LL | const ISSUE_11882_TEST: u64 = (!0 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^ help: try: `(!0_u64)` +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^^^ help: try: `(!0_u64)` error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast.rs:664:32 + --> tests/ui/unnecessary_cast.rs:667:45 | -LL | const ISSUE_11882_TEST2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast.rs:666:32 - | -LL | const ISSUE_11882_TEST3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(!0_u64))` +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:668:33 + --> tests/ui/unnecessary_cast.rs:670:46 | -LL | const ISSUE_11882_TEST4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64 + 0).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:670:35 - | -LL | const ISSUE_11882_TEST5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; - | ^^^^^^^^ help: try: `0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:674:35 - | -LL | const ISSUE_11882_CHK1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast.rs:676:31 + --> tests/ui/unnecessary_cast.rs:672:48 | -LL | const ISSUE_11882_CHK2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(0_u64))` +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0 as u64 + 0)).overflowing_shr(1_u32).0); + | ^^^^^^^^ help: try: `0_u64` error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:678:37 - | -LL | const ISSUE_11882_CHK3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; - | ^^^^^^^^ help: try: `0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:680:35 - | -LL | const ISSUE_11882_CHK4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:699:33 - | -LL | let issue_11882_test: u64 = (!0 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^ help: try: `(!0_u64)` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast.rs:701:34 - | -LL | let issue_11882_test2: u64 = (!0_u64 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast.rs:703:34 - | -LL | let issue_11882_test3: u64 = (!not(!0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(!0_u64))` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:705:35 - | -LL | let issue_11882_test4: u64 = (!0 as u64 + 0).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:707:37 - | -LL | let issue_11882_test5: u64 = (!(0 as u64 + 0)).overflowing_shr(1_u32).0; - | ^^^^^^^^ help: try: `0_u64` - -error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:711:37 - | -LL | let issue_11882_chk1: u64 = not(!0 as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` - -error: casting to the same type is unnecessary (`u64` -> `u64`) - --> tests/ui/unnecessary_cast.rs:713:33 + --> tests/ui/unnecessary_cast.rs:675:54 | -LL | let issue_11882_chk2: u64 = (!not(0_u64) as u64).overflowing_shr(1_u32).0; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `(!not(0_u64))` +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:715:39 + --> tests/ui/unnecessary_cast.rs:678:56 | -LL | let issue_11882_chk3: u64 = (!not(0 as u64)).overflowing_shr(1_u32).0; - | ^^^^^^^^ help: try: `0_u64` +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0 as u64)).overflowing_shr(1_u32).0); + | ^^^^^^^^ help: try: `0_u64` error: casting integer literal to `u64` is unnecessary - --> tests/ui/unnecessary_cast.rs:717:37 + --> tests/ui/unnecessary_cast.rs:680:54 | -LL | let issue_11882_chk4: u64 = not(!0 as u64 + 0).overflowing_shr(1_u32).0; - | ^^^^^^^^^ help: try: `!0_u64` +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64 + 0).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` -error: aborting due to 83 previous errors +error: aborting due to 72 previous errors From cc45b69ee5f66a560c88a92fe588aa5d88a34be6 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 22 Jun 2026 06:12:25 +0000 Subject: [PATCH 127/278] Prepare for merging from rust-lang/rust This updates the rust-version file to 942ac9ce4116d4ea784c9882659372b34978b1f8. --- 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 4a2bfdb2cd558..5db47ca8fc59b 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -01f54e80e888b66d6486a3a95d481b87353016df +942ac9ce4116d4ea784c9882659372b34978b1f8 From 76418128dc4c6c50067df392cc8419b747b11396 Mon Sep 17 00:00:00 2001 From: Hamdan-Khan Date: Mon, 22 Jun 2026 13:02:26 +0500 Subject: [PATCH 128/278] Do not trigger `manual_option_zip` when map receiver is a lazy evaluated expression --- clippy_lints/src/methods/manual_option_zip.rs | 3 ++ tests/ui/manual_option_zip.fixed | 28 +++++++++++++++---- tests/ui/manual_option_zip.rs | 28 +++++++++++++++---- tests/ui/manual_option_zip.stderr | 16 ++++------- 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/methods/manual_option_zip.rs b/clippy_lints/src/methods/manual_option_zip.rs index aa23b9deff45b..538f8a2fe93dc 100644 --- a/clippy_lints/src/methods/manual_option_zip.rs +++ b/clippy_lints/src/methods/manual_option_zip.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::peel_blocks; use clippy_utils::res::{MaybeDef, MaybeResPath}; @@ -29,6 +30,8 @@ pub(super) fn check<'tcx>( && let ExprKind::MethodCall(method_path, map_recv, [map_arg], _) = peel_blocks(outer_value).kind && method_path.ident.name == sym::map && cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Option) + // `b` is not lazy evaluated + && switch_to_eager_eval(cx, map_recv) // `b` does not reference the outer closure parameter `a`. && !local_used_in(cx, outer_param_id, map_recv) // `|b| (a, b)` diff --git a/tests/ui/manual_option_zip.fixed b/tests/ui/manual_option_zip.fixed index 0a903fb784075..8b821f8737cbb 100644 --- a/tests/ui/manual_option_zip.fixed +++ b/tests/ui/manual_option_zip.fixed @@ -21,11 +21,6 @@ fn should_lint() { let _ = None::.zip(b); //~^ manual_option_zip - // with function call as map receiver - let a: Option = Some(1); - let _ = a.zip(get_option()); - //~^ manual_option_zip - // tuple order reversed: (inner, outer) instead of (outer, inner) let a: Option = Some(1); let b: Option = Some(2); @@ -123,3 +118,26 @@ fn issue16968() { let opts = [1, 2]; let _ = a.and_then(|a| opts.into_iter().find(|b| *b == a).map(|b| (a, b))); } + +fn issue17253() { + // don't trigger the lint if map receiver is a lazy evaluated expression + // because `a.zip(b_func())` requires eager evaluation of the argument, + // preventing the otherwise conditional execution of `b_func()` + + use std::hint::black_box; + let a: Option = Some(1); + + // conditional function call + let _ = a.and_then(|a| black_box(get_option()).map(|b| (a, b))); + + let mut b = 2; + + // conditional side effects + let _ = a.and_then(|a| { + { + b /= 2; + Some(b) + } + .map(|b| (a, b)) + }); +} diff --git a/tests/ui/manual_option_zip.rs b/tests/ui/manual_option_zip.rs index 942d8aea3e835..5d059a98ee12c 100644 --- a/tests/ui/manual_option_zip.rs +++ b/tests/ui/manual_option_zip.rs @@ -21,11 +21,6 @@ fn should_lint() { let _ = None::.and_then(|a| b.map(|b| (a, b))); //~^ manual_option_zip - // with function call as map receiver - let a: Option = Some(1); - let _ = a.and_then(|a| get_option().map(|b| (a, b))); - //~^ manual_option_zip - // tuple order reversed: (inner, outer) instead of (outer, inner) let a: Option = Some(1); let b: Option = Some(2); @@ -123,3 +118,26 @@ fn issue16968() { let opts = [1, 2]; let _ = a.and_then(|a| opts.into_iter().find(|b| *b == a).map(|b| (a, b))); } + +fn issue17253() { + // don't trigger the lint if map receiver is a lazy evaluated expression + // because `a.zip(b_func())` requires eager evaluation of the argument, + // preventing the otherwise conditional execution of `b_func()` + + use std::hint::black_box; + let a: Option = Some(1); + + // conditional function call + let _ = a.and_then(|a| black_box(get_option()).map(|b| (a, b))); + + let mut b = 2; + + // conditional side effects + let _ = a.and_then(|a| { + { + b /= 2; + Some(b) + } + .map(|b| (a, b)) + }); +} diff --git a/tests/ui/manual_option_zip.stderr b/tests/ui/manual_option_zip.stderr index 473f21702654c..55175660b7a26 100644 --- a/tests/ui/manual_option_zip.stderr +++ b/tests/ui/manual_option_zip.stderr @@ -20,34 +20,28 @@ LL | let _ = None::.and_then(|a| b.map(|b| (a, b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `None::.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:26:13 - | -LL | let _ = a.and_then(|a| get_option().map(|b| (a, b))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(get_option())` - -error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:32:13 + --> tests/ui/manual_option_zip.rs:27:13 | LL | let _ = a.and_then(|a| b.map(|b| (b, a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.zip(a)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:39:13 + --> tests/ui/manual_option_zip.rs:34:13 | LL | let _ = a.and_then(|a| { b.map(|b| (a, b)) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:42:13 + --> tests/ui/manual_option_zip.rs:37:13 | LL | let _ = a.and_then(|a| b.map(|b| { (a, b) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:45:13 + --> tests/ui/manual_option_zip.rs:40:13 | LL | let _ = a.and_then(|a| { b.map(|b| { (a, b) }) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors From b973eb94a52630da5606b44272b89198f60968cc Mon Sep 17 00:00:00 2001 From: Emmanuel Ugwu Date: Mon, 22 Jun 2026 19:02:02 +0100 Subject: [PATCH 129/278] Allow unstable attribute on foreign types and add stability test --- compiler/rustc_attr_parsing/src/attributes/stability.rs | 1 + tests/ui/lint/lint-stability.rs | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index b38bb6d535770..734acb9ea21e7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -40,6 +40,7 @@ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Static), Allow(Target::ForeignFn), Allow(Target::ForeignStatic), + Allow(Target::ForeignTy), Allow(Target::ExternCrate), ]); diff --git a/tests/ui/lint/lint-stability.rs b/tests/ui/lint/lint-stability.rs index f080b5e4bbeb1..21009b1e61db4 100644 --- a/tests/ui/lint/lint-stability.rs +++ b/tests/ui/lint/lint-stability.rs @@ -6,9 +6,13 @@ #![allow(deprecated)] #![allow(dead_code)] #![feature(staged_api)] - +#![feature(extern_types)] #![stable(feature = "rust1", since = "1.0.0")] +extern "C" { + #[unstable(feature = "fn_static", issue = "none")] + type Ty; +} #[macro_use] extern crate lint_stability; From 6e6921c9d88dcb8b20146059a78d0014659c7b3f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 22 Jun 2026 13:45:16 -0400 Subject: [PATCH 130/278] Simplify `missing_inline_in_public_items`. --- clippy_lints/src/missing_inline.rs | 151 ++++------------------ tests/ui/missing_inline.stderr | 12 +- tests/ui/missing_inline_executable.stderr | 2 +- 3 files changed, 35 insertions(+), 130 deletions(-) diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 93cfed38c43ed..32a86db67c194 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -1,8 +1,6 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir}; -use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, Attribute, find_attr}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::AssocContainer; +use clippy_utils::diagnostics::span_lint; +use rustc_hir::{ImplItem, ImplItemKind, Item, ItemKind, OwnerId, TraitFn, TraitItem, TraitItemKind, find_attr}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::config::CrateType; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -66,134 +64,41 @@ declare_clippy_lint! { declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]); -fn check_missing_inline_attrs( - cx: &LateContext<'_>, - attrs: &[Attribute], - sp: Span, - desc: &'static str, - hir_id: Option, -) { - if !find_attr!(attrs, Inline(..)) { - let msg = format!("missing `#[inline]` for {desc}"); - if let Some(hir_id) = hir_id { - span_lint_hir(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, hir_id, sp, msg); - } else { - span_lint(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, msg); - } +fn check(cx: &LateContext<'_>, item: OwnerId, sp: Span) { + if cx.effective_visibilities.is_exported(item.def_id) + && !find_attr!(cx.tcx.hir_attrs(item.into()), Inline(..)) + // Rust `inline` doesn't mean anything with external linkage. + && !cx.tcx.codegen_fn_attrs(item.def_id).contains_extern_indicator() + && !cx.tcx.crate_types().iter().any(|&t| matches!(t, CrateType::ProcMacro)) + && !sp.in_external_macro(cx.tcx.sess.source_map()) + { + span_lint( + cx, + MISSING_INLINE_IN_PUBLIC_ITEMS, + sp, + "missing `#[inline]` on a publicly callable function", + ); } } impl<'tcx> LateLintPass<'tcx> for MissingInline { - fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - if it.span.in_external_macro(cx.sess().source_map()) { - return; - } - - if cx - .tcx - .crate_types() - .iter() - .any(|t: &CrateType| matches!(t, CrateType::ProcMacro)) - { - return; - } - - if !cx.effective_visibilities.is_exported(it.owner_id.def_id) { - return; - } - match it.kind { - hir::ItemKind::Fn { .. } => { - if fn_is_externally_exported(cx, it.owner_id.to_def_id()) { - return; - } - - let desc = "a function"; - let attrs = cx.tcx.hir_attrs(it.hir_id()); - check_missing_inline_attrs(cx, attrs, it.span, desc, None); - }, - hir::ItemKind::Trait { items: trait_items, .. } => { - // note: we need to check if the trait is exported so we can't use - // `LateLintPass::check_trait_item` here. - for &tit in trait_items { - let tit_ = cx.tcx.hir_trait_item(tit); - match tit_.kind { - hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {}, - hir::TraitItemKind::Fn(..) => { - if cx.tcx.defaultness(tit.owner_id).has_value() { - // trait method with default body needs inline in case - // an impl is not provided - let desc = "a default trait method"; - let item = cx.tcx.hir_trait_item(tit); - let attrs = cx.tcx.hir_attrs(item.hir_id()); - check_missing_inline_attrs(cx, attrs, item.span, desc, Some(tit.hir_id())); - } - }, - } - } - }, - hir::ItemKind::Const(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Macro(..) - | hir::ItemKind::Mod(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::TraitAlias(..) - | hir::ItemKind::GlobalAsm { .. } - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::ExternCrate(..) - | hir::ItemKind::ForeignMod { .. } - | hir::ItemKind::Impl { .. } - | hir::ItemKind::Use(..) => {}, + fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { + if let ItemKind::Fn { .. } = it.kind { + check(cx, it.owner_id, it.span); } } - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - if impl_item.span.in_external_macro(cx.sess().source_map()) - || cx - .tcx - .crate_types() - .iter() - .any(|t: &CrateType| matches!(t, CrateType::ProcMacro)) + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Fn(_, f) = item.kind + && let TraitFn::Provided(_) = f { - return; + check(cx, item.owner_id, item.span); } + } - // If the item being implemented is not exported, then we don't need #[inline] - if !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { - return; - } - - let desc = match impl_item.kind { - hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(_) => return, - }; - - let assoc_item = cx.tcx.associated_item(impl_item.owner_id); - let container_id = assoc_item.container_id(cx.tcx); - let trait_def_id = match assoc_item.container { - AssocContainer::Trait => Some(container_id), - AssocContainer::TraitImpl(_) => Some(cx.tcx.impl_trait_id(container_id)), - AssocContainer::InherentImpl => None, - }; - - if let Some(trait_def_id) = trait_def_id - && trait_def_id.is_local() - && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) - { - // If a trait is being implemented for an item, and the - // trait is not exported, we don't need #[inline] - return; + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Fn(..) = item.kind { + check(cx, item.owner_id, item.span); } - - let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); - check_missing_inline_attrs(cx, attrs, impl_item.span, desc, None); } } - -/// Checks if this function is externally exported, where #[inline] wouldn't have the desired effect -/// and a rustc warning would be triggered, see #15301 -fn fn_is_externally_exported(cx: &LateContext<'_>, def_id: DefId) -> bool { - let attrs = cx.tcx.codegen_fn_attrs(def_id); - attrs.contains_extern_indicator() -} diff --git a/tests/ui/missing_inline.stderr b/tests/ui/missing_inline.stderr index 99ee828e805ca..9f5987fb8e79b 100644 --- a/tests/ui/missing_inline.stderr +++ b/tests/ui/missing_inline.stderr @@ -1,4 +1,4 @@ -error: missing `#[inline]` for a function +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:20:1 | LL | pub fn pub_foo() {} @@ -7,31 +7,31 @@ LL | pub fn pub_foo() {} = note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_inline_in_public_items)]` -error: missing `#[inline]` for a default trait method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:39:5 | LL | fn PubBar_b() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:56:5 | LL | fn PubBar_a() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:60:5 | LL | fn PubBar_b() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:64:5 | LL | fn PubBar_c() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:76:5 | LL | pub fn PubFooImpl() {} diff --git a/tests/ui/missing_inline_executable.stderr b/tests/ui/missing_inline_executable.stderr index 3108e4e490659..d083cb2b54915 100644 --- a/tests/ui/missing_inline_executable.stderr +++ b/tests/ui/missing_inline_executable.stderr @@ -1,4 +1,4 @@ -error: missing `#[inline]` for a function +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline_executable.rs:3:1 | LL | pub fn foo() {} From b11a76b9814081a056763e6b9943c6f620d7d966 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Tue, 23 Jun 2026 10:39:23 -0400 Subject: [PATCH 131/278] tests: modify s390x vector test to be robust to instruction scheduling A recent LLVM change causes some changes here, if I'm understanding correctly it allows some better latency reduction. From what I can tell, this test doesn't care that only a single register is used, so we use -DAG instead of -NEXT to allow some instruction reordering. By happy coincidence, the z10 and z13 code matches now, which collapsed some of the test lines. I'm happy to split them back out if that's bad for some reason though! --- tests/assembly-llvm/s390x-vector-abi.rs | 60 +++++++++---------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/tests/assembly-llvm/s390x-vector-abi.rs b/tests/assembly-llvm/s390x-vector-abi.rs index 90139df17ca1a..c0f770c3f0c35 100644 --- a/tests/assembly-llvm/s390x-vector-abi.rs +++ b/tests/assembly-llvm/s390x-vector-abi.rs @@ -62,16 +62,11 @@ unsafe extern "C" fn vector_ret(x: &i8x16) -> i8x16 { *x } // CHECK-LABEL: vector_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 0(%r3), 4 -// z13-NEXT: vl %v1, 16(%r3), 4 -// z13-NEXT: vst %v1, 16(%r2), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_ret_large(x: &i8x32) -> i8x32 { @@ -95,16 +90,11 @@ unsafe extern "C" fn vector_wrapper_ret(x: &Wrapper) -> Wrapper { *x } // CHECK-LABEL: vector_wrapper_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 16(%r3), 4 -// z13-NEXT: vst %v0, 16(%r2), 4 -// z13-NEXT: vl %v0, 0(%r3), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_ret_large(x: &Wrapper) -> Wrapper { @@ -141,16 +131,11 @@ unsafe extern "C" fn vector_wrapper_with_zst_ret( *x } // CHECK-LABEL: vector_wrapper_with_zst_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 16(%r3), 4 -// z13-NEXT: vst %v0, 16(%r2), 4 -// z13-NEXT: vl %v0, 0(%r3), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_with_zst_ret_large( @@ -180,16 +165,11 @@ unsafe extern "C" fn vector_transparent_wrapper_ret( *x } // CHECK-LABEL: vector_transparent_wrapper_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 0(%r3), 4 -// z13-NEXT: vl %v1, 16(%r3), 4 -// z13-NEXT: vst %v1, 16(%r2), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_transparent_wrapper_ret_large( From 753b3a9c4e8e0eab34fce4ed414f52ac6eee2521 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 23 Jun 2026 12:05:08 -0400 Subject: [PATCH 132/278] Move `empty_enums` to the nursery --- clippy_lints/src/empty_enums.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/empty_enums.rs b/clippy_lints/src/empty_enums.rs index f96854411fe6f..2cac6597949de 100644 --- a/clippy_lints/src/empty_enums.rs +++ b/clippy_lints/src/empty_enums.rs @@ -51,7 +51,7 @@ declare_clippy_lint! { /// [visibility]: https://doc.rust-lang.org/reference/visibility-and-privacy.html #[clippy::version = "pre 1.29.0"] pub EMPTY_ENUMS, - pedantic, + nursery, "enum with no variants" } From 8d2cf93085c8593be84112237c4186f7d581a4ba Mon Sep 17 00:00:00 2001 From: Ed Swartz <875407+eswartz@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:35:23 -0500 Subject: [PATCH 133/278] Update `cargo miri --help` to point to README.md. --- src/tools/miri/cargo-miri/src/phases.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index f58cec827cf5d..4a41c4997cac5 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -36,6 +36,9 @@ Examples: This will print the path to the generated sysroot (and nothing else) on stdout. stderr will still contain progress information about how the build is doing. +For documentation on `-Zmiri-...` flags, see miri's local README.md +(for example, $(rustc --print sysroot)/share/doc/miri/README.md) +or the rendered version at [https://github.com/rust-lang/miri/blob/master/README.md]. "; fn show_help() { From aa5792a9e7bc75f13cdb2895cc3947b93548a830 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 Jun 2026 19:59:01 +0200 Subject: [PATCH 134/278] reformat pointers to README --- src/tools/miri/cargo-miri/src/phases.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 4a41c4997cac5..caf6987291970 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -36,9 +36,9 @@ Examples: This will print the path to the generated sysroot (and nothing else) on stdout. stderr will still contain progress information about how the build is doing. -For documentation on `-Zmiri-...` flags, see miri's local README.md -(for example, $(rustc --print sysroot)/share/doc/miri/README.md) -or the rendered version at [https://github.com/rust-lang/miri/blob/master/README.md]. +For documentation on `-Zmiri-...` flags, see Miri's README.md, available at: +- $(rustc --print sysroot)/share/doc/miri/README.md +- https://github.com/rust-lang/miri/blob/master/README.md "; fn show_help() { From b429456924bb43549c98af77e80807f3e3fb98cf Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 23 Jun 2026 16:37:49 +0200 Subject: [PATCH 135/278] Move some methods that only need the region context onto that --- .../src/diagnostics/explain_borrow.rs | 2 +- .../src/diagnostics/region_errors.rs | 40 ++++++++++++------- .../src/diagnostics/region_name.rs | 6 +-- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 5d154cc7b9050..f6abafb874165 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -706,7 +706,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { Some(Cause::LiveVar(..) | Cause::DropVar(..)) | None => { // Here, under NLL: no cause was found. Under polonius: no cause was found, or a // boring local was found, which we ignore like NLLs do to match its diagnostics. - if let Some(region) = self.to_error_region_vid(borrow_region_vid) { + if let Some(region) = self.regioncx.to_error_region_vid(borrow_region_vid) { let (category, from_closure, span, region_name, path) = self.free_region_constraint_info(borrow_region_vid, region); if let Some(region_name) = region_name { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index e436d95cdb087..66d35f8b8bf2d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -28,6 +28,7 @@ use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; use tracing::{debug, instrument, trace}; use super::{LIMITATION_NOTE, OutlivesSuggestionBuilder, RegionName, RegionNameSource}; +use crate::consumers::RegionInferenceContext; use crate::nll::ConstraintDescription; use crate::region_infer::{BlameConstraint, TypeTest}; use crate::session_diagnostics::{ @@ -134,7 +135,7 @@ pub(crate) struct ErrorConstraintInfo<'tcx> { pub(super) span: Span, } -impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { +impl<'tcx> RegionInferenceContext<'tcx> { /// Converts a region inference variable into a `ty::Region` that /// we can use for error reporting. If `r` is universally bound, /// then we use the name that we have on record for it. If `r` is @@ -142,20 +143,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// to find a good name from that. Returns `None` if we can't find /// one (e.g., this is just some random part of the CFG). pub(super) fn to_error_region(&self, r: RegionVid) -> Option> { - self.to_error_region_vid(r).and_then(|r| self.regioncx.region_definition(r).external_name) + self.to_error_region_vid(r).and_then(|r| self.region_definition(r).external_name) } /// Returns the `RegionVid` corresponding to the region returned by /// `to_error_region`. pub(super) fn to_error_region_vid(&self, r: RegionVid) -> Option { - if self.regioncx.universal_regions().is_universal_region(r) { + if self.universal_regions().is_universal_region(r) { Some(r) } else { // We just want something nameable, even if it's not // actually an upper bound. - let upper_bound = self.regioncx.approx_universal_upper_bound(r); + let upper_bound = self.approx_universal_upper_bound(r); - if self.regioncx.upper_bound_in_region_scc(r, upper_bound) { + if self.upper_bound_in_region_scc(r, upper_bound) { self.to_error_region_vid(upper_bound) } else { None @@ -179,14 +180,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(r) = self.to_error_region(fr) && let ty::ReLateParam(late_param) = r.kind() && let ty::LateParamRegionKind::ClosureEnv = late_param.kind - && let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty + && let DefiningTy::Closure(_, args) = self.universal_regions().defining_ty { return args.as_closure().kind() == ty::ClosureKind::FnMut; } false } +} +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // For generic associated types (GATs) which implied 'static requirement // from higher-ranked trait bounds (HRTB). Try to locate span of the trait // and the span which bounded to the trait for adding 'static lifetime suggestion @@ -309,12 +312,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { RegionErrorKind::TypeTestError { type_test } => { // Try to convert the lower-bound region into something named we can print for // the user. - let lower_bound_region = self.to_error_region(type_test.lower_bound); + let lower_bound_region = self.regioncx.to_error_region(type_test.lower_bound); let type_test_span = type_test.span; if let Some(lower_bound_region) = lower_bound_region { - let generic_ty = self.name_regions( + let generic_ty = self.regioncx.name_regions( self.infcx.tcx, type_test.generic_kind.to_ty(self.infcx.tcx), ); @@ -324,7 +327,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.body.source.def_id().expect_local(), type_test_span, Some(origin), - self.name_regions(self.infcx.tcx, type_test.generic_kind), + self.regioncx.name_regions(self.infcx.tcx, type_test.generic_kind), lower_bound_region, )); } else { @@ -450,7 +453,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info); // Check if we can use one of the "nice region errors". - if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { + if let (Some(f), Some(o)) = + (self.regioncx.to_error_region(fr), self.regioncx.to_error_region(outlived_fr)) + { let infer_err = self.infcx.err_ctxt(); let nice = NiceRegionError::new_from_span(&infer_err, self.mir_def_id(), cause.span, o, f); @@ -481,7 +486,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { d.note("meoow :c"); d } - (ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => { + (ConstraintCategory::Return(kind), true, false) + if self.regioncx.is_closure_fn_mut(fr) => + { self.report_fnmut_error(&errci, kind) } (ConstraintCategory::Assignment, true, false) @@ -736,7 +743,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // Only show an extra note if we can find an 'error region' for both of the region // variables. This avoids showing a noisy note that just mentions 'synthetic' regions // that don't help the user understand the error. - match (self.to_error_region(errci.fr), self.to_error_region(errci.outlived_fr)) { + match ( + self.regioncx.to_error_region(errci.fr), + self.regioncx.to_error_region(errci.outlived_fr), + ) { (Some(f), Some(o)) => { self.maybe_suggest_constrain_dyn_trait_impl(&mut diag, f, o, category); @@ -842,7 +852,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { outlived_fr: RegionVid, ) { if let (Some(f), Some(outlived_f)) = - (self.to_error_region(fr), self.to_error_region(outlived_fr)) + (self.regioncx.to_error_region(fr), self.regioncx.to_error_region(outlived_fr)) { if outlived_f.kind() != ty::ReStatic { return; @@ -1013,7 +1023,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } fn suggest_adding_lifetime_params(&self, diag: &mut Diag<'_>, sub: RegionVid, sup: RegionVid) { - let (Some(sub), Some(sup)) = (self.to_error_region(sub), self.to_error_region(sup)) else { + let (Some(sub), Some(sup)) = + (self.regioncx.to_error_region(sub), self.regioncx.to_error_region(sup)) + else { return; }; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index a00d39eea12d3..b8037fc83aad0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -283,7 +283,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { /// named variants. #[instrument(level = "trace", skip(self))] fn give_name_from_error_region(&self, fr: RegionVid) -> Option { - let error_region = self.to_error_region(fr)?; + let error_region = self.regioncx.to_error_region(fr)?; let tcx = self.infcx.tcx; @@ -1015,7 +1015,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &self, fr: RegionVid, ) -> Option { - let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else { + let ty::ReEarlyParam(region) = self.regioncx.to_error_region(fr)?.kind() else { return None; }; if region.is_named() { @@ -1050,7 +1050,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &self, fr: RegionVid, ) -> Option { - let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else { + let ty::ReEarlyParam(region) = self.regioncx.to_error_region(fr)?.kind() else { return None; }; if region.is_named() { From 7ebb8596d118af0144d60d60c29135c1095a2bd5 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 24 Jun 2026 09:10:13 +0200 Subject: [PATCH 136/278] Consistently taint root cx Instead of having to taint both infcx and root_cx, we always just taint infcx (which usually gets automatically tainted from emitting diagnostics), and at the end (before dropping the infcx) we move the taint over to the root_cx --- .../rustc_borrowck/src/diagnostics/mod.rs | 61 ++++++++++--------- .../src/diagnostics/opaque_types.rs | 8 +-- compiler/rustc_borrowck/src/lib.rs | 8 +-- compiler/rustc_borrowck/src/type_check/mod.rs | 1 - 4 files changed, 38 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 56e0c74c85e97..a5b7a26a10ab4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -116,11 +116,42 @@ impl<'infcx, 'tcx> BorrowckDiagnosticsBuffer<'infcx, 'tcx> { pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) { self.buffered_diags.push(BufferedDiag::NonError(diag)); } + pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) { + self.buffered_diags.push(BufferedDiag::Error(diag)); + } + + pub(crate) fn emit_errors(&mut self) -> Option { + let mut res = None; + + // Buffer any move errors that we collected and de-duplicated. + for (_, (_, diag)) in std::mem::take(&mut self.buffered_move_errors) { + // We have already set tainted for this error, so just buffer it. + self.buffer_error(diag); + } + for (_, (mut diag, count)) in std::mem::take(&mut self.buffered_mut_errors) { + if count > 10 { + diag.note(format!("...and {} other attempted mutable borrows", count - 10)); + } + self.buffer_error(diag); + } + + if !self.buffered_diags.is_empty() { + self.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span()); + for buffered_diag in self.buffered_diags.drain(..) { + match buffered_diag { + BufferedDiag::Error(diag) => res = Some(diag.emit()), + BufferedDiag::NonError(diag) => diag.emit(), + } + } + } + + res + } } impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) { - self.diags_buffer.buffered_diags.push(BufferedDiag::Error(diag)); + self.diags_buffer.buffer_error(diag); } pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) { @@ -152,34 +183,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.diags_buffer.buffered_mut_errors.insert(span, (diag, count)); } - pub(crate) fn emit_errors(&mut self) -> Option { - let mut res = self.infcx.tainted_by_errors(); - - // Buffer any move errors that we collected and de-duplicated. - for (_, (_, diag)) in std::mem::take(&mut self.diags_buffer.buffered_move_errors) { - // We have already set tainted for this error, so just buffer it. - self.buffer_error(diag); - } - for (_, (mut diag, count)) in std::mem::take(&mut self.diags_buffer.buffered_mut_errors) { - if count > 10 { - diag.note(format!("...and {} other attempted mutable borrows", count - 10)); - } - self.buffer_error(diag); - } - - if !self.diags_buffer.buffered_diags.is_empty() { - self.diags_buffer.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span()); - for buffered_diag in self.diags_buffer.buffered_diags.drain(..) { - match buffered_diag { - BufferedDiag::Error(diag) => res = Some(diag.emit()), - BufferedDiag::NonError(diag) => diag.emit(), - } - } - } - - res - } - pub(crate) fn has_buffered_diags(&self) -> bool { self.diags_buffer.buffered_diags.is_empty() } diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs index 590c2c9c3965b..1df16595e2d86 100644 --- a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs +++ b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs @@ -28,11 +28,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } let infcx = self.infcx; - let mut guar = None; let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> = None; for error in errors { - guar = Some(match error { + match error { DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx), DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => { infcx.dcx().emit_err(err) @@ -81,11 +80,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ) ), ), - }); + }; } - let guar = guar.unwrap(); - self.root_cx.set_tainted_by_errors(guar); - self.infcx.set_tainted_by_errors(guar); } /// Try to note when an opaque is involved in a borrowck error and that diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 94ad74b67b629..b9ff75421a52f 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -323,7 +323,6 @@ fn borrowck_collect_region_constraints<'tcx>( let input_promoted: &IndexSlice<_, _> = &promoted.borrow(); if let Some(e) = input_body.tainted_by_errors { infcx.set_tainted_by_errors(e); - root_cx.set_tainted_by_errors(e); } // Replace all regions with fresh inference variables. This @@ -571,15 +570,16 @@ fn borrowck_check_region_constraints<'tcx>( debug!("mbcx.used_mut: {:?}", mbcx.used_mut); mbcx.lint_unused_mut(); - if let Some(guar) = mbcx.emit_errors() { - mbcx.root_cx.set_tainted_by_errors(guar); - } let result = PropagatedBorrowCheckResults { closure_requirements: opt_closure_req, used_mut_upvars: mbcx.used_mut_upvars, }; + if let Some(guar) = mbcx.diags_buffer.emit_errors().or(infcx.tainted_by_errors()) { + root_cx.set_tainted_by_errors(guar); + } + if let Some(consumer) = &mut root_cx.consumer { consumer.insert_body( def, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 450ac46aab521..3dc104e251cbe 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -198,7 +198,6 @@ pub(crate) fn type_check<'tcx>( debug!("encountered an error region; removing constraints!"); constraints.outlives_constraints = Default::default(); constraints.type_tests = Default::default(); - root_cx.set_tainted_by_errors(guar); infcx.set_tainted_by_errors(guar); } From f44093d3136d935c649f3560081941a1a4d5e462 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 24 Jun 2026 09:10:13 +0200 Subject: [PATCH 137/278] Remove some unnecessary mutabilities --- compiler/rustc_borrowck/src/lib.rs | 2 +- compiler/rustc_borrowck/src/nll.rs | 2 +- compiler/rustc_borrowck/src/root_cx.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index b9ff75421a52f..e6d0163044947 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -718,7 +718,7 @@ impl<'tcx> Deref for BorrowckInferCtxt<'tcx> { } pub(crate) struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { - root_cx: &'a mut BorrowCheckRootCtxt<'tcx>, + root_cx: &'a BorrowCheckRootCtxt<'tcx>, infcx: &'infcx BorrowckInferCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>, diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index dd6eb17947577..b11f1868b64c9 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -111,7 +111,7 @@ pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>( /// /// This may result in errors being reported. pub(crate) fn compute_regions<'tcx>( - root_cx: &mut BorrowCheckRootCtxt<'tcx>, + root_cx: &BorrowCheckRootCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, location_table: &PoloniusLocationTable, diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index a082aba35b8a7..e406f158db216 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -72,7 +72,7 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { } pub(super) fn used_mut_upvars( - &mut self, + &self, nested_body_def_id: LocalDefId, ) -> &SmallVec<[FieldIdx; 8]> { &self.propagated_borrowck_results[&nested_body_def_id].used_mut_upvars diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 3dc104e251cbe..b7bf4efcb6d38 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -95,7 +95,7 @@ mod relate_tys; /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis /// - `location_map` -- map between MIR `Location` and `PointIndex` pub(crate) fn type_check<'tcx>( - root_cx: &mut BorrowCheckRootCtxt<'tcx>, + root_cx: &BorrowCheckRootCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, @@ -228,7 +228,7 @@ enum FieldAccessError { /// way, it accrues region constraints -- these can later be used by /// NLL region checking. struct TypeChecker<'a, 'tcx> { - root_cx: &'a mut BorrowCheckRootCtxt<'tcx>, + root_cx: &'a BorrowCheckRootCtxt<'tcx>, infcx: &'a BorrowckInferCtxt<'tcx>, last_span: Span, body: &'a Body<'tcx>, From 7102a6ca9fec52d986f03b6a5bd5d8bb315633ee Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 24 Jun 2026 12:17:24 +0200 Subject: [PATCH 138/278] Typeck children always have the same param env as their parent --- compiler/rustc_ty_utils/src/ty.rs | 3 +++ .../ui/lifetimes/issue-76168-hr-outlives-3.rs | 5 ++--- .../issue-76168-hr-outlives-3.stderr | 20 ++++--------------- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 367e2e1b97059..c7a3d7252e1ce 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -149,6 +149,9 @@ fn adt_sizedness_constraint<'tcx>( /// See `ParamEnv` struct definition for details. fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { + if tcx.is_typeck_child(def_id) { + return tcx.param_env(tcx.typeck_root_def_id(def_id)); + } // Compute the bounds on Self and the type parameters. let ty::InstantiatedPredicates { predicates, .. } = tcx.predicates_of(def_id).instantiate_identity(tcx); diff --git a/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs b/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs index 85eeb5d4c901e..77a1f0b4f25d6 100644 --- a/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs +++ b/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs @@ -8,10 +8,9 @@ async fn wrapper(f: F) //~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` //~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` where -F:, -for<'a> >::Output: Future + 'a, + F:, + for<'a> >::Output: Future + 'a, { - //~^ ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` let mut i = 41; &mut i; } diff --git a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr index 6da9f7380d597..bf70826165341 100644 --- a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr +++ b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr @@ -3,9 +3,9 @@ error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` | LL | / async fn wrapper(f: F) ... | -LL | | F:, -LL | | for<'a> >::Output: Future + 'a, - | |__________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32` +LL | | F:, +LL | | for<'a> >::Output: Future + 'a, + | |______________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32` | = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` @@ -26,18 +26,6 @@ LL | async fn wrapper(f: F) = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` - --> $DIR/issue-76168-hr-outlives-3.rs:13:1 - | -LL | / { -LL | | -LL | | let mut i = 41; -LL | | &mut i; -LL | | } - | |_^ expected an `FnOnce(&'a mut i32)` closure, found `i32` - | - = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. From 7cb94f0f36df282b6f2b6896b975f946069db233 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 24 Jun 2026 12:38:01 +0200 Subject: [PATCH 139/278] Don't compute opaque types for typeck children. They are always the same as their parent --- compiler/rustc_ty_utils/src/opaque_types.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index d95624b9a24d2..939b51fa7dbc0 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -316,6 +316,9 @@ fn opaque_types_defined_by<'tcx>( tcx: TyCtxt<'tcx>, item: LocalDefId, ) -> &'tcx ty::List { + if tcx.is_typeck_child(item.to_def_id()) { + return tcx.opaque_types_defined_by(tcx.local_parent(item)); + } let kind = tcx.def_kind(item); trace!(?kind); let mut collector = OpaqueTypeCollector::new(tcx, item); @@ -331,13 +334,6 @@ fn opaque_types_defined_by<'tcx>( | DefKind::AnonConst => { collector.collect_taits_declared_in_body(); } - // Closures and coroutines are type checked with their parent - // Note that we also support `SyntheticCoroutineBody` since we create - // a MIR body for the def kind, and some MIR passes (like promotion) - // may require doing analysis using its typing env. - DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => { - collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item))); - } DefKind::AssocTy | DefKind::TyAlias | DefKind::GlobalAsm => {} DefKind::OpaqueTy | DefKind::Mod @@ -348,6 +344,15 @@ fn opaque_types_defined_by<'tcx>( | DefKind::Trait | DefKind::ForeignTy | DefKind::TraitAlias + + // Closures and coroutines are type checked with their parent + // Note that we also support `SyntheticCoroutineBody` since we create + // a MIR body for the def kind, and some MIR passes (like promotion) + // may require doing analysis using its typing env. + | DefKind::Closure + | DefKind::InlineConst + | DefKind::SyntheticCoroutineBody + | DefKind::TyParam | DefKind::ConstParam | DefKind::Ctor(_, _) From 7ccc34feb25be450833e6ad132a3672dbbcee706 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 24 Jun 2026 16:37:31 +0200 Subject: [PATCH 140/278] Add a convenience helper for doing type ops --- compiler/rustc_borrowck/src/lib.rs | 9 +++++ .../src/type_check/constraint_conversion.rs | 8 ++--- .../src/type_check/free_region_relations.rs | 34 +++++++------------ .../src/type_check/liveness/trace.rs | 10 +++--- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index e6d0163044947..cc01207141a28 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -48,6 +48,7 @@ use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results}; use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_trait_selection::traits::query::type_op::{QueryTypeOp, TypeOp, TypeOpOutput}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -707,6 +708,14 @@ impl<'tcx> BorrowckInferCtxt<'tcx> { next_region } + + fn fully_perform + TypeVisitable>>( + &self, + q: Q, + span: Span, + ) -> Result>, ErrorGuaranteed> { + self.param_env.and(q).fully_perform(&self.infcx, self.root_def_id, span) + } } impl<'tcx> Deref for BorrowckInferCtxt<'tcx> { diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 4d93fa08fe0bd..82e623bb42535 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::{ self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, elaborate, fold_regions, }; use rustc_span::Span; -use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; +use rustc_trait_selection::traits::query::type_op::TypeOpOutput; use tracing::{debug, instrument}; use crate::constraints::OutlivesConstraint; @@ -287,11 +287,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { ConstraintCategory<'tcx>, )>, ) -> Ty<'tcx> { - match self.infcx.param_env.and(DeeplyNormalize { value: ty }).fully_perform( - self.infcx, - self.infcx.root_def_id, - self.span, - ) { + match self.infcx.fully_perform(DeeplyNormalize { value: ty }, self.span) { Ok(TypeOpOutput { output: ty, constraints, .. }) => { // FIXME(higher_ranked_auto): What should we do with the assumptions here? if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints { diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 6c7beb85f1e8c..62becc8e298f4 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, Span}; -use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; +use rustc_trait_selection::traits::query::type_op; use tracing::{debug, instrument}; use type_op::TypeOpOutput; @@ -242,15 +242,14 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { if let Some(c) = constraints_unnorm { constraints.push(c) } - let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = - param_env - .and(DeeplyNormalize { value: ty }) - .fully_perform(self.infcx, self.infcx.root_def_id, span) - .unwrap_or_else(|guar| TypeOpOutput { - output: Ty::new_error(self.infcx.tcx, guar), - constraints: None, - error_info: None, - }); + let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self + .infcx + .fully_perform(DeeplyNormalize { value: ty }, span) + .unwrap_or_else(|guar| TypeOpOutput { + output: Ty::new_error(self.infcx.tcx, guar), + constraints: None, + error_info: None, + }); if let Some(c) = constraints_normalize { constraints.push(c) } @@ -299,9 +298,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst { .. }) { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { - let result: Result<_, ErrorGuaranteed> = param_env - .and(DeeplyNormalize { value: ty }) - .fully_perform(self.infcx, self.infcx.root_def_id, span); + let result: Result<_, ErrorGuaranteed> = + self.infcx.fully_perform(DeeplyNormalize { value: ty }, span); let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { continue; }; @@ -354,11 +352,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { output: normalized_outlives, constraints: constraints_normalize, error_info: _, - }) = self.infcx.param_env.and(DeeplyNormalize { value: outlives }).fully_perform( - self.infcx, - self.infcx.root_def_id, - span, - ) + }) = self.infcx.fully_perform(DeeplyNormalize { value: outlives }, span) else { self.infcx.dcx().delayed_bug(format!("could not normalize {outlives:?}")); return; @@ -381,9 +375,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { ) -> Option<&'tcx QueryRegionConstraints<'tcx>> { let TypeOpOutput { output: bounds, constraints, .. } = self .infcx - .param_env - .and(type_op::ImpliedOutlivesBounds { ty }) - .fully_perform(self.infcx, self.infcx.root_def_id, span) + .fully_perform(type_op::ImpliedOutlivesBounds { ty }, span) .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) .ok()?; debug!(?bounds, ?constraints); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 840210496eb44..5a06f54cd2e41 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -15,7 +15,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::query::dropck_outlives; -use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, TypeOpOutput}; +use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOpOutput}; use tracing::debug; use crate::polonius; @@ -638,9 +638,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { ) -> DropData<'tcx> { debug!("compute_drop_data(dropped_ty={:?})", dropped_ty); - let op = typeck.infcx.param_env.and(DropckOutlives { dropped_ty }); + let goal = DropckOutlives { dropped_ty }; - match op.fully_perform(typeck.infcx, typeck.root_cx.root_def_id(), DUMMY_SP) { + match typeck.infcx.fully_perform(goal, DUMMY_SP) { Ok(TypeOpOutput { output, constraints, .. }) => { DropData { dropck_result: output, region_constraint_data: constraints } } @@ -655,7 +655,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { typeck.infcx.probe(|_| { let ocx = ObligationCtxt::new_with_diagnostics(&typeck.infcx); let errors = match dropck_outlives::compute_dropck_outlives_with_errors( - &ocx, op, span, + &ocx, + typeck.infcx.param_env.and(goal), + span, ) { Ok(_) => ocx.evaluate_obligations_error_on_ambiguity(), Err(e) => e, From b2a46c9d9a3773d48f1e20585e9c26bf60d3b59c Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Wed, 24 Jun 2026 18:12:42 +0300 Subject: [PATCH 141/278] Move `check_ffi_pure` into the attribute parser convert `FfiPureParser` to a full `AttributeParser` so its `finalize` can check for a sibling `#[ffi_const]` and reject `#[ffi_pure]` during attribute parsing, replacing `check_ffi_pure` in `rustc_passes`. --- .../src/attributes/link_attrs.rs | 41 ++++++++++++++----- compiler/rustc_attr_parsing/src/context.rs | 2 +- .../src/session_diagnostics.rs | 7 ++++ compiler/rustc_passes/src/check_attr.rs | 9 +--- compiler/rustc_passes/src/diagnostics.rs | 7 ---- 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index f260863840080..0ff97b47fc696 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -14,11 +14,11 @@ use super::util::parse_single_integer; use crate::attributes::AttributeSafety; use crate::attributes::cfg::parse_cfg_entry; use crate::session_diagnostics::{ - AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic, - ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, - InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange, - LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, RawDylibOnlyWindows, - WholeArchiveNeedsStatic, + AsNeededCompatibility, BothFfiConstAndPure, BundleNeedsStatic, EmptyLinkName, + ExportSymbolsNeedsStatic, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, + InvalidLinkModifier, InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, + LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, + RawDylibOnlyWindows, UnusedMultiple, WholeArchiveNeedsStatic, }; pub(crate) struct LinkNameParser; @@ -563,16 +563,37 @@ impl NoArgsAttributeParser for FfiConstParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::FfiConst; } -pub(crate) struct FfiPureParser; -impl NoArgsAttributeParser for FfiPureParser { - const PATH: &[Symbol] = &[sym::ffi_pure]; +#[derive(Default)] +pub(crate) struct FfiPureParser { + first_span: Option, +} + +impl AttributeParser for FfiPureParser { + const ATTRIBUTES: AcceptMapping = + &[(&[sym::ffi_pure], template!(Word), unstable!(ffi_pure), |this, cx, args| { + let _ = cx.expect_no_args(args); + let span = cx.attr_span; + // `#[ffi_pure]` may only appear once; mirror the previous `OnDuplicate::Error`. + if let Some(first) = this.first_span { + cx.emit_err(UnusedMultiple { this: span, other: first, name: sym::ffi_pure }); + } else { + this.first_span = Some(span); + } + })]; const SAFETY: AttributeSafety = AttributeSafety::Unsafe { note: "`#[ffi_pure]` functions shall have no effects except for its return value, which shall not change across two consecutive function calls with the same parameters.", unsafe_since: None, }; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); - const STABILITY: AttributeStability = unstable!(ffi_pure); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure; + + fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { + let span = self.first_span?; + // `#[ffi_const]` functions cannot be `#[ffi_pure]`. + if cx.all_attrs.iter().any(|a| a.word_is(sym::ffi_const)) { + cx.emit_err(BothFfiConstAndPure { attr_span: span }); + } + Some(AttributeKind::FfiPure(span)) + } } pub(crate) struct RustcStdInternalSymbolParser; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 14e443c922711..16458f65b1116 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -143,6 +143,7 @@ attribute_parsers!( ConfusablesParser, ConstStabilityParser, DocParser, + FfiPureParser, MacroUseParser, NakedParser, OnConstParser, @@ -246,7 +247,6 @@ attribute_parsers!( Single>, Single>, Single>, - Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 494824a8393b3..74b028ecc35be 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -13,6 +13,13 @@ use rustc_target::spec::TargetTuple; use crate::AttributeTemplate; use crate::context::Suggestion; +#[derive(Diagnostic)] +#[diag("`#[ffi_const]` function cannot be `#[ffi_pure]`", code = E0757)] +pub(crate) struct BothFfiConstAndPure { + #[primary_span] + pub attr_span: Span, +} + #[derive(Diagnostic)] #[diag("{$attr_str} attribute cannot have empty value")] pub(crate) struct DocAliasEmpty<'a> { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 079fdd923ac0d..c91c1776c81f4 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -219,7 +219,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::NonExhaustive(attr_span) => { self.check_non_exhaustive(*attr_span, span, target, item) } - &AttributeKind::FfiPure(attr_span) => self.check_ffi_pure(attr_span, attrs), AttributeKind::MayDangle(attr_span) => self.check_may_dangle(hir_id, *attr_span), AttributeKind::Link(_, attr_span) => self.check_link(hir_id, *attr_span, target), AttributeKind::MacroExport { span, .. } => { @@ -271,6 +270,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::ExportStable => (), AttributeKind::Feature(..) => (), AttributeKind::FfiConst => (), + AttributeKind::FfiPure(..) => (), AttributeKind::Fundamental => (), AttributeKind::Ignore { .. } => (), AttributeKind::InstructionSet(..) => (), @@ -1112,13 +1112,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute]) { - if find_attr!(attrs, FfiConst) { - // `#[ffi_const]` functions cannot be `#[ffi_pure]` - self.dcx().emit_err(diagnostics::BothFfiConstAndPure { attr_span }); - } - } - /// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl. fn check_may_dangle(&self, hir_id: HirId, attr_span: Span) { if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id) diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index f6c4b0cf77d12..2576b84716664 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -153,13 +153,6 @@ pub(crate) struct DocMaskedNotExternCrateSelf { pub item_span: Span, } -#[derive(Diagnostic)] -#[diag("`#[ffi_const]` function cannot be `#[ffi_pure]`", code = E0757)] -pub(crate) struct BothFfiConstAndPure { - #[primary_span] - pub attr_span: Span, -} - #[derive(Diagnostic)] #[diag("`#[optimize(none)]` cannot be used with `#[inline]` attributes")] pub(crate) struct BothOptimizeNoneAndInline { From 0cf893ebe23bb181cd9949e22f0ae3007d0018f1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Jun 2026 17:24:42 +0200 Subject: [PATCH 142/278] dont ICE on generic no_mangle items --- src/tools/miri/src/helpers.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 730c7d9fac611..9a601265defb9 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -129,7 +129,13 @@ pub fn iter_exported_symbols<'tcx>( || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) }; - if exported { + // FIXME: `#[no_mangle]` makes no sense on a generic item, but still causes it to be + // considered "extern". Remove this once `no_mangle_generic_items` is a hard error. + let exported_mono = exported && { + let generics = tcx.generics_of(def_id); + !generics.requires_monomorphization(tcx) + }; + if exported_mono { f(LOCAL_CRATE, def_id.into())?; } } From dc5b1a958e4fd5b5fe5c4ba1b062143272792011 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Jun 2026 17:30:07 +0200 Subject: [PATCH 143/278] test-cargo-miri: test all workspace members --- src/tools/miri/test-cargo-miri/run-test.py | 7 +- .../test.proc-macro.stdout.ref | 5 - ...o.stderr.ref => test.workspace.stderr.ref} | 0 .../test-cargo-miri/test.workspace.stdout.ref | 103 ++++++++++++++++++ 4 files changed, 107 insertions(+), 8 deletions(-) delete mode 100644 src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref rename src/tools/miri/test-cargo-miri/{test.proc-macro.stderr.ref => test.workspace.stderr.ref} (100%) create mode 100644 src/tools/miri/test-cargo-miri/test.workspace.stdout.ref diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index cfbe3098e54ff..afebdff51157d 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -170,9 +170,10 @@ def test_cargo_miri_test(): "test.empty.ref", env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) - test("`cargo miri test` (proc-macro crate)", - cargo_miri("test") + ["-p", "proc_macro_crate"], - "test.proc-macro.stdout.ref", "test.proc-macro.stderr.ref", + test("`cargo miri test` (entire workspace, no isolation)", + cargo_miri("test") + ["--workspace"], + "test.workspace.stdout.ref", "test.workspace.stderr.ref", + env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) test("`cargo miri test` (custom target dir)", cargo_miri("test") + ["--target-dir=custom-test"], diff --git a/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref b/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref deleted file mode 100644 index 7326c0a25a069..0000000000000 --- a/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref +++ /dev/null @@ -1,5 +0,0 @@ - -running 0 tests - -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME - diff --git a/src/tools/miri/test-cargo-miri/test.proc-macro.stderr.ref b/src/tools/miri/test-cargo-miri/test.workspace.stderr.ref similarity index 100% rename from src/tools/miri/test-cargo-miri/test.proc-macro.stderr.ref rename to src/tools/miri/test-cargo-miri/test.workspace.stderr.ref diff --git a/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref b/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref new file mode 100644 index 0000000000000..a4e224a329797 --- /dev/null +++ b/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref @@ -0,0 +1,103 @@ + +running 2 tests +.. +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + +imported main + +running 6 tests +...i.. +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + +subcrate testing + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $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 + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 1 test +. +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + From 01a12a403fc64eb7e47ed7ec4d8883e221bc1206 Mon Sep 17 00:00:00 2001 From: Cai Congcong Date: Sun, 29 Mar 2026 22:17:10 +0800 Subject: [PATCH 144/278] add ui test for no mangle generic --- .../tests/pass/issues/issue-154385-no-mangle-generic.rs | 9 +++++++++ .../pass/issues/issue-154385-no-mangle-generic.stdout | 1 + 2 files changed, 10 insertions(+) create mode 100644 src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs create mode 100644 src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout diff --git a/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs new file mode 100644 index 0000000000000..0989cdd86a362 --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs @@ -0,0 +1,9 @@ +fn main() { + foo(1234); +} + +#[allow(no_mangle_generic_items)] +#[unsafe(no_mangle)] +fn foo(value: T) { + println!("{value:?}"); +} diff --git a/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout new file mode 100644 index 0000000000000..81c545efebe5f --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout @@ -0,0 +1 @@ +1234 From c19c46298276396bb7b27498fd5f9877ec8fd844 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Thu, 25 Jun 2026 00:15:36 +0300 Subject: [PATCH 145/278] Use `NoArgsAttributeParser` for parsing `#[ffi_pure]` --- .../src/attributes/link_attrs.rs | 30 +++++-------------- .../rustc_attr_parsing/src/attributes/mod.rs | 26 ++++++++++++++-- compiler/rustc_attr_parsing/src/context.rs | 2 +- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 0ff97b47fc696..713d8db1524e0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -18,7 +18,7 @@ use crate::session_diagnostics::{ ExportSymbolsNeedsStatic, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, - RawDylibOnlyWindows, UnusedMultiple, WholeArchiveNeedsStatic, + RawDylibOnlyWindows, WholeArchiveNeedsStatic, }; pub(crate) struct LinkNameParser; @@ -563,36 +563,22 @@ impl NoArgsAttributeParser for FfiConstParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::FfiConst; } -#[derive(Default)] -pub(crate) struct FfiPureParser { - first_span: Option, -} - -impl AttributeParser for FfiPureParser { - const ATTRIBUTES: AcceptMapping = - &[(&[sym::ffi_pure], template!(Word), unstable!(ffi_pure), |this, cx, args| { - let _ = cx.expect_no_args(args); - let span = cx.attr_span; - // `#[ffi_pure]` may only appear once; mirror the previous `OnDuplicate::Error`. - if let Some(first) = this.first_span { - cx.emit_err(UnusedMultiple { this: span, other: first, name: sym::ffi_pure }); - } else { - this.first_span = Some(span); - } - })]; +pub(crate) struct FfiPureParser; +impl NoArgsAttributeParser for FfiPureParser { + const PATH: &[Symbol] = &[sym::ffi_pure]; const SAFETY: AttributeSafety = AttributeSafety::Unsafe { note: "`#[ffi_pure]` functions shall have no effects except for its return value, which shall not change across two consecutive function calls with the same parameters.", unsafe_since: None, }; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); + const STABILITY: AttributeStability = unstable!(ffi_pure); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure; - fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { - let span = self.first_span?; + fn finalize_check(attr_span: Span, cx: &FinalizeContext<'_, '_>) { // `#[ffi_const]` functions cannot be `#[ffi_pure]`. if cx.all_attrs.iter().any(|a| a.word_is(sym::ffi_const)) { - cx.emit_err(BothFfiConstAndPure { attr_span: span }); + cx.emit_err(BothFfiConstAndPure { attr_span }); } - Some(AttributeKind::FfiPure(span)) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 5c64e9f2eaed6..e04341508bb7e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -147,6 +147,14 @@ pub(crate) trait SingleAttributeParser: 'static { /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option; + + /// Optional cross-attribute validation, run once during finalization after all + /// attributes on the item have been parsed. Unlike [`convert`](Self::convert), this + /// has access to the sibling attributes via [`FinalizeContext::all_attrs`], so it can + /// reject incompatible combinations. `attr_span` is the span of this attribute. + /// + /// Defaults to a no-op. + fn finalize_check(_attr_span: Span, _cx: &FinalizeContext<'_, '_>) {} } /// Use in combination with [`SingleAttributeParser`]. @@ -177,8 +185,10 @@ impl AttributeParser for Single { const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; const SAFETY: AttributeSafety = T::SAFETY; - fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { - Some(self.1?.0) + fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { + let (kind, span) = self.1?; + T::finalize_check(span, cx); + Some(kind) } } @@ -258,6 +268,14 @@ pub(crate) trait NoArgsAttributeParser: 'static { /// Create the [`AttributeKind`] given attribute's [`Span`]. const CREATE: fn(Span) -> AttributeKind; + + /// Optional cross-attribute validation, run once during finalization after all + /// attributes on the item have been parsed. Has access to the sibling attributes via + /// [`FinalizeContext::all_attrs`], so it can reject incompatible combinations. + /// `attr_span` is the span of this attribute. + /// + /// Defaults to a no-op. + fn finalize_check(_attr_span: Span, _cx: &FinalizeContext<'_, '_>) {} } pub(crate) struct WithoutArgs(PhantomData); @@ -280,6 +298,10 @@ impl SingleAttributeParser for WithoutArgs { let _ = cx.expect_no_args(args); Some(T::CREATE(cx.attr_span)) } + + fn finalize_check(attr_span: Span, cx: &FinalizeContext<'_, '_>) { + T::finalize_check(attr_span, cx) + } } type ConvertFn = fn(ThinVec, Span) -> AttributeKind; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 16458f65b1116..14e443c922711 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -143,7 +143,6 @@ attribute_parsers!( ConfusablesParser, ConstStabilityParser, DocParser, - FfiPureParser, MacroUseParser, NakedParser, OnConstParser, @@ -247,6 +246,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, From efce3046cddcfa14178c3fcc3b87775f5efe8c2f Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 10 May 2026 22:18:50 +0100 Subject: [PATCH 146/278] Update itertools to 0.15 --- Cargo.lock | 53 +++++++++++++---------- compiler/rustc_ast_passes/Cargo.toml | 2 +- compiler/rustc_ast_pretty/Cargo.toml | 2 +- compiler/rustc_borrowck/Cargo.toml | 2 +- compiler/rustc_codegen_llvm/Cargo.toml | 2 +- compiler/rustc_codegen_ssa/Cargo.toml | 2 +- compiler/rustc_hir_analysis/Cargo.toml | 2 +- compiler/rustc_hir_typeck/Cargo.toml | 2 +- compiler/rustc_mir_build/Cargo.toml | 2 +- compiler/rustc_mir_transform/Cargo.toml | 2 +- compiler/rustc_resolve/Cargo.toml | 2 +- compiler/rustc_trait_selection/Cargo.toml | 2 +- compiler/rustc_transmute/Cargo.toml | 2 +- compiler/rustc_ty_utils/Cargo.toml | 2 +- src/librustdoc/Cargo.toml | 2 +- src/tools/coverage-dump/Cargo.toml | 2 +- 16 files changed, 46 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f05b186b3fc9..1e20167b7aacf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,7 +687,7 @@ dependencies = [ "color-print", "declare_clippy_lint", "filetime", - "itertools", + "itertools 0.12.1", "pulldown-cmark", "regex", "rustc_tools_util 0.4.2", @@ -705,7 +705,7 @@ name = "clippy_config" version = "0.1.98" dependencies = [ "clippy_utils", - "itertools", + "itertools 0.12.1", "serde", "toml 0.7.8", "walkdir", @@ -718,7 +718,7 @@ dependencies = [ "chrono", "clap", "indoc", - "itertools", + "itertools 0.12.1", "opener", "rustc-literal-escaper", "walkdir", @@ -733,7 +733,7 @@ dependencies = [ "clippy_config", "clippy_utils", "declare_clippy_lint", - "itertools", + "itertools 0.12.1", "quine-mc_cluskey", "regex-syntax", "semver", @@ -751,7 +751,7 @@ version = "0.0.1" dependencies = [ "clippy_config", "clippy_utils", - "itertools", + "itertools 0.12.1", "regex", "rustc-semver", ] @@ -761,7 +761,7 @@ name = "clippy_utils" version = "0.1.98" dependencies = [ "arrayvec", - "itertools", + "itertools 0.12.1", "rustc_apfloat", "serde", ] @@ -933,7 +933,7 @@ name = "coverage-dump" version = "0.1.0" dependencies = [ "anyhow", - "itertools", + "itertools 0.15.0", "leb128", "md-5", "miniz_oxide", @@ -2129,6 +2129,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -3686,7 +3695,7 @@ dependencies = [ name = "rustc_ast_passes" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_ast", "rustc_ast_pretty", @@ -3706,7 +3715,7 @@ dependencies = [ name = "rustc_ast_pretty" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_ast", "rustc_lexer", "rustc_span", @@ -3750,7 +3759,7 @@ name = "rustc_borrowck" version = "0.0.0" dependencies = [ "either", - "itertools", + "itertools 0.15.0", "polonius-engine", "rustc_abi", "rustc_data_structures", @@ -3804,7 +3813,7 @@ version = "0.0.0" dependencies = [ "bitflags", "gimli 0.31.1", - "itertools", + "itertools 0.15.0", "libc", "libloading 0.9.0", "measureme", @@ -3840,7 +3849,7 @@ dependencies = [ "bitflags", "bstr", "find-msvc-tools", - "itertools", + "itertools 0.15.0", "libc", "object 0.37.3", "pathdiff", @@ -4110,7 +4119,7 @@ dependencies = [ name = "rustc_hir_analysis" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_arena", "rustc_ast", @@ -4157,7 +4166,7 @@ dependencies = [ name = "rustc_hir_typeck" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_ast", "rustc_data_structures", @@ -4435,7 +4444,7 @@ dependencies = [ name = "rustc_mir_build" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_apfloat", "rustc_arena", @@ -4479,7 +4488,7 @@ name = "rustc_mir_transform" version = "0.0.0" dependencies = [ "either", - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_arena", "rustc_ast", @@ -4684,7 +4693,7 @@ name = "rustc_resolve" version = "0.0.0" dependencies = [ "indexmap", - "itertools", + "itertools 0.15.0", "pulldown-cmark", "rustc_arena", "rustc_ast", @@ -4846,7 +4855,7 @@ checksum = "a3b75158011a63889ba12084cf1224baad7bcad50f6ee7c842f772b74aa148ed" name = "rustc_trait_selection" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_ast", "rustc_data_structures", @@ -4880,7 +4889,7 @@ dependencies = [ name = "rustc_transmute" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_data_structures", "rustc_hir", @@ -4894,7 +4903,7 @@ dependencies = [ name = "rustc_ty_utils" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_data_structures", "rustc_errors", @@ -4971,7 +4980,7 @@ dependencies = [ "base64", "expect-test", "indexmap", - "itertools", + "itertools 0.15.0", "minifier", "proc-macro2", "pulldown-cmark-escape", @@ -5059,7 +5068,7 @@ dependencies = [ "dirs", "getopts", "ignore", - "itertools", + "itertools 0.12.1", "regex", "rustfmt-config_proc_macro", "semver", diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index da9b4633fdae9..4fa0e2b1fc7fa 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml index 70da6ba0591ce..b33f41eb302c0 100644 --- a/compiler/rustc_ast_pretty/Cargo.toml +++ b/compiler/rustc_ast_pretty/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_ast = { path = "../rustc_ast" } rustc_lexer = { path = "../rustc_lexer" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml index 55a09dbf1d724..8c9c8349c3149 100644 --- a/compiler/rustc_borrowck/Cargo.toml +++ b/compiler/rustc_borrowck/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1.5.0" -itertools = "0.12" +itertools = "0.15" polonius-engine = "0.13.0" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 0ffff2d331b1d..ec850cf7ab3d2 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -12,7 +12,7 @@ bitflags = "2.4.1" # To avoid duplicate dependencies, this should match the version of gimli used # by `rustc_codegen_ssa` via its `thorin-dwp` dependency. gimli = "0.31" -itertools = "0.12" +itertools = "0.15" libc = "0.2" libloading = { version = "0.9.0" } measureme = "12.0.1" diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index c7d7b7429f34a..7996e808b2779 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -9,7 +9,7 @@ ar_archive_writer = "0.5" bitflags = "2.4.1" bstr = "1.11.3" find-msvc-tools = "0.1.2" -itertools = "0.12" +itertools = "0.15" pathdiff = "0.2.0" regex = "1.4" rustc_abi = { path = "../rustc_abi" } diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 0dbd4333ed3ca..955629d40cee2 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index 43db09b0eab8f..9d185a565fe00 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index fa22e07612cfe..d90c2a4e759af 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 395127edd1e0a..f94b3f49ff6d1 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1" -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index cb52c21c87ae9..a1d61d0d9f2f0 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start indexmap = "2.4.0" -itertools = "0.12" +itertools = "0.15" pulldown-cmark = { version = "0.11", features = [ "html", ], default-features = false } diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 3ce76d78e6c8a..0cd9128762cbc 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml index e61717e5e9ce2..5da9435e77e63 100644 --- a/compiler/rustc_transmute/Cargo.toml +++ b/compiler/rustc_transmute/Cargo.toml @@ -16,7 +16,7 @@ tracing = "0.1" [dev-dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index 682cf941561fc..45e71d8a38860 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index c06b4857e4903..45d084b2f9bf7 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -13,7 +13,7 @@ arrayvec = { version = "0.7", default-features = false } askama = { version = "0.16.0", default-features = false, features = ["alloc", "config", "derive"] } base64 = "0.21.7" indexmap = { version = "2", features = ["serde"] } -itertools = "0.12" +itertools = "0.15" minifier = { version = "0.3.5", default-features = false } proc-macro2 = "1.0.103" pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] } diff --git a/src/tools/coverage-dump/Cargo.toml b/src/tools/coverage-dump/Cargo.toml index 36a66f16030ec..cb5755a22968a 100644 --- a/src/tools/coverage-dump/Cargo.toml +++ b/src/tools/coverage-dump/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] anyhow = "1.0.71" -itertools = "0.12" +itertools = "0.15" leb128 = "0.2.5" md5 = { package = "md-5" , version = "0.10.5" } miniz_oxide = "0.8.8" From 3dc3d573dee89790fa1478c2a8bd9f85a84049e0 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 10 May 2026 22:40:28 +0100 Subject: [PATCH 147/278] Convert pattern matches of `itertools::Position` into the new appropriate method calls --- compiler/rustc_ast_pretty/src/pprust/state/expr.rs | 6 +++--- compiler/rustc_ast_pretty/src/pprust/state/item.rs | 4 ++-- compiler/rustc_mir_build/src/builder/matches/mod.rs | 6 +++--- src/librustdoc/html/render/sorted_template.rs | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 78aedbd4f7f4b..176e91e544ec5 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -1,7 +1,7 @@ use std::fmt::Write; use ast::{ForLoopKind, MatchKind}; -use itertools::{Itertools, Position}; +use itertools::Itertools; use rustc_ast::util::classify; use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; @@ -170,8 +170,8 @@ impl<'a> State<'a> { } let cb = self.cbox(0); for (pos, field) in fields.iter().with_position() { - let is_first = matches!(pos, Position::First | Position::Only); - let is_last = matches!(pos, Position::Last | Position::Only); + let is_first = pos.is_first(); + let is_last = pos.is_last(); self.maybe_print_comment(field.span.hi()); self.print_outer_attributes(&field.attrs); if is_first { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 86fb0e62bdaa4..1dbd5719ae066 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -1,5 +1,5 @@ use ast::StaticItem; -use itertools::{Itertools, Position}; +use itertools::Itertools; use rustc_ast::{self as ast, EiiImpl, ModKind, Safety, TraitAlias}; use rustc_span::Ident; @@ -923,7 +923,7 @@ impl<'a> State<'a> { self.zerobreak(); let ib = self.ibox(0); for (pos, use_tree) in items.iter().with_position() { - let is_last = matches!(pos, Position::Last | Position::Only); + let is_last = pos.is_last(); self.print_use_tree(&use_tree.0); if !is_last { self.word(","); diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 8352ec92e88b0..f70416b2cf867 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -9,7 +9,7 @@ use std::borrow::Borrow; use std::sync::Arc; use std::{debug_assert_matches, mem}; -use itertools::{Itertools, Position}; +use itertools::Itertools; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -551,9 +551,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // the drop order for the first sub-branch, we lower sub-branches in reverse (#142163). let target_block = self.cfg.start_new_block(); for (pos, sub_branch) in branch.sub_branches.into_iter().rev().with_position() { - debug_assert!(pos != Position::Only); + debug_assert!(!pos.is_exactly_one()); let schedule_drops = - if pos == Position::Last { ScheduleDrops::Yes } else { ScheduleDrops::No }; + if pos.is_last() { ScheduleDrops::Yes } else { ScheduleDrops::No }; let binding_end = self.bind_and_guard_matched_candidate( sub_branch, fake_borrow_temps, diff --git a/src/librustdoc/html/render/sorted_template.rs b/src/librustdoc/html/render/sorted_template.rs index 659c5e6093bdd..8501421651ab6 100644 --- a/src/librustdoc/html/render/sorted_template.rs +++ b/src/librustdoc/html/render/sorted_template.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Write as _}; use std::marker::PhantomData; use std::str::FromStr; -use itertools::{Itertools as _, Position}; +use itertools::Itertools as _; use serde::{Deserialize, Serialize}; /// Append-only templates for sorted, deduplicated lists of items. @@ -62,7 +62,7 @@ impl fmt::Display for SortedTemplate { write!(f, "{}", self.before)?; for (p, fragment) in self.fragments.iter().with_position() { let mut f = DeltaWriter { inner: &mut f, delta: 0 }; - let sep = if matches!(p, Position::First | Position::Only) { "" } else { F::SEPARATOR }; + let sep = if p.is_first() { "" } else { F::SEPARATOR }; f.write_str(sep)?; f.write_str(fragment)?; fragment_lengths.push(f.delta); @@ -95,7 +95,7 @@ impl FromStr for SortedTemplate { let (fragment, rest) = s.split_at_checked(index).ok_or(Error("invalid fragment length: out of bounds"))?; s = rest; - let sep = if matches!(p, Position::First | Position::Only) { "" } else { F::SEPARATOR }; + let sep = if p.is_first() { "" } else { F::SEPARATOR }; let fragment = fragment .strip_prefix(sep) .ok_or(Error("invalid fragment length: expected to find separator here"))?; From 0d2070c00920a66be724f0492379ddbcb17de880 Mon Sep 17 00:00:00 2001 From: Yilin Chen <1479826151@qq.com> Date: Thu, 25 Jun 2026 10:57:28 +0800 Subject: [PATCH 148/278] Add safety section for SliceIndex::get_unchecked(mut) --- library/core/src/slice/index.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 51ab9b5fd1eac..b82d79232becc 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -139,6 +139,8 @@ pub impl(crate) const unsafe trait SliceIndex { /// Returns a pointer to the output at this location, without /// performing any bounds checking. /// + /// # Safety + /// /// Calling this method with an out-of-bounds index or a dangling `slice` pointer /// is *[undefined behavior]* even if the resulting pointer is not used. /// @@ -149,6 +151,8 @@ pub impl(crate) const unsafe trait SliceIndex { /// Returns a mutable pointer to the output at this location, without /// performing any bounds checking. /// + /// # Safety + /// /// Calling this method with an out-of-bounds index or a dangling `slice` pointer /// is *[undefined behavior]* even if the resulting pointer is not used. /// From e0588c2ecf346aaa867d7113b01a10e0d0625bf0 Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Thu, 12 Mar 2026 21:37:19 +0000 Subject: [PATCH 149/278] fix: `needless_late_init` FN for if/match in block expr --- clippy_lints/src/needless_late_init.rs | 24 +++++++++++++++++------- tests/ui/manual_abs_diff.fixed | 1 + tests/ui/manual_abs_diff.rs | 1 + tests/ui/manual_abs_diff.stderr | 2 +- tests/ui/needless_late_init.fixed | 10 ++++++++++ tests/ui/needless_late_init.rs | 10 ++++++++++ tests/ui/needless_late_init.stderr | 19 ++++++++++++++++++- 7 files changed, 58 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 82ab15578e064..42a59113c7296 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -204,7 +204,7 @@ fn assignment_suggestions<'tcx>( } struct Usage<'tcx> { - stmt: &'tcx Stmt<'tcx>, + span: Span, expr: &'tcx Expr<'tcx>, needs_semi: bool, } @@ -226,17 +226,27 @@ fn first_usage<'tcx>( .find(|&stmt| is_local_used(cx, stmt, binding_id)) .and_then(|stmt| match stmt.kind { StmtKind::Expr(expr) => Some(Usage { - stmt, + span: stmt.span, expr, needs_semi: true, }), StmtKind::Semi(expr) => Some(Usage { - stmt, + span: stmt.span, expr, needs_semi: false, }), _ => None, }) + .or_else(|| { + block + .expr + .filter(|expr| is_local_used(cx, *expr, binding_id)) + .map(|expr| Usage { + span: expr.span, + expr, + needs_semi: true, + }) + }) } fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option { @@ -300,10 +310,10 @@ fn check<'tcx>( "unneeded late initialization", |diag| { suggestions.push((local_stmt.span, String::new())); - suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); + suggestions.push((usage.span.shrink_to_lo(), format!("{let_snippet} = "))); if usage.needs_semi { - suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); + suggestions.push((usage.span.shrink_to_hi(), ";".to_owned())); } diag.multipart_suggestion( @@ -327,10 +337,10 @@ fn check<'tcx>( "unneeded late initialization", |diag| { suggestions.push((local_stmt.span, String::new())); - suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); + suggestions.push((usage.span.shrink_to_lo(), format!("{let_snippet} = "))); if usage.needs_semi { - suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); + suggestions.push((usage.span.shrink_to_hi(), ";".to_owned())); } diag.multipart_suggestion( diff --git a/tests/ui/manual_abs_diff.fixed b/tests/ui/manual_abs_diff.fixed index 2766942140ce4..3b5cac5dfc986 100644 --- a/tests/ui/manual_abs_diff.fixed +++ b/tests/ui/manual_abs_diff.fixed @@ -51,6 +51,7 @@ fn main() { } // FIXME: bunch of patterns that should be linted +#[expect(clippy::needless_late_init)] fn fixme() { let a: usize = 5; let b: usize = 3; diff --git a/tests/ui/manual_abs_diff.rs b/tests/ui/manual_abs_diff.rs index 2c408f2be3754..f845dd9b149a9 100644 --- a/tests/ui/manual_abs_diff.rs +++ b/tests/ui/manual_abs_diff.rs @@ -61,6 +61,7 @@ fn main() { } // FIXME: bunch of patterns that should be linted +#[expect(clippy::needless_late_init)] fn fixme() { let a: usize = 5; let b: usize = 3; diff --git a/tests/ui/manual_abs_diff.stderr b/tests/ui/manual_abs_diff.stderr index bb6d312b435f8..b441ac9751f05 100644 --- a/tests/ui/manual_abs_diff.stderr +++ b/tests/ui/manual_abs_diff.stderr @@ -80,7 +80,7 @@ LL | let _ = if a > b { (a - b) as u32 } else { (b - a) as u32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` error: manual absolute difference pattern without using `abs_diff` - --> tests/ui/manual_abs_diff.rs:119:5 + --> tests/ui/manual_abs_diff.rs:120:5 | LL | / if a < b { LL | | diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index b686a8e9f1a06..fff5bc00d4278 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -299,3 +299,13 @@ fn issue9895() { //~^ needless_late_init let r = 5; } + +fn if_or_match_in_block_expr() { + + //~^ needless_late_init + let z = if true { + 1 + } else { + 2 + }; +} diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 23772ff702931..327de94570ca2 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -299,3 +299,13 @@ fn issue9895() { //~^ needless_late_init (r = 5); } + +fn if_or_match_in_block_expr() { + let z; + //~^ needless_late_init + if true { + z = 1; + } else { + z = 2; + } +} diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index e3e25cdc8d769..1623bc032a255 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -291,5 +291,22 @@ LL | LL ~ let r = 5; | -error: aborting due to 17 previous errors +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:304:5 + | +LL | let z; + | ^^^^^^ + | +help: move the declaration `z` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let z = if true { +LL ~ 1 +LL | } else { +LL ~ 2 +LL ~ }; + | + +error: aborting due to 18 previous errors From 6d4ad6330073eebdd137aac55f2b0b2f1ad8907e Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Sun, 22 Mar 2026 23:24:06 +0000 Subject: [PATCH 150/278] Extend `needless_late_init` to cover grouped assignments --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 31 + clippy_config/src/conf.rs | 24 + clippy_lints/src/lib.rs | 2 +- clippy_lints/src/needless_late_init.rs | 529 +++++++++++------- tests/ui-toml/needless_late_init/clippy.toml | 1 + .../needless_late_init/needless_late_init.rs | 64 +++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui/needless_late_init.fixed | 181 +++++- tests/ui/needless_late_init.rs | 173 +++++- tests/ui/needless_late_init.stderr | 180 +++++- 11 files changed, 977 insertions(+), 212 deletions(-) create mode 100644 tests/ui-toml/needless_late_init/clippy.toml create mode 100644 tests/ui-toml/needless_late_init/needless_late_init.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 91c5cbcd7bbf4..689d56d0fb7c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7574,6 +7574,7 @@ Released 2018-09-13 [`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api [`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types [`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish +[`check-grouped-late-init`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-grouped-late-init [`check-incompatible-msrv-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-incompatible-msrv-in-tests [`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index dd10ef0538c25..6c6ccd4747c1f 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -452,6 +452,37 @@ For internal testing only, ignores the current `publish` settings in the Cargo m * [`cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata) +## `check-grouped-late-init` +Whether to check for grouped late initializations from multiple `let` statements. + +#### Example +```rust +let a; +let b; +if true { + a = 1; + b = 2; +} else { + a = 3; + b = 4; +} +``` +Use instead: +```rust +let (a, b) = if true { + (1, 2) +} else { + (3, 4) +}; +``` + +**Default Value:** `true` + +--- +**Affected lints:** +* [`needless_late_init`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init) + + ## `check-incompatible-msrv-in-tests` Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 5a06cb477c304..d910eb47752b5 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -554,6 +554,30 @@ define_Conf! { /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. #[lints(cargo_common_metadata)] cargo_ignore_publish: bool = false, + /// Whether to check for grouped late initializations from multiple `let` statements. + /// + /// #### Example + /// ```rust + /// let a; + /// let b; + /// if true { + /// a = 1; + /// b = 2; + /// } else { + /// a = 3; + /// b = 4; + /// } + /// ``` + /// Use instead: + /// ```rust + /// let (a, b) = if true { + /// (1, 2) + /// } else { + /// (3, 4) + /// }; + /// ``` + #[lints(needless_late_init)] + check_grouped_late_init: bool = true, /// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. #[lints(incompatible_msrv)] check_incompatible_msrv_in_tests: bool = false, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 600b37c4204c0..175f01e8f81fa 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -731,7 +731,7 @@ rustc_lint::late_lint_methods!( UndocumentedUnsafeBlocks: undocumented_unsafe_blocks::UndocumentedUnsafeBlocks = undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf), FormatArgs: format_args::FormatArgs<'tcx> = format_args::FormatArgs::new(tcx, conf, format_args.clone()), TrailingEmptyArray: trailing_empty_array::TrailingEmptyArray = trailing_empty_array::TrailingEmptyArray, - NeedlessLateInit: needless_late_init::NeedlessLateInit = needless_late_init::NeedlessLateInit, + NeedlessLateInit: needless_late_init::NeedlessLateInit<'tcx> = needless_late_init::NeedlessLateInit::new(conf), ReturnSelfNotMustUse: return_self_not_must_use::ReturnSelfNotMustUse = return_self_not_must_use::ReturnSelfNotMustUse, NumberedFields: init_numbered_fields::NumberedFields = init_numbered_fields::NumberedFields, ManualBits: manual_bits::ManualBits = manual_bits::ManualBits::new(conf), diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 42a59113c7296..69184d1894025 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -1,16 +1,19 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{SourceText, SpanExt, snippet}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{ - BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind, + BindingMode, Block, Expr, ExprKind, HirId, HirIdMap, HirIdSet, LetStmt, LocalSource, MatchSource, Node, Pat, + PatKind, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::Span; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -61,17 +64,126 @@ declare_clippy_lint! { "late initializations that can be replaced by a `let` statement with an initializer" } -declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]); +impl_lint_pass!(NeedlessLateInit<'_> => [NEEDLESS_LATE_INIT]); -fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool { - for_each_expr(cx.tcx, stmt, |e| { - if matches!(e.kind, ExprKind::Assign(..)) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) +pub struct NeedlessLateInit<'tcx> { + check_grouped_late_init: bool, + grouped_late_inits: Vec<(HirId, HirIdMap>)>, +} + +impl<'tcx> NeedlessLateInit<'tcx> { + pub fn new(conf: &'static Conf) -> Self { + Self { + check_grouped_late_init: conf.check_grouped_late_init, + grouped_late_inits: Vec::default(), } - }) - .is_some() + } + + fn check_if_or_match( + &mut self, + cx: &LateContext<'tcx>, + local_stmt: &'tcx LetStmt<'tcx>, + block: &'tcx Block<'tcx>, + binding_id: HirId, + usage: Usage<'tcx>, + exprs: impl IntoIterator>, + ) { + let mut assigns: Vec> = Vec::new(); + for expr in exprs { + let ty = cx.typeck_results().expr_ty(expr); + if ty.is_never() { + continue; + } + if !ty.is_unit() { + return; + } + + let Some(assign_group) = LocalAssignGroup::new(cx, expr) else { + return; + }; + + if let Some(last_group) = assigns.last() + && !assign_group.is_parallel(last_group) + { + return; + } + + assigns.push(assign_group); + } + + let Some(first_group) = assigns.first() else { + return; + }; + // If there are multiple assignments grouped together, lazyly check them after processing the block. + if first_group.0.len() > 1 { + if self.check_grouped_late_init { + let late_inits = if let Some((hir_id, late_inits)) = self.grouped_late_inits.last_mut() + && *hir_id == block.hir_id + { + late_inits + } else { + &mut self.grouped_late_inits.push_mut((block.hir_id, HirIdMap::default())).1 + }; + + let mut decls = HirIdMap::default(); + decls.insert(binding_id, local_stmt); + late_inits.insert(usage.expr.hir_id, GroupedLateInit { usage, assigns, decls }); + } + + return; + } + + if first_group.0[0].lhs_id == binding_id { + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + local_stmt.span, + "unneeded late initialization", + |diag| { + let mut suggestions = vec![]; + for group in assigns { + suggestions.extend( + group + .0 + .iter() + .flat_map(|assign| { + let rhs_span = assign.rhs.span.source_callsite(); + let mut spans = vec![assign.span.until(rhs_span)]; + + if rhs_span.hi() != assign.span.hi() { + spans.push(rhs_span.shrink_to_hi().with_hi(assign.span.hi())); + } + + spans + }) + .map(|span| (span, String::new())), + ); + } + + suggestions.push((local_stmt.span, String::new())); + let mut applicability = Applicability::MachineApplicable; + let let_snippet = local_snippet_without_semicolon(cx, local_stmt, &mut applicability); + suggestions.push((usage.span.shrink_to_lo(), format!("{let_snippet} = "))); + if usage.needs_semi { + suggestions.push((usage.span.shrink_to_hi(), ";".to_owned())); + } + let binding_name = cx.tcx.hir_name(binding_id); + let descriptor = if matches!(usage.expr.kind, ExprKind::If(..)) { + "branches" + } else { + "`match` arms" + }; + diag.multipart_suggestion( + format!( + "move the declaration `{binding_name}` here and remove the assignments from the {descriptor}", + ), + suggestions, + applicability, + ); + }, + ); + } + } } fn contains_let(cond: &Expr<'_>) -> bool { @@ -99,110 +211,33 @@ fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { } #[derive(Debug)] -struct LocalAssign { +struct LocalAssign<'tcx> { lhs_id: HirId, - rhs_span: Span, + rhs: &'tcx Expr<'tcx>, span: Span, } -impl LocalAssign { - fn from_expr(expr: &Expr<'_>, span: Span) -> Option { +impl<'tcx> LocalAssign<'tcx> { + fn new(expr: &'tcx Expr<'tcx>, span: Span) -> Option { if expr.span.from_expansion() { return None; } - if let ExprKind::Assign(lhs, rhs, _) = expr.kind { - if lhs.span.from_expansion() { - return None; - } - - Some(Self { + if let ExprKind::Assign(lhs, rhs, _) = expr.kind + && !lhs.span.from_expansion() + { + return Some(Self { lhs_id: lhs.res_local_id()?, - rhs_span: rhs.span.source_callsite(), + rhs, span, - }) - } else { - None + }); } - } - - fn new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, binding_id: HirId) -> Option { - let assign = match expr.kind { - ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span), - ExprKind::Block(block, _) => { - if let Some((last, other_stmts)) = block.stmts.split_last() - && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind - - && let assign = Self::from_expr(expr, last.span)? - // avoid visiting if not needed - && assign.lhs_id == binding_id - && other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt)) - { - Some(assign) - } else { - None - } - }, - ExprKind::Assign(..) => Self::from_expr(expr, expr.span), - _ => None, - }?; - - if assign.lhs_id == binding_id { - Some(assign) - } else { - None - } - } -} - -fn assignment_suggestions<'tcx>( - cx: &LateContext<'tcx>, - binding_id: HirId, - exprs: impl IntoIterator>, -) -> Option<(Applicability, Vec<(Span, String)>)> { - let mut assignments = Vec::new(); - - for expr in exprs { - let ty = cx.typeck_results().expr_ty(expr); - - if ty.is_never() { - continue; - } - if !ty.is_unit() { - return None; - } - - let assign = LocalAssign::new(cx, expr, binding_id)?; - - assignments.push(assign); - } - - let suggestions = assignments - .iter() - .flat_map(|assignment| { - let mut spans = vec![assignment.span.until(assignment.rhs_span)]; - - if assignment.rhs_span.hi() != assignment.span.hi() { - spans.push(assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi())); - } - - spans - }) - .map(|span| (span, String::new())) - .collect::>(); - - match suggestions.len() { - // All of `exprs` are never types - // https://github.com/rust-lang/rust-clippy/issues/8911 - 0 => None, - 1 => Some((Applicability::MachineApplicable, suggestions)), - // multiple suggestions don't work with rustfix in multipart_suggest - // https://github.com/rust-lang/rustfix/issues/141 - _ => Some((Applicability::Unspecified, suggestions)), + None } } +#[derive(Debug)] struct Usage<'tcx> { span: Span, expr: &'tcx Expr<'tcx>, @@ -249,7 +284,11 @@ fn first_usage<'tcx>( }) } -fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option { +fn local_snippet_without_semicolon<'a>( + cx: &LateContext<'_>, + local: &LetStmt<'_>, + applicability: &mut Applicability, +) -> Cow<'a, str> { let span = local.span.with_hi(match local.ty { // let : ; // ~~~~~~~~~~~~~~~ @@ -259,105 +298,70 @@ fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> None => local.pat.span.hi(), }); - span.get_text(cx) + snippet_with_applicability(cx, span, "..", applicability) } -fn check<'tcx>( - cx: &LateContext<'tcx>, - local: &'tcx LetStmt<'tcx>, - local_stmt: &'tcx Stmt<'tcx>, - block: &'tcx Block<'tcx>, - binding_id: HirId, -) -> Option<()> { - let usage = first_usage(cx, binding_id, local_stmt.hir_id, block)?; - let binding_name = cx.tcx.hir_opt_name(binding_id)?; - let let_snippet = local_snippet_without_semicolon(cx, local)?; - - match usage.expr.kind { - ExprKind::Assign(..) => { - let assign = LocalAssign::new(cx, usage.expr, binding_id)?; - let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); - msg_span.push_span_label(local_stmt.span, "created here"); - msg_span.push_span_label(assign.span, "initialised here"); - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - msg_span, - "unneeded late initialization", - |diag| { - diag.multipart_suggestion( - format!("move the declaration `{binding_name}` here"), - vec![ - (local_stmt.span, String::new()), - ( - assign.span, - let_snippet.to_owned() + " = " + &snippet(cx, assign.rhs_span, ".."), - ), - ], - Applicability::MachineApplicable, - ); - }, - ); - }, - ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => { - let (applicability, mut suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - local_stmt.span, - "unneeded late initialization", - |diag| { - suggestions.push((local_stmt.span, String::new())); - suggestions.push((usage.span.shrink_to_lo(), format!("{let_snippet} = "))); - - if usage.needs_semi { - suggestions.push((usage.span.shrink_to_hi(), ";".to_owned())); +#[derive(Debug)] +struct LocalAssignGroup<'tcx>(Vec>); + +impl<'tcx> LocalAssignGroup<'tcx> { + fn new(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { + match expr.kind { + ExprKind::Block(Block { expr: Some(expr), .. }, _) + if let Some(assign) = LocalAssign::new(expr, expr.span) => + { + Some(LocalAssignGroup(vec![assign])) + }, + ExprKind::Block(Block { expr: None, stmts, .. }, _) => { + let mut assign_group = Vec::new(); + // Avoid cases when the assignee is used or reassigned in the subsequent assignments + let mut used_locals = HirIdSet::default(); + for stmt in stmts.iter().rev() { + if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind + && let Some(assign) = LocalAssign::new(expr, stmt.span) + && !used_locals.contains(&assign.lhs_id) + { + used_locals.insert(assign.lhs_id); + for_each_expr(cx.tcx, assign.rhs, |e| { + if let Some(id) = e.res_local_id() { + used_locals.insert(id); + } + ControlFlow::<()>::Continue(()) + }); + assign_group.push(assign); + continue; } - diag.multipart_suggestion( - format!( - "move the declaration `{binding_name}` here and remove the assignments from the branches" - ), - suggestions, - applicability, - ); - }, - ); - }, - ExprKind::Match(_, arms, MatchSource::Normal) => { - let (applicability, mut suggestions) = - assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?; - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - local_stmt.span, - "unneeded late initialization", - |diag| { - suggestions.push((local_stmt.span, String::new())); - suggestions.push((usage.span.shrink_to_lo(), format!("{let_snippet} = "))); - - if usage.needs_semi { - suggestions.push((usage.span.shrink_to_hi(), ";".to_owned())); - } + break; + } + if assign_group.is_empty() { + None + } else { + Some(LocalAssignGroup(assign_group)) + } + }, + ExprKind::Assign(..) if let Some(assign) = LocalAssign::new(expr, expr.span) => { + Some(LocalAssignGroup(vec![assign])) + }, + _ => None, + } + } - diag.multipart_suggestion( - format!("move the declaration `{binding_name}` here and remove the assignments from the `match` arms"), - suggestions, - applicability, - ); - }, - ); - }, - _ => {}, + /// Checks if the assignments in `self` and `other` are parallel, i.e. they have the same number + /// of assignments and the same assignees in the same order. + fn is_parallel(&self, other: &Self) -> bool { + self.0.len() == other.0.len() && self.0.iter().zip(other.0.iter()).all(|(a, b)| a.lhs_id == b.lhs_id) } +} - Some(()) +#[derive(Debug)] +struct GroupedLateInit<'tcx> { + usage: Usage<'tcx>, + assigns: Vec>, + decls: HirIdMap<&'tcx LetStmt<'tcx>>, } -impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { +impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit<'tcx> { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { let mut parents = cx.tcx.hir_parent_iter(local.hir_id); if let LetStmt { @@ -372,8 +376,147 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { } = local && let Some((_, Node::Stmt(local_stmt))) = parents.next() && let Some((_, Node::Block(block))) = parents.next() + && let Some(usage) = first_usage(cx, *binding_id, local_stmt.hir_id, block) { - check(cx, local, local_stmt, block, *binding_id); + if self.check_grouped_late_init + && let Some((hir_id, late_inits)) = self.grouped_late_inits.last_mut() + && *hir_id == block.hir_id + && let Some(late_init) = late_inits.get_mut(&usage.expr.hir_id) + { + late_init.decls.insert(*binding_id, local); + return; + } + + match usage.expr.kind { + ExprKind::Assign(..) + if let Some(assign) = LocalAssign::new(usage.expr, usage.expr.span) + && assign.lhs_id == *binding_id => + { + let mut applicability = Applicability::MachineApplicable; + let let_snippet = local_snippet_without_semicolon(cx, local, &mut applicability); + let binding_name = cx.tcx.hir_name(*binding_id); + let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); + msg_span.push_span_label(local_stmt.span, "created here"); + msg_span.push_span_label(assign.span, "initialised here"); + + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + msg_span, + "unneeded late initialization", + |diag| { + let mut applicability = Applicability::MachineApplicable; + let rhs_snippet = snippet_with_applicability( + cx, + assign.rhs.span.source_callsite(), + "..", + &mut applicability, + ); + diag.multipart_suggestion( + format!("move the declaration `{binding_name}` here"), + vec![ + (local_stmt.span, String::new()), + (assign.span, format!("{let_snippet} = {rhs_snippet}")), + ], + applicability, + ); + }, + ); + }, + ExprKind::If(cond, then_expr, Some(mut else_expr)) if !contains_let(cond) => { + // Flatten multiple if branches + let mut exprs = vec![then_expr]; + while let ExprKind::If(cond, then, Some(else_)) = else_expr.kind { + if contains_let(cond) { + return; + } + exprs.push(then); + else_expr = else_; + } + exprs.push(else_expr); + self.check_if_or_match(cx, local, block, *binding_id, usage, exprs); + }, + ExprKind::Match(_, arms, MatchSource::Normal) => { + self.check_if_or_match(cx, local, block, *binding_id, usage, arms.iter().map(|arm| arm.body)); + }, + _ => {}, + } + } + } + + fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if self.check_grouped_late_init + && let Some((_, late_inits)) = self.grouped_late_inits.pop_if(|(hir_id, _)| *hir_id == block.hir_id) + { + 'outer: for (_, late_init) in late_inits { + if late_init.decls.len() < late_init.assigns[0].0.len() { + continue; + } + + let mut suggestions = vec![]; + for assign in &late_init.assigns[0].0 { + if let Some(local) = late_init.decls.get(&assign.lhs_id) + // If the local has a type annotation, skip it since removing the annotation might cause type + // inference issues while annotating the tuple makes the suggestion harder to read. + && local.ty.is_none() + { + suggestions.push((local.span, String::new())); + } else { + continue 'outer; + } + } + + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + late_init.usage.span, + "unneeded late initialization", + |diag| { + let mut applicability = Applicability::MachineApplicable; + for group in &late_init.assigns { + let rhs_snippet = group + .0 + .iter() + .rev() + .map(|assign| { + snippet_with_applicability( + cx, + assign.rhs.span.source_callsite(), + "..", + &mut applicability, + ) + }) + .intersperse(", ".into()) + .collect::(); + suggestions.push(( + group.0.last().unwrap().span.to(group.0[0].span), + format!("({rhs_snippet})"), + )); + } + let let_snippet = late_init.assigns[0] + .0 + .iter() + .rev() + .map(|assign| cx.tcx.hir_name(assign.lhs_id).to_string()) + .intersperse(", ".to_owned()) + .collect::(); + suggestions.push((late_init.usage.span.shrink_to_lo(), format!("let ({let_snippet}) = "))); + if late_init.usage.needs_semi { + suggestions.push((late_init.usage.span.shrink_to_hi(), ";".to_owned())); + } + let descriptor = if matches!(late_init.usage.expr.kind, ExprKind::If(..)) { + "branches" + } else { + "`match` arms" + }; + diag.multipart_suggestion( + format!("move the declarations here and remove the assignments from the {descriptor}"), + suggestions, + applicability, + ); + }, + ); + } } } } diff --git a/tests/ui-toml/needless_late_init/clippy.toml b/tests/ui-toml/needless_late_init/clippy.toml new file mode 100644 index 0000000000000..2ff5a13e893be --- /dev/null +++ b/tests/ui-toml/needless_late_init/clippy.toml @@ -0,0 +1 @@ +check-grouped-late-init = false diff --git a/tests/ui-toml/needless_late_init/needless_late_init.rs b/tests/ui-toml/needless_late_init/needless_late_init.rs new file mode 100644 index 0000000000000..5a5e657b295c3 --- /dev/null +++ b/tests/ui-toml/needless_late_init/needless_late_init.rs @@ -0,0 +1,64 @@ +//@check-pass + +fn main() {} + +fn issue16330() { + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 3; + b = 4; + } + + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + let a; + let b; + let c; + match 1 { + 1 => { + a = 1; + b = 2; + c = 3; + }, + _ if false => { + a = 4; + b = 5; + c = 6; + }, + _ => { + a = 7; + b = 8; + c = 9; + }, + } +} diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6bb3db8db67f0..528e2f1089c04 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -33,6 +33,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items @@ -134,6 +135,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items @@ -235,6 +237,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index fff5bc00d4278..a5c9d435b3eea 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -185,25 +185,24 @@ fn does_not_lint() { }; // using tuples would be possible, but not always preferable - let x; - let y; - if true { - x = 1; - y = 2; + + + let (x, y) = if true { + //~^ needless_late_init + (1, 2) } else { - x = 3; - y = 4; - } + (3, 4) + }; - // could match with a smarter heuristic to avoid multiple assignments - let x; - if true { + + //~^ needless_late_init + let x = if true { let mut y = 5; y = 6; - x = y; + y } else { - x = 2; - } + 2 + }; let (x, y); if true { @@ -309,3 +308,157 @@ fn if_or_match_in_block_expr() { 2 }; } + +fn issue16330() { + // Late init in both if branches, should lint + + + let (a, b) = if true { + //~^ needless_late_init + (1, 2) + } else { + (3, 4) + }; + + // One of the variables is not late init, should not lint + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + // One of the variables is defined outside the block, should not lint + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + // Late init in all match arms, should lint + + + + let (a, b, c) = match 1 { + //~^ needless_late_init + 1 => { + (1, 2, 3) + }, + _ if false => { + (4, 5, 6) + }, + _ => { + (7, 8, 9) + }, + }; + + // Late init in all if branches, should lint + + + + let (a, b, c) = if true { + //~^ needless_late_init + (1, 2, 3) + } else if false { + (4, 5, 6) + } else { + (7, 8, 9) + }; + + // One of the variables is not assigned in all branches, should not lint + let a; + let b; + let c; + if true { + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is assigned multiple times, should not lint + let mut a; + let b; + if true { + a = 1; + b = 2; + a = 3; + } else { + a = 4; + b = 5; + } + + // One of the variables is assigned in a nested block, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 4; + { + b = 5; + } + } + + // The order of the variables is different in different branches, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + b = 5; + a = 4; + } + + // Later assignments depend on the earlier ones, should only lint the last ones + let a; + let b; + + //~^ needless_late_init + let c = if true { + a = 1; + b = a + 1; + b + 1 + } else { + a = 4; + b = a + 2; + b + 2 + }; + let a; + let b; + + + let (c, d) = if true { + //~^ needless_late_init + a = 1; + b = a + 1; + (b + 1, 1) + } else { + a = 4; + b = a + 2; + (b + 2, 2) + }; +} diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 327de94570ca2..ee413814750c7 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -188,6 +188,7 @@ fn does_not_lint() { let x; let y; if true { + //~^ needless_late_init x = 1; y = 2; } else { @@ -195,8 +196,8 @@ fn does_not_lint() { y = 4; } - // could match with a smarter heuristic to avoid multiple assignments let x; + //~^ needless_late_init if true { let mut y = 5; y = 6; @@ -309,3 +310,173 @@ fn if_or_match_in_block_expr() { z = 2; } } + +fn issue16330() { + // Late init in both if branches, should lint + let a; + let b; + if true { + //~^ needless_late_init + a = 1; + b = 2; + } else { + a = 3; + b = 4; + } + + // One of the variables is not late init, should not lint + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + // One of the variables is defined outside the block, should not lint + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + // Late init in all match arms, should lint + let a; + let b; + let c; + match 1 { + //~^ needless_late_init + 1 => { + a = 1; + b = 2; + c = 3; + }, + _ if false => { + a = 4; + b = 5; + c = 6; + }, + _ => { + a = 7; + b = 8; + c = 9; + }, + } + + // Late init in all if branches, should lint + let a; + let b; + let c; + if true { + //~^ needless_late_init + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + b = 5; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is not assigned in all branches, should not lint + let a; + let b; + let c; + if true { + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is assigned multiple times, should not lint + let mut a; + let b; + if true { + a = 1; + b = 2; + a = 3; + } else { + a = 4; + b = 5; + } + + // One of the variables is assigned in a nested block, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 4; + { + b = 5; + } + } + + // The order of the variables is different in different branches, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + b = 5; + a = 4; + } + + // Later assignments depend on the earlier ones, should only lint the last ones + let a; + let b; + let c; + //~^ needless_late_init + if true { + a = 1; + b = a + 1; + c = b + 1; + } else { + a = 4; + b = a + 2; + c = b + 2; + } + let a; + let b; + let c; + let d; + if true { + //~^ needless_late_init + a = 1; + b = a + 1; + c = b + 1; + d = 1; + } else { + a = 4; + b = a + 2; + c = b + 2; + d = 2; + } +} diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 1623bc032a255..12bfc029d9488 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -276,7 +276,50 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:298:5 + --> tests/ui/needless_late_init.rs:199:5 + | +LL | let x; + | ^^^^^^ + | +help: move the declaration `x` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let x = if true { +LL | let mut y = 5; +LL | y = 6; +LL ~ y +LL | } else { +LL ~ 2 +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:190:5 + | +LL | / if true { +LL | | +LL | | x = 1; +LL | | y = 2; +... | +LL | | y = 4; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (x, y) = if true { +LL | +LL ~ (1, 2) +LL | } else { +LL ~ (3, 4) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:299:5 | LL | let r; | ^^^^^^ created here @@ -292,7 +335,7 @@ LL ~ let r = 5; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:304:5 + --> tests/ui/needless_late_init.rs:305:5 | LL | let z; | ^^^^^^ @@ -308,5 +351,136 @@ LL ~ 2 LL ~ }; | -error: aborting due to 18 previous errors +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:455:5 + | +LL | let c; + | ^^^^^^ + | +help: move the declaration `c` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let c = if true { +LL | a = 1; +LL | b = a + 1; +LL ~ b + 1 +LL | } else { +LL | a = 4; +LL | b = a + 2; +LL ~ b + 2 +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:318:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = 2; +... | +LL | | b = 4; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (a, b) = if true { +LL | +LL ~ (1, 2) +LL | } else { +LL ~ (3, 4) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:361:5 + | +LL | / match 1 { +LL | | +LL | | 1 => { +LL | | a = 1; +... | +LL | | }, +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the `match` arms + | +LL ~ +LL ~ +LL ~ +LL ~ let (a, b, c) = match 1 { +LL | +LL | 1 => { +LL ~ (1, 2, 3) +LL | }, +LL | _ if false => { +LL ~ (4, 5, 6) +LL | }, +LL | _ => { +LL ~ (7, 8, 9) +LL | }, +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:384:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = 2; +... | +LL | | c = 9; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ +LL ~ let (a, b, c) = if true { +LL | +LL ~ (1, 2, 3) +LL | } else if false { +LL ~ (4, 5, 6) +LL | } else { +LL ~ (7, 8, 9) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:470:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = a + 1; +... | +LL | | d = 2; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (c, d) = if true { +LL | +LL | a = 1; +LL | b = a + 1; +LL ~ (b + 1, 1) +LL | } else { +LL | a = 4; +LL | b = a + 2; +LL ~ (b + 2, 2) +LL ~ }; + | + +error: aborting due to 25 previous errors From eca4b5b83d60c97247c41846aec6a215d33794ae Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jun 2026 14:00:22 +0200 Subject: [PATCH 151/278] move nextest section up and mark it more clearly as such --- src/tools/miri/README.md | 53 +++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 6b8d8ed1f8018..fdc746ead51f5 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -171,6 +171,38 @@ available (which affects `cfg(target_feature)`), and it tells Miri to consider t that the interpreted program runs on as having the feature available (meaning the code is allowed to invoke the corresponding intrinsics). +### Nextest integration + +Miri can be combined with [`cargo-nextest`](https://nexte.st): + +``` +cargo install --locked cargo-nextest +cargo miri nextest run +``` + +Nextest spawns a separate instance of Miri for each test, which has several advantages: +- Tests can run in parallel. Miri itself only uses a single thread per interpreter so this can + give a massive speedup (but see the caveat below). +- Tests do not stop when a single problem is found. Miri aborts execution when it encounters + Undefined Behavior or an unsupported operation (there is often not really any way to continue), + so once a single test fails, the remaining tests cannot be executed. Nextest's process-per-test + model means that you end up with a full list of which tests worked in Miri and which tests had a + problem. + +However, there is also a big caveat: Miri will [re-compile the test crate every time it is +invoked](https://github.com/rust-lang/miri/issues/5013), which means a crate with N tests will be +compiled N+1 times. If the test crate takes a long time to build, this can outweigh the benefits of +parallelization. + +For more information about nextest, see the [`cargo-nextest` Miri +documentation](https://nexte.st/book/miri.html). + +Note: Nextest's one-test-per-process model means that `cargo miri test` is able to detect data +races where two tests race on a shared resource, but `cargo miri nextest run` will not detect +such races. + +Note: `cargo-nextest` [does not support doctests](https://github.com/nextest-rs/nextest/issues/16). + ### Testing multiple different executions Certain parts of the execution are picked randomly by Miri, such as the exact base address @@ -184,6 +216,7 @@ MIRIFLAGS="-Zmiri-many-seeds" cargo miri test # tries the seeds in 0..64 MIRIFLAGS="-Zmiri-many-seeds=0..16" cargo miri test ``` +Miri will test the given range of seeds with parallel interpreter instances. The default of 64 different seeds can be quite slow, so you often want to specify a smaller range. ### Running Miri on CI @@ -243,26 +276,6 @@ However, even for targets that we do support, the degree of support for accessin (such as the file system) differs between targets: generally, Linux targets have the best support, and macOS targets are usually on par. Windows is supported less well. -### Running tests in parallel - -Though it implements Rust threading, Miri itself is a single-threaded interpreter -(it works like a multi-threaded OS on a single-core CPU). -This means that when running `cargo miri test`, you will probably see a dramatic -increase in the amount of time it takes to run your whole test suite due to the -inherent interpreter slowdown and a loss of parallelism. - -You can get your test suite's parallelism back by running `cargo miri nextest run -jN` -(note that you will need [`cargo-nextest`](https://nexte.st) installed). -This works because `cargo-nextest` collects a list of all tests then launches a -separate `cargo miri run` for each test. For more information about nextest, see the -[`cargo-nextest` Miri documentation](https://nexte.st/book/miri.html). - -Note: This one-test-per-process model means that `cargo miri test` is able to detect data -races where two tests race on a shared resource, but `cargo miri nextest run` will not detect -such races. - -Note: `cargo-nextest` does not support doctests, see https://github.com/nextest-rs/nextest/issues/16 - ### Directly invoking the `miri` driver The recommended way to invoke Miri is via `cargo miri`. Directly invoking the underlying `miri` From 233759097cf498c6cd441cb4529c7b984d256044 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jun 2026 14:17:44 +0200 Subject: [PATCH 152/278] add some more errno_result --- .../fail-dep/libc/eventfd_block_read_twice.rs | 6 +++++- .../libc/eventfd_block_write_twice.rs | 6 +++++- .../fail-dep/libc/libc-epoll-data-race.rs | 3 +-- .../libc/libc_epoll_block_two_thread.rs | 8 ++++---- .../libc/libc_epoll_unsupported_fd.rs | 10 ++++++---- .../pass-dep/libc/libc-epoll-no-blocking.rs | 20 +++++++------------ .../miri/tests/pass-dep/libc/libc-eventfd.rs | 19 +++++++++--------- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 2 +- .../libc/libc-socket-no-blocking-epoll.rs | 6 +++--- 9 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs index 98cc80b6b4ea2..d4d14d449aa68 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs @@ -4,6 +4,10 @@ use std::thread; +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // Test the behaviour of a thread being blocked on an eventfd read, get unblocked, and then // get blocked again. @@ -18,7 +22,7 @@ fn main() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs index 1a1d76eda2003..39d00a7522761 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs @@ -4,6 +4,10 @@ use std::thread; +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // Test the behaviour of a thread being blocked on an eventfd `write`, get unblocked, and then // get blocked again. @@ -17,7 +21,7 @@ fn main() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs index eecf6abb9379f..6a6d363eea6bb 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs @@ -16,8 +16,7 @@ use libc_utils::*; fn main() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create two socketpair instances. let mut fds_a = [-1, -1]; diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs index 3eb79121a2f8d..22fe014d5831c 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs @@ -7,6 +7,7 @@ use std::thread; #[path = "../../utils/libc.rs"] mod libc_utils; use libc_utils::epoll::*; +use libc_utils::*; // Test if only one thread is unblocked if multiple threads blocked on same epfd. // Expected execution: @@ -16,14 +17,13 @@ use libc_utils::epoll::*; // 4. Thread 1 deadlocks. fn main() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create an eventfd instance. let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd1 = unsafe { libc::eventfd(0, flags) }; + let fd1 = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Make a duplicate so that we have two file descriptors for the same file description. - let fd2 = unsafe { libc::dup(fd1) }; + let fd2 = errno_result(unsafe { libc::dup(fd1) }).unwrap(); // Register both with epoll. epoll_ctl_add(epfd, fd1, EPOLLIN | EPOLLOUT | EPOLLET).unwrap(); diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs index 59cf0fc2ba026..d611feb30a703 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs @@ -1,13 +1,15 @@ //@only-target: linux android illumos +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // This is a test for registering unsupported fd with epoll. // Register epoll fd with epoll is allowed in real system, but we do not support this. fn main() { // Create two epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Register epoll with epoll. let mut ev = diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs index 75eb06bc12cfb..90f70064932c1 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs @@ -146,7 +146,7 @@ fn test_epoll_ctl_del() { events: (EPOLLIN | EPOLLOUT | EPOLLET_OR_ZERO) as u32, u64: u64::try_from(fds[1]).unwrap(), }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; + let res = unsafe { libc::epoll_ctl(epfd, EPOLL_CTL_ADD, fds[1], &mut ev) }; assert_eq!(res, 0); // Test EPOLL_CTL_DEL. @@ -158,10 +158,8 @@ fn test_epoll_ctl_del() { // This test is for one fd registered under two different epoll instance. fn test_two_epoll_instance() { // Create two epoll instance. - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); - let epfd2 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd2, -1); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + let epfd2 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance. let mut fds = [-1, -1]; @@ -570,8 +568,7 @@ fn test_epoll_ctl_epfd_equal_fd() { // epfd that shouldn't receive a notification in edge-triggered mode. fn test_epoll_ctl_notification() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance. let mut fds = [-1, -1]; @@ -584,8 +581,7 @@ fn test_epoll_ctl_notification() { check_epoll_wait_noblock(epfd0, &[Ev { events: EPOLLOUT, data: fds[0] }]); // Create another epoll instance. - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Register the same file description for epfd1. epoll_ctl_add(epfd1, fds[0], EPOLLIN | EPOLLOUT | EPOLLET_OR_ZERO).unwrap(); @@ -692,8 +688,7 @@ fn test_issue_3858() { /// Ensure that if a socket becomes un-writable, we don't see it any more. fn test_issue_4374() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance, make it non-blocking. let mut fds = [-1, -1]; @@ -721,8 +716,7 @@ fn test_issue_4374() { /// Same as above, but for becoming un-readable. fn test_issue_4374_reads() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance, make it non-blocking. let mut fds = [-1, -1]; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs index e86c70b590b36..fac5d6e7e02ad 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -9,6 +9,7 @@ use std::{io, thread}; #[path = "../../utils/libc.rs"] mod libc_utils; +use libc_utils::*; fn main() { test_read_write(); @@ -35,8 +36,8 @@ fn write_bytes(fd: i32, data: [u8; N]) -> io::Result { } fn test_read_write() { - let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = + errno_result(unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) }).unwrap(); let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); // Write 1 to the counter. let res = write_bytes(fd, sized_8_data).unwrap(); @@ -97,8 +98,9 @@ fn test_read_write() { fn test_race() { static mut VAL: u8 = 0; - let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + + let fd = + errno_result(unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) }).unwrap(); let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; let res = read_bytes(fd, &mut buf).unwrap(); @@ -130,8 +132,7 @@ fn test_syscall() { // This test will block on eventfd read then get unblocked by `write`. fn test_blocking_read() { // eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; // This will block. @@ -154,8 +155,7 @@ fn test_blocking_read() { fn test_blocking_write() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { @@ -192,8 +192,7 @@ fn test_blocking_write() { fn test_two_threads_blocked_on_eventfd() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index a131112ee258d..71f077b0a4397 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -272,7 +272,7 @@ fn test_dup() { let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); unsafe { - let fd = libc::open(name.as_ptr(), libc::O_RDONLY); + let fd = errno_result(libc::open(name.as_ptr(), libc::O_RDONLY)).unwrap(); let new_fd = libc::dup(fd); let new_fd2 = libc::dup2(fd, 8); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs index 9ed0b9c735979..3d39723d43752 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs @@ -359,7 +359,7 @@ fn test_shutdown_read_write() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); @@ -387,7 +387,7 @@ fn test_shutdown_read() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); @@ -411,7 +411,7 @@ fn test_shutdown_write() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || { From d2c6980911d7f19aae70b6fe1457c639c011c017 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jun 2026 14:18:27 +0200 Subject: [PATCH 153/278] =?UTF-8?q?rename=20read=5Fsplit=5Fslice=20?= =?UTF-8?q?=E2=86=92=20read=5Fpartial?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tools/miri/tests/pass-dep/libc/libc-pipe.rs | 2 +- src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs | 8 ++++---- src/tools/miri/tests/utils/libc.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs index 98d7340fa9db3..256997babd25f 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs @@ -39,7 +39,7 @@ fn test_pipe() { let data = b"123"; write_all(fds[1], data).unwrap(); let mut buf4: [u8; 5] = [0; 5]; - let (part1, rest) = read_split_slice(fds[0], &mut buf4).unwrap(); + let (part1, rest) = read_partial(fds[0], &mut buf4).unwrap(); assert_eq!(part1[..], data[..part1.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[1], b"34").unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index da521600d84a6..2508f0c7030ac 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -34,7 +34,7 @@ fn test_socketpair() { let data = b"abc"; write_all(fds[0], data).unwrap(); let mut buf2: [u8; 5] = [0; 5]; - let (read, rest) = read_split_slice(fds[1], &mut buf2).unwrap(); + let (read, rest) = read_partial(fds[1], &mut buf2).unwrap(); assert_eq!(read[..], data[..read.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[0], b"12").unwrap(); @@ -52,7 +52,7 @@ fn test_socketpair() { let data = b"abc"; write_all(fds[1], data).unwrap(); let mut buf4: [u8; 5] = [0; 5]; - let (read, rest) = read_split_slice(fds[0], &mut buf4).unwrap(); + let (read, rest) = read_partial(fds[0], &mut buf4).unwrap(); assert_eq!(read[..], data[..read.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[1], b"12").unwrap(); @@ -64,9 +64,9 @@ fn test_socketpair() { errno_check(unsafe { libc::close(fds[0]) }); // Reading the other end should return that data, then EOF. let mut buf: [u8; 5] = [0; 5]; - let (read, _tail) = read_split_slice(fds[1], &mut buf).unwrap(); + let (read, _tail) = read_partial(fds[1], &mut buf).unwrap(); assert_eq!(read, data); - let (read, _tail) = read_split_slice(fds[1], &mut buf).unwrap(); + let (read, _tail) = read_partial(fds[1], &mut buf).unwrap(); assert_eq!(read, &[]); // Writing the other end should emit EPIPE. let err = write_all(fds[1], &mut buf).unwrap_err(); diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs index f46e6aad01a5b..0f762ed916de5 100644 --- a/src/tools/miri/tests/utils/libc.rs +++ b/src/tools/miri/tests/utils/libc.rs @@ -86,7 +86,7 @@ pub fn read_exact_array(fd: libc::c_int) -> io::Result<[u8; N]> /// Do a single read from `fd` and return the part of the buffer that was written into, /// and the rest. #[track_caller] -pub fn read_split_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> { +pub fn read_partial(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> { let res = errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) })?; Ok(buf.split_at_mut(res as usize)) } From 5445de0403afe154fecacd577d1e7e188e57a1a5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jun 2026 14:35:07 +0200 Subject: [PATCH 154/278] remove dangling comment --- .../miri/tests/fail-dep/libc/socketpair_block_read_twice.rs | 1 - .../miri/tests/fail-dep/libc/socketpair_block_write_twice.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs index 37aa4590647b4..edf3f405056f4 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # No libc socketpair on Windows -// test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock //@require-annotations-for-level: error diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs index 2f8b5be0c0c57..d062aeaf34873 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # No libc socketpair on Windows -// test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock //@require-annotations-for-level: error From ba2c9baccbb22e97aefe46724a36f2f95908250e Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 25 Jun 2026 15:51:06 +0200 Subject: [PATCH 155/278] std: truncate thread names on NetBSD --- library/std/src/sys/thread/unix.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index eb3fcff05ea9e..dd7b7940de510 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -392,6 +392,7 @@ pub fn current_os_id() -> Option { target_os = "vxworks", target_os = "cygwin", target_vendor = "apple", + target_os = "netbsd", ))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { let mut result = [0; MAX_WITH_NUL]; @@ -463,7 +464,12 @@ pub fn set_name(name: &CStr) { #[cfg(target_os = "netbsd")] pub fn set_name(name: &CStr) { + // See https://github.com/NetBSD/src/blob/8d40872b4c550a802379f3b9c22a40212d5e149d/lib/libpthread/pthread.h#L281 + // FIXME: move to libc. + const PTHREAD_MAX_NAMELEN_NP: usize = 32; + unsafe { + let name = truncate_cstr::<{ PTHREAD_MAX_NAMELEN_NP }>(name); let res = libc::pthread_setname_np( libc::pthread_self(), c"%s".as_ptr(), From ab8866908ba2cb1ba4f32b31dfa5a4ec0a040d49 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Thu, 25 Jun 2026 13:04:25 +0000 Subject: [PATCH 156/278] replace cfg_if with cfg_select --- src/tools/miri/tests/deps/Cargo.toml | 1 - src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 7 +- .../tests/pass-dep/libc/pthread-threadname.rs | 84 +++++++++++-------- src/tools/miri/tests/pass-dep/shims/gettid.rs | 28 ++++--- 4 files changed, 71 insertions(+), 49 deletions(-) diff --git a/src/tools/miri/tests/deps/Cargo.toml b/src/tools/miri/tests/deps/Cargo.toml index a4d06b628081c..bbbcc316f31c2 100644 --- a/src/tools/miri/tests/deps/Cargo.toml +++ b/src/tools/miri/tests/deps/Cargo.toml @@ -10,7 +10,6 @@ edition = "2021" # all dependencies (and their transitive ones) listed here can be used in `tests/*-dep`. libc = "0.2" num_cpus = "1.10.1" -cfg-if = "1" getrandom_01 = { package = "getrandom", version = "0.1" } getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index a131112ee258d..5f6aacc641238 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -856,8 +856,8 @@ fn test_readdir() { assert!(!dirp.is_null()); let mut entries = Vec::new(); loop { - cfg_if::cfg_if! { - if #[cfg(target_os = "macos")] { + cfg_select! { + target_os = "macos" => { // On macos we only support readdir_r as that's what std uses there. use std::mem::MaybeUninit; use libc::dirent; @@ -866,7 +866,8 @@ fn test_readdir() { let ret = libc::readdir_r(dirp, entry.as_mut_ptr(), &mut result); assert_eq!(ret, 0); let entry_ptr = result; - } else { + } + _ => { let entry_ptr = libc::readdir(dirp); } } diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs index cf7ea4c6abba1..6cba672c6569c 100644 --- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs @@ -4,17 +4,21 @@ use std::ffi::{CStr, CString}; use std::thread; const MAX_THREAD_NAME_LEN: usize = { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "linux"))] { + cfg_select! { + target_os = "linux" => { 16 - } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { + } + any(target_os = "illumos", target_os = "solaris") => { 32 - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { libc::MAXTHREADNAMESIZE // 64, at the time of writing - } else if #[cfg(target_os = "freebsd")] { + } + target_os = "freebsd" => { usize::MAX // as far as I can tell - } else { - panic!() + } + _ => { + compile_error!("unsupported OS"); } } }; @@ -28,35 +32,38 @@ fn main() { .collect::(); fn set_thread_name(name: &CStr) -> i32 { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "linux", target_os = "freebsd", target_os = "illumos", target_os = "solaris" - ))] { + ) => { unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) } - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { unsafe { libc::pthread_setname_np(name.as_ptr().cast()) } - } else { + } + _ => { compile_error!("set_thread_name not supported for this OS") } } } fn get_thread_name(name: &mut [u8]) -> i32 { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "linux", target_os = "freebsd", target_os = "illumos", target_os = "solaris", target_os = "macos" - ))] { + ) => { unsafe { libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) } - } else { + } + _ => { compile_error!("get_thread_name not supported for this OS") } } @@ -95,13 +102,14 @@ fn main() { // Test what happens when the buffer is shorter than 16, but still long enough. let res = get_thread_name(&mut buf[..15]); - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + cfg_select! { + target_os = "linux" => { // For glibc used by linux-gnu there should be a failue, // if a shorter than 16 bytes buffer is provided, even if that would be // large enough for the thread name. assert_eq!(res, libc::ERANGE); - } else { + } + _ => { // Everywhere else, this should work. assert_eq!(res, 0); // POSIX seems to promise at least 15 chars excluding a null terminator. @@ -112,15 +120,16 @@ fn main() { // Test what happens when the buffer is too short even for the short name. let res = get_thread_name(&mut buf[..4]); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- they truncate instead. assert_eq!(res, 0); let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); assert_eq!(cstr.to_bytes_with_nul().len(), 4); assert!(short_name.as_bytes().starts_with(cstr.to_bytes())); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -128,12 +137,13 @@ fn main() { // Test zero-sized buffer. let res = get_thread_name(&mut []); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- even with size 0. assert_eq!(res, 0); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -149,16 +159,18 @@ fn main() { // Set full thread name. let cstr = CString::new(long_name.clone()).unwrap(); let res = set_thread_name(&cstr); - cfg_if::cfg_if! { - if #[cfg(target_os = "freebsd")] { + cfg_select! { + target_os = "freebsd" => { // Names of all size are supported. assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); assert_eq!(res, 0); - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { // Name is too long. assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); assert_eq!(res, libc::ENAMETOOLONG); - } else { + } + _ => { // Name is too long. assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); assert_eq!(res, libc::ERANGE); @@ -179,14 +191,15 @@ fn main() { // Test what happens when our buffer is just one byte too small. let res = get_thread_name(&mut buf[..truncated_name.len()]); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- they truncate instead. assert_eq!(res, 0); let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); assert_eq!(cstr.to_bytes(), &truncated_name.as_bytes()[..(truncated_name.len() - 1)]); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -199,10 +212,11 @@ fn main() { // Now set the name for a non-existing thread and verify error codes. let invalid_thread = 0xdeadbeef; let error = { - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + cfg_select! { + target_os = "linux" => { libc::ENOENT - } else { + } + _ => { libc::ESRCH } } diff --git a/src/tools/miri/tests/pass-dep/shims/gettid.rs b/src/tools/miri/tests/pass-dep/shims/gettid.rs index 0dce7bdc0c52e..9b186699ce275 100644 --- a/src/tools/miri/tests/pass-dep/shims/gettid.rs +++ b/src/tools/miri/tests/pass-dep/shims/gettid.rs @@ -5,29 +5,37 @@ #![feature(linkage)] fn gettid() -> u64 { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { + cfg_select! { + any(target_os = "android", target_os = "linux") => { gettid_linux_like() - } else if #[cfg(target_os = "nto")] { + } + target_os = "nto" => { unsafe { libc::gettid() as u64 } - } else if #[cfg(target_os = "openbsd")] { + } + target_os = "openbsd" => { unsafe { libc::getthrid() as u64 } - } else if #[cfg(target_os = "freebsd")] { + } + target_os = "freebsd" => { unsafe { libc::pthread_getthreadid_np() as u64 } - } else if #[cfg(target_os = "netbsd")] { + } + target_os = "netbsd" => { unsafe { libc::_lwp_self() as u64 } - } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + } + any(target_os = "solaris", target_os = "illumos") => { // On Solaris and Illumos, the `pthread_t` is the OS TID. unsafe { libc::pthread_self() as u64 } - } else if #[cfg(target_vendor = "apple")] { + } + target_vendor = "apple" => { let mut id = 0u64; let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) }; assert_eq!(status, 0); id - } else if #[cfg(windows)] { + } + windows => { use windows_sys::Win32::System::Threading::GetCurrentThreadId; unsafe { GetCurrentThreadId() as u64 } - } else { + } + _ => { compile_error!("platform has no gettid") } } From bf8aa084e58b94f18ff987e90f8315dd9f186902 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 25 Jun 2026 16:10:58 +0200 Subject: [PATCH 157/278] Bump nightly version -> 2026-06-25 --- clippy_utils/README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 95b8bdde5cd91..f322d03f18795 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-2026-06-11 +nightly-2026-06-25 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 3afa9da1f947d..ac044327aafe9 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2026-06-11" +channel = "nightly-2026-06-25" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 37ffa9ad058f4b29203da82f79b9de2870b637b0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jun 2026 14:52:05 +0200 Subject: [PATCH 158/278] run some more tests natively --- .../miri/tests/pass-dep/libc/libc-eventfd.rs | 6 + .../miri/tests/pass-dep/libc/libc-pipe.rs | 1 + .../tests/pass-dep/libc/libc-socketpair.rs | 6 + .../miri/tests/pass-dep/libc/libc-time.rs | 104 +++++++++++------- 4 files changed, 80 insertions(+), 37 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs index e86c70b590b36..d32d5ecf05e4f 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -1,6 +1,7 @@ //@only-target: linux android illumos // test_race, test_blocking_read and test_blocking_write depend on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] @@ -100,6 +101,11 @@ fn test_race() { let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; let fd = unsafe { libc::eventfd(0, flags) }; let thread1 = thread::spawn(move || { + if !cfg!(miri) { + // Make sure the write goes first. + thread::sleep(std::time::Duration::from_millis(10)); + } + let mut buf: [u8; 8] = [0; 8]; let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs index 98d7340fa9db3..91b89aaf97d6b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs @@ -1,6 +1,7 @@ //@ignore-target: windows # No libc pipe on Windows // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native use std::thread; #[path = "../../utils/libc.rs"] diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index da521600d84a6..af351802eec83 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -1,6 +1,7 @@ //@ignore-target: windows # No libc socketpair on Windows // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] @@ -132,6 +133,11 @@ fn test_blocking_read() { // Test the behaviour of a socketpair getting blocked on write and subsequently unblocked. fn test_blocking_write() { + // The test uses Miri's exact buffer size. + if !cfg!(miri) { + return; + } + let mut fds = [-1, -1]; errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); let arr1: [u8; 0x34000] = [1; 0x34000]; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs index f315d2ab117f9..3bfcfab099d7b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs @@ -1,5 +1,6 @@ //@ignore-target: windows # no libc time APIs on Windows //@compile-flags: -Zmiri-disable-isolation +//@run-native #[path = "../../utils/libc.rs"] mod libc_utils; @@ -9,6 +10,17 @@ use std::{env, mem, ptr}; use libc_utils::errno_check; +fn set_tz(name: &str) { + extern "C" { + fn tzset(); + } + + env::set_var("TZ", name); + if !cfg!(miri) { + unsafe { tzset() }; // re-read TZ env var (natively, it may be cached) + } +} + fn main() { test_clocks(); test_posix_gettimeofday(); @@ -66,11 +78,13 @@ fn test_posix_gettimeofday() { assert!(tv.tv_sec > 0); assert!(tv.tv_usec >= 0); // Theoretically this could be 0. - // Test that non-null tz returns an error (because we don't support it). - let mut tz = mem::MaybeUninit::::uninit(); - let tz_ptr = tz.as_mut_ptr(); - let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) }; - assert_eq!(is_error, -1); + if cfg!(miri) { + // Test that non-null tz returns an error (because we don't support it). + let mut tz = mem::MaybeUninit::::uninit(); + let tz_ptr = tz.as_mut_ptr(); + let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) }; + assert_eq!(is_error, -1); + } } /// Helper function to create an empty tm struct. @@ -104,9 +118,8 @@ fn create_empty_tm() -> libc::tm { /// Original GMT test fn test_localtime_r_gmt() { - // Set timezone to GMT. - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); + const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -120,7 +133,9 @@ fn test_localtime_r_gmt() { assert_eq!(tm.tm_year, 124); assert_eq!(tm.tm_wday, 0); assert_eq!(tm.tm_yday, 97); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", target_os = "macos", @@ -130,21 +145,21 @@ fn test_localtime_r_gmt() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } // The returned value is the pointer passed in. assert!(ptr::eq(res, &mut tm)); - - // Remove timezone setting. - env::remove_var(key); } /// PST timezone test (testing different timezone handling). fn test_localtime_r_pst() { - let key = "TZ"; - env::set_var(key, "PST8PDT"); + set_tz("PST8PDT"); + const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -159,7 +174,9 @@ fn test_localtime_r_pst() { assert_eq!(tm.tm_year, 124); assert_eq!(tm.tm_wday, 0); assert_eq!(tm.tm_yday, 97); - assert_eq!(tm.tm_isdst, -1); // DST information unavailable + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); // DST information unavailable + } #[cfg(any( target_os = "linux", @@ -170,18 +187,20 @@ fn test_localtime_r_pst() { { assert_eq!(tm.tm_gmtoff, -7 * 3600); // -7 hours in seconds unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "-07"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "-07" } else { "PDT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Unix epoch test (edge case testing). fn test_localtime_r_epoch() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); + const TIME_SINCE_EPOCH: libc::time_t = 0; // 1970-01-01 00:00:00 let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -196,7 +215,9 @@ fn test_localtime_r_epoch() { assert_eq!(tm.tm_year, 70); assert_eq!(tm.tm_wday, 4); // Thursday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -207,19 +228,20 @@ fn test_localtime_r_epoch() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Future date test (testing large values). #[cfg(target_pointer_width = "64")] fn test_localtime_r_future_64b() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); // Using 2050-01-01 00:00:00 for 64-bit systems // value that's safe for 64-bit time_t @@ -237,7 +259,9 @@ fn test_localtime_r_future_64b() { assert_eq!(tm.tm_year, 150); // 2050 - 1900 assert_eq!(tm.tm_wday, 6); // Saturday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -248,19 +272,20 @@ fn test_localtime_r_future_64b() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Future date test (testing large values for 32b target). #[cfg(target_pointer_width = "32")] fn test_localtime_r_future_32b() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); // Using 2030-01-01 00:00:00 for 32-bit systems // Safe value within i32 range @@ -279,7 +304,9 @@ fn test_localtime_r_future_32b() { assert_eq!(tm.tm_year, 130); // 2030 - 1900 assert_eq!(tm.tm_wday, 2); // Tuesday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -290,19 +317,20 @@ fn test_localtime_r_future_32b() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Tests the behavior of `localtime_r` with multiple calls to ensure deduplication of `tm_zone` pointers. #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "android"))] fn test_localtime_r_multiple_calls_deduplication() { - let key = "TZ"; - env::set_var(key, "PST8PDT"); + set_tz("PST8PDT"); const TIME_SINCE_EPOCH_BASE: libc::time_t = 1712475836; // Base timestamp: 2024-04-07 07:43:56 GMT const NUM_CALLS: usize = 50; @@ -321,9 +349,11 @@ fn test_localtime_r_multiple_calls_deduplication() { let unique_count = unique_pointers.len(); + // Miri non-determinisitcally de-duplicates. Native always deduplicates. + let min = if cfg!(miri) { 2 } else { 1 }; assert!( - unique_count >= 2 && unique_count <= (NUM_CALLS - 1), - "Unexpected number of unique tm_zone pointers: {} (expected between 2 and {})", + unique_count >= min && unique_count <= (NUM_CALLS - 1), + "Unexpected number of unique tm_zone pointers: {} (expected between {min} and {})", unique_count, NUM_CALLS - 1 ); From 528bd23ff58bd43787708ea7a5e1a06c7e369291 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jun 2026 16:35:51 +0200 Subject: [PATCH 159/278] update lockfile --- src/tools/miri/tests/deps/Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/miri/tests/deps/Cargo.lock b/src/tools/miri/tests/deps/Cargo.lock index cbd2aa22b5236..4691588eefb40 100644 --- a/src/tools/miri/tests/deps/Cargo.lock +++ b/src/tools/miri/tests/deps/Cargo.lock @@ -290,7 +290,6 @@ dependencies = [ name = "miri-test-deps" version = "0.1.0" dependencies = [ - "cfg-if", "futures", "getrandom 0.1.16", "getrandom 0.2.17", From 5ba35ba2599fdfc7f168a72b567909be49e769b6 Mon Sep 17 00:00:00 2001 From: Vilim Lendvaj Date: Thu, 25 Jun 2026 22:34:35 +0200 Subject: [PATCH 160/278] Eliminate double length check in `Vec::into_array` --- library/alloc/src/vec/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index b37f865020fd9..c3b86a635bd0f 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1760,7 +1760,11 @@ impl Vec { #[must_use] pub fn into_array(self) -> Result, Self> { if self.len() == N { - Ok(self.into_boxed_slice().into_array().ok().unwrap()) + // SAFETY: `Box::into_array` is guaranteed to return `Ok` if the + // length of the slice is equal to `N`. + // `self.into_boxed_slice().len()` is equal to `self.len()`, + // which we just checked. + Ok(unsafe { self.into_boxed_slice().into_array().unwrap_unchecked() }) } else { Err(self) } From b6a4c3aeed60f9e75690618f461936494200f048 Mon Sep 17 00:00:00 2001 From: Chronocoder Date: Wed, 24 Jun 2026 15:19:10 -0400 Subject: [PATCH 161/278] Suggest `>=` for `=>` typo in closure and call argument positions The parser suggests replacing `=>` with `>=` when it looks like a typo in a comparison, but it skipped the suggestion whenever a comma was an expected token. That excluded closure bodies used as call arguments, such as `iter.position(|x| x => &y)`, which is the case in #149805. The comma exclusion was there to avoid suggesting `>=` for a missing comma between match arms, where `=>` is a real arm arrow. Those cases have a close brace in the expected token set, while the comparison cases do not, so gate on the close brace instead of the comma. Fixes #149805 --- compiler/rustc_parse/src/parser/diagnostics.rs | 4 +++- tests/ui/asm/parse-error.stderr | 6 ++++++ tests/ui/parser/eq-gt-to-gt-eq.fixed | 6 ++++++ tests/ui/parser/eq-gt-to-gt-eq.rs | 6 ++++++ tests/ui/parser/eq-gt-to-gt-eq.stderr | 14 +++++++++++++- 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index fbceb185c190c..7040fc60e4f1a 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -602,7 +602,9 @@ impl<'a> Parser<'a> { // Look for usages of '=>' where '>=' was probably intended if self.token == token::FatArrow && expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le)) - && !expected.iter().any(|tok| matches!(tok, TokenType::FatArrow | TokenType::Comma)) + && !expected + .iter() + .any(|tok| matches!(tok, TokenType::FatArrow | TokenType::CloseBrace)) { err.span_suggestion( self.token.span, diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index 9bb7b28b4424e..0048fa5075db2 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -57,6 +57,12 @@ error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` | LL | asm!("{}", in(reg) foo => bar); | ^^ expected one of 7 possible tokens + | +help: you might have meant to write a "greater than or equal to" comparison + | +LL - asm!("{}", in(reg) foo => bar); +LL + asm!("{}", in(reg) foo >= bar); + | error: expected a path for argument to `sym` --> $DIR/parse-error.rs:32:24 diff --git a/tests/ui/parser/eq-gt-to-gt-eq.fixed b/tests/ui/parser/eq-gt-to-gt-eq.fixed index abb328399be2a..93940a5971f08 100644 --- a/tests/ui/parser/eq-gt-to-gt-eq.fixed +++ b/tests/ui/parser/eq-gt-to-gt-eq.fixed @@ -43,3 +43,9 @@ fn b() { _ => todo!(), } } + +fn closure() { + let a = 0; + let b = 1; + let _ = [a].iter().any(|x| x >= &b); //~ERROR +} diff --git a/tests/ui/parser/eq-gt-to-gt-eq.rs b/tests/ui/parser/eq-gt-to-gt-eq.rs index 1f57fa8328198..92ca43ba3155b 100644 --- a/tests/ui/parser/eq-gt-to-gt-eq.rs +++ b/tests/ui/parser/eq-gt-to-gt-eq.rs @@ -43,3 +43,9 @@ fn b() { _ => todo!(), } } + +fn closure() { + let a = 0; + let b = 1; + let _ = [a].iter().any(|x| x => &b); //~ERROR +} diff --git a/tests/ui/parser/eq-gt-to-gt-eq.stderr b/tests/ui/parser/eq-gt-to-gt-eq.stderr index aa47ddecce9ef..d5cf71db2df38 100644 --- a/tests/ui/parser/eq-gt-to-gt-eq.stderr +++ b/tests/ui/parser/eq-gt-to-gt-eq.stderr @@ -109,5 +109,17 @@ LL - match a => b { LL + match a >= b { | -error: aborting due to 7 previous errors +error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` + --> $DIR/eq-gt-to-gt-eq.rs:50:34 + | +LL | let _ = [a].iter().any(|x| x => &b); + | ^^ expected one of 8 possible tokens + | +help: you might have meant to write a "greater than or equal to" comparison + | +LL - let _ = [a].iter().any(|x| x => &b); +LL + let _ = [a].iter().any(|x| x >= &b); + | + +error: aborting due to 8 previous errors From a3c123bc98c013fa17897089b32d4d07c7f7196b Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 19 Apr 2026 17:22:58 +0200 Subject: [PATCH 162/278] Add function to extract the symbol name from the attributes --- compiler/rustc_symbol_mangling/src/lib.rs | 47 +++++++++++++++-------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index c3b02eb46ba1e..d61437212a266 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -92,7 +92,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mono::{InstantiationMode, MonoItem}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Instance, TyCtxt}; +use rustc_middle::ty::{self, Instance, InstanceKind, TyCtxt}; use rustc_session::config::SymbolManglingVersion; use tracing::debug; @@ -149,29 +149,22 @@ pub fn typeid_for_trait_ref<'tcx>( v0::mangle_typeid_for_trait_ref(tcx, trait_ref) } -/// Computes the symbol name for the given instance. This function will call -/// `compute_instantiating_crate` if it needs to factor the instantiating crate -/// into the symbol name. -fn compute_symbol_name<'tcx>( +pub fn symbol_name_from_attrs<'tcx>( tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - compute_instantiating_crate: impl FnOnce() -> CrateNum, -) -> String { - let def_id = instance.def_id(); - let args = instance.args; - - debug!("symbol_name(def_id={:?}, args={:?})", def_id, args); + instance_kind: InstanceKind<'tcx>, +) -> Option { + let def_id = instance_kind.def_id(); if let Some(def_id) = def_id.as_local() { if tcx.proc_macro_decls_static(()) == Some(def_id) { let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE); - return rustc_session::generate_proc_macro_decls_symbol(stable_crate_id); + return Some(rustc_session::generate_proc_macro_decls_symbol(stable_crate_id)); } } // FIXME(eddyb) Precompute a custom symbol name based on attributes. let attrs = if tcx.def_kind(def_id).has_codegen_attrs() { - &tcx.codegen_instance_attrs(instance.def) + &tcx.codegen_instance_attrs(instance_kind) } else { CodegenFnAttrs::EMPTY }; @@ -197,7 +190,7 @@ fn compute_symbol_name<'tcx>( // legacy symbol mangling scheme. let name = if let Some(name) = attrs.symbol_name { name } else { tcx.item_name(def_id) }; - return v0::mangle_internal_symbol(tcx, name.as_str()); + return Some(v0::mangle_internal_symbol(tcx, name.as_str())); } let wasm_import_module_exception_force_mangling = { @@ -225,15 +218,35 @@ fn compute_symbol_name<'tcx>( if !wasm_import_module_exception_force_mangling { if let Some(name) = attrs.symbol_name { // Use provided name - return name.to_string(); + return Some(name.to_string()); } if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { // Don't mangle - return tcx.item_name(def_id).to_string(); + return Some(tcx.item_name(def_id).to_string()); } } + None +} + +/// Computes the symbol name for the given instance. This function will call +/// `compute_instantiating_crate` if it needs to factor the instantiating crate +/// into the symbol name. +fn compute_symbol_name<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + compute_instantiating_crate: impl FnOnce() -> CrateNum, +) -> String { + let def_id = instance.def_id(); + let args = instance.args; + + debug!("symbol_name(def_id={:?}, args={:?})", def_id, args); + + if let Some(symbol) = symbol_name_from_attrs(tcx, instance.def) { + return symbol; + } + // If we're dealing with an instance of a function that's inlined from // another crate but we're marking it as globally shared to our // compilation (aka we're not making an internal copy in each of our From aed963990e62f801d4add0bb95018b00eeee9e2c Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 19 Apr 2026 17:25:55 +0200 Subject: [PATCH 163/278] Add weak-only lang items for core runtime symbols --- .../src/attributes/rustc_internal.rs | 4 +- compiler/rustc_hir/src/lang_items.rs | 8 ++++ compiler/rustc_hir/src/weak_lang_items.rs | 21 ++++++++ compiler/rustc_passes/src/lang_items.rs | 48 +++++++++++++++---- compiler/rustc_span/src/symbol.rs | 6 +++ library/core/src/ffi/mod.rs | 27 +++++++++++ 6 files changed, 104 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index d4e15b8964ff7..24df9b100ce95 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -582,7 +582,7 @@ impl SingleAttributeParser for LangParser { // Only weak lang items may be applied to foreign items if [Target::ForeignFn, Target::ForeignStatic, Target::ForeignTy, Target::ForeignMod] .contains(&cx.target) - && !lang_item.is_weak() + && !(lang_item.is_weak() || lang_item.is_weak_only()) { cx.emit_err(UnknownExternLangItem { span: cx.attr_span, lang_item: lang_item.name() }); return None; @@ -591,6 +591,8 @@ impl SingleAttributeParser for LangParser { // Check the target let allowed_targets: &[_] = if lang_item == LangItem::PanicImpl { &[Allow(Target::Fn), Allow(Target::ForeignFn)] + } else if lang_item.is_weak_only() { + &[Allow(Target::ForeignFn)] } else { &[Allow(lang_item.target())] }; diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 2069b746b5a7f..9c3f7789f9ed9 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -447,6 +447,14 @@ language_item_table! { // Used to fallback `{float}` to `f32` when `f32: From<{float}>` From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1); + + // Runtime symbols + MemCpy, sym::memcpy_fn, memcpy_fn, Target::Fn, GenericRequirement::None; + MemMove, sym::memmove_fn, memmove_fn, Target::Fn, GenericRequirement::None; + MemSet, sym::memset_fn, memset_fn, Target::Fn, GenericRequirement::None; + MemCmp, sym::memcmp_fn, memcmp_fn, Target::Fn, GenericRequirement::None; + Bcmp, sym::bcmp_fn, bcmp_fn, Target::Fn, GenericRequirement::None; + StrLen, sym::strlen_fn, strlen_fn, Target::Fn, GenericRequirement::None; } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index 85b0e3958c2d6..f9d94bd505724 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -23,7 +23,28 @@ macro_rules! weak_lang_items { } } +macro_rules! weak_only_lang_items { + ($($item:ident,)*) => { + pub static WEAK_ONLY_LANG_ITEMS: &[LangItem] = &[$(LangItem::$item,)*]; + + impl LangItem { + pub fn is_weak_only(self) -> bool { + matches!(self, $(LangItem::$item)|*) + } + } + } +} + weak_lang_items! { PanicImpl, rust_begin_unwind; EhPersonality, rust_eh_personality; } + +weak_only_lang_items! { + MemCpy, + MemMove, + MemSet, + MemCmp, + Bcmp, + StrLen, +} diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 1686b4d595933..a5a835d4a78c3 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -27,6 +27,11 @@ pub(crate) enum Duplicate { CrateDepends, } +enum CollectWeak { + Allowed, + Ignore, +} + struct LanguageItemCollector<'ast, 'tcx> { items: LanguageItems, tcx: TyCtxt<'tcx>, @@ -58,6 +63,7 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { attrs: &'ast [ast::Attribute], item_span: Span, generics: Option<&'ast ast::Generics>, + collect_weak: CollectWeak, ) { if let Some((name, attr_span)) = extract_ast(attrs) { match LangItem::from_name(name) { @@ -69,14 +75,18 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { .delayed_bug("lang item target is checked in attribute parser"); return; } - self.collect_item_extended( - lang_item, - def_id, - item_span, - attr_span, - generics, - actual_target, - ); + // Weak lang items are handled separately + // Weak only lang items are always handled here + if !lang_item.is_weak() || matches!(collect_weak, CollectWeak::Allowed) { + self.collect_item_extended( + lang_item, + def_id, + item_span, + attr_span, + generics, + actual_target, + ); + } } // Unknown lang item. _ => { @@ -295,6 +305,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { &i.attrs, i.span, i.opt_generics(), + CollectWeak::Allowed, ); let parent_item = self.parent_item.replace(i); @@ -302,6 +313,17 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { self.parent_item = parent_item; } + fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) { + self.check_for_lang( + Target::Fn, + self.resolver.owners[&i.id].def_id, + &i.attrs, + i.span, + None, + CollectWeak::Ignore, + ); + } + fn visit_variant(&mut self, variant: &'ast ast::Variant) { self.check_for_lang( Target::Variant, @@ -309,6 +331,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { &variant.attrs, variant.span, None, + CollectWeak::Allowed, ); } @@ -342,7 +365,14 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { } }; - self.check_for_lang(target, self.resolver.owners[&i.id].def_id, &i.attrs, i.span, generics); + self.check_for_lang( + target, + self.resolver.owners[&i.id].def_id, + &i.attrs, + i.span, + generics, + CollectWeak::Allowed, + ); visit::walk_assoc_item(self, i, ctxt); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 827e33dcd6632..be14a706ff7ba 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -515,6 +515,7 @@ symbols! { await_macro, backchain, backend_repr, + bcmp_fn, begin_panic, bench, bevy_ecs, @@ -1280,7 +1281,11 @@ symbols! { mem_variant_count, mem_zeroed, member_constraints, + memcmp_fn, + memcpy_fn, + memmove_fn, memory, + memset_fn, memtag, message, meta, @@ -2036,6 +2041,7 @@ symbols! { strict_provenance_lints, string_deref_patterns, stringify, + strlen_fn, struct_field_attributes, struct_inherit, struct_variant, diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 3f1aa54050a31..df6396b84264d 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -85,3 +85,30 @@ impl fmt::Debug for c_void { )] #[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))] unsafe extern "C" {} + +// Used by rustc for checking the definitions of other function with the same symbol names +// +// See the `invalid_runtime_symbols_definitions` lint. +mod runtime_symbols { + use crate::ffi::{c_char, c_int, c_void}; + + unsafe extern "C" { + #[lang = "memcpy_fn"] + fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + #[lang = "memmove_fn"] + fn memmove(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + #[lang = "memset_fn"] + fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void; + + #[lang = "memcmp_fn"] + fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + #[lang = "bcmp_fn"] + fn bcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + #[lang = "strlen_fn"] + fn strlen(s: *const c_char) -> usize; + } +} From dba34470b5a69d1e58695c3b5e024a00d0675484 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 12 Apr 2026 20:46:06 +0200 Subject: [PATCH 164/278] Add lint against invalid runtime symbol definitions --- Cargo.lock | 1 + compiler/rustc_lint/Cargo.toml | 1 + compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/lints.rs | 27 +++ compiler/rustc_lint/src/runtime_symbols.rs | 196 +++++++++++++++++++++ tests/ui/lint/runtime-symbols.rs | 54 ++++++ tests/ui/lint/runtime-symbols.stderr | 63 +++++++ 7 files changed, 345 insertions(+) create mode 100644 compiler/rustc_lint/src/runtime_symbols.rs create mode 100644 tests/ui/lint/runtime-symbols.rs create mode 100644 tests/ui/lint/runtime-symbols.stderr diff --git a/Cargo.lock b/Cargo.lock index c50e836574641..3fae7958e8157 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4312,6 +4312,7 @@ dependencies = [ "rustc_parse_format", "rustc_session", "rustc_span", + "rustc_symbol_mangling", "rustc_target", "rustc_trait_selection", "smallvec", diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 758d2762a6af4..176890bab85f2 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -22,6 +22,7 @@ rustc_middle = { path = "../rustc_middle" } rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 14c70100c5f97..9b87da79ee155 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -72,6 +72,7 @@ mod precedence; mod ptr_nulls; mod redundant_semicolon; mod reference_casting; +mod runtime_symbols; mod shadowed_into_iter; mod static_mut_refs; mod traits; @@ -117,6 +118,7 @@ use precedence::*; use ptr_nulls::*; use redundant_semicolon::*; use reference_casting::*; +use runtime_symbols::*; use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::LocalModDefId; use rustc_middle::query::Providers; @@ -259,6 +261,7 @@ late_lint_methods!( AsyncFnInTrait: AsyncFnInTrait, NonLocalDefinitions: NonLocalDefinitions::default(), InteriorMutableConsts: InteriorMutableConsts, + RuntimeSymbols: RuntimeSymbols, ImplTraitOvercaptures: ImplTraitOvercaptures, IfLetRescope: IfLetRescope::default(), StaticMutRefs: StaticMutRefs, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index d3048d08ebf77..931d07bf12f9b 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -808,6 +808,33 @@ pub(crate) enum UseLetUnderscoreIgnoreSuggestion { }, } +// runtime_symbols.rs +#[derive(Diagnostic)] +pub(crate) enum RedefiningRuntimeSymbolsDiag<'tcx> { + #[diag( + "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" + )] + #[note( + "expected `{$expected_fn_sig}` + found `{$found_fn_sig}`" + )] + #[help( + "either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = \"{$symbol_name}\")]`, or `#[link_name = \"{$symbol_name}\"]`" + )] + FnDef { symbol_name: String, expected_fn_sig: Ty<'tcx>, found_fn_sig: Ty<'tcx> }, + #[diag( + "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" + )] + #[note( + "expected `{$expected_fn_sig}` + found `static {$symbol_name}: {$static_ty}`" + )] + #[help( + "either fix the signature or remove any attributes `#[unsafe(no_mangle)]` or `#[unsafe(export_name = \"{$symbol_name}\")]`" + )] + Static { symbol_name: String, static_ty: Ty<'tcx>, expected_fn_sig: Ty<'tcx> }, +} + // drop_forget_useless.rs #[derive(Diagnostic)] #[diag("calls to `std::mem::drop` with a reference instead of an owned value does nothing")] diff --git a/compiler/rustc_lint/src/runtime_symbols.rs b/compiler/rustc_lint/src/runtime_symbols.rs new file mode 100644 index 0000000000000..2fd2c355902f8 --- /dev/null +++ b/compiler/rustc_lint/src/runtime_symbols.rs @@ -0,0 +1,196 @@ +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{self as hir, FnSig, ForeignItemKind, LanguageItems}; +use rustc_infer::infer::DefineOpaqueTypes; +use rustc_middle::ty::{self, Instance, Ty}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::Span; +use rustc_trait_selection::infer::TyCtxtInferExt; + +use crate::lints::RedefiningRuntimeSymbolsDiag; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `invalid_runtime_symbol_definitions` lint checks the signature of items whose + /// symbol name is a runtime symbol expected by `core`. + /// + /// ### Example + /// + #[cfg_attr(bootstrap, doc = "```rust")] + #[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")] + #[cfg_attr(not(bootstrap), doc = "#[unsafe(no_mangle)]")] + /// pub fn strlen() {} // invalid definition of the `strlen` function + #[doc = "```"] + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Up-most care is required when defining runtime symbols assumed and + /// used by the standard library. They must follow the C specification, not use any + /// standard-library facility or undefined behavior may occur. + /// + /// The symbols currently checked are `memcpy`, `memmove`, `memset`, `memcmp`, + /// `bcmp` and `strlen`. + /// + /// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library + pub INVALID_RUNTIME_SYMBOL_DEFINITIONS, + Deny, + "invalid definition of a symbol used by the standard library" +} + +declare_lint_pass!(RuntimeSymbols => [INVALID_RUNTIME_SYMBOL_DEFINITIONS]); + +static EXPECTED_SYMBOLS: &[ExpectedSymbol] = &[ + ExpectedSymbol { symbol: "memcpy", lang: LanguageItems::memcpy_fn }, + ExpectedSymbol { symbol: "memmove", lang: LanguageItems::memmove_fn }, + ExpectedSymbol { symbol: "memset", lang: LanguageItems::memset_fn }, + ExpectedSymbol { symbol: "memcmp", lang: LanguageItems::memcmp_fn }, + ExpectedSymbol { symbol: "bcmp", lang: LanguageItems::bcmp_fn }, + ExpectedSymbol { symbol: "strlen", lang: LanguageItems::strlen_fn }, +]; + +#[derive(Copy, Clone, Debug)] +struct ExpectedSymbol { + symbol: &'static str, + lang: fn(&LanguageItems) -> Option, +} + +impl<'tcx> LateLintPass<'tcx> for RuntimeSymbols { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + // Bail-out if the item is not a function/method or static. + match item.kind { + hir::ItemKind::Fn { sig, ident: _, generics, body: _, has_body: _ } => { + // Generic functions cannot have the same runtime symbol as we do not allow + // any symbol attributes. + if !generics.params.is_empty() { + return; + } + + // Try to get the overridden symbol name of this function (our mangling + // cannot ever conflict with runtime symbols, so no need to check for those). + let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs( + cx.tcx, + rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()), + ) else { + return; + }; + + check_fn(cx, &symbol_name, sig, item.owner_id.def_id); + } + hir::ItemKind::Static(..) => { + // Compute the symbol name of this static (without mangling, as our mangling + // cannot ever conflict with runtime symbols). + let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs( + cx.tcx, + rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()), + ) else { + return; + }; + + let def_id = item.owner_id.def_id; + let static_ty = cx.tcx.type_of(def_id).instantiate_identity(); + + check_static(cx, &symbol_name, static_ty, item.span); + } + hir::ItemKind::ForeignMod { abi: _, items } => { + for item in items { + let item = cx.tcx.hir_foreign_item(*item); + + let did = item.owner_id.def_id; + let instance = Instance::new_raw( + did.to_def_id(), + ty::List::identity_for_item(cx.tcx, did), + ); + let symbol_name = cx.tcx.symbol_name(instance); + + match item.kind { + ForeignItemKind::Fn(fn_sig, _idents, _generics) => { + check_fn(cx, &symbol_name.name, fn_sig, did); + } + ForeignItemKind::Static(..) => { + let def_id = item.owner_id.def_id; + let static_ty = cx.tcx.type_of(def_id).instantiate_identity(); + check_static(cx, &symbol_name.name, static_ty, item.span); + } + ForeignItemKind::Type => return, + } + } + } + _ => return, + } + } +} + +fn check_fn(cx: &LateContext<'_>, symbol_name: &str, sig: FnSig<'_>, did: LocalDefId) { + let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else { + // The symbol name does not correspond to a runtime symbols, bail out + return; + }; + + let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else { + // Can't find the corresponding language item, bail out + return; + }; + + // Get the two function signatures + let lang_sig = cx.tcx.normalize_erasing_regions( + cx.typing_env(), + cx.tcx.fn_sig(expected_def_id).instantiate_identity(), + ); + let user_sig = cx + .tcx + .normalize_erasing_regions(cx.typing_env(), cx.tcx.fn_sig(did).instantiate_identity()); + + // Compare the two signatures with an inference context + let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); + let cause = rustc_middle::traits::ObligationCause::misc(sig.span, did); + let result = infcx.at(&cause, cx.param_env).eq(DefineOpaqueTypes::No, lang_sig, user_sig); + + // If they don't match, emit our own mismatch signatures + if let Err(_terr) = result { + // Create fn pointers for diagnostics purpose + let expected = Ty::new_fn_ptr(cx.tcx, lang_sig); + let actual = Ty::new_fn_ptr(cx.tcx, user_sig); + + cx.emit_span_lint( + INVALID_RUNTIME_SYMBOL_DEFINITIONS, + sig.span, + RedefiningRuntimeSymbolsDiag::FnDef { + symbol_name: symbol_name.to_string(), + found_fn_sig: actual, + expected_fn_sig: expected, + }, + ); + } +} + +fn check_static<'tcx>(cx: &LateContext<'tcx>, symbol_name: &str, static_ty: Ty<'tcx>, sp: Span) { + let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else { + // The symbol name does not correspond to a runtime symbols, bail out + return; + }; + + let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else { + // Can't find the corresponding language item, bail out + return; + }; + + // Unconditionally report a mismatch, a static cannot ever be a function definition + + let lang_sig = cx.tcx.normalize_erasing_regions( + cx.typing_env(), + cx.tcx.fn_sig(expected_def_id).instantiate_identity(), + ); + + let expected = Ty::new_fn_ptr(cx.tcx, lang_sig); + + cx.emit_span_lint( + INVALID_RUNTIME_SYMBOL_DEFINITIONS, + sp, + RedefiningRuntimeSymbolsDiag::Static { + static_ty, + symbol_name: symbol_name.to_string(), + expected_fn_sig: expected, + }, + ); +} diff --git a/tests/ui/lint/runtime-symbols.rs b/tests/ui/lint/runtime-symbols.rs new file mode 100644 index 0000000000000..6c8a79f7718cb --- /dev/null +++ b/tests/ui/lint/runtime-symbols.rs @@ -0,0 +1,54 @@ +// This test checks the runtime symbols lint. + +//@ edition: 2021 +//@ normalize-stderr: "\*const [iu]8" -> "*const U8" + +#![allow(clashing_extern_declarations)] // we are volontary testing differents defs + +use core::ffi::{c_char, c_int, c_void}; + +fn invalid() { + #[no_mangle] + pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + std::ptr::null_mut() + } + //~^^^ ERROR invalid definition of the runtime `memcpy` symbol + + #[no_mangle] + pub fn memmove() {} + //~^ ERROR invalid definition of the runtime `memmove` symbol + + extern "C" { + pub fn memset(); + //~^ ERROR invalid definition of the runtime `memset` symbol + + pub fn memcmp(); + //~^ ERROR invalid definition of the runtime `memcmp` symbol + } + + #[export_name = "bcmp"] + pub fn bcmp_() {} + //~^ ERROR invalid definition of the runtime `bcmp` symbol + + #[no_mangle] + pub static strlen: () = (); + //~^ ERROR invalid definition of the runtime `strlen` symbol +} + +fn valid() { + extern "C" { + fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + fn memmove(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void; + + fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + fn bcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + fn strlen(s: *const c_char) -> usize; + } +} + +fn main() {} diff --git a/tests/ui/lint/runtime-symbols.stderr b/tests/ui/lint/runtime-symbols.stderr new file mode 100644 index 0000000000000..bac3c352bdca6 --- /dev/null +++ b/tests/ui/lint/runtime-symbols.stderr @@ -0,0 +1,63 @@ +error: invalid definition of the runtime `memcpy` symbol used by the standard library + --> $DIR/runtime-symbols.rs:12:5 + | +LL | pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `extern "C" fn(*mut c_void, *const c_void, i64) -> *mut c_void` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcpy")]`, or `#[link_name = "memcpy"]` + = note: `#[deny(invalid_runtime_symbol_definitions)]` on by default + +error: invalid definition of the runtime `memmove` symbol used by the standard library + --> $DIR/runtime-symbols.rs:18:5 + | +LL | pub fn memmove() {} + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memmove")]`, or `#[link_name = "memmove"]` + +error: invalid definition of the runtime `memset` symbol used by the standard library + --> $DIR/runtime-symbols.rs:22:9 + | +LL | pub fn memset(); + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, i32, usize) -> *mut c_void` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memset")]`, or `#[link_name = "memset"]` + +error: invalid definition of the runtime `memcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:25:9 + | +LL | pub fn memcmp(); + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcmp")]`, or `#[link_name = "memcmp"]` + +error: invalid definition of the runtime `bcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:30:5 + | +LL | pub fn bcmp_() {} + | ^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "bcmp")]`, or `#[link_name = "bcmp"]` + +error: invalid definition of the runtime `strlen` symbol used by the standard library + --> $DIR/runtime-symbols.rs:34:5 + | +LL | pub static strlen: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const U8) -> usize` + found `static strlen: ()` + = help: either fix the signature or remove any attributes `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "strlen")]` + +error: aborting due to 6 previous errors + From 47448f36b0904702edc3ea8aa0d0b8b13f68aca0 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 19 Apr 2026 20:43:18 +0200 Subject: [PATCH 165/278] Handle static whose type is a function pointer --- compiler/rustc_lint/src/runtime_symbols.rs | 42 +++++++++++++--------- tests/ui/lint/runtime-symbols.rs | 2 +- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_lint/src/runtime_symbols.rs b/compiler/rustc_lint/src/runtime_symbols.rs index 2fd2c355902f8..1cc3329ac71f9 100644 --- a/compiler/rustc_lint/src/runtime_symbols.rs +++ b/compiler/rustc_lint/src/runtime_symbols.rs @@ -88,9 +88,8 @@ impl<'tcx> LateLintPass<'tcx> for RuntimeSymbols { }; let def_id = item.owner_id.def_id; - let static_ty = cx.tcx.type_of(def_id).instantiate_identity(); - check_static(cx, &symbol_name, static_ty, item.span); + check_static(cx, &symbol_name, def_id, item.span); } hir::ItemKind::ForeignMod { abi: _, items } => { for item in items { @@ -108,9 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for RuntimeSymbols { check_fn(cx, &symbol_name.name, fn_sig, did); } ForeignItemKind::Static(..) => { - let def_id = item.owner_id.def_id; - let static_ty = cx.tcx.type_of(def_id).instantiate_identity(); - check_static(cx, &symbol_name.name, static_ty, item.span); + check_static(cx, &symbol_name.name, did, item.span); } ForeignItemKind::Type => return, } @@ -164,7 +161,7 @@ fn check_fn(cx: &LateContext<'_>, symbol_name: &str, sig: FnSig<'_>, did: LocalD } } -fn check_static<'tcx>(cx: &LateContext<'tcx>, symbol_name: &str, static_ty: Ty<'tcx>, sp: Span) { +fn check_static<'tcx>(cx: &LateContext<'tcx>, symbol_name: &str, did: LocalDefId, sp: Span) { let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else { // The symbol name does not correspond to a runtime symbols, bail out return; @@ -175,8 +172,18 @@ fn check_static<'tcx>(cx: &LateContext<'tcx>, symbol_name: &str, static_ty: Ty<' return; }; - // Unconditionally report a mismatch, a static cannot ever be a function definition + // Get the static type + let static_ty = cx.tcx.type_of(did).instantiate_identity().skip_norm_wip(); + // Peel Option<...> and get the inner type (see std weak! macro with #[linkage = "extern_weak"]) + let inner_static_ty: Ty<'_> = match static_ty.kind() { + ty::Adt(def, args) if Some(def.did()) == cx.tcx.lang_items().option_type() => { + args.type_at(0) + } + _ => static_ty, + }; + + // Get the expected symbol function signature let lang_sig = cx.tcx.normalize_erasing_regions( cx.typing_env(), cx.tcx.fn_sig(expected_def_id).instantiate_identity(), @@ -184,13 +191,16 @@ fn check_static<'tcx>(cx: &LateContext<'tcx>, symbol_name: &str, static_ty: Ty<' let expected = Ty::new_fn_ptr(cx.tcx, lang_sig); - cx.emit_span_lint( - INVALID_RUNTIME_SYMBOL_DEFINITIONS, - sp, - RedefiningRuntimeSymbolsDiag::Static { - static_ty, - symbol_name: symbol_name.to_string(), - expected_fn_sig: expected, - }, - ); + // Compare the expected function signature with the static type, report an error if they don't match + if expected != inner_static_ty { + cx.emit_span_lint( + INVALID_RUNTIME_SYMBOL_DEFINITIONS, + sp, + RedefiningRuntimeSymbolsDiag::Static { + static_ty, + symbol_name: symbol_name.to_string(), + expected_fn_sig: expected, + }, + ); + } } diff --git a/tests/ui/lint/runtime-symbols.rs b/tests/ui/lint/runtime-symbols.rs index 6c8a79f7718cb..813ebff4ff4de 100644 --- a/tests/ui/lint/runtime-symbols.rs +++ b/tests/ui/lint/runtime-symbols.rs @@ -47,7 +47,7 @@ fn valid() { fn bcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; - fn strlen(s: *const c_char) -> usize; + static strlen: Option usize>; } } From 7411873a9dbcf312b05c98d182fb69e2c5174866 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 19 Apr 2026 23:02:53 +0200 Subject: [PATCH 166/278] Fix invalid `memcpy` definition in codegen test and UI tests --- tests/codegen-llvm/no_builtins-at-crate.rs | 6 ++++-- tests/ui/foreign/foreign-int-types.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/codegen-llvm/no_builtins-at-crate.rs b/tests/codegen-llvm/no_builtins-at-crate.rs index ba1d31f60c390..de80a925aebc5 100644 --- a/tests/codegen-llvm/no_builtins-at-crate.rs +++ b/tests/codegen-llvm/no_builtins-at-crate.rs @@ -3,11 +3,13 @@ #![no_builtins] #![crate_type = "lib"] +use std::ffi::c_void; + // CHECK: define // CHECK-SAME: @__aeabi_memcpy // CHECK-SAME: #0 #[no_mangle] -pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, size: usize) { +pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut c_void, src: *const c_void, size: usize) { // CHECK: call // CHECK-SAME: @memcpy( memcpy(dest, src, size); @@ -17,7 +19,7 @@ pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, size: usi // CHECK-SAME: @memcpy // CHECK-SAME: #0 extern "C" { - pub fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; + pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; } // CHECK: attributes #0 diff --git a/tests/ui/foreign/foreign-int-types.rs b/tests/ui/foreign/foreign-int-types.rs index d20a4c96ea0e2..bac9d8c603918 100644 --- a/tests/ui/foreign/foreign-int-types.rs +++ b/tests/ui/foreign/foreign-int-types.rs @@ -4,7 +4,7 @@ mod xx { extern "C" { - pub fn strlen(str: *const u8) -> usize; + pub fn strlen2(str: *const u8) -> usize; pub fn foo(x: isize, y: usize); } } From 20a75d1f60f60da0a48869681e7ab11507c331b2 Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 20 Apr 2026 00:10:38 +0200 Subject: [PATCH 167/278] Fix invalid definition of `strlen` in error codes documentation --- compiler/rustc_error_codes/src/error_codes/E0755.md | 2 +- compiler/rustc_error_codes/src/error_codes/E0756.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0755.md b/compiler/rustc_error_codes/src/error_codes/E0755.md index bd93626a8db4d..b194e11b7f85e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0755.md +++ b/compiler/rustc_error_codes/src/error_codes/E0755.md @@ -20,7 +20,7 @@ side effects or infinite loops: extern "C" { #[unsafe(ffi_pure)] // ok! - pub fn strlen(s: *const i8) -> isize; + pub fn strlen(s: *const std::ffi::c_char) -> usize; } # fn main() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0756.md b/compiler/rustc_error_codes/src/error_codes/E0756.md index daafc2a5ac092..74233a6ad7a7a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0756.md +++ b/compiler/rustc_error_codes/src/error_codes/E0756.md @@ -21,7 +21,7 @@ which have no side effects except for their return value: extern "C" { #[unsafe(ffi_const)] // ok! - pub fn strlen(s: *const i8) -> i32; + pub fn strlen(s: *const std::ffi::c_char) -> usize; } # fn main() {} ``` From b6d1d922b4badf39203ca4e21b4963f5b7386a7c Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 14 May 2026 21:30:00 +0200 Subject: [PATCH 168/278] Fix `compiler-builtins` runtime symbols definitions --- .../compiler-builtins/src/arm.rs | 64 +++++++++++++++---- .../compiler-builtins/src/mem/mod.rs | 40 +++++++++--- 2 files changed, 80 insertions(+), 24 deletions(-) diff --git a/library/compiler-builtins/compiler-builtins/src/arm.rs b/library/compiler-builtins/compiler-builtins/src/arm.rs index 0c15b37df1dc1..f26fce2d912b6 100644 --- a/library/compiler-builtins/compiler-builtins/src/arm.rs +++ b/library/compiler-builtins/compiler-builtins/src/arm.rs @@ -84,7 +84,11 @@ intrinsics! { /// /// Usual `memcpy` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memcpy(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memcpy( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { // SAFETY: memcpy preconditions apply. unsafe { crate::mem::memcpy(dst, src, n) }; } @@ -96,7 +100,11 @@ intrinsics! { /// Usual `memcpy` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memcpy4(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memcpy4( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { // We are guaranteed 4-alignment, so accessing at u32 is okay. let mut dst = dst.cast::(); let mut src = src.cast::(); @@ -121,7 +129,7 @@ intrinsics! { } // SAFETY: `dst` and `src` will still be valid for `n` bytes - unsafe { __aeabi_memcpy(dst.cast::(), src.cast::(), n) }; + unsafe { __aeabi_memcpy(dst.cast::(), src.cast::(), n) }; } /// `memcpy` for 8-byte alignment. @@ -131,7 +139,11 @@ intrinsics! { /// Usual `memcpy` requirements apply. Additionally, `dest` and `src` must be aligned to /// eight bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memcpy8(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memcpy8( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { debug_assert!(dst.addr().is_multiple_of(8)); debug_assert!(src.addr().is_multiple_of(8)); @@ -145,7 +157,11 @@ intrinsics! { /// /// Usual `memmove` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memmove(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memmove( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { // SAFETY: memmove preconditions apply. unsafe { crate::mem::memmove(dst, src, n) }; } @@ -157,7 +173,11 @@ intrinsics! { /// Usual `memmove` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memmove4(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memmove4( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { debug_assert!(dst.addr().is_multiple_of(4)); debug_assert!(src.addr().is_multiple_of(4)); @@ -172,7 +192,11 @@ intrinsics! { /// Usual `memmove` requirements apply. Additionally, `dst` and `src` must be aligned to /// eight bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memmove8(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memmove8( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { debug_assert!(dst.addr().is_multiple_of(8)); debug_assert!(src.addr().is_multiple_of(8)); @@ -186,7 +210,11 @@ intrinsics! { /// /// Usual `memset` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memset(dst: *mut u8, n: usize, c: i32) { + pub unsafe extern "aapcs" fn __aeabi_memset( + dst: *mut core::ffi::c_void, + n: usize, + c: i32 + ) { // Note the different argument order // SAFETY: memset preconditions apply. unsafe { crate::mem::memset(dst, c, n) }; @@ -199,7 +227,11 @@ intrinsics! { /// Usual `memset` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memset4(dst: *mut u8, n: usize, c: i32) { + pub unsafe extern "aapcs" fn __aeabi_memset4( + dst: *mut core::ffi::c_void, + n: usize, + c: i32 + ) { let mut dst = dst.cast::(); debug_assert!(dst.is_aligned()); let mut n = n; @@ -222,7 +254,7 @@ intrinsics! { } // SAFETY: `dst` will still be valid for `n` bytes - unsafe { __aeabi_memset(dst.cast::(), n, byte as i32) }; + unsafe { __aeabi_memset(dst.cast::(), n, byte as i32) }; } /// `memset` for 8-byte alignment. @@ -232,7 +264,11 @@ intrinsics! { /// Usual `memset` requirements apply. Additionally, `dst` and `src` must be aligned to /// eight bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memset8(dst: *mut u8, n: usize, c: i32) { + pub unsafe extern "aapcs" fn __aeabi_memset8( + dst: *mut core::ffi::c_void, + n: usize, + c: i32 + ) { debug_assert!(dst.addr().is_multiple_of(8)); // SAFETY: memset preconditions apply, less strict alignment. @@ -245,7 +281,7 @@ intrinsics! { /// /// Usual `memclr` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memclr(dst: *mut u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memclr(dst: *mut core::ffi::c_void, n: usize) { // SAFETY: memclr preconditions apply, less strict alignment. unsafe { __aeabi_memset(dst, n, 0) }; } @@ -257,7 +293,7 @@ intrinsics! { /// Usual `memclr` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memclr4(dst: *mut u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memclr4(dst: *mut core::ffi::c_void, n: usize) { debug_assert!(dst.addr().is_multiple_of(4)); // SAFETY: memclr preconditions apply, less strict alignment. @@ -271,7 +307,7 @@ intrinsics! { /// Usual `memclr` requirements apply. Additionally, `dst` and `src` must be aligned to /// eight bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memclr8(dst: *mut u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memclr8(dst: *mut core::ffi::c_void, n: usize) { debug_assert!(dst.addr().is_multiple_of(8)); // SAFETY: memclr preconditions apply, less strict alignment. diff --git a/library/compiler-builtins/compiler-builtins/src/mem/mod.rs b/library/compiler-builtins/compiler-builtins/src/mem/mod.rs index ac41cd33416f6..ef0f4da228e96 100644 --- a/library/compiler-builtins/compiler-builtins/src/mem/mod.rs +++ b/library/compiler-builtins/compiler-builtins/src/mem/mod.rs @@ -9,37 +9,57 @@ mod impls; intrinsics! { #[mem_builtin] - pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { - impls::copy_forward(dest, src, n); + pub unsafe extern "C" fn memcpy( + dest: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) -> *mut core::ffi::c_void { + impls::copy_forward(dest.cast(), src.cast(), n); dest } #[mem_builtin] - pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + pub unsafe extern "C" fn memmove( + dest: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) -> *mut core::ffi::c_void { let delta = (dest as usize).wrapping_sub(src as usize); if delta >= n { // We can copy forwards because either dest is far enough ahead of src, // or src is ahead of dest (and delta overflowed). - impls::copy_forward(dest, src, n); + impls::copy_forward(dest.cast(), src.cast(), n); } else { - impls::copy_backward(dest, src, n); + impls::copy_backward(dest.cast(), src.cast(), n); } dest } #[mem_builtin] - pub unsafe extern "C" fn memset(s: *mut u8, c: core::ffi::c_int, n: usize) -> *mut u8 { - impls::set_bytes(s, c as u8, n); + pub unsafe extern "C" fn memset( + s: *mut core::ffi::c_void, + c: core::ffi::c_int, + n: usize + ) -> *mut core::ffi::c_void { + impls::set_bytes(s.cast(), c as u8, n); s } #[mem_builtin] - pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> core::ffi::c_int { - impls::compare_bytes(s1, s2, n) + pub unsafe extern "C" fn memcmp( + s1: *const core::ffi::c_void, + s2: *const core::ffi::c_void, + n: usize + ) -> core::ffi::c_int { + impls::compare_bytes(s1.cast(), s2.cast(), n) } #[mem_builtin] - pub unsafe extern "C" fn bcmp(s1: *const u8, s2: *const u8, n: usize) -> core::ffi::c_int { + pub unsafe extern "C" fn bcmp( + s1: *const core::ffi::c_void, + s2: *const core::ffi::c_void, + n: usize + ) -> core::ffi::c_int { memcmp(s1, s2, n) } From 56d17c4d191feba4e4a016e1ffa74420f61a2703 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 14 Jun 2026 17:15:38 +0200 Subject: [PATCH 169/278] Split lint in two based on the certainty of the mismatch --- compiler/rustc_lint/src/lints.rs | 14 ++- compiler/rustc_lint/src/runtime_symbols.rs | 71 +++++++++++-- tests/ui/lint/runtime-symbols.rs | 61 +++++++++-- tests/ui/lint/runtime-symbols.stderr | 116 ++++++++++++++++----- 4 files changed, 215 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 931d07bf12f9b..43c0ab4804c84 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -821,7 +821,19 @@ pub(crate) enum RedefiningRuntimeSymbolsDiag<'tcx> { #[help( "either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = \"{$symbol_name}\")]`, or `#[link_name = \"{$symbol_name}\"]`" )] - FnDef { symbol_name: String, expected_fn_sig: Ty<'tcx>, found_fn_sig: Ty<'tcx> }, + FnDefInvalid { symbol_name: String, expected_fn_sig: Ty<'tcx>, found_fn_sig: Ty<'tcx> }, + #[diag( + "suspicious definition of the runtime `{$symbol_name}` symbol used by the standard library" + )] + #[note( + "expected `{$expected_fn_sig}` + found `{$found_fn_sig}`" + )] + #[help( + "either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = \"{$symbol_name}\")]`, or `#[link_name = \"{$symbol_name}\"]`" + )] + #[help("allow this lint if the signature is compatible")] + FnDefSuspicious { symbol_name: String, expected_fn_sig: Ty<'tcx>, found_fn_sig: Ty<'tcx> }, #[diag( "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" )] diff --git a/compiler/rustc_lint/src/runtime_symbols.rs b/compiler/rustc_lint/src/runtime_symbols.rs index 1cc3329ac71f9..a583ee70074b6 100644 --- a/compiler/rustc_lint/src/runtime_symbols.rs +++ b/compiler/rustc_lint/src/runtime_symbols.rs @@ -11,7 +11,9 @@ use crate::{LateContext, LateLintPass, LintContext}; declare_lint! { /// The `invalid_runtime_symbol_definitions` lint checks the signature of items whose - /// symbol name is a runtime symbol expected by `core`. + /// symbol name is a runtime symbol expected by `core` differs significantly from the + /// expected signature (like mismatch ABI, mismatch C variadics, mismatch argument count, + /// missing return type, ...). /// /// ### Example /// @@ -38,7 +40,37 @@ declare_lint! { "invalid definition of a symbol used by the standard library" } -declare_lint_pass!(RuntimeSymbols => [INVALID_RUNTIME_SYMBOL_DEFINITIONS]); +declare_lint! { + /// The `suspicious_runtime_symbol_definitions` lint checks the signature of items whose + /// symbol name is a runtime symbol expected by `core`. + /// + /// ### Example + /// + /// ```rust,no_run,standalone_crate + #[cfg_attr(not(bootstrap), doc = "#[unsafe(no_mangle)]")] + /// pub extern "C" fn strlen(ptr: *mut f32) -> usize { 0 } + /// // suspicious definition of the `strlen` function + /// // `ptr` should be `*const std::ffi::c_char` + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Up-most care is required when defining runtime symbols assumed and + /// used by the standard library. They must follow the C specification, not use any + /// standard-library facility or undefined behavior may occur. + /// + /// The symbols currently checked are `memcpy`, `memmove`, `memset`, `memcmp`, + /// `bcmp` and `strlen`. + /// + /// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library + pub SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, + Warn, + "suspicious definition of a symbol used by the standard library" +} + +declare_lint_pass!(RuntimeSymbols => [INVALID_RUNTIME_SYMBOL_DEFINITIONS, SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS]); static EXPECTED_SYMBOLS: &[ExpectedSymbol] = &[ ExpectedSymbol { symbol: "memcpy", lang: LanguageItems::memcpy_fn }, @@ -149,15 +181,32 @@ fn check_fn(cx: &LateContext<'_>, symbol_name: &str, sig: FnSig<'_>, did: LocalD let expected = Ty::new_fn_ptr(cx.tcx, lang_sig); let actual = Ty::new_fn_ptr(cx.tcx, user_sig); - cx.emit_span_lint( - INVALID_RUNTIME_SYMBOL_DEFINITIONS, - sig.span, - RedefiningRuntimeSymbolsDiag::FnDef { - symbol_name: symbol_name.to_string(), - found_fn_sig: actual, - expected_fn_sig: expected, - }, - ); + if lang_sig.abi() != user_sig.abi() + || lang_sig.c_variadic() != user_sig.c_variadic() + || lang_sig.inputs().skip_binder().len() != user_sig.inputs().skip_binder().len() + || (!lang_sig.output().skip_binder().is_unit() + && user_sig.output().skip_binder().is_unit()) + { + cx.emit_span_lint( + INVALID_RUNTIME_SYMBOL_DEFINITIONS, + sig.span, + RedefiningRuntimeSymbolsDiag::FnDefInvalid { + symbol_name: symbol_name.to_string(), + found_fn_sig: actual, + expected_fn_sig: expected, + }, + ); + } else { + cx.emit_span_lint( + SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, + sig.span, + RedefiningRuntimeSymbolsDiag::FnDefSuspicious { + symbol_name: symbol_name.to_string(), + found_fn_sig: actual, + expected_fn_sig: expected, + }, + ); + }; } } diff --git a/tests/ui/lint/runtime-symbols.rs b/tests/ui/lint/runtime-symbols.rs index 813ebff4ff4de..9b03f43c8cfaf 100644 --- a/tests/ui/lint/runtime-symbols.rs +++ b/tests/ui/lint/runtime-symbols.rs @@ -3,17 +3,12 @@ //@ edition: 2021 //@ normalize-stderr: "\*const [iu]8" -> "*const U8" -#![allow(clashing_extern_declarations)] // we are volontary testing differents defs +#![feature(c_variadic)] +#![allow(clashing_extern_declarations)] // we are voluntarily testing different definitions use core::ffi::{c_char, c_int, c_void}; fn invalid() { - #[no_mangle] - pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { - std::ptr::null_mut() - } - //~^^^ ERROR invalid definition of the runtime `memcpy` symbol - #[no_mangle] pub fn memmove() {} //~^ ERROR invalid definition of the runtime `memmove` symbol @@ -26,13 +21,59 @@ fn invalid() { //~^ ERROR invalid definition of the runtime `memcmp` symbol } + #[no_mangle] + pub static strlen: () = (); + //~^ ERROR invalid definition of the runtime `strlen` symbol + + // ABI mismatch: Rust ABI instead of C ABI + #[no_mangle] + pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void { + dest + } + //~^^^ ERROR invalid definition of the runtime `memcpy` symbol + + // C-Variadic mismatch + #[no_mangle] + pub unsafe extern "C" fn bcmp(s1: *const c_void, s2: *const c_void, n: usize, _: ...) -> c_int { + 0 + } + //~^^^ ERROR invalid definition of the runtime `bcmp` symbol + + // Return type is missing #[export_name = "bcmp"] - pub fn bcmp_() {} + pub extern "C" fn bcmp_(s1: *const c_void, s2: *const c_void, n: usize) {} //~^ ERROR invalid definition of the runtime `bcmp` symbol +} +fn suspicious() { #[no_mangle] - pub static strlen: () = (); - //~^ ERROR invalid definition of the runtime `strlen` symbol + pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + std::ptr::null_mut() + } + //~^^^ WARN suspicious definition of the runtime `memcpy` symbol + + #[no_mangle] + pub extern "C" fn memmove(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + std::ptr::null_mut() + } + //~^^^ WARN suspicious definition of the runtime `memmove` symbol + + extern "C" { + fn memset(s: *mut c_void, c: c_int, n: usize) -> f64; + //~^ WARN suspicious definition of the runtime `memset` symbol + } + + #[export_name = "bcmp"] + pub extern "C" fn bcmp_(s1: *const u8, s2: *const u8, n: usize) -> c_int { + 0 + } + //~^^^ WARN suspicious definition of the runtime `bcmp` symbol + + #[no_mangle] + pub extern "C" fn strlen(s: *const u64) -> usize { + 0 + } + //~^^^ WARN suspicious definition of the runtime `strlen` symbol } fn valid() { diff --git a/tests/ui/lint/runtime-symbols.stderr b/tests/ui/lint/runtime-symbols.stderr index bac3c352bdca6..712f6532c1a59 100644 --- a/tests/ui/lint/runtime-symbols.stderr +++ b/tests/ui/lint/runtime-symbols.stderr @@ -1,16 +1,5 @@ -error: invalid definition of the runtime `memcpy` symbol used by the standard library - --> $DIR/runtime-symbols.rs:12:5 - | -LL | pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` - found `extern "C" fn(*mut c_void, *const c_void, i64) -> *mut c_void` - = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcpy")]`, or `#[link_name = "memcpy"]` - = note: `#[deny(invalid_runtime_symbol_definitions)]` on by default - error: invalid definition of the runtime `memmove` symbol used by the standard library - --> $DIR/runtime-symbols.rs:18:5 + --> $DIR/runtime-symbols.rs:13:5 | LL | pub fn memmove() {} | ^^^^^^^^^^^^^^^^ @@ -18,9 +7,10 @@ LL | pub fn memmove() {} = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` found `fn()` = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memmove")]`, or `#[link_name = "memmove"]` + = note: `#[deny(invalid_runtime_symbol_definitions)]` on by default error: invalid definition of the runtime `memset` symbol used by the standard library - --> $DIR/runtime-symbols.rs:22:9 + --> $DIR/runtime-symbols.rs:17:9 | LL | pub fn memset(); | ^^^^^^^^^^^^^^^^ @@ -30,7 +20,7 @@ LL | pub fn memset(); = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memset")]`, or `#[link_name = "memset"]` error: invalid definition of the runtime `memcmp` symbol used by the standard library - --> $DIR/runtime-symbols.rs:25:9 + --> $DIR/runtime-symbols.rs:20:9 | LL | pub fn memcmp(); | ^^^^^^^^^^^^^^^^ @@ -39,25 +29,101 @@ LL | pub fn memcmp(); found `unsafe extern "C" fn()` = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcmp")]`, or `#[link_name = "memcmp"]` -error: invalid definition of the runtime `bcmp` symbol used by the standard library +error: invalid definition of the runtime `strlen` symbol used by the standard library + --> $DIR/runtime-symbols.rs:25:5 + | +LL | pub static strlen: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const U8) -> usize` + found `static strlen: ()` + = help: either fix the signature or remove any attributes `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "strlen")]` + +error: invalid definition of the runtime `memcpy` symbol used by the standard library --> $DIR/runtime-symbols.rs:30:5 | -LL | pub fn bcmp_() {} - | ^^^^^^^^^^^^^^ +LL | pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `fn(*mut c_void, *const c_void, usize) -> *mut c_void` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcpy")]`, or `#[link_name = "memcpy"]` + +error: invalid definition of the runtime `bcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:37:5 + | +LL | pub unsafe extern "C" fn bcmp(s1: *const c_void, s2: *const c_void, n: usize, _: ...) -> c_int { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` - found `fn()` + found `unsafe extern "C" fn(*const c_void, *const c_void, usize, ...) -> i32` = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "bcmp")]`, or `#[link_name = "bcmp"]` -error: invalid definition of the runtime `strlen` symbol used by the standard library - --> $DIR/runtime-symbols.rs:34:5 +error: invalid definition of the runtime `bcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:44:5 | -LL | pub static strlen: () = (); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub extern "C" fn bcmp_(s1: *const c_void, s2: *const c_void, n: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `extern "C" fn(*const c_void, *const c_void, usize)` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "bcmp")]`, or `#[link_name = "bcmp"]` + +warning: suspicious definition of the runtime `memcpy` symbol used by the standard library + --> $DIR/runtime-symbols.rs:50:5 + | +LL | pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `extern "C" fn(*mut c_void, *const c_void, i64) -> *mut c_void` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcpy")]`, or `#[link_name = "memcpy"]` + = help: allow this lint if the signature is compatible + = note: `#[warn(suspicious_runtime_symbol_definitions)]` on by default + +warning: suspicious definition of the runtime `memmove` symbol used by the standard library + --> $DIR/runtime-symbols.rs:56:5 + | +LL | pub extern "C" fn memmove(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `extern "C" fn(*mut c_void, *const c_void, i64) -> *mut c_void` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memmove")]`, or `#[link_name = "memmove"]` + = help: allow this lint if the signature is compatible + +warning: suspicious definition of the runtime `memset` symbol used by the standard library + --> $DIR/runtime-symbols.rs:62:9 + | +LL | fn memset(s: *mut c_void, c: c_int, n: usize) -> f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, i32, usize) -> *mut c_void` + found `unsafe extern "C" fn(*mut c_void, i32, usize) -> f64` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memset")]`, or `#[link_name = "memset"]` + = help: allow this lint if the signature is compatible + +warning: suspicious definition of the runtime `bcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:67:5 + | +LL | pub extern "C" fn bcmp_(s1: *const U8, s2: *const U8, n: usize) -> c_int { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `extern "C" fn(*const U8, *const U8, usize) -> i32` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "bcmp")]`, or `#[link_name = "bcmp"]` + = help: allow this lint if the signature is compatible + +warning: suspicious definition of the runtime `strlen` symbol used by the standard library + --> $DIR/runtime-symbols.rs:73:5 + | +LL | pub extern "C" fn strlen(s: *const u64) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected `unsafe extern "C" fn(*const U8) -> usize` - found `static strlen: ()` - = help: either fix the signature or remove any attributes `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "strlen")]` + found `extern "C" fn(*const u64) -> usize` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "strlen")]`, or `#[link_name = "strlen"]` + = help: allow this lint if the signature is compatible -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors; 5 warnings emitted From 6b9de05805cc1301e26ca9214fc2e653873f382c Mon Sep 17 00:00:00 2001 From: Emmanuel Ugwu Date: Fri, 26 Jun 2026 03:23:03 +0000 Subject: [PATCH 170/278] Attribute docs `deprecated` , `warn`, `allow`, `cfg`, `deny`, and `forbid` * Added documentation for allow, cfg, deny, forbid, deprecated and warn attribute Signed-off-by: Emmanuel Ugwu * address review feedback Signed-off-by: Emmanuel Ugwu * address review feedback Signed-off-by: Emmanuel Ugwu * address review feedback Signed-off-by: Emmanuel Ugwu * address feedback Signed-off-by: Emmanuel Ugwu * add the right deprecated link Signed-off-by: Emmanuel Ugwu * add the right deprecated link Signed-off-by: Emmanuel Ugwu * fix typo Signed-off-by: Emmanuel Ugwu * fix phrasing Signed-off-by: Emmanuel Ugwu * fix phrasing Signed-off-by: Emmanuel Ugwu * address feedback Signed-off-by: Emmanuel Ugwu * address feedback and add link to rustc book Signed-off-by: Emmanuel Ugwu * address feedback Signed-off-by: Emmanuel Ugwu --- library/std/src/attribute_docs.rs | 250 ++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) diff --git a/library/std/src/attribute_docs.rs b/library/std/src/attribute_docs.rs index 1d8dddb7ca54a..e8d36483f3139 100644 --- a/library/std/src/attribute_docs.rs +++ b/library/std/src/attribute_docs.rs @@ -85,3 +85,253 @@ /// [`unused_must_use`]: ../rustc/lints/listing/warn-by-default.html#unused-must-use /// [the `must_use` attribute]: ../reference/attributes/diagnostics.html#the-must_use-attribute mod must_use_attribute {} + +#[doc(attribute = "allow")] +// +/// The `allow` attribute suppresses lint diagnostics that would otherwise produce +/// warnings or errors. It can be used on any lint or lint group (except those +/// set to `forbid`). +/// +/// ```rust +/// #[allow(dead_code)] +/// fn unused_function() { +/// // ... +/// } +/// +/// fn main() { +/// // `unused_function` does not generate a compiler warning. +/// } +/// ``` +/// +/// Without `#[allow(dead_code)]`, the example above would emit: +/// +/// ```text +/// warning: function `unused_function` is never used +/// --> main.rs:1:4 +/// | +/// 1 | fn unused_function() { +/// | ^^^^^^^^^^^^^^^ +/// | +/// = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default +/// +/// warning: 1 warning emitted +/// ``` +/// +/// Multiple lints can be set to `allow` at once with commas: +/// +/// ```rust +/// #[allow(unused_variables, unused_mut)] +/// fn main() { +/// let mut x: u32 = 42; +/// } +/// ``` +/// +/// This is mostly used to prevent lint warnings or errors while still under development. +/// +/// It cannot override a lint that has been set to `forbid`. +/// +/// It's also important to consider that overusing `allow` could make code harder to maintain +/// and possibly hide issues. To mitigate this issue, using the `expect` attribute is preferred. +/// +/// `allow` can be overridden by `warn`, `deny`, and `forbid`. +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `allow` attribute]. +/// +/// [the `allow` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod allow_attribute {} + +#[doc(attribute = "cfg")] +// +/// Used for conditional compilation. +/// +/// The `cfg` attribute allows compiling an item under specific conditions, otherwise it +/// will be ignored. +/// +/// ```rust +/// // Only compiles this function for Linux. +/// #[cfg(target_os = "linux")] +/// fn platform_specific() { +/// println!("Running on Linux"); +/// } +/// +/// // Only compiles this function if not for Linux. +/// #[cfg(not(target_os = "linux"))] +/// fn platform_specific() { +/// println!("Running on something else"); +/// } +/// ``` +/// +/// Depending on the platform you're targeting, only one of these two functions will be considered +/// during the compilation. +/// +/// Conditions can also be combined with `all(...)`, `any(...)`, and `not(...)`. +/// +/// * `all`: True if all given predicates are true. +/// * `any`: True if at least one of the given predicates is true. +/// * `not`: True if the predicate is false and false if the predicate is true. +/// +/// ```rust +/// #[cfg(all(unix, target_pointer_width = "64"))] +/// fn unix_64bit() { +/// } +/// ``` +/// +/// If you want to use this mechanism in an `if` condition in your code, you +/// can use the [`cfg!`] macro. To conditionally apply an attribute, +/// see [`cfg_attr`]. +/// +/// For more information, see the Reference on [the `cfg` attribute]. +/// +/// [`cfg_attr`]: ../reference/conditional-compilation.html#the-cfg_attr-attribute +/// [the `cfg` attribute]: ../reference/conditional-compilation.html#the-cfg-attribute +mod cfg_attribute {} + +#[doc(attribute = "deny")] +// +/// Emits an error, preventing the compilation from finishing, when a lint check has failed. +/// This is useful for enforcing rules or preventing certain patterns: +/// +/// ```rust,compile_fail +/// #[deny(unused)] +/// fn foo() { +/// let x = 42; // Emits an error because x is unused. +/// } +/// ``` +/// +/// `deny` can be overridden by `allow`, `warn`, and `forbid`: +/// +/// ```rust +/// #![deny(unused)] +/// +/// #[allow(unused)] // We override the `deny` for this function. +/// fn foo() { +/// let x = 42; // No lint emitted even though `x` is unused. +/// } +/// ``` +/// +/// Multiple lints can also be set to `deny` at once: +/// +/// ```rust,compile_fail +/// #![deny(unused_imports, unused_variables)] +/// use std::collections::*; +/// +/// fn main() { +/// let mut x = 10; +/// } +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `deny` attribute]. +/// +/// [the `deny` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod deny_attribute {} + +#[doc(attribute = "forbid")] +// +/// Emits an error, preventing the compilation from finishing, when a lint check has failed. +/// +/// A lint set to `forbid` cannot be overridden by `allow` or `warn`. +/// Attempting either will result in a compilation error. Writing `#[deny(...)]` on the same lint inside a +/// `forbid` scope is permitted, but has no effect; the lint remains at the `forbid` level. +/// +/// This is useful for enforcing strict policies that should not be relaxed +/// anywhere in the codebase. Example: +/// +/// ```rust +/// #![forbid(unsafe_code)] +/// +/// // This would cause a compilation error if uncommented: +/// // #[allow(unsafe_code)] // error: cannot override `forbid` +/// ``` +/// +/// Multiple lints can be set to `forbid` at once: +/// +/// ```rust +/// #![forbid(unsafe_code, unused)] +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `forbid` attribute]. +/// +/// [the `forbid` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod forbid_attribute {} + +#[doc(attribute = "deprecated")] +// +/// Emits a warning during compilation when an item with this attribute is used. +/// `since` and `note` are optional fields giving more detail about why the item is deprecated. +/// +/// * `since`: the version since when the item is deprecated. +/// * `note`: the reason why an item is deprecated. +/// +/// Example: +/// +/// ```rust +/// #[deprecated(since = "1.0.0", note = "Use bar instead")] +/// struct Foo; +/// struct Bar; +/// ``` +/// +/// `deprecated` attribute helps developers transition away from old code by providing warnings when +/// deprecated items are used. Note that during `Cargo` builds, warnings on dependencies get silenced +/// by default, so you may not see a deprecation warning unless you build that dependency directly. +/// +/// For more information, see the Reference on [the `deprecated` attribute]. +/// +/// [the `deprecated` attribute]: ../reference/attributes/diagnostics.html#the-deprecated-attribute +mod deprecated_attribute {} + +#[doc(attribute = "warn")] +// +/// Emits a warning during compilation when a lint check failed. +/// +/// Unlike `deny` or `forbid`, `warn` does not produce a hard error: the compilation continues, but +/// the compiler emits a warning message. `warn` can be overridden by `allow`, `deny`, and `forbid`. +/// +/// Example: +/// +/// ```rust,compile_fail +/// #![allow(unused)] +/// +/// #[warn(unused)] // We override the allowed `unused` lint. +/// fn foo() { +/// // This lint warns by default even without #[warn(unused)] being explicitly set +/// let x = 42; // warning: unused variable `x` +/// } +/// ``` +/// +/// +/// Many lints, including `unused`, are already set to `warn` by default so this attribute is +/// mainly useful for lints that are normally `allow` by default. +/// +/// Multiple lints can be set to `warn` at once: +/// +/// ```rust,compile_fail +/// #[warn(unused_mut, unused_variables)] +/// fn main() { +/// let mut x = 42; +/// } +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `warn` attribute]. +/// +/// [the `warn` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod warn_attribute {} From 9b188dd6c50ed504ed0eaf80dfe8f4ef1b56cf5c Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 23 May 2026 15:27:28 -0700 Subject: [PATCH 171/278] cg_LLVM: Stop needing an alloca for volatile loads And while I'm here, improve the tests to check that the unaligned ones are actually unaligned, since `unaligned_volatile_load::` doesn't actually test anything. --- compiler/rustc_codegen_gcc/src/builder.rs | 3 +- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 7 +- compiler/rustc_codegen_llvm/src/builder.rs | 4 +- compiler/rustc_codegen_llvm/src/context.rs | 2 +- .../rustc_codegen_llvm/src/debuginfo/gdb.rs | 6 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 46 ++++--- .../rustc_codegen_ssa/src/traits/builder.rs | 2 +- tests/codegen-llvm/i128-x86-align.rs | 6 +- tests/codegen-llvm/intrinsics/volatile.rs | 121 +++++++++++++++++- 9 files changed, 161 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 8ae4dedff8f28..6cbc0054cc015 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -993,7 +993,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { loaded_value.to_rvalue() } - fn volatile_load(&mut self, ty: Type<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> { + fn volatile_load(&mut self, ty: Type<'gcc>, ptr: RValue<'gcc>, _: Align) -> RValue<'gcc> { + // FIXME(antoyo): set alignment. let ptr = self.context.new_cast(self.location, ptr, ty.make_volatile().make_pointer()); // (FractalFir): We insert a local here, to ensure this volatile load can't move across // blocks. diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index a12116d5b9d39..78a4c7e88c895 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -5,7 +5,7 @@ mod simd; use std::iter; use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp}; -use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange}; +use rustc_abi::{Align, BackendRepr, HasDataLayout, WrappingRange}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::errors::InvalidMonomorphization; @@ -368,8 +368,9 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc sym::volatile_load | sym::unaligned_volatile_load => { let ptr = args[0].immediate(); - let load = self.volatile_load(result.layout.gcc_type(self), ptr); - // FIXME(antoyo): set alignment. + let abi_align = result_layout.align.abi; + let ptr_align = if name == sym::volatile_load { abi_align } else { Align::ONE }; + let load = self.volatile_load(result.layout.gcc_type(self), ptr, ptr_align); if let BackendRepr::Scalar(scalar) = result.layout.backend_repr { self.to_immediate_scalar(load, scalar) } else { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index afb6985d21a95..7535c41cd1ed3 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -682,9 +682,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } - fn volatile_load(&mut self, ty: &'ll Type, ptr: &'ll Value) -> &'ll Value { + fn volatile_load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value { unsafe { - let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); + let load = self.load(ty, ptr, align); llvm::LLVMSetVolatile(load, llvm::TRUE); load } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 84661f5160b14..6198a98e5f7ae 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -978,7 +978,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn intrinsic_call_expects_place_always(&self, name: Symbol) -> bool { - matches!(name, sym::volatile_load | sym::unaligned_volatile_load | sym::black_box) + matches!(name, sym::black_box) } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index f88f0c2fb0994..f5b843b4e3e74 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -1,5 +1,6 @@ // .debug_gdb_scripts binary section. +use rustc_abi::Align; use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive; use rustc_codegen_ssa::traits::*; use rustc_hir::attrs::DebuggerVisualizerType; @@ -18,10 +19,7 @@ pub(crate) fn insert_reference_to_gdb_debug_scripts_section_global(bx: &mut Buil let gdb_debug_scripts_section = get_or_insert_gdb_debug_scripts_section_global(bx); // Load just the first byte as that's all that's necessary to force // LLVM to keep around the reference to the global. - let volatile_load_instruction = bx.volatile_load(bx.type_i8(), gdb_debug_scripts_section); - unsafe { - llvm::LLVMSetAlignment(volatile_load_instruction, 1); - } + bx.volatile_load(bx.type_i8(), gdb_debug_scripts_section, Align::ONE); } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 1caa95f369360..98e2cbac03dfe 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -354,25 +354,39 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } sym::volatile_load | sym::unaligned_volatile_load => { - let result = PlaceRef { - val: result_place.unwrap(), - layout: result_layout, - }; - + // Note that we cannot just load the `llvm_type` because we should never load non-scalars. + // Trying to do so blows up horribly in some cases -- for example loading a + // `MaybeUninint<&dyn Trait>` would load as `{ [i64x2] }` which gives assertions later + // (if we're lucky) from things not being pointers that ought to be. let ptr = args[0].immediate(); - let load = self.volatile_load(result_layout.llvm_type(self), ptr); - let align = if name == sym::unaligned_volatile_load { - 1 + let abi_align = result_layout.align.abi; + let ptr_align = if name == sym::volatile_load { abi_align } else { Align::ONE }; + if result_layout.is_zst() { + return IntrinsicResult::Operand(OperandValue::ZeroSized); + } else if let BackendRepr::Scalar(scalar) = result_layout.backend_repr { + let load = self.volatile_load(self.type_from_scalar(scalar), ptr, ptr_align); + self.to_immediate_scalar(load, scalar) } else { - result_layout.align.bytes() as u32 - }; - unsafe { - llvm::LLVMSetAlignment(load, align); - } - if !result_layout.is_zst() { - self.store_to_place(load, result.val); + // One day Rust will probably want to define how we split up a volatile load + // of something that's *not* just an ordinary scalar, but for now we can just + // use an LLVM integer type of the correct width and let it split it however. + let llty = self.type_ix(result_layout.size.bits()); + let temp = if let Some(result_place) = result_place { + PlaceRef { + val: result_place, + layout: result_layout, + } + } else { + PlaceRef::alloca(self, result_layout) + }; + let llval = self.volatile_load(llty, ptr, ptr_align); + self.store(llval, temp.val.llval, abi_align); + return if result_place.is_none() { + IntrinsicResult::Operand(self.load_operand(temp).val) + } else { + IntrinsicResult::WroteIntoPlace + }; } - return IntrinsicResult::WroteIntoPlace; } sym::volatile_store => { let dst = args[0].deref(self.cx()); diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index d68549c6871f4..39c2529152566 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -242,7 +242,7 @@ pub trait BuilderMethods<'a, 'tcx>: fn alloca_with_ty(&mut self, layout: TyAndLayout<'tcx>) -> Self::Value; fn load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; - fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value) -> Self::Value; + fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; fn atomic_load( &mut self, ty: Self::Type, diff --git a/tests/codegen-llvm/i128-x86-align.rs b/tests/codegen-llvm/i128-x86-align.rs index 75802b0c5056a..998821d17e80c 100644 --- a/tests/codegen-llvm/i128-x86-align.rs +++ b/tests/codegen-llvm/i128-x86-align.rs @@ -5,8 +5,6 @@ // while rustc wants it to have 16 byte alignment. This test checks that we handle this // correctly. -// CHECK: %ScalarPair = type { i32, [3 x i32], i128 } - #![feature(core_intrinsics)] #[repr(C)] @@ -62,8 +60,8 @@ pub fn load_volatile(x: &ScalarPair) -> ScalarPair { // CHECK-SAME: dereferenceable(32) %_0, // CHECK-SAME: align 16 // CHECK-SAME: dereferenceable(32) %x - // CHECK: [[LOAD:%.*]] = load volatile %ScalarPair, ptr %x, align 16 - // CHECK-NEXT: store %ScalarPair [[LOAD]], ptr %_0, align 16 + // CHECK: [[LOAD:%.*]] = load volatile i256, ptr %x, align 16 + // CHECK-NEXT: store i256 [[LOAD]], ptr %_0, align 16 // CHECK-NEXT: ret void unsafe { std::intrinsics::volatile_load(x) } } diff --git a/tests/codegen-llvm/intrinsics/volatile.rs b/tests/codegen-llvm/intrinsics/volatile.rs index 2dea5ecb2ca92..4ba4c5edd2df6 100644 --- a/tests/codegen-llvm/intrinsics/volatile.rs +++ b/tests/codegen-llvm/intrinsics/volatile.rs @@ -5,6 +5,11 @@ use std::intrinsics; +#[repr(align(32))] +pub struct CustomZst; + +type UninitFatPointer = std::mem::MaybeUninit<&'static dyn std::fmt::Debug>; + // CHECK-LABEL: @volatile_copy_memory #[no_mangle] pub unsafe fn volatile_copy_memory(a: *mut u8, b: *const u8) { @@ -28,8 +33,62 @@ pub unsafe fn volatile_set_memory(a: *mut u8, b: u8) { // CHECK-LABEL: @volatile_load #[no_mangle] -pub unsafe fn volatile_load(a: *const u8) -> u8 { - // CHECK: load volatile +pub unsafe fn volatile_load(a: *const u16) -> u16 { + // CHECK: [[TEMP:%.+]] = load volatile i16, ptr %a + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret i16 [[TEMP]] + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_bool +#[no_mangle] +pub unsafe fn volatile_load_bool(a: *const bool) -> bool { + // CHECK: [[TEMP:%.+]] = load volatile i8, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK: [[TRUNC:%.+]] = trunc nuw i8 [[TEMP]] to i1 + // CHECK: ret i1 [[TRUNC]] + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_zst +#[no_mangle] +pub unsafe fn volatile_load_zst(a: *const CustomZst) -> CustomZst { + // CHECK: start: + // CHECK-NEXT: ret void + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_array +// CHECK-SAME: ptr{{.+}}sret([16 x i8]){{.+}}%_0 +#[no_mangle] +pub unsafe fn volatile_load_array(a: *const [u16; 8]) -> [u16; 8] { + // CHECK-NOT: alloca + // CHECK: [[TEMP:%.+]] = load volatile i128, ptr %a, + // CHECK-SAME: align 2{{,|$}} + // CHECK: store i128 [[TEMP]], ptr %_0, + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret void + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_fat +#[no_mangle] +pub unsafe fn volatile_load_fat(a: *const UninitFatPointer) -> UninitFatPointer { + // CHECK: [[ALLOCA:%.+]] = alloca + // CHECK-SAME: [[SIZE:4|8|16]] x i8 + // CHECK-SAME: align [[ALIGN:2|4|8]] + + // CHECK: [[TEMP:%.+]] = load volatile [[INT:i32|i64|i128]], ptr %a, + // CHECK-SAME: align [[ALIGN]]{{,|$}} + // CHECK: store [[INT]] [[TEMP]], ptr [[ALLOCA]], + // CHECK-SAME: align [[ALIGN]]{{,|$}} + + // CHECK: [[T0:%.+]] = load ptr, ptr [[ALLOCA]], align [[ALIGN]] + // CHECK: [[T1:%.+]] = getelementptr inbounds i8, ptr [[ALLOCA]] + // CHECK: [[T2:%.+]] = load ptr, ptr [[T1]], align [[ALIGN]] + // CHECK: [[P1:%.+]] = insertvalue { ptr, ptr } poison, ptr [[T0]], 0 + // CHECK: [[P2:%.+]] = insertvalue { ptr, ptr } [[P1]], ptr [[T2]], 1 + // CHECK: ret { ptr, ptr } [[P2]] intrinsics::volatile_load(a) } @@ -42,8 +101,62 @@ pub unsafe fn volatile_store(a: *mut u8, b: u8) { // CHECK-LABEL: @unaligned_volatile_load #[no_mangle] -pub unsafe fn unaligned_volatile_load(a: *const u8) -> u8 { - // CHECK: load volatile +pub unsafe fn unaligned_volatile_load(a: *const u16) -> u16 { + // CHECK: [[TEMP:%.+]] = load volatile i16, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK-NEXT: ret i16 [[TEMP]] + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_bool +#[no_mangle] +pub unsafe fn unaligned_volatile_load_bool(a: *const bool) -> bool { + // CHECK: [[TEMP:%.+]] = load volatile i8, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK: [[TRUNC:%.+]] = trunc nuw i8 [[TEMP]] to i1 + // CHECK: ret i1 [[TRUNC]] + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_zst +#[no_mangle] +pub unsafe fn unaligned_volatile_load_zst(a: *const CustomZst) -> CustomZst { + // CHECK: start: + // CHECK-NEXT: ret void + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_array +// CHECK-SAME: ptr{{.+}}sret([16 x i8]){{.+}}%_0, +#[no_mangle] +pub unsafe fn unaligned_volatile_load_array(a: *const [u16; 8]) -> [u16; 8] { + // CHECK-NOT: alloca + // CHECK: [[TEMP:%.+]] = load volatile i128, ptr %a, + // CHECK-SAME: align 1{{,|$}} + // CHECK: store i128 [[TEMP]], ptr %_0, + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret void + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_fat +#[no_mangle] +pub unsafe fn unaligned_volatile_load_fat(a: *const UninitFatPointer) -> UninitFatPointer { + // CHECK: [[ALLOCA:%.+]] = alloca + // CHECK-SAME: [[SIZE]] x i8 + // CHECK-SAME: align [[ALIGN]] + + // CHECK: [[TEMP:%.+]] = load volatile [[INT]], ptr %a, + // CHECK-SAME: align 1{{,|$}} + // CHECK: store [[INT]] [[TEMP]], ptr [[ALLOCA]], + // CHECK-SAME: align [[ALIGN]]{{,|$}} + + // CHECK: [[T0:%.+]] = load ptr, ptr [[ALLOCA]], align [[ALIGN]] + // CHECK: [[T1:%.+]] = getelementptr inbounds i8, ptr [[ALLOCA]] + // CHECK: [[T2:%.+]] = load ptr, ptr [[T1]], align [[ALIGN]] + // CHECK: [[P1:%.+]] = insertvalue { ptr, ptr } poison, ptr [[T0]], 0 + // CHECK: [[P2:%.+]] = insertvalue { ptr, ptr } [[P1]], ptr [[T2]], 1 + // CHECK: ret { ptr, ptr } [[P2]] intrinsics::unaligned_volatile_load(a) } From 6d6fcc9f77e4473047c3763c155a9cf5995c3142 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 26 Jun 2026 14:36:44 +0900 Subject: [PATCH 172/278] add regression test for clone suggestion empty obligations --- ...constrained-impl-param-clone-suggestion.rs | 20 +++++++++++++++++++ ...trained-impl-param-clone-suggestion.stderr | 19 ++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs create mode 100644 tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr diff --git a/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs new file mode 100644 index 0000000000000..694c95da7b482 --- /dev/null +++ b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs @@ -0,0 +1,20 @@ +// Regression test for https://github.com/rust-lang/rust/issues/148631 + +struct C; + +struct S(T); + +trait Tr {} + +impl Clone for S +//~^ ERROR the type parameter `T` is not constrained +where + S: Tr, +{ + fn clone(&self) -> Self { + *self + //~^ ERROR cannot move out of `*self` + } +} + +fn main() {} diff --git a/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr new file mode 100644 index 0000000000000..9cf17045b0296 --- /dev/null +++ b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr @@ -0,0 +1,19 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-impl-param-clone-suggestion.rs:9:6 + | +LL | impl Clone for S + | -^- + | || + | |unconstrained type parameter + | help: remove the unused type parameter `T` + +error[E0507]: cannot move out of `*self` which is behind a shared reference + --> $DIR/unconstrained-impl-param-clone-suggestion.rs:15:9 + | +LL | *self + | ^^^^^ move occurs because `*self` has type `S`, which does not implement the `Copy` trait + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0207, E0507. +For more information about an error, try `rustc --explain E0207`. From f08762cf489011e9d91fb5dbe47f6d31bcb2e83e Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 26 Jun 2026 14:38:51 +0900 Subject: [PATCH 173/278] guard clone suggestion against empty obligation errors --- .../src/diagnostics/conflict_errors.rs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index caa566e79e29b..6acf685459cf3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1461,15 +1461,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let cause = ObligationCause::misc(expr.span, self.mir_def_id()); ocx.register_bound(cause, self.infcx.param_env, ty, clone_trait); let errors = ocx.evaluate_obligations_error_on_ambiguity(); - if errors.iter().all(|error| { - match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) { - Some(clause) => match clause.self_ty().skip_binder().kind() { - ty::Adt(def, _) => def.did().is_local() && clause.def_id() == clone_trait, - _ => false, - }, - None => false, - } - }) { + if !errors.is_empty() + && errors.iter().all(|error| { + match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) { + Some(clause) => match clause.self_ty().skip_binder().kind() { + ty::Adt(def, _) => { + def.did().is_local() && clause.def_id() == clone_trait + } + _ => false, + }, + None => false, + } + }) + { let mut type_spans = vec![]; let mut types = FxIndexSet::default(); for clause in errors From 4a2d36a1f9de30e0a97f6b0f70e284b200c7b090 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 26 Jun 2026 05:45:57 +0000 Subject: [PATCH 174/278] Prepare for merging from rust-lang/rust This updates the rust-version file to 40557f6225e337d68c8d4f086557ce54135f5dd9. --- 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 5db47ca8fc59b..10d09bd1f1ebd 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -942ac9ce4116d4ea784c9882659372b34978b1f8 +40557f6225e337d68c8d4f086557ce54135f5dd9 From 8461b0dd616dffd751d8c47618cc7a025d151fb1 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 19 Jun 2026 04:36:02 +0000 Subject: [PATCH 175/278] Rename unevaluatedConst to AliasConst and UnevaluatedConstKind to AliasConstKind --- compiler/rustc_borrowck/src/type_check/mod.rs | 8 +-- .../src/collect/predicates_of.rs | 4 +- .../src/hir_ty_lowering/mod.rs | 12 ++-- .../src/infer/relate/generalize.rs | 4 +- compiler/rustc_middle/src/mir/consts.rs | 8 +-- .../rustc_middle/src/mir/interpret/queries.rs | 10 +-- compiler/rustc_middle/src/mir/pretty.rs | 10 ++- compiler/rustc_middle/src/ty/consts.rs | 8 +-- .../src/ty/context/impl_interner.rs | 15 ++--- compiler/rustc_middle/src/ty/mod.rs | 6 +- compiler/rustc_middle/src/ty/print/pretty.rs | 10 +-- .../src/builder/expr/as_constant.rs | 4 +- .../src/thir/pattern/const_to_pat.rs | 22 +++---- .../rustc_mir_build/src/thir/pattern/mod.rs | 4 +- .../rustc_next_trait_solver/src/delegate.rs | 2 +- .../src/solve/eval_ctxt/mod.rs | 4 +- .../src/solve/normalizes_to.rs | 4 +- compiler/rustc_symbol_mangling/src/v0.rs | 10 +-- .../rustc_trait_selection/src/diagnostics.rs | 2 +- .../src/solve/delegate.rs | 2 +- .../src/traits/const_evaluatable.rs | 8 +-- .../src/traits/dyn_compatibility.rs | 13 ++-- .../src/traits/fulfill.rs | 16 ++--- .../rustc_trait_selection/src/traits/mod.rs | 12 ++-- .../src/traits/normalize.rs | 10 ++- .../src/traits/query/normalize.rs | 14 ++-- .../src/traits/select/mod.rs | 16 ++--- .../rustc_trait_selection/src/traits/wf.rs | 8 +-- compiler/rustc_ty_utils/src/consts.rs | 7 +- compiler/rustc_type_ir/src/const_kind.rs | 64 +++++++++---------- compiler/rustc_type_ir/src/inherent.rs | 2 +- compiler/rustc_type_ir/src/interner.rs | 5 +- compiler/rustc_type_ir/src/ir_print.rs | 6 +- compiler/rustc_type_ir/src/relate.rs | 10 +-- compiler/rustc_type_ir/src/term_kind.rs | 40 ++++++------ compiler/rustc_type_ir/src/ty/alias.rs | 4 +- src/librustdoc/clean/utils.rs | 10 +-- 37 files changed, 179 insertions(+), 215 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 5281b85b1ed04..4b4e6e0ac9ccd 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1761,10 +1761,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let maybe_uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Unevaluated(_, uv) => match uv.kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => { Some(UnevaluatedConst { def: def_id, args: uv.args, promoted: None }) } }, diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index e1798c24838d1..fe2b56d46135b 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -415,8 +415,8 @@ fn const_evaluatable_predicates_of<'tcx>( preds: FxIndexSet<(ty::Clause<'tcx>, Span)>, } - fn is_const_param_default(tcx: TyCtxt<'_>, kind: ty::UnevaluatedConstKind<'_>) -> bool { - let ty::UnevaluatedConstKind::Anon { def_id } = kind else { return false }; + fn is_const_param_default(tcx: TyCtxt<'_>, kind: ty::AliasConstKind<'_>) -> bool { + let ty::AliasConstKind::Anon { def_id } = kind else { return false }; let Some(local) = def_id.as_local() else { return false }; let hir_id = tcx.local_def_id_to_hir_id(local); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index baa9fddc2a651..deae42ebfbb53 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1865,9 +1865,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::AssocTag::Const, )?; self.require_type_const_attribute(item_def_id, span)?; - let uv = ty::UnevaluatedConst::new( + let uv = ty::AliasConst::new( tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, item_def_id), + ty::AliasConstKind::new_from_def_id(tcx, item_def_id), item_args, ); Ok(Const::new_unevaluated(tcx, ty::IsRigid::No, uv)) @@ -2775,9 +2775,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Const::new_unevaluated( tcx, ty::IsRigid::No, - ty::UnevaluatedConst::new( + ty::AliasConst::new( tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, did), + ty::AliasConstKind::new_from_def_id(tcx, did), args, ), ) @@ -2933,9 +2933,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { None => ty::Const::new_unevaluated( tcx, ty::IsRigid::No, - ty::UnevaluatedConst::new( + ty::AliasConst::new( tcx, - ty::UnevaluatedConstKind::Anon { def_id: anon.def_id.to_def_id() }, + ty::AliasConstKind::Anon { def_id: anon.def_id.to_def_id() }, ty::GenericArgs::identity_for_item(tcx, anon.def_id.to_def_id()), ), ), diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index f50fd4a32328e..7a9cb4b1690a9 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -769,7 +769,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { self.generalize_alias_term(uv.into()).map(|v| v.expect_const()) } _ => { - let ty::UnevaluatedConst { kind, args, .. } = uv; + let ty::AliasConst { kind, args, .. } = uv; let args = self.relate_with_variance( ty::Invariant, ty::VarianceDiagInfo::default(), @@ -779,7 +779,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { Ok(ty::Const::new_unevaluated( tcx, ty::IsRigid::No, - ty::UnevaluatedConst::new(tcx, kind, args), + ty::AliasConst::new(tcx, kind, args), )) } } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 6effeacac5fcf..82ea4bcaa21f9 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -472,13 +472,9 @@ pub struct UnevaluatedConst<'tcx> { impl<'tcx> UnevaluatedConst<'tcx> { #[inline] - pub fn shrink(self, tcx: TyCtxt<'tcx>) -> ty::UnevaluatedConst<'tcx> { + pub fn shrink(self, tcx: TyCtxt<'tcx>) -> ty::AliasConst<'tcx> { assert_eq!(self.promoted, None); - ty::UnevaluatedConst::new( - tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, self.def), - self.args, - ) + ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, self.def), self.args) } } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 51cf856a2d477..accd00a8ebb51 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -90,7 +90,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn const_eval_resolve_for_typeck( self, typing_env: ty::TypingEnv<'tcx>, - ct: ty::UnevaluatedConst<'tcx>, + ct: ty::AliasConst<'tcx>, span: Span, ) -> ConstToValTreeResult<'tcx> { // Cannot resolve `Unevaluated` constants that contain inference @@ -104,10 +104,10 @@ impl<'tcx> TyCtxt<'tcx> { } let def_id = match ct.kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => def_id, + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => def_id, }; let cid = match ty::Instance::try_resolve(self, typing_env, def_id, ct.args) { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index c1fd68b5702c8..a6de25ac93f31 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1493,12 +1493,10 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { ty::ConstKind::Param(p) => format!("ty::Param({p})"), ty::ConstKind::Unevaluated(_, uv) => { let kind = match uv.kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { - self.tcx.def_path_str(def_id) - } + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => self.tcx.def_path_str(def_id), }; format!("ty::Unevaluated({}, {:?})", kind, uv.args) } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 3455e770b40d1..8d28660676eff 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -21,8 +21,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed}; pub use valtree::*; pub type ConstKind<'tcx> = ir::ConstKind>; -pub type UnevaluatedConst<'tcx> = ir::UnevaluatedConst>; -pub type UnevaluatedConstKind<'tcx> = ir::UnevaluatedConstKind>; +pub type AliasConst<'tcx> = ir::AliasConst>; +pub type AliasConstKind<'tcx> = ir::AliasConstKind>; #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(ConstKind<'_>, 32); @@ -110,7 +110,7 @@ impl<'tcx> Const<'tcx> { pub fn new_unevaluated( tcx: TyCtxt<'tcx>, is_rigid: ty::IsRigid, - uv: ty::UnevaluatedConst<'tcx>, + uv: ty::AliasConst<'tcx>, ) -> Const<'tcx> { Const::new(tcx, ty::ConstKind::Unevaluated(is_rigid, uv)) } @@ -197,7 +197,7 @@ impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { fn new_unevaluated( interner: TyCtxt<'tcx>, is_rigid: ty::IsRigid, - uv: ty::UnevaluatedConst<'tcx>, + uv: ty::AliasConst<'tcx>, ) -> Self { Const::new_unevaluated(interner, is_rigid, uv) } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 60c2acdb4c4f9..f3794bab8716a 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -208,23 +208,20 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.adt_def(adt_def_id) } - fn unevaluated_const_kind_from_def_id( - self, - def_id: Self::DefId, - ) -> ty::UnevaluatedConstKind<'tcx> { + fn alias_const_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasConstKind<'tcx> { match self.def_kind(def_id) { DefKind::AssocConst { .. } => { if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - ty::UnevaluatedConstKind::Inherent { def_id } + ty::AliasConstKind::Inherent { def_id } } else { - ty::UnevaluatedConstKind::Projection { def_id } + ty::AliasConstKind::Projection { def_id } } } - DefKind::Const { .. } => ty::UnevaluatedConstKind::Free { def_id }, + DefKind::Const { .. } => ty::AliasConstKind::Free { def_id }, DefKind::AnonConst | DefKind::InlineConst | DefKind::Ctor(_, CtorKind::Const) => { - ty::UnevaluatedConstKind::Anon { def_id } + ty::AliasConstKind::Anon { def_id } } - kind => bug!("unexpected DefKind in UnevaluatedConst: {kind:?}"), + kind => bug!("unexpected DefKind in AliasConst: {kind:?}"), } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3ea798ee45fb2..857c5a8c06282 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -76,9 +76,9 @@ pub use self::closure::{ place_to_string_for_capture, }; pub use self::consts::{ - AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, Expr, ExprKind, - LitToConstInput, ScalarInt, SimdAlign, UnevaluatedConst, UnevaluatedConstKind, ValTree, - ValTreeKindExt, Value, const_lit_matches_ty, + AliasConst, AliasConstKind, AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, + Expr, ExprKind, LitToConstInput, ScalarInt, SimdAlign, ValTree, ValTreeKindExt, Value, + const_lit_matches_ty, }; pub use self::context::{ CtxtInterners, CurrentGcx, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, tls, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c1f96c3a94fdd..15a915e2d64a4 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1568,14 +1568,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } match ct.kind() { - ty::ConstKind::Unevaluated(_, ty::UnevaluatedConst { kind, args, .. }) => { + ty::ConstKind::Unevaluated(_, ty::AliasConst { kind, args, .. }) => { match kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } => { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } => { self.pretty_print_value_path(def_id, args)?; } - ty::UnevaluatedConstKind::Anon { def_id } => { + ty::AliasConstKind::Anon { def_id } => { if def_id.is_local() && let span = self.tcx().def_span(def_id) && let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index f0f7e166e9672..1a5c05189611e 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -72,9 +72,9 @@ pub(crate) fn as_constant_inner<'tcx>( ExprKind::NamedConst { def_id, args, ref user_ty } => { let user_ty = user_ty.as_ref().and_then(push_cuta); if tcx.is_type_const(def_id) { - let uneval = ty::UnevaluatedConst::new( + let uneval = ty::AliasConst::new( tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, def_id), + ty::AliasConstKind::new_from_def_id(tcx, def_id), args, ); let ct = ty::Const::new_unevaluated(tcx, ty::IsRigid::No, uneval); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index a9c194959b28a..939f60e4f3334 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -78,16 +78,16 @@ impl<'tcx> ConstToPat<'tcx> { /// We errored. Signal that in the pattern, so that follow up errors can be silenced. fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box> { if let ty::ConstKind::Unevaluated(_, uv) = self.c.kind() { - if let ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } = uv.kind + if let ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } = uv.kind && let Some(def_id) = def_id.as_local() { // Include the container item in the output. err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), ""); } - if let ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } = uv.kind + if let ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } = uv.kind { err.span_label(self.tcx.def_span(def_id), msg!("constant defined here")); } @@ -95,11 +95,7 @@ impl<'tcx> ConstToPat<'tcx> { Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()), extra: None }) } - fn unevaluated_to_pat( - &mut self, - uv: ty::UnevaluatedConst<'tcx>, - ty: Ty<'tcx>, - ) -> Box> { + fn unevaluated_to_pat(&mut self, uv: ty::AliasConst<'tcx>, ty: Ty<'tcx>) -> Box> { // It's not *technically* correct to be revealing opaque types here as borrowcheck has // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). @@ -141,9 +137,9 @@ impl<'tcx> ConstToPat<'tcx> { // We've emitted an error on the original const, it would be redundant to complain // on its use as well. if let ty::ConstKind::Unevaluated(_, uv) = self.c.kind() - && let ty::UnevaluatedConstKind::Projection { .. } - | ty::UnevaluatedConstKind::Inherent { .. } - | ty::UnevaluatedConstKind::Free { .. } = uv.kind + && let ty::AliasConstKind::Projection { .. } + | ty::AliasConstKind::Inherent { .. } + | ty::AliasConstKind::Free { .. } = uv.kind { err.downgrade_to_delayed_bug(); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 4bf69b3a85520..32aec21a46649 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -660,9 +660,9 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { let c = ty::Const::new_unevaluated( self.tcx, ty::IsRigid::No, - ty::UnevaluatedConst::new( + ty::AliasConst::new( self.tcx, - ty::UnevaluatedConstKind::new_from_def_id(self.tcx, def_id), + ty::AliasConstKind::new_from_def_id(self.tcx, def_id), args, ), ); diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 8fd7d6d0471c7..d7c7585cb8f88 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -37,7 +37,7 @@ pub trait SolverDelegate: Deref + Sized { fn evaluate_const( &self, param_env: ::ParamEnv, - uv: ty::UnevaluatedConst, + uv: ty::AliasConst, ) -> Option<::Const>; // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`! diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 05532cdb8924b..3cd8dbb7bf388 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1439,7 +1439,7 @@ where pub(super) fn evaluate_const( &mut self, param_env: I::ParamEnv, - uv: ty::UnevaluatedConst, + uv: ty::AliasConst, ) -> Result, RerunNonErased> { if self.typing_mode().is_erased_not_coherence() { self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)?; @@ -1453,7 +1453,7 @@ where param_env: I::ParamEnv, projection_term: ty::AliasTerm, expected_term: I::Term, - uv: ty::UnevaluatedConst, + uv: ty::AliasConst, ) -> QueryResultOrRerunNonErased { match self.evaluate_const(param_env, uv)? { Some(evaluated) => { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index e0290dfbdebdf..b2bb0099fd0f8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -454,9 +454,9 @@ where c.into() } ty::AliasTermKind::ProjectionConst { .. } => { - let uv = ty::UnevaluatedConst::new( + let uv = ty::AliasConst::new( cx, - ty::UnevaluatedConstKind::Projection { + ty::AliasConstKind::Projection { def_id: target_item_def_id.into().try_into().unwrap(), }, target_args, diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index ed4489636f2d8..03223f0a7845a 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -698,11 +698,11 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // We may still encounter unevaluated consts due to the printing // logic sometimes passing identity-substituted impl headers. - ty::ConstKind::Unevaluated(_, ty::UnevaluatedConst { kind, args, .. }) => match kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { + ty::ConstKind::Unevaluated(_, ty::AliasConst { kind, args, .. }) => match kind { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => { return self.print_def_path(def_id, args); } }, diff --git a/compiler/rustc_trait_selection/src/diagnostics.rs b/compiler/rustc_trait_selection/src/diagnostics.rs index 662480587fdd2..c85575bf2527e 100644 --- a/compiler/rustc_trait_selection/src/diagnostics.rs +++ b/compiler/rustc_trait_selection/src/diagnostics.rs @@ -25,7 +25,7 @@ pub(crate) mod note_and_explain; pub(crate) struct UnableToConstructConstantValue<'a> { #[primary_span] pub span: Span, - pub unevaluated: ty::UnevaluatedConst<'a>, + pub unevaluated: ty::AliasConst<'a>, } pub(crate) struct NegativePositiveConflict<'tcx> { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index f907b4ba19122..e104a6b1db794 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -218,7 +218,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn evaluate_const( &self, param_env: ty::ParamEnv<'tcx>, - uv: ty::UnevaluatedConst<'tcx>, + uv: ty::AliasConst<'tcx>, ) -> Option> { let ct = ty::Const::new_unevaluated(self.tcx, ty::IsRigid::No, uv); diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 16009e5295f27..fe3dcf92050fb 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -44,10 +44,10 @@ pub fn is_const_evaluatable<'tcx>( let is_anon_ct = matches!( ct.kind(), - ty::ConstKind::Unevaluated( - _, - ty::UnevaluatedConst { kind: ty::UnevaluatedConstKind::Anon { .. }, .. } - ) + ty::ConstKind::Unevaluated(_, ty::AliasConst { + kind: ty::AliasConstKind::Anon { .. }, + .. + }) ); if !is_anon_ct { diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 36a562ab862fa..a306690d5b316 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -886,14 +886,11 @@ impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { let ct = self.tcx.expand_abstract_consts(ct); match ct.kind() { - ty::ConstKind::Unevaluated( - _, - ty::UnevaluatedConst { - kind: ty::UnevaluatedConstKind::Projection { def_id }, - args, - .. - }, - ) if self.tcx.features().min_generic_const_args() => { + ty::ConstKind::Unevaluated(_, ty::AliasConst { + kind: ty::AliasConstKind::Projection { def_id }, + args, + .. + }) if self.tcx.features().min_generic_const_args() => { match self.allow_self_projections { AllowSelfProjections::Yes => { let trait_def_id = self.tcx.parent(def_id); diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index ea2f17d266b78..03299ae191f2f 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -717,15 +717,13 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2); match (c1.kind(), c2.kind()) { - ( - ty::ConstKind::Unevaluated(_, a), - ty::ConstKind::Unevaluated(_, b), - ) if a.kind == b.kind - && matches!( - a.kind, - ty::UnevaluatedConstKind::Projection { .. } - | ty::UnevaluatedConstKind::Inherent { .. } - ) => + (ty::ConstKind::Unevaluated(_, a), ty::ConstKind::Unevaluated(_, b)) + if a.kind == b.kind + && matches!( + a.kind, + ty::AliasConstKind::Projection { .. } + | ty::AliasConstKind::Inherent { .. } + ) => { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index c062198861ebe..3cc15e3d31772 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -370,7 +370,7 @@ pub fn normalize_param_env_or_error<'tcx>( // const arguments that have a non-empty param env are array repeat counts. These // do not appear in the type system though. if let ty::ConstKind::Unevaluated(_, uv) = c.kind() - && matches!(uv.kind, ty::UnevaluatedConstKind::Anon { .. }) + && matches!(uv.kind, ty::AliasConstKind::Anon { .. }) { let infcx = self.0.infer_ctxt().build(TypingMode::non_body_analysis()); let c = evaluate_const(&infcx, c, ty::ParamEnv::empty()); @@ -560,7 +560,7 @@ pub enum EvaluateConstErr { } // FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine -// FIXME(generic_const_exprs): Consider accepting a `ty::UnevaluatedConst` when we are not rolling our own +// FIXME(generic_const_exprs): Consider accepting a `ty::AliasConst` when we are not rolling our own // normalization scheme /// Evaluates a type system constant returning a `ConstKind::Error` in cases where CTFE failed and /// returning the passed in constant if it was not fully concrete (i.e. depended on generic parameters @@ -583,7 +583,7 @@ pub fn evaluate_const<'tcx>( } // FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine -// FIXME(generic_const_exprs): Consider accepting a `ty::UnevaluatedConst` when we are not rolling our own +// FIXME(generic_const_exprs): Consider accepting a `ty::AliasConst` when we are not rolling our own // normalization scheme /// Evaluates a type system constant making sure to not allow constants that depend on generic parameters /// or inference variables to succeed in evaluating. @@ -610,9 +610,7 @@ pub fn try_evaluate_const<'tcx>( | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), ty::ConstKind::Unevaluated(_, uv) => { let opt_anon_const_kind = match uv.kind { - ty::UnevaluatedConstKind::Anon { def_id } => { - Some((def_id, tcx.anon_const_kind(def_id))) - } + ty::AliasConstKind::Anon { def_id } => Some((def_id, tcx.anon_const_kind(def_id))), _ => None, }; @@ -721,7 +719,7 @@ pub fn try_evaluate_const<'tcx>( } }; - let uv = ty::UnevaluatedConst::new(tcx, uv.kind, args); + let uv = ty::AliasConst::new(tcx, uv.kind, args); let erased_uv = tcx.erase_and_anonymize_regions(uv); use rustc_middle::mir::interpret::ErrorHandled; diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index f212079c76fbe..3f7fac5278f2d 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -488,16 +488,14 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx // if it was marked with `type const`. Using this attribute without the mgca // feature gate causes a parse error. let ct = match uv.kind { - ty::UnevaluatedConstKind::Projection { .. } => { + ty::AliasConstKind::Projection { .. } => { self.normalize_trait_projection(uv.into()).expect_const() } - ty::UnevaluatedConstKind::Inherent { .. } => { + ty::AliasConstKind::Inherent { .. } => { self.normalize_inherent_projection(uv.into()).expect_const() } - ty::UnevaluatedConstKind::Free { .. } => { - self.normalize_free_alias(uv.into()).expect_const() - } - ty::UnevaluatedConstKind::Anon { .. } => { + ty::AliasConstKind::Free { .. } => self.normalize_free_alias(uv.into()).expect_const(), + ty::AliasConstKind::Anon { .. } => { let ct = ct.super_fold_with(self); super::with_replaced_escaping_bound_vars( self.selcx.infcx, diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 45a3b5c85cdc8..84c5990777bf3 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -278,14 +278,12 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { }; let constant = match uv.kind { - ty::UnevaluatedConstKind::Anon { .. } => { - crate::traits::with_replaced_escaping_bound_vars( - self.infcx, - &mut self.universes, - constant, - |constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env), - ) - } + ty::AliasConstKind::Anon { .. } => crate::traits::with_replaced_escaping_bound_vars( + self.infcx, + &mut self.universes, + constant, + |constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env), + ), _ => self.try_fold_free_or_assoc(uv.into())?.expect_const(), }; debug!(?constant, ?self.param_env); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e33311ed07719..e35ca3747ad7c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -882,15 +882,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); match (c1.kind(), c2.kind()) { - ( - ty::ConstKind::Unevaluated(_, a), - ty::ConstKind::Unevaluated(_, b), - ) if a.kind == b.kind - && matches!( - a.kind, - ty::UnevaluatedConstKind::Projection { .. } - | ty::UnevaluatedConstKind::Inherent { .. } - ) => + (ty::ConstKind::Unevaluated(_, a), ty::ConstKind::Unevaluated(_, b)) + if a.kind == b.kind + && matches!( + a.kind, + ty::AliasConstKind::Projection { .. } + | ty::AliasConstKind::Inherent { .. } + ) => { if let Ok(InferOk { obligations, value: () }) = self .infcx diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 8b1b2acc1f5a3..1c9e7251dc88a 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1082,13 +1082,13 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } match uv.kind { - ty::UnevaluatedConstKind::Inherent { .. } => { + ty::AliasConstKind::Inherent { .. } => { self.add_wf_preds_for_inherent_projection(uv.into()); return; // Subtree is handled by above function } - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => { let obligations = self.nominal_obligations(def_id, uv.args); self.out.extend(obligations); } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 52964ae72f015..b6ea1322f2bc7 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -70,11 +70,8 @@ fn recurse_build<'tcx>( } &ExprKind::ZstLiteral { user_ty: _ } => ty::Const::zero_sized(tcx, node.ty), &ExprKind::NamedConst { def_id, args, user_ty: _ } => { - let uneval = ty::UnevaluatedConst::new( - tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, def_id), - args, - ); + let uneval = + ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, def_id), args); ty::Const::new_unevaluated(tcx, ty::IsRigid::No, uneval) } ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param), diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index f353b156757d9..46dd8ca5786cd 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -9,7 +9,7 @@ use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; -use crate::{self as ty, BoundVarIndexKind, Interner, UnevaluatedConst}; +use crate::{self as ty, AliasConst, BoundVarIndexKind, Interner}; /// Represents a constant in Rust. #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] @@ -34,7 +34,7 @@ pub enum ConstKind { /// An unnormalized const item such as an anon const or assoc const or free const item. /// Right now anything other than anon consts does not actually work properly but this /// should - Unevaluated(ty::IsRigid, ty::UnevaluatedConst), + Unevaluated(ty::IsRigid, ty::AliasConst), /// Used to hold computed value. Value(I::ValueConst), @@ -67,46 +67,42 @@ impl fmt::Debug for ConstKind { } } -impl UnevaluatedConst { +impl AliasConst { #[inline] - pub fn new( - interner: I, - kind: UnevaluatedConstKind, - args: I::GenericArgs, - ) -> UnevaluatedConst { + pub fn new(interner: I, kind: AliasConstKind, args: I::GenericArgs) -> AliasConst { if cfg!(debug_assertions) { let def_id = match kind { - ty::UnevaluatedConstKind::Projection { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Inherent { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Free { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Anon { def_id } => def_id.into(), + ty::AliasConstKind::Projection { def_id } => def_id.into(), + ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::Free { def_id } => def_id.into(), + ty::AliasConstKind::Anon { def_id } => def_id.into(), }; interner.debug_assert_args_compatible(def_id, args); } - UnevaluatedConst { kind, args, _use_alias_new_instead: () } + AliasConst { kind, args, _use_alias_new_instead: () } } pub fn type_of(self, interner: I) -> ty::Unnormalized { let def_id = match self.kind { - ty::UnevaluatedConstKind::Projection { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Inherent { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Free { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Anon { def_id } => def_id.into(), + ty::AliasConstKind::Projection { def_id } => def_id.into(), + ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::Free { def_id } => def_id.into(), + ty::AliasConstKind::Anon { def_id } => def_id.into(), }; interner.type_of(def_id).instantiate(interner, self.args) } } -/// UnevaluatedConstKind is extremely similar to AliasTyKind, and likely should be reasoned about +/// AliasConstKind is extremely similar to AliasTyKind, and likely should be reasoned about /// and handled in very similar ways. The documentation for AliasTyKind/etc. may be helpful when -/// learning about UnevaluatedConstKind. +/// learning about AliasConstKind. #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) )] -pub enum UnevaluatedConstKind { +pub enum AliasConstKind { /// A projection `::AssocConst` Projection { def_id: I::TraitAssocConstId }, /// An associated constant in an inherent `impl` @@ -117,35 +113,35 @@ pub enum UnevaluatedConstKind { Anon { def_id: I::UnevaluatedConstId }, } -impl UnevaluatedConstKind { +impl AliasConstKind { pub fn new_from_def_id(interner: I, def_id: I::DefId) -> Self { - interner.unevaluated_const_kind_from_def_id(def_id) + interner.alias_const_kind_from_def_id(def_id) } pub fn is_type_const(self, interner: I) -> bool { match self { - UnevaluatedConstKind::Projection { def_id } => interner.is_type_const(def_id.into()), - UnevaluatedConstKind::Inherent { def_id } => interner.is_type_const(def_id.into()), - UnevaluatedConstKind::Free { def_id } => interner.is_type_const(def_id.into()), - UnevaluatedConstKind::Anon { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Projection { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Inherent { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Free { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Anon { def_id } => interner.is_type_const(def_id.into()), } } pub fn def_span(self, interner: I) -> I::Span { match self { - UnevaluatedConstKind::Projection { def_id } => interner.def_span(def_id.into()), - UnevaluatedConstKind::Inherent { def_id } => interner.def_span(def_id.into()), - UnevaluatedConstKind::Free { def_id } => interner.def_span(def_id.into()), - UnevaluatedConstKind::Anon { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Projection { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Inherent { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Free { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Anon { def_id } => interner.def_span(def_id.into()), } } pub fn opt_def_id(self) -> Option { match self { - UnevaluatedConstKind::Projection { def_id } => Some(def_id.into()), - UnevaluatedConstKind::Inherent { def_id } => Some(def_id.into()), - UnevaluatedConstKind::Free { def_id } => Some(def_id.into()), - UnevaluatedConstKind::Anon { def_id } => Some(def_id.into()), + AliasConstKind::Projection { def_id } => Some(def_id.into()), + AliasConstKind::Inherent { def_id } => Some(def_id.into()), + AliasConstKind::Free { def_id } => Some(def_id.into()), + AliasConstKind::Anon { def_id } => Some(def_id.into()), } } } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 6263f4436abd4..74046b5342a78 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -280,7 +280,7 @@ pub trait Const>: fn new_placeholder(interner: I, param: ty::PlaceholderConst) -> Self; - fn new_unevaluated(interner: I, is_rigid: ty::IsRigid, uv: ty::UnevaluatedConst) -> Self; + fn new_unevaluated(interner: I, is_rigid: ty::IsRigid, uv: ty::AliasConst) -> Self; fn new_expr(interner: I, expr: I::ExprConst) -> Self; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 1c1fbcff0d928..454657011e71c 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -244,10 +244,7 @@ pub trait Interner: type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef; - fn unevaluated_const_kind_from_def_id( - self, - def_id: Self::DefId, - ) -> ty::UnevaluatedConstKind; + fn alias_const_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasConstKind; // FIXME: remove in favor of explicit construction fn alias_term_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasTermKind; diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index c6ab804d81c9f..f3bacff256d19 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -1,12 +1,12 @@ use std::fmt; +#[cfg(feature = "nightly")] +use crate::{AliasConst, ClosureKind}; use crate::{ AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, PatternKind, Placeholder, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, }; -#[cfg(feature = "nightly")] -use crate::{ClosureKind, UnevaluatedConst}; pub trait IrPrint { fn print(t: &T, fmt: &mut fmt::Formatter<'_>) -> fmt::Result; @@ -100,7 +100,7 @@ mod into_diag_arg_impls { } } - impl IntoDiagArg for UnevaluatedConst { + impl IntoDiagArg for AliasConst { fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { format!("{self:?}").into_diag_arg(path) } diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index d64cc2450eb19..b7655fd70002c 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -227,12 +227,12 @@ impl Relate for ty::AliasTy { } } -impl Relate for ty::UnevaluatedConst { +impl Relate for ty::AliasConst { fn relate>( relation: &mut R, - a: ty::UnevaluatedConst, - b: ty::UnevaluatedConst, - ) -> RelateResult> { + a: ty::AliasConst, + b: ty::AliasConst, + ) -> RelateResult> { let cx = relation.cx(); if a.kind != b.kind { Err(TypeError::ConstMismatch(ExpectedFound::new( @@ -245,7 +245,7 @@ impl Relate for ty::UnevaluatedConst { let args = relate_args_invariantly(relation, a.args, b.args)?; - Ok(ty::UnevaluatedConst::new(cx, a.kind, args)) + Ok(ty::AliasConst::new(cx, a.kind, args)) } } } diff --git a/compiler/rustc_type_ir/src/term_kind.rs b/compiler/rustc_type_ir/src/term_kind.rs index 8a6c2c8231adf..ae69283dc94ec 100644 --- a/compiler/rustc_type_ir/src/term_kind.rs +++ b/compiler/rustc_type_ir/src/term_kind.rs @@ -122,17 +122,17 @@ impl From> for AliasTermKind { } } -impl From> for AliasTermKind { - fn from(value: ty::UnevaluatedConstKind) -> Self { +impl From> for AliasTermKind { + fn from(value: ty::AliasConstKind) -> Self { match value { - ty::UnevaluatedConstKind::Projection { def_id } => { + ty::AliasConstKind::Projection { def_id } => { AliasTermKind::ProjectionConst { def_id } } - ty::UnevaluatedConstKind::Inherent { def_id } => { + ty::AliasConstKind::Inherent { def_id } => { AliasTermKind::InherentConst { def_id } } - ty::UnevaluatedConstKind::Free { def_id } => AliasTermKind::FreeConst { def_id }, - ty::UnevaluatedConstKind::Anon { def_id } => AliasTermKind::AnonConst { def_id }, + ty::AliasConstKind::Free { def_id } => AliasTermKind::FreeConst { def_id }, + ty::AliasConstKind::Anon { def_id } => AliasTermKind::AnonConst { def_id }, } } } @@ -189,24 +189,24 @@ impl AliasTerm { ty::AliasTy { kind, args: self.args, _use_alias_new_instead: () } } - pub fn expect_ct(self) -> ty::UnevaluatedConst { + pub fn expect_ct(self) -> ty::AliasConst { let kind = match self.kind { AliasTermKind::InherentConst { def_id } => { - ty::UnevaluatedConstKind::Inherent { def_id } + ty::AliasConstKind::Inherent { def_id } } - AliasTermKind::FreeConst { def_id } => ty::UnevaluatedConstKind::Free { def_id }, - AliasTermKind::AnonConst { def_id } => ty::UnevaluatedConstKind::Anon { def_id }, + AliasTermKind::FreeConst { def_id } => ty::AliasConstKind::Free { def_id }, + AliasTermKind::AnonConst { def_id } => ty::AliasConstKind::Anon { def_id }, AliasTermKind::ProjectionConst { def_id } => { - ty::UnevaluatedConstKind::Projection { def_id } + ty::AliasConstKind::Projection { def_id } } kind @ (AliasTermKind::ProjectionTy { .. } | AliasTermKind::InherentTy { .. } | AliasTermKind::OpaqueTy { .. } | AliasTermKind::FreeTy { .. }) => { - panic!("Cannot turn `{}` into `UnevaluatedConst`", kind.descr()) + panic!("Cannot turn `{}` into `AliasConst`", kind.descr()) } }; - ty::UnevaluatedConst { kind, args: self.args, _use_alias_new_instead: () } + ty::AliasConst { kind, args: self.args, _use_alias_new_instead: () } } pub fn to_term(self, interner: I, is_rigid: ty::IsRigid) -> I::Term { @@ -218,22 +218,22 @@ impl AliasTerm { I::Const::new_unevaluated( interner, is_rigid, - ty::UnevaluatedConst::new(interner, kind, self.args), + ty::AliasConst::new(interner, kind, self.args), ) .into() }; match self.kind { AliasTermKind::FreeConst { def_id } => { - unevaluated_const(ty::UnevaluatedConstKind::Free { def_id }) + unevaluated_const(ty::AliasConstKind::Free { def_id }) } AliasTermKind::InherentConst { def_id } => { - unevaluated_const(ty::UnevaluatedConstKind::Inherent { def_id }) + unevaluated_const(ty::AliasConstKind::Inherent { def_id }) } AliasTermKind::AnonConst { def_id } => { - unevaluated_const(ty::UnevaluatedConstKind::Anon { def_id }) + unevaluated_const(ty::AliasConstKind::Anon { def_id }) } AliasTermKind::ProjectionConst { def_id } => { - unevaluated_const(ty::UnevaluatedConstKind::Projection { def_id }) + unevaluated_const(ty::AliasConstKind::Projection { def_id }) } AliasTermKind::ProjectionTy { def_id } => alias_ty(ty::Projection { def_id }), AliasTermKind::InherentTy { def_id } => alias_ty(ty::Inherent { def_id }), @@ -367,8 +367,8 @@ impl From> for AliasTerm { } } -impl From> for AliasTerm { - fn from(ty: ty::UnevaluatedConst) -> Self { +impl From> for AliasTerm { + fn from(ty: ty::AliasConst) -> Self { AliasTerm { args: ty.args, kind: AliasTermKind::from(ty.kind), _use_alias_new_instead: () } } } diff --git a/compiler/rustc_type_ir/src/ty/alias.rs b/compiler/rustc_type_ir/src/ty/alias.rs index 0f99c8d787ee7..59abe1ae71150 100644 --- a/compiler/rustc_type_ir/src/ty/alias.rs +++ b/compiler/rustc_type_ir/src/ty/alias.rs @@ -5,7 +5,7 @@ use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; -use crate::{AliasTermKind, AliasTyKind, Interner, UnevaluatedConstKind}; +use crate::{AliasTermKind, AliasTyKind, Interner, AliasConstKind}; /// Represents an alias of a type, constant, or other term-like item. /// @@ -55,4 +55,4 @@ pub type ProjectionAliasTy = Alias::TraitAssocTyId>; pub type InherentAliasTy = Alias::InherentAssocTyId>; pub type OpaqueAliasTy = Alias::OpaqueTyId>; pub type FreeAliasTy = Alias::FreeTyAliasId>; -pub type UnevaluatedConst = Alias>; +pub type AliasConst = Alias>; diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index ec5c9a66d4b65..ae2ee299268bb 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -350,8 +350,8 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { match n.kind() { - ty::ConstKind::Unevaluated(_, ty::UnevaluatedConst { kind, .. }) => match kind { - ty::UnevaluatedConstKind::Projection { def_id } => { + ty::ConstKind::Unevaluated(_, ty::AliasConst { kind, .. }) => match kind { + ty::AliasConstKind::Projection { def_id } => { if let Some(local_def_id) = def_id.as_local() && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) { @@ -360,9 +360,9 @@ pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { n.to_string() } } - ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { + ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => { if let Some(local_def_id) = def_id.as_local() && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) { From 0af46a845c7513dc78882b17c3f277a2767e9ff7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Jun 2026 03:54:46 +0000 Subject: [PATCH 176/278] * ConstKind::Unevaluated -> ConstKind::Alias * Const::new_unevaluated / interner method -> new_alias * Interner::UnevaluatedConstId -> AnonConstId, since assoc type is only used for anonnymous consts --- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- .../rustc_const_eval/src/check_consts/qualifs.rs | 6 +++--- compiler/rustc_hir_analysis/src/check/check.rs | 2 +- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 4 ++-- .../rustc_hir_analysis/src/collect/generics_of.rs | 4 ++-- .../src/collect/predicates_of.rs | 2 +- .../src/constrained_generic_params.rs | 2 +- .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 10 +++++----- .../rustc_hir_analysis/src/variance/constraints.rs | 2 +- compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs | 2 +- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 2 +- compiler/rustc_infer/src/infer/freshen.rs | 2 +- compiler/rustc_infer/src/infer/mod.rs | 2 +- .../rustc_infer/src/infer/relate/generalize.rs | 14 +++++++------- compiler/rustc_middle/src/mir/consts.rs | 2 +- compiler/rustc_middle/src/mir/pretty.rs | 4 ++-- compiler/rustc_middle/src/ty/abstract_const.rs | 2 +- compiler/rustc_middle/src/ty/consts.rs | 10 +++++----- .../rustc_middle/src/ty/context/impl_interner.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 2 +- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- compiler/rustc_middle/src/ty/structural_impls.rs | 10 +++++----- compiler/rustc_middle/src/ty/visit.rs | 4 ++-- .../src/builder/expr/as_constant.rs | 2 +- .../src/thir/pattern/const_to_pat.rs | 10 +++++----- compiler/rustc_mir_build/src/thir/pattern/mod.rs | 2 +- .../src/impossible_predicates.rs | 2 +- compiler/rustc_mir_transform/src/inline.rs | 2 +- .../src/canonical/canonicalizer.rs | 2 +- compiler/rustc_next_trait_solver/src/normalize.rs | 2 +- compiler/rustc_next_trait_solver/src/solve/mod.rs | 6 +++--- .../rustc_public/src/unstable/convert/stable/ty.rs | 8 +++----- compiler/rustc_symbol_mangling/src/v0.rs | 4 ++-- compiler/rustc_trait_selection/src/diagnostics.rs | 4 ++-- .../src/error_reporting/infer/need_type_info.rs | 4 ++-- .../error_reporting/traits/fulfillment_errors.rs | 4 ++-- .../rustc_trait_selection/src/solve/delegate.rs | 2 +- .../src/solve/fulfill/derive_errors.rs | 2 +- .../rustc_trait_selection/src/solve/normalize.rs | 2 +- .../rustc_trait_selection/src/traits/auto_trait.rs | 6 +++--- .../src/traits/const_evaluatable.rs | 8 ++++---- .../src/traits/dyn_compatibility.rs | 2 +- .../rustc_trait_selection/src/traits/fulfill.rs | 12 ++++++------ compiler/rustc_trait_selection/src/traits/mod.rs | 12 ++++++------ .../rustc_trait_selection/src/traits/normalize.rs | 6 +++--- .../src/traits/query/normalize.rs | 6 +++--- .../rustc_trait_selection/src/traits/select/mod.rs | 10 +++++----- compiler/rustc_trait_selection/src/traits/wf.rs | 4 ++-- compiler/rustc_ty_utils/src/consts.rs | 2 +- compiler/rustc_ty_utils/src/layout.rs | 2 +- compiler/rustc_type_ir/src/const_kind.rs | 8 ++++---- compiler/rustc_type_ir/src/fast_reject.rs | 4 ++-- compiler/rustc_type_ir/src/flags.rs | 6 +++--- compiler/rustc_type_ir/src/inherent.rs | 4 ++-- compiler/rustc_type_ir/src/interner.rs | 12 ++++++------ compiler/rustc_type_ir/src/relate.rs | 12 ++++++------ compiler/rustc_type_ir/src/relate/combine.rs | 6 +++--- compiler/rustc_type_ir/src/walk.rs | 2 +- src/librustdoc/clean/utils.rs | 2 +- 59 files changed, 138 insertions(+), 140 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 4b4e6e0ac9ccd..f8c052857ac5f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1760,7 +1760,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let tcx = self.tcx(); let maybe_uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => match uv.kind { + ty::ConstKind::Alias(_, uv) => match uv.kind { ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Inherent { def_id } | ty::AliasConstKind::Free { def_id } diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 111ada0241116..d6578def58692 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -344,13 +344,13 @@ where let uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Param(_) | ty::ConstKind::Error(_) => None, - // Unevaluated consts in MIR bodies don't have associated MIR (e.g. `type const`). - ty::ConstKind::Unevaluated(_, _) => None, + // Alias consts in MIR bodies don't have associated MIR (e.g. `type const`). + ty::ConstKind::Alias(_, _) => None, // FIXME(mgca): Investigate whether using `None` for `ConstKind::Value` is overly // strict, and if instead we should be doing some kind of value-based analysis. ty::ConstKind::Value(_) => None, _ => bug!( - "expected ConstKind::Param, ConstKind::Value, ConstKind::Unevaluated, or ConstKind::Error here, found {:?}", + "expected ConstKind::Param, ConstKind::Value, ConstKind::Alias, or ConstKind::Error here, found {:?}", ct ), }, diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index e98307c7c7b18..b60f19ea8d30c 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -778,7 +778,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), if has_default { // need to store default and type of default let ct = tcx.const_param_default(param.def_id).skip_binder(); - if let ty::ConstKind::Unevaluated(_, uv) = ct.kind() + if let ty::ConstKind::Alias(_, uv) = ct.kind() && let Some(def_id) = uv.kind.opt_def_id() { tcx.ensure_ok().type_of(def_id); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index bf8593eb61882..c2b3861a7f476 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1488,7 +1488,7 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: // be sure if it will error or not as user might always specify the other. // FIXME(generic_const_exprs): This is incorrect when dealing with unused const params. // E.g: `struct Foo;`. Here, we should - // eagerly error but we don't as we have `ConstKind::Unevaluated(.., [N, M])`. + // eagerly error but we don't as we have `ConstKind::Alias(.., [N, M])`. if !default.has_param() { wfcx.register_wf_obligation( tcx.def_span(param.def_id), @@ -1509,7 +1509,7 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: | ty::ConstKind::Bound(_, _) => unreachable!(), ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => continue, ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Unevaluated(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), ty::ConstKind::Param(param_ct) => { param_ct.find_const_ty_from_env(wfcx.param_env) } diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 64f12fd6644c5..811ed83e0bf48 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -124,11 +124,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // ^ parent_def_id // // then we only want to return generics for params to the left of `N`. If we don't do that we - // end up with that const looking like: `ty::ConstKind::Unevaluated(def_id, args: [N#0])`. + // end up with that const looking like: `ty::ConstKind::Alias(def_id, args: [N#0])`. // // This causes ICEs (#86580) when building the args for Foo in `fn foo() -> Foo { .. }` as // we instantiate the defaults with the partially built args when we build the args. Instantiating - // the `N#0` on the unevaluated const indexes into the empty args we're in the process of building. + // the `N#0` on the alias const indexes into the empty args we're in the process of building. // // We fix this by having this function return the parent's generics ourselves and truncating the // generics to only include non-forward declared params (with the exception of the `Self` ty) diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index fe2b56d46135b..f62b2a224dbbd 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -433,7 +433,7 @@ fn const_evaluatable_predicates_of<'tcx>( impl<'tcx> TypeVisitor> for ConstCollector<'tcx> { fn visit_const(&mut self, c: ty::Const<'tcx>) { - if let ty::ConstKind::Unevaluated(_, uv) = c.kind() { + if let ty::ConstKind::Alias(_, uv) = c.kind() { if is_const_param_default(self.tcx, uv.kind) { // Do not look into const param defaults, // these get checked when they are actually instantiated. diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 46b2eb538f080..a392ff5d3a552 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -93,7 +93,7 @@ impl<'tcx> TypeVisitor> for ParameterCollector { fn visit_const(&mut self, c: ty::Const<'tcx>) { match c.kind() { - ty::ConstKind::Unevaluated(..) if !self.include_nonconstraining => { + ty::ConstKind::Alias(..) if !self.include_nonconstraining => { // Constant expressions are not injective in general. return; } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index deae42ebfbb53..1bdfefcf7032c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1402,7 +1402,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let Some(def_id) = alias_ct.kind.opt_def_id() { self.require_type_const_attribute(def_id, span)?; } - let ct = Const::new_unevaluated(tcx, ty::IsRigid::No, alias_ct); + let ct = Const::new_alias(tcx, ty::IsRigid::No, alias_ct); let ct = self.check_param_uses_if_mcg(ct, span, false); Ok(ct) } @@ -1870,7 +1870,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::AliasConstKind::new_from_def_id(tcx, item_def_id), item_args, ); - Ok(Const::new_unevaluated(tcx, ty::IsRigid::No, uv)) + Ok(Const::new_alias(tcx, ty::IsRigid::No, uv)) } /// Lower a [resolved][hir::QPath::Resolved] (type-level) associated item path. @@ -2772,7 +2772,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let _ = self .prohibit_generic_args(leading_segments.iter(), GenericsArgsErrExtend::None); let args = self.lower_generic_args_of_path_segment(span, did, segment); - ty::Const::new_unevaluated( + ty::Const::new_alias( tcx, ty::IsRigid::No, ty::AliasConst::new( @@ -2915,7 +2915,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.check_param_uses_if_mcg(ct, span, false) } - /// Literals are eagerly converted to a constant, everything else becomes `Unevaluated`. + /// Literals are eagerly converted to a constant, everything else becomes `ConstKind::Alias`. #[instrument(skip(self), level = "debug")] fn lower_const_arg_anon(&self, anon: &AnonConst) -> Const<'tcx> { let tcx = self.tcx(); @@ -2930,7 +2930,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { match self.try_lower_anon_const_lit(ty, expr) { Some(v) => v, - None => ty::Const::new_unevaluated( + None => ty::Const::new_alias( tcx, ty::IsRigid::No, ty::AliasConst::new( diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 095213322a9fd..d5a5c63de4515 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -408,7 +408,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { debug!("add_constraints_from_const(c={:?}, variance={:?})", c, variance); match &c.kind() { - ty::ConstKind::Unevaluated(_, uv) => { + ty::ConstKind::Alias(_, uv) => { self.add_constraints_from_invariant_args(current, uv.args, variance); } _ => {} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index c8c6de4f99c03..c94d278e99e56 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1453,7 +1453,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ct = self.resolve_vars_with_obligations(ct); if self.next_trait_solver() - && let ty::ConstKind::Unevaluated(..) = ct.kind() + && let ty::ConstKind::Alias(..) = ct.kind() { // We need to use a separate variable here as otherwise the temporary for // `self.fulfillment_cx.borrow_mut()` is alive in the `Err` branch, resulting diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 890110e431fa2..0ccda42515345 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -161,7 +161,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Unevaluated(_, _) => enforce_copy_bound(element, element_ty), + | ty::ConstKind::Alias(_, _) => enforce_copy_bound(element, element_ty), ty::ConstKind::Bound(_, _) | ty::ConstKind::Infer(_) | ty::ConstKind::Error(_) => { unreachable!() diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index c058bebddcf03..f0fd46e6cac74 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -158,7 +158,7 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Value(_) - | ty::ConstKind::Unevaluated(..) + | ty::ConstKind::Alias(..) | ty::ConstKind::Expr(..) | ty::ConstKind::Error(_) => ct.super_fold_with(self), } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 92be5dec28542..aa2fe2ee21dfa 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1266,7 +1266,7 @@ impl<'tcx> InferCtxt<'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Unevaluated(_, _) + | ty::ConstKind::Alias(_, _) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => ct, diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 7a9cb4b1690a9..bac0d62c50073 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -77,7 +77,7 @@ impl<'tcx> InferCtxt<'tcx> { /// would result in an infinite type as we continuously replace an inference variable /// in `ct` with `ct` itself. /// - /// This is especially important as unevaluated consts use their parents generics. + /// This is especially important as alias consts use their parents generics. /// They therefore often contain unused args, making these errors far more likely. /// /// A good example of this is the following: @@ -95,7 +95,7 @@ impl<'tcx> InferCtxt<'tcx> { /// } /// ``` /// - /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics + /// Here `3 + 4` ends up as `ConstKind::Alias` which uses the generics /// of `fn bind` (meaning that its args contain `N`). /// /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. @@ -112,8 +112,8 @@ impl<'tcx> InferCtxt<'tcx> { target_vid: ty::ConstVid, source_ct: ty::Const<'tcx>, ) -> RelateResult<'tcx, ()> { - // FIXME(generic_const_exprs): Occurs check failures for unevaluated - // constants and generic expressions are not yet handled correctly. + // FIXME(generic_const_exprs): Occurs check failures for alias consts + // and generic expressions are not yet handled correctly. debug_assert!( self.inner.borrow_mut().const_unification_table().probe_value(target_vid).is_unknown() ); @@ -752,7 +752,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { } } } - // FIXME: Unevaluated constants are also not rigid, so the current + // FIXME: Alias consts are also not rigid, so the current // approach of always relating them structurally is incomplete. // // FIXME: replace the StructurallyRelateAliases::Yes branch with @@ -760,7 +760,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { // // We only need to be careful with potentially normalizeable // aliases here. See `generalize_alias_term` for more information. - ty::ConstKind::Unevaluated(ty::IsRigid::No, uv) => { + ty::ConstKind::Alias(ty::IsRigid::No, uv) => { match self.structurally_relate_aliases { // Hack: Fall back to old behavior if GCE is enabled (it used to just be the Yes // path), as doing this new No path breaks some GCE things. I expect GCE to be @@ -776,7 +776,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { args, args, )?; - Ok(ty::Const::new_unevaluated( + Ok(ty::Const::new_alias( tcx, ty::IsRigid::No, ty::AliasConst::new(tcx, kind, args), diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 82ea4bcaa21f9..44c5e66279c14 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -221,7 +221,7 @@ pub enum Const<'tcx> { /// An unevaluated mir constant which is not part of the type system. /// - /// Note that `Ty(ty::ConstKind::Unevaluated)` and this variant are *not* identical! `Ty` will + /// Note that `Ty(ty::ConstKind::Alias)` and this variant are *not* identical! `Ty` will /// always flow through a valtree, so all data not captured in the valtree is lost. This variant /// directly uses the evaluated result of the given constant, including e.g. data stored in /// padding. diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index a6de25ac93f31..621a8d38a1c9b 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1491,14 +1491,14 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { let val = match const_ { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Param(p) => format!("ty::Param({p})"), - ty::ConstKind::Unevaluated(_, uv) => { + ty::ConstKind::Alias(_, uv) => { let kind = match uv.kind { ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Inherent { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => self.tcx.def_path_str(def_id), }; - format!("ty::Unevaluated({}, {:?})", kind, uv.args) + format!("ty::AliasConst({}, {:?})", kind, uv.args) } ty::ConstKind::Value(cv) => { format!("ty::Valtree({})", fmt_valtree(&cv)) diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 08a34fd397357..ebf4863e3590b 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -52,7 +52,7 @@ impl<'tcx> TyCtxt<'tcx> { } fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> { let ct = match c.kind() { - ty::ConstKind::Unevaluated(_, uv) + ty::ConstKind::Alias(_, uv) if let Some(def_id) = uv.kind.opt_def_id() => { match self.tcx.thir_abstract_const(def_id) { diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 8d28660676eff..6eaa468e57450 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -107,12 +107,12 @@ impl<'tcx> Const<'tcx> { } #[inline] - pub fn new_unevaluated( + pub fn new_alias( tcx: TyCtxt<'tcx>, is_rigid: ty::IsRigid, uv: ty::AliasConst<'tcx>, ) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Unevaluated(is_rigid, uv)) + Const::new(tcx, ty::ConstKind::Alias(is_rigid, uv)) } #[inline] @@ -157,7 +157,7 @@ impl<'tcx> Const<'tcx> { true } ty::ConstKind::Infer(_) - | ty::ConstKind::Unevaluated(..) + | ty::ConstKind::Alias(..) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => false, @@ -194,12 +194,12 @@ impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { Const::new_placeholder(tcx, placeholder) } - fn new_unevaluated( + fn new_alias( interner: TyCtxt<'tcx>, is_rigid: ty::IsRigid, uv: ty::AliasConst<'tcx>, ) -> Self { - Const::new_unevaluated(interner, is_rigid, uv) + Const::new_alias(interner, is_rigid, uv) } fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self { diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index f3794bab8716a..5819470765e69 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -41,7 +41,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type CoroutineId = DefId; type AdtId = DefId; type ImplId = DefId; - type UnevaluatedConstId = DefId; + type AnonConstId = DefId; type TraitAssocTyId = DefId; type TraitAssocConstId = DefId; type TraitAssocTermId = DefId; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 857c5a8c06282..7e81837b7d970 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -692,7 +692,7 @@ impl<'tcx> Term<'tcx> { _ => None, }, TermKind::Const(ct) => match ct.kind() { - ConstKind::Unevaluated(_, uv) => Some(uv.into()), + ConstKind::Alias(_, uv) => Some(uv.into()), _ => None, }, } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 15a915e2d64a4..a9258ba6c9a2d 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1568,7 +1568,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } match ct.kind() { - ty::ConstKind::Unevaluated(_, ty::AliasConst { kind, args, .. }) => { + ty::ConstKind::Alias(_, ty::AliasConst { kind, args, .. }) => { match kind { ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Inherent { def_id } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 0bcc10a760725..f973c9ba2c896 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -634,8 +634,8 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { folder: &mut F, ) -> Result { let kind = match self.kind() { - ConstKind::Unevaluated(is_rigid, uv) => { - ConstKind::Unevaluated(is_rigid, uv.try_fold_with(folder)?) + ConstKind::Alias(is_rigid, uv) => { + ConstKind::Alias(is_rigid, uv.try_fold_with(folder)?) } ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?), ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?), @@ -651,8 +651,8 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { fn super_fold_with>>(self, folder: &mut F) -> Self { let kind = match self.kind() { - ConstKind::Unevaluated(is_rigid, uv) => { - ConstKind::Unevaluated(is_rigid, uv.fold_with(folder)) + ConstKind::Alias(is_rigid, uv) => { + ConstKind::Alias(is_rigid, uv.fold_with(folder)) } ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)), ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)), @@ -670,7 +670,7 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { impl<'tcx> TypeSuperVisitable> for ty::Const<'tcx> { fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { match self.kind() { - ConstKind::Unevaluated(_, uv) => uv.visit_with(visitor), + ConstKind::Alias(_, uv) => uv.visit_with(visitor), ConstKind::Value(v) => v.visit_with(visitor), ConstKind::Expr(e) => e.visit_with(visitor), ConstKind::Error(e) => e.visit_with(visitor), diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 534629edd7895..620e6da6a8f88 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -200,10 +200,10 @@ impl<'tcx> TypeVisitor> for LateBoundRegionsCollector<'tcx> { fn visit_const(&mut self, c: ty::Const<'tcx>) { // if we are only looking for "constrained" region, we have to - // ignore the inputs of an unevaluated const, as they may not appear + // ignore the inputs of an alias const, as they may not appear // in the normalized form if self.just_constrained { - if let ty::ConstKind::Unevaluated(..) = c.kind() { + if let ty::ConstKind::Alias(..) = c.kind() { return; } } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 1a5c05189611e..caf4f8d295c8a 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -77,7 +77,7 @@ pub(crate) fn as_constant_inner<'tcx>( ty::AliasConstKind::new_from_def_id(tcx, def_id), args, ); - let ct = ty::Const::new_unevaluated(tcx, ty::IsRigid::No, uneval); + let ct = ty::Const::new_alias(tcx, ty::IsRigid::No, uneval); let const_ = Const::Ty(ty, ct); return ConstOperand { span, user_ty, const_ }; diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 939f60e4f3334..7a71411a1336f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -49,7 +49,7 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { let mut convert = ConstToPat::new(self, id, span, c); match c.kind() { - ty::ConstKind::Unevaluated(_, uv) => convert.unevaluated_to_pat(uv, ty), + ty::ConstKind::Alias(_, uv) => convert.alias_to_pat(uv, ty), ty::ConstKind::Value(value) => convert.valtree_to_pat(value), _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c), } @@ -77,7 +77,7 @@ impl<'tcx> ConstToPat<'tcx> { /// We errored. Signal that in the pattern, so that follow up errors can be silenced. fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box> { - if let ty::ConstKind::Unevaluated(_, uv) = self.c.kind() { + if let ty::ConstKind::Alias(_, uv) = self.c.kind() { if let ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Inherent { def_id } = uv.kind && let Some(def_id) = def_id.as_local() @@ -95,7 +95,7 @@ impl<'tcx> ConstToPat<'tcx> { Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()), extra: None }) } - fn unevaluated_to_pat(&mut self, uv: ty::AliasConst<'tcx>, ty: Ty<'tcx>) -> Box> { + fn alias_to_pat(&mut self, uv: ty::AliasConst<'tcx>, ty: Ty<'tcx>) -> Box> { // It's not *technically* correct to be revealing opaque types here as borrowcheck has // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). @@ -136,7 +136,7 @@ impl<'tcx> ConstToPat<'tcx> { self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span }); // We've emitted an error on the original const, it would be redundant to complain // on its use as well. - if let ty::ConstKind::Unevaluated(_, uv) = self.c.kind() + if let ty::ConstKind::Alias(_, uv) = self.c.kind() && let ty::AliasConstKind::Projection { .. } | ty::AliasConstKind::Inherent { .. } | ty::AliasConstKind::Free { .. } = uv.kind @@ -249,7 +249,7 @@ impl<'tcx> ConstToPat<'tcx> { // then error about that instead, because `TypeNotStructural` gives advice that is // relevant only when the problem is that `ty` does not derive `PartialEq`. // - // Note that this is a duplicate of a check in `unevaluated_to_pat()`, + // Note that this is a duplicate of a check in `alias_to_pat()`, // which we would run later if we weren’t emitting an error now. if possibly_inapplicable_derived_partial_eq && !has_impl { let mut err = diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 32aec21a46649..11b31cb3976c9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -657,7 +657,7 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { let args = self.typeck_results.node_args(id); // FIXME(mgca): we will need to special case IACs here to have type system compatible // generic args, instead of how we represent them in body expressions. - let c = ty::Const::new_unevaluated( + let c = ty::Const::new_alias( self.tcx, ty::IsRigid::No, ty::AliasConst::new( diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs index d099fc3b9ddeb..cc216c39b8599 100644 --- a/compiler/rustc_mir_transform/src/impossible_predicates.rs +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -44,7 +44,7 @@ pub(crate) fn has_impossible_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> bool !p.has_type_flags( // Only consider global clauses to simplify. TypeFlags::HAS_FREE_LOCAL_NAMES - // Clauses that refer to unevaluated constants as they cause cycles. + // Clauses that refer to alias constants as they cause cycles. | TypeFlags::HAS_CT_PROJECTION, ) }); diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 7c8bef2aa3fe6..c36b687111c01 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -1026,7 +1026,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( }); // Copy required constants from the callee_body into the caller_body. Although we are only - // pushing unevaluated consts to `required_consts`, here they may have been evaluated + // pushing constants that still need evaluation to `required_consts`, here they may have been evaluated // because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again. caller_body.required_consts.as_mut().unwrap().extend( callee_body.required_consts().into_iter().filter(|ct| ct.const_.is_required_const()), diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index c7697e5b78ecb..5aa09e1ba7932 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -565,7 +565,7 @@ impl, I: Interner> TypeFolder for Canonicaliz }, // FIXME: See comment above -- we could fold the region separately or something. ty::ConstKind::Bound(_, _) - | ty::ConstKind::Unevaluated(_, _) + | ty::ConstKind::Alias(_, _) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => return c.super_fold_with(self), diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index 1f9c4407af4c5..8e341d147ed35 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -231,7 +231,7 @@ where // With eager normalization, we should normalize the args of alias before // normalizing the alias itself. let ct = ct.try_super_fold_with(self)?; - let ty::ConstKind::Unevaluated(orig_is_rigid, uv) = ct.kind() else { return Ok(ct) }; + let ty::ConstKind::Alias(orig_is_rigid, uv) = ct.kind() else { return Ok(ct) }; // We support ambiguous aliases inside rigid alias. So we still recognize // the rigidness of the outer alias. if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 27efb582dafcf..6d36eb3740288 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -196,7 +196,7 @@ where Goal { param_env, predicate: ct }: Goal, ) -> QueryResultOrRerunNonErased { match ct.kind() { - ty::ConstKind::Unevaluated(ty::IsRigid::Yes, _) + ty::ConstKind::Alias(ty::IsRigid::Yes, _) | ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => { @@ -252,10 +252,10 @@ where .evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into); } - ty::ConstKind::Unevaluated(ty::IsRigid::Yes, uv) => { + ty::ConstKind::Alias(ty::IsRigid::Yes, uv) => { uv.type_of(self.cx()).skip_norm_wip() } - ty::ConstKind::Unevaluated(ty::IsRigid::No, _) => unimplemented!( + ty::ConstKind::Alias(ty::IsRigid::No, _) => unimplemented!( "non-rigid unevaluated constant for compute_const_arg_has_type_goal: {ct:?}" ), ty::ConstKind::Expr(_) => unimplemented!( diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 4d92660a1b8bb..b506963e790e5 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -552,12 +552,10 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { } } ty::ConstKind::Param(param) => crate::ty::TyConstKind::Param(param.stable(tables, cx)), - ty::ConstKind::Unevaluated(_, uv) => { + ty::ConstKind::Alias(_, uv) => { let Some(def_id) = uv.kind.opt_def_id() else { - // FIXME: implement (both AliasTy and UnevaluatedConst will be needing this soon) - panic!( - "non-defid unevaluated constants are not supported by rustc_public at the moment" - ) + // FIXME: implement (both AliasTy and AliasConst will be needing this soon) + panic!("non-defid alias consts are not supported by rustc_public at the moment") }; crate::ty::TyConstKind::Unevaluated( tables.const_def(def_id), diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 03223f0a7845a..b26545fa2b4b0 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -696,9 +696,9 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { return Ok(()); } - // We may still encounter unevaluated consts due to the printing + // We may still encounter alias consts due to the printing // logic sometimes passing identity-substituted impl headers. - ty::ConstKind::Unevaluated(_, ty::AliasConst { kind, args, .. }) => match kind { + ty::ConstKind::Alias(_, ty::AliasConst { kind, args, .. }) => match kind { ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Inherent { def_id } | ty::AliasConstKind::Free { def_id } diff --git a/compiler/rustc_trait_selection/src/diagnostics.rs b/compiler/rustc_trait_selection/src/diagnostics.rs index c85575bf2527e..ba89b0987659e 100644 --- a/compiler/rustc_trait_selection/src/diagnostics.rs +++ b/compiler/rustc_trait_selection/src/diagnostics.rs @@ -21,11 +21,11 @@ use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlig pub(crate) mod note_and_explain; #[derive(Diagnostic)] -#[diag("unable to construct a constant value for the unevaluated constant {$unevaluated}")] +#[diag("unable to construct a constant value for the alias const {$alias_const}")] pub(crate) struct UnableToConstructConstantValue<'a> { #[primary_span] pub span: Span, - pub unevaluated: ty::AliasConst<'a>, + pub alias_const: ty::AliasConst<'a>, } pub(crate) struct NegativePositiveConflict<'tcx> { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 333f6f01c0ef1..624aef89911f7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -1079,9 +1079,9 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { } } GenericArgKind::Const(ct) => { - if matches!(ct.kind(), ty::ConstKind::Unevaluated(..)) { + if matches!(ct.kind(), ty::ConstKind::Alias(..)) { // You can't write the generic arguments for - // unevaluated constants. + // alias constants. walker.skip_current_subtree(); } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d85a9c9d57ef8..69fe997339eb6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -3769,7 +3769,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { match obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => match ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => { + ty::ConstKind::Alias(_, uv) => { let mut err = self.dcx().struct_span_err(span, "unconstrained generic constant"); let const_span = uv.kind.def_span(self.tcx); @@ -3812,7 +3812,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Ok(err) } _ => { - bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); + bug!("const evaluatable failed for non-alias const `{ct:?}`"); } }, _ => { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index e104a6b1db794..f6f4caeb75121 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -220,7 +220,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< param_env: ty::ParamEnv<'tcx>, uv: ty::AliasConst<'tcx>, ) -> Option> { - let ct = ty::Const::new_unevaluated(self.tcx, ty::IsRigid::No, uv); + let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, uv); match crate::traits::try_evaluate_const(&self.0, ct, param_env) { Ok(ct) => Some(ct), diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 0d3e2500d0e05..d303adb7ebe2c 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -34,7 +34,7 @@ pub(super) fn fulfillment_error_for_no_solution<'tcx>( } ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => { let ct_ty = match ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), ty::ConstKind::Param(param_ct) => { param_ct.find_const_ty_from_env(obligation.param_env) } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 56c89773a6285..13c68ec7280c1 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -146,7 +146,7 @@ impl<'me, 'tcx> TypeFolder> for ReplaceAliasWithInfer<'me, 'tcx> { } let ct = ct.super_fold_with(self); - let ty::ConstKind::Unevaluated(orig_is_rigid, uv) = ct.kind() else { return ct }; + let ty::ConstKind::Alias(orig_is_rigid, uv) = ct.kind() else { return ct }; if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { return ct; } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 6d9c11789f2b7..a948d2b8cff80 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -774,15 +774,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { let evaluate = |c: ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(_, unevaluated) = c.kind() { + if let ty::ConstKind::Alias(_, unevaluated) = c.kind() { let ct = super::try_evaluate_const(selcx.infcx, c, obligation.param_env); if let Err(EvaluateConstErr::InvalidConstParamTy(_)) = ct { - let span = unevaluated.kind.def_span(self.tcx); + let span = alias_const.kind.def_span(self.tcx); self.tcx .dcx() - .emit_err(UnableToConstructConstantValue { span, unevaluated }); + .emit_err(UnableToConstructConstantValue { span, alias_const }); } ct diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index fe3dcf92050fb..8cb59c36514e9 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -30,7 +30,7 @@ pub fn is_const_evaluatable<'tcx>( ) -> Result<(), NotConstEvaluatable> { let tcx = infcx.tcx; match tcx.expand_abstract_consts(unexpanded_ct).kind() { - ty::ConstKind::Unevaluated(_, _) | ty::ConstKind::Expr(_) => (), + ty::ConstKind::Alias(_, _) | ty::ConstKind::Expr(_) => (), ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) @@ -44,7 +44,7 @@ pub fn is_const_evaluatable<'tcx>( let is_anon_ct = matches!( ct.kind(), - ty::ConstKind::Unevaluated(_, ty::AliasConst { + ty::ConstKind::Alias(_, ty::AliasConst { kind: ty::AliasConstKind::Anon { .. }, .. }) @@ -69,7 +69,7 @@ pub fn is_const_evaluatable<'tcx>( // here. tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported"); } - ty::ConstKind::Unevaluated(_, _) => { + ty::ConstKind::Alias(_, _) => { match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) { Err(EvaluateConstErr::HasGenericsOrInfers) => { Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug( @@ -94,7 +94,7 @@ pub fn is_const_evaluatable<'tcx>( Ok(()) } else { let uv = match unexpanded_ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => uv, + ty::ConstKind::Alias(_, uv) => uv, ty::ConstKind::Expr(_) => { bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled") } diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index a306690d5b316..82cd21ca64abb 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -886,7 +886,7 @@ impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { let ct = self.tcx.expand_abstract_consts(ct); match ct.kind() { - ty::ConstKind::Unevaluated(_, ty::AliasConst { + ty::ConstKind::Alias(_, ty::AliasConst { kind: ty::AliasConstKind::Projection { def_id }, args, .. diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 03299ae191f2f..164182dff3732 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -559,7 +559,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { return ProcessResult::Changed(PendingPredicateObligations::new()); } ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Unevaluated(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), // FIXME(generic_const_exprs): we should construct an alias like // `>::Output` when this is an `Expr` representing // `lhs + rhs`. @@ -717,7 +717,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2); match (c1.kind(), c2.kind()) { - (ty::ConstKind::Unevaluated(_, a), ty::ConstKind::Unevaluated(_, b)) + (ty::ConstKind::Alias(_, a), ty::ConstKind::Alias(_, b)) if a.kind == b.kind && matches!( a.kind, @@ -741,8 +741,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { )); } } - (_, ty::ConstKind::Unevaluated(_, _)) - | (ty::ConstKind::Unevaluated(_, _), _) => (), + (_, ty::ConstKind::Alias(_, _)) + | (ty::ConstKind::Alias(_, _), _) => (), (_, _) => { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) @@ -762,7 +762,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let stalled_on = &mut pending_obligation.stalled_on; let mut evaluate = |c: Const<'tcx>| { - if let ty::ConstKind::Unevaluated(_, unevaluated) = c.kind() { + if let ty::ConstKind::Alias(_, unevaluated) = c.kind() { match super::try_evaluate_const( self.selcx.infcx, c, @@ -771,7 +771,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { Ok(val) => Ok(val), e @ Err(EvaluateConstErr::HasGenericsOrInfers) => { stalled_on.extend( - unevaluated + alias_const .args .iter() .filter_map(TyOrConstInferVar::maybe_from_generic_arg), diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 3cc15e3d31772..aa0c9448a8056 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -369,7 +369,7 @@ pub fn normalize_param_env_or_error<'tcx>( // should actually be okay since without `feature(generic_const_exprs)` the only // const arguments that have a non-empty param env are array repeat counts. These // do not appear in the type system though. - if let ty::ConstKind::Unevaluated(_, uv) = c.kind() + if let ty::ConstKind::Alias(_, uv) = c.kind() && matches!(uv.kind, ty::AliasConstKind::Anon { .. }) { let infcx = self.0.infer_ctxt().build(TypingMode::non_body_analysis()); @@ -401,10 +401,10 @@ pub fn normalize_param_env_or_error<'tcx>( // we cannot prove `T: Trait`. // // The same thing is true for const generics- attempting to prove - // `T: Trait` with the same thing as a where clauses + // `T: Trait` with the same thing as a where clauses // will fail. After normalization we may be attempting to prove `T: Trait<4>` with - // the unnormalized where clause `T: Trait`. In order - // for the obligation to hold `4` must be equal to `ConstKind::Unevaluated(...)` + // the unnormalized where clause `T: Trait`. In order + // for the obligation to hold `4` must be equal to `ConstKind::Alias(...)` // but as we do not have lazy norm implemented, equating the two consts fails outright. // // Ideally we would not normalize consts here at all but it is required for backwards @@ -547,7 +547,7 @@ pub fn deeply_normalize_param_env_ignoring_regions<'tcx>( #[derive(Debug)] pub enum EvaluateConstErr { /// The constant being evaluated was either a generic parameter or inference variable, *or*, - /// some unevaluated constant with either generic parameters or inference variables in its + /// some alias const with either generic parameters or inference variables in its /// generic arguments. HasGenericsOrInfers, /// The type this constant evaluated to is not valid for use in const generics. This should @@ -608,7 +608,7 @@ pub fn try_evaluate_const<'tcx>( | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), - ty::ConstKind::Unevaluated(_, uv) => { + ty::ConstKind::Alias(_, uv) => { let opt_anon_const_kind = match uv.kind { ty::AliasConstKind::Anon { def_id } => Some((def_id, tcx.anon_const_kind(def_id))), _ => None, diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 3f7fac5278f2d..c97c5660bbade 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -469,14 +469,14 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx if tcx.features().generic_const_exprs() // Normalize type_const items even with feature `generic_const_exprs`. - && !matches!(ct.kind(), ty::ConstKind::Unevaluated(_, uv) if uv.kind.is_type_const(tcx)) + && !matches!(ct.kind(), ty::ConstKind::Alias(_, uv) if uv.kind.is_type_const(tcx)) || !needs_normalization(self.selcx.infcx, &ct) { return ct; } let uv = match ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => uv, + ty::ConstKind::Alias(_, uv) => uv, _ => return ct.super_fold_with(self), }; @@ -484,7 +484,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx // unless a `min_generic_const_args` feature gate error has already // been emitted earlier in compilation. // - // That's because we can only end up with an Unevaluated ty::Const for a const item + // That's because we can only end up with an Alias ty::Const for a const item // if it was marked with `type const`. Using this attribute without the mgca // feature gate causes a parse error. let ct = match uv.kind { diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 84c5990777bf3..93cd161cf566a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -273,7 +273,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { } let uv = match constant.kind() { - ty::ConstKind::Unevaluated(_, uv) => uv, + ty::ConstKind::Alias(_, uv) => uv, _ => return constant.try_super_fold_with(self), }; @@ -371,10 +371,10 @@ impl<'a, 'tcx> QueryNormalizer<'a, 'tcx> { result.normalized_term }; // `tcx.normalize_canonicalized_projection` may normalize to a type that - // still has unevaluated consts, so keep normalizing here if that's the case. + // still has alias consts, so keep normalizing here if that's the case. // Similarly, `tcx.normalize_canonicalized_free_alias` will only unwrap one layer // of type/const and we need to continue folding it to reveal the TAIT behind it - // or further normalize nested unevaluated consts. + // or further normalize nested alias consts. if res != term.to_term(tcx, ty::IsRigid::No) && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || matches!( diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e35ca3747ad7c..ce310dc693622 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -882,7 +882,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); match (c1.kind(), c2.kind()) { - (ty::ConstKind::Unevaluated(_, a), ty::ConstKind::Unevaluated(_, b)) + (ty::ConstKind::Alias(_, a), ty::ConstKind::Alias(_, b)) if a.kind == b.kind && matches!( a.kind, @@ -907,8 +907,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } } - (_, ty::ConstKind::Unevaluated(_, _)) - | (ty::ConstKind::Unevaluated(_, _), _) => (), + (_, ty::ConstKind::Alias(_, _)) + | (ty::ConstKind::Alias(_, _), _) => (), (_, _) => { if let Ok(InferOk { obligations, value: () }) = self .infcx @@ -927,7 +927,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let evaluate = |c: ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(_, _) = c.kind() { + if let ty::ConstKind::Alias(_, _) = c.kind() { match crate::traits::try_evaluate_const( self.infcx, c, @@ -987,7 +987,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::ConstKind::Error(_) => return Ok(EvaluatedToOk), ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Unevaluated(_, uv) => uv.type_of(self.tcx()).skip_norm_wip(), + ty::ConstKind::Alias(_, uv) => uv.type_of(self.tcx()).skip_norm_wip(), // FIXME(generic_const_exprs): See comment in `fulfill.rs` ty::ConstKind::Expr(_) => return Ok(EvaluatedToOk), ty::ConstKind::Placeholder(_) => { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 1c9e7251dc88a..095bf511cc87d 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1064,7 +1064,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { let tcx = self.tcx(); match c.kind() { - ty::ConstKind::Unevaluated(_, uv) => { + ty::ConstKind::Alias(_, uv) => { if !c.has_escaping_bound_vars() { // Skip type consts as mGCA doesn't support evaluatable clauses if !uv.kind.is_type_const(tcx) && !tcx.features().generic_const_args() { @@ -1111,7 +1111,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { ty::ConstKind::Expr(_) => { // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the // trait bound `typeof(N): Add` holds. This is currently unnecessary - // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` + // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Alias` // which means that the `DefId` would have been typeck'd elsewhere. However in // the future we may allow directly lowering to `ConstKind::Expr` in which case // we would not be proving bounds we should. diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index b6ea1322f2bc7..6f7a17c1e4a92 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -72,7 +72,7 @@ fn recurse_build<'tcx>( &ExprKind::NamedConst { def_id, args, user_ty: _ } => { let uneval = ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, def_id), args); - ty::Const::new_unevaluated(tcx, ty::IsRigid::No, uneval) + ty::Const::new_alias(tcx, ty::IsRigid::No, uneval) } ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param), diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 4126f9c887194..cdf280381247a 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -177,7 +177,7 @@ fn extract_const_value<'tcx>( } Err(error(cx, LayoutError::TooGeneric(ty))) } - ty::ConstKind::Unevaluated(_, _) => { + ty::ConstKind::Alias(_, _) => { let err = if ct.has_param() { LayoutError::TooGeneric(ty) } else { diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 46dd8ca5786cd..826a9e6d72db4 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -34,7 +34,7 @@ pub enum ConstKind { /// An unnormalized const item such as an anon const or assoc const or free const item. /// Right now anything other than anon consts does not actually work properly but this /// should - Unevaluated(ty::IsRigid, ty::AliasConst), + Alias(ty::IsRigid, ty::AliasConst), /// Used to hold computed value. Value(I::ValueConst), @@ -43,7 +43,7 @@ pub enum ConstKind { /// propagated to avoid useless error messages. Error(I::ErrorGuaranteed), - /// Unevaluated non-const-item, used by `feature(generic_const_exprs)` to represent + /// A non-const-item expression awaiting evaluation, used by `feature(generic_const_exprs)` to represent /// const arguments such as `N + 1` or `foo(N)` Expr(I::ExprConst), } @@ -59,7 +59,7 @@ impl fmt::Debug for ConstKind { Infer(var) => write!(f, "{var:?}"), Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var), Placeholder(placeholder) => write!(f, "{placeholder:?}"), - Unevaluated(is_rigid, uv) => write!(f, "Unevaluated({is_rigid:?}, {uv:?})"), + Alias(is_rigid, uv) => write!(f, "Unevaluated({is_rigid:?}, {uv:?})"), Value(val) => write!(f, "{val:?}"), Error(_) => write!(f, "{{const error}}"), Expr(expr) => write!(f, "{expr:?}"), @@ -110,7 +110,7 @@ pub enum AliasConstKind { /// A free constant, outside an impl block. Free { def_id: I::FreeConstAliasId }, /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`. - Anon { def_id: I::UnevaluatedConstId }, + Anon { def_id: I::AnonConstId }, } impl AliasConstKind { diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 14ebffb70b8bf..e74c4d2d9333c 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -469,7 +469,7 @@ impl { @@ -500,7 +500,7 @@ impl { + ty::ConstKind::Expr(_) | ty::ConstKind::Alias(_, _) | ty::ConstKind::Error(_) => { true } diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 1749ec99cad4e..e756caa22a71e 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -79,10 +79,10 @@ bitflags::bitflags! { const HAS_TY_OPAQUE = 1 << 12; /// Does this have `Inherent`? const HAS_TY_INHERENT = 1 << 13; - /// Does this have `ConstKind::Unevaluated`? + /// Does this have `ConstKind::Alias`? const HAS_CT_PROJECTION = 1 << 14; - /// Does this have `Alias` or `ConstKind::Unevaluated`? + /// Does this have `Alias` or `ConstKind::Alias`? /// /// Rephrased, could this term be normalized further? const HAS_ALIAS = TypeFlags::HAS_TY_PROJECTION.bits() @@ -477,7 +477,7 @@ impl FlagComputation { fn add_const_kind(&mut self, c: &ty::ConstKind) { match *c { - ty::ConstKind::Unevaluated(is_rigid, uv) => { + ty::ConstKind::Alias(is_rigid, uv) => { self.add_is_rigid(is_rigid); self.add_args(uv.args.as_slice()); self.add_flags(TypeFlags::HAS_CT_PROJECTION); diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 74046b5342a78..f2c5ef8d52271 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -280,7 +280,7 @@ pub trait Const>: fn new_placeholder(interner: I, param: ty::PlaceholderConst) -> Self; - fn new_unevaluated(interner: I, is_rigid: ty::IsRigid, uv: ty::AliasConst) -> Self; + fn new_alias(interner: I, is_rigid: ty::IsRigid, uv: ty::AliasConst) -> Self; fn new_expr(interner: I, expr: I::ExprConst) -> Self; @@ -411,7 +411,7 @@ pub trait Term>: _ => None, }, ty::TermKind::Const(ct) => match ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => Some(uv.into()), + ty::ConstKind::Alias(_, uv) => Some(uv.into()), _ => None, }, } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 454657011e71c..ce890d1259a3a 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -56,13 +56,13 @@ pub trait Interner: type CoroutineId: SpecificDefId; type AdtId: SpecificDefId; type ImplId: SpecificDefId; - type UnevaluatedConstId: SpecificDefId; + type AnonConstId: SpecificDefId; type TraitAssocTyId: SpecificDefId + Into + TryFrom; type TraitAssocConstId: SpecificDefId + Into - + Into + + Into + TryFrom; type TraitAssocTermId: SpecificDefId; type OpaqueTyId: SpecificDefId; @@ -76,17 +76,17 @@ pub trait Interner: + TypeFoldable; type FreeTyAliasId: SpecificDefId + Into; type FreeConstAliasId: SpecificDefId - + Into + + Into + Into; type FreeTermAliasId: SpecificDefId; type ImplOrTraitAssocTyId: SpecificDefId + Into; type ImplOrTraitAssocConstId: SpecificDefId - + Into + + Into + Into; type ImplOrTraitAssocTermId: SpecificDefId; type InherentAssocTyId: SpecificDefId + Into; type InherentAssocConstId: SpecificDefId - + Into + + Into + Into; type InherentAssocTermId: SpecificDefId; type Span: Span; @@ -516,7 +516,7 @@ declare_lift_into! { TraitId, Ty, Tys, - UnevaluatedConstId, + AnonConstId, } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index b7655fd70002c..6e7dea661bac1 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -236,8 +236,8 @@ impl Relate for ty::AliasConst { let cx = relation.cx(); if a.kind != b.kind { Err(TypeError::ConstMismatch(ExpectedFound::new( - Const::new_unevaluated(cx, ty::IsRigid::yes_if_next_solver(cx), a), - Const::new_unevaluated(cx, ty::IsRigid::yes_if_next_solver(cx), b), + Const::new_alias(cx, ty::IsRigid::yes_if_next_solver(cx), a), + Const::new_alias(cx, ty::IsRigid::yes_if_next_solver(cx), b), ))) } else { // FIXME(mgca): remove this @@ -546,7 +546,7 @@ pub fn structurally_relate_tys>( } /// Relates `a` and `b` structurally, calling the relation for all nested values. -/// Any semantic equality, e.g. of unevaluated consts, and inference variables have +/// Any semantic equality, e.g. of alias consts, and inference variables have /// to be handled by the caller. /// /// FIXME: This is not totally structural, which probably should be fixed. @@ -615,13 +615,13 @@ pub fn structurally_relate_consts>( // and is the better alternative to waiting until `generic_const_exprs` can // be stabilized. ( - ty::ConstKind::Unevaluated(is_rigid_a, au), - ty::ConstKind::Unevaluated(is_rigid_b, bu), + ty::ConstKind::Alias(is_rigid_a, au), + ty::ConstKind::Alias(is_rigid_b, bu), ) => { // Users shouldn't know about this so the mismatch should be caught // during development rather than presented as type error. debug_assert_eq!(is_rigid_a, is_rigid_b, "{a:?} != {b:?}"); - return Ok(Const::new_unevaluated(cx, is_rigid_a, relation.relate(au, bu)?)); + return Ok(Const::new_alias(cx, is_rigid_a, relation.relate(au, bu)?)); } (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => { let expr = relation.relate(ae, be)?; diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 76fa888147971..0c3e82b4e6102 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -240,13 +240,13 @@ where } ( - ty::ConstKind::Unevaluated(ty::IsRigid::Yes, _), - ty::ConstKind::Unevaluated(ty::IsRigid::Yes, _), + ty::ConstKind::Alias(ty::IsRigid::Yes, _), + ty::ConstKind::Alias(ty::IsRigid::Yes, _), ) if (infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver()) => { structurally_relate_consts(relation, a, b) } - (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..)) + (ty::ConstKind::Alias(..), _) | (_, ty::ConstKind::Alias(..)) if infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver() => { match relation.structurally_relate_aliases() { diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index 196c14d5197eb..d2442358b13cb 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -160,7 +160,7 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg ty::ConstKind::Value(cv) => stack.push(cv.ty().into()), ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()), - ty::ConstKind::Unevaluated(_, ct) => { + ty::ConstKind::Alias(_, ct) => { stack.extend(ct.args.iter().rev()); } }, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index ae2ee299268bb..554bcb2621ac6 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -350,7 +350,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { match n.kind() { - ty::ConstKind::Unevaluated(_, ty::AliasConst { kind, .. }) => match kind { + ty::ConstKind::Alias(_, ty::AliasConst { kind, .. }) => match kind { ty::AliasConstKind::Projection { def_id } => { if let Some(local_def_id) = def_id.as_local() && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) From 6b772f576ef1edce0ea1246157768ba52dbf801b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Jun 2026 04:25:25 +0000 Subject: [PATCH 177/278] Correct the type_const_recursive error message to be not vague --- tests/ui/const-generics/mgca/cyclic-type-const-151251.rs | 2 +- tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr | 2 +- tests/ui/const-generics/mgca/type_const-recursive.rs | 2 +- tests/ui/const-generics/mgca/type_const-recursive.stderr | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs b/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs index a7e46ad877e72..b19e1af69a53d 100644 --- a/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs +++ b/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs @@ -5,6 +5,6 @@ #![expect(incomplete_features)] type const A: u8 = A; -//~^ ERROR overflow normalizing the unevaluated constant `A` +//~^ ERROR overflow normalizing the alias const `A` fn main() {} diff --git a/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr b/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr index 47653dd1896f7..dc2858c032612 100644 --- a/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr +++ b/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow normalizing the unevaluated constant `A` +error[E0275]: overflow normalizing the alias const `A` --> $DIR/cyclic-type-const-151251.rs:7:1 | LL | type const A: u8 = A; diff --git a/tests/ui/const-generics/mgca/type_const-recursive.rs b/tests/ui/const-generics/mgca/type_const-recursive.rs index ebaab51bbc3b6..8222071faf2a3 100644 --- a/tests/ui/const-generics/mgca/type_const-recursive.rs +++ b/tests/ui/const-generics/mgca/type_const-recursive.rs @@ -3,6 +3,6 @@ type const A: u8 = A; -//~^ ERROR: overflow normalizing the unevaluated constant `A` [E0275] +//~^ ERROR: overflow normalizing the alias const `A` [E0275] fn main() {} diff --git a/tests/ui/const-generics/mgca/type_const-recursive.stderr b/tests/ui/const-generics/mgca/type_const-recursive.stderr index d21ccb22bc90b..892409c5f0057 100644 --- a/tests/ui/const-generics/mgca/type_const-recursive.stderr +++ b/tests/ui/const-generics/mgca/type_const-recursive.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow normalizing the unevaluated constant `A` +error[E0275]: overflow normalizing the alias const `A` --> $DIR/type_const-recursive.rs:5:1 | LL | type const A: u8 = A; From 40fc3c54f6163c028ff4a17e2ac876784eed4fb7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Jun 2026 06:54:15 +0000 Subject: [PATCH 178/278] Remove unused bounds in interners --- compiler/rustc_type_ir/src/interner.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index ce890d1259a3a..6fd19f9362a9b 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -62,7 +62,6 @@ pub trait Interner: + TryFrom; type TraitAssocConstId: SpecificDefId + Into - + Into + TryFrom; type TraitAssocTermId: SpecificDefId; type OpaqueTyId: SpecificDefId; @@ -75,19 +74,13 @@ pub trait Interner: + Into + TypeFoldable; type FreeTyAliasId: SpecificDefId + Into; - type FreeConstAliasId: SpecificDefId - + Into - + Into; + type FreeConstAliasId: SpecificDefId + Into; type FreeTermAliasId: SpecificDefId; type ImplOrTraitAssocTyId: SpecificDefId + Into; - type ImplOrTraitAssocConstId: SpecificDefId - + Into - + Into; + type ImplOrTraitAssocConstId: SpecificDefId + Into; type ImplOrTraitAssocTermId: SpecificDefId; type InherentAssocTyId: SpecificDefId + Into; - type InherentAssocConstId: SpecificDefId - + Into - + Into; + type InherentAssocConstId: SpecificDefId + Into; type InherentAssocTermId: SpecificDefId; type Span: Span; From cfb7508f825375a15a4156e26f7d755cdf7f7321 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Jun 2026 07:05:51 +0000 Subject: [PATCH 179/278] Rename uv to alias_const --- compiler/rustc_borrowck/src/type_check/mod.rs | 10 +- .../rustc_hir_analysis/src/check/check.rs | 4 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- .../src/collect/predicates_of.rs | 8 +- .../src/hir_ty_lowering/mod.rs | 4 +- .../src/variance/constraints.rs | 4 +- .../src/infer/relate/generalize.rs | 6 +- compiler/rustc_middle/src/mir/pretty.rs | 6 +- .../rustc_middle/src/ty/abstract_const.rs | 6 +- compiler/rustc_middle/src/ty/consts.rs | 9 +- compiler/rustc_middle/src/ty/mod.rs | 2 +- .../rustc_middle/src/ty/structural_impls.rs | 10 +- .../src/thir/pattern/const_to_pat.rs | 99 +++++++++++-------- .../rustc_next_trait_solver/src/delegate.rs | 2 +- .../rustc_next_trait_solver/src/normalize.rs | 10 +- .../src/solve/eval_ctxt/mod.rs | 14 +-- .../rustc_next_trait_solver/src/solve/mod.rs | 8 +- .../src/solve/normalizes_to.rs | 4 +- .../src/solve/project_goals/anon_const.rs | 4 +- .../src/unstable/convert/stable/ty.rs | 6 +- .../traits/fulfillment_errors.rs | 6 +- .../src/solve/delegate.rs | 4 +- .../src/solve/fulfill/derive_errors.rs | 2 +- .../src/solve/normalize.rs | 11 ++- .../src/traits/const_evaluatable.rs | 10 +- .../src/traits/fulfill.rs | 10 +- .../rustc_trait_selection/src/traits/mod.rs | 42 ++++---- .../src/traits/normalize.rs | 16 +-- .../src/traits/query/normalize.rs | 8 +- .../src/traits/select/mod.rs | 6 +- .../rustc_trait_selection/src/traits/wf.rs | 11 ++- compiler/rustc_type_ir/src/const_kind.rs | 2 +- compiler/rustc_type_ir/src/flags.rs | 8 +- compiler/rustc_type_ir/src/inherent.rs | 4 +- 34 files changed, 195 insertions(+), 163 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index f8c052857ac5f..42f9e0aea8910 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1760,13 +1760,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let tcx = self.tcx(); let maybe_uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { - ty::ConstKind::Alias(_, uv) => match uv.kind { + ty::ConstKind::Alias(_, alias_const) => match alias_const.kind { ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Inherent { def_id } | ty::AliasConstKind::Free { def_id } - | ty::AliasConstKind::Anon { def_id } => { - Some(UnevaluatedConst { def: def_id, args: uv.args, promoted: None }) - } + | ty::AliasConstKind::Anon { def_id } => Some(UnevaluatedConst { + def: def_id, + args: alias_const.args, + promoted: None, + }), }, _ => None, }, diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index b60f19ea8d30c..0aa31c3a016b9 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -778,8 +778,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), if has_default { // need to store default and type of default let ct = tcx.const_param_default(param.def_id).skip_binder(); - if let ty::ConstKind::Alias(_, uv) = ct.kind() - && let Some(def_id) = uv.kind.opt_def_id() + if let ty::ConstKind::Alias(_, alias_const) = ct.kind() + && let Some(def_id) = alias_const.kind.opt_def_id() { tcx.ensure_ok().type_of(def_id); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index c2b3861a7f476..b062471a1a5ab 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1509,7 +1509,7 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: | ty::ConstKind::Bound(_, _) => unreachable!(), ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => continue, ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Alias(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => alias_const.type_of(infcx.tcx).skip_norm_wip(), ty::ConstKind::Param(param_ct) => { param_ct.find_const_ty_from_env(wfcx.param_env) } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index f62b2a224dbbd..0dc89381c5eb6 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -433,8 +433,8 @@ fn const_evaluatable_predicates_of<'tcx>( impl<'tcx> TypeVisitor> for ConstCollector<'tcx> { fn visit_const(&mut self, c: ty::Const<'tcx>) { - if let ty::ConstKind::Alias(_, uv) = c.kind() { - if is_const_param_default(self.tcx, uv.kind) { + if let ty::ConstKind::Alias(_, alias_const) = c.kind() { + if is_const_param_default(self.tcx, alias_const.kind) { // Do not look into const param defaults, // these get checked when they are actually instantiated. // @@ -446,11 +446,11 @@ fn const_evaluatable_predicates_of<'tcx>( } // Skip type consts as mGCA doesn't support evaluatable clauses. - if uv.kind.is_type_const(self.tcx) { + if alias_const.kind.is_type_const(self.tcx) { return; } - let span = uv.kind.def_span(self.tcx); + let span = alias_const.kind.def_span(self.tcx); self.preds.insert((ty::ClauseKind::ConstEvaluatable(c).upcast(self.tcx), span)); } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 1bdfefcf7032c..812e451b379d5 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1865,12 +1865,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::AssocTag::Const, )?; self.require_type_const_attribute(item_def_id, span)?; - let uv = ty::AliasConst::new( + let alias_const = ty::AliasConst::new( tcx, ty::AliasConstKind::new_from_def_id(tcx, item_def_id), item_args, ); - Ok(Const::new_alias(tcx, ty::IsRigid::No, uv)) + Ok(Const::new_alias(tcx, ty::IsRigid::No, alias_const)) } /// Lower a [resolved][hir::QPath::Resolved] (type-level) associated item path. diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index d5a5c63de4515..a2e3883b0684e 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -408,8 +408,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { debug!("add_constraints_from_const(c={:?}, variance={:?})", c, variance); match &c.kind() { - ty::ConstKind::Alias(_, uv) => { - self.add_constraints_from_invariant_args(current, uv.args, variance); + ty::ConstKind::Alias(_, alias_const) => { + self.add_constraints_from_invariant_args(current, alias_const.args, variance); } _ => {} } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index bac0d62c50073..7881b85997e8c 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -760,16 +760,16 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { // // We only need to be careful with potentially normalizeable // aliases here. See `generalize_alias_term` for more information. - ty::ConstKind::Alias(ty::IsRigid::No, uv) => { + ty::ConstKind::Alias(ty::IsRigid::No, alias_const) => { match self.structurally_relate_aliases { // Hack: Fall back to old behavior if GCE is enabled (it used to just be the Yes // path), as doing this new No path breaks some GCE things. I expect GCE to be // ripped out soon so this shouldn't matter soon. StructurallyRelateAliases::No if !tcx.features().generic_const_exprs() => { - self.generalize_alias_term(uv.into()).map(|v| v.expect_const()) + self.generalize_alias_term(alias_const.into()).map(|v| v.expect_const()) } _ => { - let ty::AliasConst { kind, args, .. } = uv; + let ty::AliasConst { kind, args, .. } = alias_const; let args = self.relate_with_variance( ty::Invariant, ty::VarianceDiagInfo::default(), diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 621a8d38a1c9b..8e83d44fbb78b 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1491,14 +1491,14 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { let val = match const_ { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Param(p) => format!("ty::Param({p})"), - ty::ConstKind::Alias(_, uv) => { - let kind = match uv.kind { + ty::ConstKind::Alias(_, alias_const) => { + let kind = match alias_const.kind { ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Inherent { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => self.tcx.def_path_str(def_id), }; - format!("ty::AliasConst({}, {:?})", kind, uv.args) + format!("ty::AliasConst({}, {:?})", kind, alias_const.args) } ty::ConstKind::Value(cv) => { format!("ty::Valtree({})", fmt_valtree(&cv)) diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index ebf4863e3590b..43ed22927da56 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -52,13 +52,13 @@ impl<'tcx> TyCtxt<'tcx> { } fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> { let ct = match c.kind() { - ty::ConstKind::Alias(_, uv) - if let Some(def_id) = uv.kind.opt_def_id() => + ty::ConstKind::Alias(_, alias_const) + if let Some(def_id) = alias_const.kind.opt_def_id() => { match self.tcx.thir_abstract_const(def_id) { Err(e) => ty::Const::new_error(self.tcx, e), Ok(Some(bac)) => { - let args = self.tcx.erase_and_anonymize_regions(uv.args); + let args = self.tcx.erase_and_anonymize_regions(alias_const.args); let bac = bac.instantiate(self.tcx, args).skip_norm_wip(); return bac.fold_with(self); } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 6eaa468e57450..42098eb989c0e 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -110,9 +110,9 @@ impl<'tcx> Const<'tcx> { pub fn new_alias( tcx: TyCtxt<'tcx>, is_rigid: ty::IsRigid, - uv: ty::AliasConst<'tcx>, + alias_const: ty::AliasConst<'tcx>, ) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Alias(is_rigid, uv)) + Const::new(tcx, ty::ConstKind::Alias(is_rigid, alias_const)) } #[inline] @@ -194,12 +194,17 @@ impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { Const::new_placeholder(tcx, placeholder) } +<<<<<<< HEAD fn new_alias( interner: TyCtxt<'tcx>, is_rigid: ty::IsRigid, uv: ty::AliasConst<'tcx>, ) -> Self { Const::new_alias(interner, is_rigid, uv) +======= + fn new_alias(interner: TyCtxt<'tcx>, alias_const: ty::AliasConst<'tcx>) -> Self { + Const::new_alias(interner, alias_const) +>>>>>>> ee149c4c59c (Rename uv to alias_const) } fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 7e81837b7d970..77389a76249b0 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -692,7 +692,7 @@ impl<'tcx> Term<'tcx> { _ => None, }, TermKind::Const(ct) => match ct.kind() { - ConstKind::Alias(_, uv) => Some(uv.into()), + ConstKind::Alias(_, alias_const) => Some(alias_const.into()), _ => None, }, } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index f973c9ba2c896..49bac6a130231 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -634,8 +634,8 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { folder: &mut F, ) -> Result { let kind = match self.kind() { - ConstKind::Alias(is_rigid, uv) => { - ConstKind::Alias(is_rigid, uv.try_fold_with(folder)?) + ConstKind::Alias(is_rigid, alias_const) => { + ConstKind::Alias(is_rigid, alias_const.try_fold_with(folder)?) } ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?), ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?), @@ -651,8 +651,8 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { fn super_fold_with>>(self, folder: &mut F) -> Self { let kind = match self.kind() { - ConstKind::Alias(is_rigid, uv) => { - ConstKind::Alias(is_rigid, uv.fold_with(folder)) + ConstKind::Alias(is_rigid, alias_const) => { + ConstKind::Alias(is_rigid, alias_const.fold_with(folder)) } ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)), ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)), @@ -670,7 +670,7 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { impl<'tcx> TypeSuperVisitable> for ty::Const<'tcx> { fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { match self.kind() { - ConstKind::Alias(_, uv) => uv.visit_with(visitor), + ConstKind::Alias(_, alias_const) => alias_const.visit_with(visitor), ConstKind::Value(v) => v.visit_with(visitor), ConstKind::Expr(e) => e.visit_with(visitor), ConstKind::Error(e) => e.visit_with(visitor), diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 7a71411a1336f..f48260a014ddd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -49,7 +49,7 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { let mut convert = ConstToPat::new(self, id, span, c); match c.kind() { - ty::ConstKind::Alias(_, uv) => convert.alias_to_pat(uv, ty), + ty::ConstKind::Alias(_, alias_const) => convert.alias_to_pat(alias_const, ty), ty::ConstKind::Value(value) => convert.valtree_to_pat(value), _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c), } @@ -77,9 +77,9 @@ impl<'tcx> ConstToPat<'tcx> { /// We errored. Signal that in the pattern, so that follow up errors can be silenced. fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box> { - if let ty::ConstKind::Alias(_, uv) = self.c.kind() { + if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() { if let ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } = uv.kind + | ty::AliasConstKind::Inherent { def_id } = alias_const.kind && let Some(def_id) = def_id.as_local() { // Include the container item in the output. @@ -87,7 +87,7 @@ impl<'tcx> ConstToPat<'tcx> { } if let ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Inherent { def_id } - | ty::AliasConstKind::Free { def_id } = uv.kind + | ty::AliasConstKind::Free { def_id } = alias_const.kind { err.span_label(self.tcx.def_span(def_id), msg!("constant defined here")); } @@ -95,7 +95,7 @@ impl<'tcx> ConstToPat<'tcx> { Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()), extra: None }) } - fn alias_to_pat(&mut self, uv: ty::AliasConst<'tcx>, ty: Ty<'tcx>) -> Box> { + fn alias_to_pat(&mut self, alias_const: ty::AliasConst<'tcx>, ty: Ty<'tcx>) -> Box> { // It's not *technically* correct to be revealing opaque types here as borrowcheck has // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). @@ -105,13 +105,13 @@ impl<'tcx> ConstToPat<'tcx> { // instead of having this logic here let typing_env = self.tcx.erase_and_anonymize_regions(self.typing_env).with_codegen_normalized(self.tcx); - let uv = self.tcx.erase_and_anonymize_regions(uv); + let alias_const = self.tcx.erase_and_anonymize_regions(alias_const); // FIXME(gca): This will become insufficient once associated constants can be // implemented as `type` consts (project-const-generics#76). At that point it'll // become necessary to just use type system normalization for all const patterns // but that's not yet possible. - let mut thir_pat = if uv.kind.is_type_const(self.tcx) { + let mut thir_pat = if alias_const.kind.is_type_const(self.tcx) { let Ok(normalize) = self .tcx .try_normalize_erasing_regions(self.typing_env, Unnormalized::new_wip(self.c)) @@ -128,7 +128,7 @@ impl<'tcx> ConstToPat<'tcx> { } else { // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. - let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span) { + let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, alias_const, self.span) { Ok(Ok(c)) => c, Err(ErrorHandled::Reported(_, _)) => { // Let's tell the use where this failing const occurs. @@ -136,10 +136,10 @@ impl<'tcx> ConstToPat<'tcx> { self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span }); // We've emitted an error on the original const, it would be redundant to complain // on its use as well. - if let ty::ConstKind::Alias(_, uv) = self.c.kind() + if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() && let ty::AliasConstKind::Projection { .. } | ty::AliasConstKind::Inherent { .. } - | ty::AliasConstKind::Free { .. } = uv.kind + | ty::AliasConstKind::Free { .. } = alias_const.kind { err.downgrade_to_delayed_bug(); } @@ -150,45 +150,58 @@ impl<'tcx> ConstToPat<'tcx> { .tcx .dcx() .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); - for arg in uv.args { + for arg in alias_const.args { if let ty::GenericArgKind::Type(ty) = arg.kind() && let ty::Param(param_ty) = ty.kind() { - let def_id = self.tcx.hir_enclosing_body_owner(self.id); - let generics = self.tcx.generics_of(def_id); - let param = generics.type_param(*param_ty, self.tcx); - let span = self.tcx.def_span(param.def_id); - e.span_label(span, "constant depends on this generic parameter"); - if let Some(ident) = self.tcx.def_ident_span(def_id) - && self.tcx.sess.source_map().is_multiline(ident.between(span)) + err.downgrade_to_delayed_bug(); + } + return self.mk_err(err, ty); + } + Err(ErrorHandled::TooGeneric(_)) => { + let mut e = self + .tcx + .dcx() + .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); + for arg in alias_const.args { + if let ty::GenericArgKind::Type(ty) = arg.kind() + && let ty::Param(param_ty) = ty.kind() { - // Display the `fn` name as well in the diagnostic, as the generic isn't - // in the same line and it could be confusing otherwise. - e.span_label(ident, ""); + let def_id = self.tcx.hir_enclosing_body_owner(self.id); + let generics = self.tcx.generics_of(def_id); + let param = generics.type_param(*param_ty, self.tcx); + let span = self.tcx.def_span(param.def_id); + e.span_label(span, "constant depends on this generic parameter"); + if let Some(ident) = self.tcx.def_ident_span(def_id) + && self.tcx.sess.source_map().is_multiline(ident.between(span)) + { + // Display the `fn` name as well in the diagnostic, as the generic isn't + // in the same line and it could be confusing otherwise. + e.span_label(ident, ""); + } } } + return self.mk_err(e, ty); } - return self.mk_err(e, ty); - } - Ok(Err(bad_ty)) => { - // The pattern cannot be turned into a valtree. - let e = match bad_ty.kind() { - ty::Adt(def, ..) => { - assert!(def.is_union()); - self.tcx.dcx().create_err(UnionPattern { span: self.span }) - } - ty::FnPtr(..) | ty::RawPtr(..) => { - self.tcx.dcx().create_err(PointerPattern { span: self.span }) - } - _ => self.tcx.dcx().create_err(InvalidPattern { - span: self.span, - non_sm_ty: bad_ty, - prefix: bad_ty.prefix_string(self.tcx).to_string(), - }), - }; - return self.mk_err(e, ty); - } - }; + Ok(Err(bad_ty)) => { + // The pattern cannot be turned into a valtree. + let e = match bad_ty.kind() { + ty::Adt(def, ..) => { + assert!(def.is_union()); + self.tcx.dcx().create_err(UnionPattern { span: self.span }) + } + ty::FnPtr(..) | ty::RawPtr(..) => { + self.tcx.dcx().create_err(PointerPattern { span: self.span }) + } + _ => self.tcx.dcx().create_err(InvalidPattern { + span: self.span, + non_sm_ty: bad_ty, + prefix: bad_ty.prefix_string(self.tcx).to_string(), + }), + }; + return self.mk_err(e, ty); + } + }; // Lower the valtree to a THIR pattern. self.valtree_to_pat(ty::Value { ty, valtree }) @@ -205,7 +218,7 @@ impl<'tcx> ConstToPat<'tcx> { // Mark the pattern to indicate that it is the result of lowering a named // constant. This is used for diagnostics. - thir_pat.extra.get_or_insert_default().expanded_const = uv.kind.opt_def_id(); + thir_pat.extra.get_or_insert_default().expanded_const = alias_const.kind.opt_def_id(); thir_pat } diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index d7c7585cb8f88..2ebb0fb851a4c 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -37,7 +37,7 @@ pub trait SolverDelegate: Deref + Sized { fn evaluate_const( &self, param_env: ::ParamEnv, - uv: ty::AliasConst, + alias_const: ty::AliasConst, ) -> Option<::Const>; // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`! diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index 8e341d147ed35..761b141202d98 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -231,7 +231,7 @@ where // With eager normalization, we should normalize the args of alias before // normalizing the alias itself. let ct = ct.try_super_fold_with(self)?; - let ty::ConstKind::Alias(orig_is_rigid, uv) = ct.kind() else { return Ok(ct) }; + let ty::ConstKind::Alias(orig_is_rigid, alias_const) = ct.kind() else { return Ok(ct) }; // We support ambiguous aliases inside rigid alias. So we still recognize // the rigidness of the outer alias. if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { @@ -239,10 +239,10 @@ where } let normalized = if ct.has_escaping_bound_vars() { - let (uv, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv); + let (alias_const, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_const); let Some(result) = ensure_sufficient_stack(|| { - self.normalize_alias_term(uv.into(), HasEscapingBoundVars::Yes) + self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::Yes) })? else { return Ok(ct); @@ -257,7 +257,7 @@ where ) } else { ensure_sufficient_stack(|| { - self.normalize_alias_term(uv.into(), HasEscapingBoundVars::No) + self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::No) })? .map(|term| term.expect_const()) .unwrap_or(ct) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 3cd8dbb7bf388..dd65342ea519d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1439,13 +1439,13 @@ where pub(super) fn evaluate_const( &mut self, param_env: I::ParamEnv, - uv: ty::AliasConst, + alias_const: ty::AliasConst, ) -> Result, RerunNonErased> { if self.typing_mode().is_erased_not_coherence() { self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)?; } - Ok(self.delegate.evaluate_const(param_env, uv)) + Ok(self.delegate.evaluate_const(param_env, alias_const)) } pub(super) fn evaluate_const_and_instantiate_projection_term( @@ -1453,9 +1453,9 @@ where param_env: I::ParamEnv, projection_term: ty::AliasTerm, expected_term: I::Term, - uv: ty::AliasConst, + alias_const: ty::AliasConst, ) -> QueryResultOrRerunNonErased { - match self.evaluate_const(param_env, uv)? { + match self.evaluate_const(param_env, alias_const)? { Some(evaluated) => { self.eq(param_env, expected_term, evaluated.into())?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -1468,11 +1468,11 @@ where // Perhaps we could split EvaluateConstErr::HasGenericsOrInfers into HasGenerics and // HasInfers or something, make evaluate_const return that, and make this branch be // based on that, rather than checking `has_non_region_infer`. - if self.resolve_vars_if_possible(uv).has_non_region_infer() { + if self.resolve_vars_if_possible(alias_const).has_non_region_infer() { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } else { - // We do not instantiate to the `uv` passed in, but rather - // `goal.predicate.alias`. The `uv` passed in might correspond to the `impl` + // We do not instantiate to the `alias_const` passed in, but rather + // `goal.predicate.alias`. The `alias_const` passed in might correspond to the `impl` // form of a constant (with generic arguments corresponding to the impl block), // however, we want to structurally instantiate to the original, non-rebased, // trait `Self` form of the constant (with generic arguments being the trait diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 6d36eb3740288..fcd7e00a27688 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -207,7 +207,7 @@ where self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } - ty::ConstKind::Unevaluated(ty::IsRigid::No, uv) => { + ty::ConstKind::Unevaluated(ty::IsRigid::No, alias_const) => { // We never return `NoSolution` here as `evaluate_const` emits an // error itself when failing to evaluate, so emitting an additional fulfillment // error in that case is unnecessary noise. This may change in the future once @@ -216,7 +216,7 @@ where // FIXME(generic_const_exprs): Implement handling for generic // const expressions here. - if let Some(_normalized) = self.evaluate_const(param_env, uv)? { + if let Some(_normalized) = self.evaluate_const(param_env, alias_const)? { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) @@ -252,8 +252,8 @@ where .evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into); } - ty::ConstKind::Alias(ty::IsRigid::Yes, uv) => { - uv.type_of(self.cx()).skip_norm_wip() + ty::ConstKind::Alias(ty::IsRigid::Yes, alias_const) => { + alias_const.type_of(self.cx()).skip_norm_wip() } ty::ConstKind::Alias(ty::IsRigid::No, _) => unimplemented!( "non-rigid unevaluated constant for compute_const_arg_has_type_goal: {ct:?}" diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index b2bb0099fd0f8..b161fcb520afb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -454,7 +454,7 @@ where c.into() } ty::AliasTermKind::ProjectionConst { .. } => { - let uv = ty::AliasConst::new( + let alias_const = ty::AliasConst::new( cx, ty::AliasConstKind::Projection { def_id: target_item_def_id.into().try_into().unwrap(), @@ -465,7 +465,7 @@ where goal.param_env, goal.predicate.alias, goal.predicate.term, - uv, + alias_const, ); } kind => panic!("expected projection, found {kind:?}"), diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs index f1baa608798fa..fc0c34e63e187 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs @@ -15,12 +15,12 @@ where &mut self, goal: Goal>, ) -> QueryResultOrRerunNonErased { - let uv = goal.predicate.projection_term.expect_ct(); + let alias_const = goal.predicate.projection_term.expect_ct(); self.evaluate_const_and_instantiate_projection_term( goal.param_env, goal.predicate.projection_term, goal.predicate.term, - uv, + alias_const, ) } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index b506963e790e5..2f280ef9fe4c0 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -552,14 +552,14 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { } } ty::ConstKind::Param(param) => crate::ty::TyConstKind::Param(param.stable(tables, cx)), - ty::ConstKind::Alias(_, uv) => { - let Some(def_id) = uv.kind.opt_def_id() else { + ty::ConstKind::Alias(_, alias_const) => { + let Some(def_id) = alias_const.kind.opt_def_id() else { // FIXME: implement (both AliasTy and AliasConst will be needing this soon) panic!("non-defid alias consts are not supported by rustc_public at the moment") }; crate::ty::TyConstKind::Unevaluated( tables.const_def(def_id), - uv.args.stable(tables, cx), + alias_const.args.stable(tables, cx), ) } ty::ConstKind::Error(_) => unreachable!(), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 69fe997339eb6..973a088a7f5d4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -3769,12 +3769,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { match obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => match ct.kind() { - ty::ConstKind::Alias(_, uv) => { + ty::ConstKind::Alias(_, alias_const) => { let mut err = self.dcx().struct_span_err(span, "unconstrained generic constant"); - let const_span = uv.kind.def_span(self.tcx); + let const_span = alias_const.kind.def_span(self.tcx); - let const_ty = uv.type_of(self.tcx).skip_norm_wip(); + let const_ty = alias_const.type_of(self.tcx).skip_norm_wip(); let cast = if const_ty != self.tcx.types.usize { " as usize" } else { "" }; let msg = "try adding a `where` bound"; if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(const_span) { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index f6f4caeb75121..e0877829b25f3 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -218,9 +218,9 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn evaluate_const( &self, param_env: ty::ParamEnv<'tcx>, - uv: ty::AliasConst<'tcx>, + alias_const: ty::AliasConst<'tcx>, ) -> Option> { - let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, uv); + let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias_const); match crate::traits::try_evaluate_const(&self.0, ct, param_env) { Ok(ct) => Some(ct), diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index d303adb7ebe2c..c93cd2b3ef71d 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -34,7 +34,7 @@ pub(super) fn fulfillment_error_for_no_solution<'tcx>( } ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => { let ct_ty = match ct.kind() { - ty::ConstKind::Alias(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => alias_const.type_of(infcx.tcx).skip_norm_wip(), ty::ConstKind::Param(param_ct) => { param_ct.find_const_ty_from_env(obligation.param_env) } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 13c68ec7280c1..a1a7fdc6f6b82 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -146,18 +146,21 @@ impl<'me, 'tcx> TypeFolder> for ReplaceAliasWithInfer<'me, 'tcx> { } let ct = ct.super_fold_with(self); - let ty::ConstKind::Alias(orig_is_rigid, uv) = ct.kind() else { return ct }; + let ty::ConstKind::Alias(orig_is_rigid, alias_const) = ct.kind() else { return ct }; if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { return ct; } if ct.has_escaping_bound_vars() { - let (replaced, ..) = - BoundVarReplacer::replace_bound_vars(self.at.infcx, &mut self.universes, uv); + let (replaced, ..) = BoundVarReplacer::replace_bound_vars( + self.at.infcx, + &mut self.universes, + alias_const, + ); let _ = self.term_to_infer(replaced.into()); ct } else { - self.term_to_infer(uv.into()).expect_const() + self.term_to_infer(alias_const.into()).expect_const() } } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 8cb59c36514e9..16de61f71fae0 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -93,8 +93,8 @@ pub fn is_const_evaluatable<'tcx>( crate::traits::evaluate_const(infcx, unexpanded_ct, param_env); Ok(()) } else { - let uv = match unexpanded_ct.kind() { - ty::ConstKind::Alias(_, uv) => uv, + let alias_const = match unexpanded_ct.kind() { + ty::ConstKind::Alias(_, alias_const) => alias_const, ty::ConstKind::Expr(_) => { bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled") } @@ -117,7 +117,7 @@ pub fn is_const_evaluatable<'tcx>( tcx.dcx() .struct_span_fatal( // Slightly better span than just using `span` alone - if span == DUMMY_SP { uv.kind.def_span(tcx) } else { span }, + if span == DUMMY_SP { alias_const.kind.def_span(tcx) } else { span }, "failed to evaluate generic const expression", ) .with_note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") @@ -131,9 +131,9 @@ pub fn is_const_evaluatable<'tcx>( } Err(EvaluateConstErr::HasGenericsOrInfers) => { - let err = if uv.has_non_region_infer() { + let err = if alias_const.has_non_region_infer() { NotConstEvaluatable::MentionsInfer - } else if uv.has_non_region_param() { + } else if alias_const.has_non_region_param() { NotConstEvaluatable::MentionsParam } else { let guar = infcx.dcx().span_delayed_bug( diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 164182dff3732..b9b74683623f1 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -559,7 +559,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { return ProcessResult::Changed(PendingPredicateObligations::new()); } ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Alias(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => alias_const.type_of(infcx.tcx).skip_norm_wip(), // FIXME(generic_const_exprs): we should construct an alias like // `>::Output` when this is an `Expr` representing // `lhs + rhs`. @@ -677,10 +677,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(alias_const)) => { match const_evaluatable::is_const_evaluatable( self.selcx.infcx, - uv, + alias_const, obligation.param_env, obligation.cause.span, ) { @@ -688,7 +688,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { Err(NotConstEvaluatable::MentionsInfer) => { pending_obligation.stalled_on.clear(); pending_obligation.stalled_on.extend( - uv.walk().filter_map(TyOrConstInferVar::maybe_from_generic_arg), + alias_const + .walk() + .filter_map(TyOrConstInferVar::maybe_from_generic_arg), ); ProcessResult::Unchanged } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index aa0c9448a8056..a98d06351f258 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -369,8 +369,8 @@ pub fn normalize_param_env_or_error<'tcx>( // should actually be okay since without `feature(generic_const_exprs)` the only // const arguments that have a non-empty param env are array repeat counts. These // do not appear in the type system though. - if let ty::ConstKind::Alias(_, uv) = c.kind() - && matches!(uv.kind, ty::AliasConstKind::Anon { .. }) + if let ty::ConstKind::Alias(_, alias_const) = c.kind() + && matches!(alias_const.kind, ty::AliasConstKind::Anon { .. }) { let infcx = self.0.infer_ctxt().build(TypingMode::non_body_analysis()); let c = evaluate_const(&infcx, c, ty::ParamEnv::empty()); @@ -608,8 +608,8 @@ pub fn try_evaluate_const<'tcx>( | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), - ty::ConstKind::Alias(_, uv) => { - let opt_anon_const_kind = match uv.kind { + ty::ConstKind::Alias(_, alias_const) => { + let opt_anon_const_kind = match alias_const.kind { ty::AliasConstKind::Anon { def_id } => Some((def_id, tcx.anon_const_kind(def_id))), _ => None, }; @@ -630,14 +630,14 @@ pub fn try_evaluate_const<'tcx>( // completely fall apart under `generic_const_exprs` and makes this whole function Really hard to reason // about if you have to consider gce whatsoever. Some((def_id, ty::AnonConstKind::GCE)) => { - if uv.has_non_region_infer() || uv.has_non_region_param() { + if alias_const.has_non_region_infer() || alias_const.has_non_region_param() { // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause // inference variables and generic parameters to show up in `ty::Const` even though the anon const // does not actually make use of them. We handle this case specially and attempt to evaluate anyway. match tcx.thir_abstract_const(def_id) { Ok(Some(ct)) => { let ct = tcx.expand_abstract_consts( - ct.instantiate(tcx, uv.args).skip_norm_wip(), + ct.instantiate(tcx, alias_const.args).skip_norm_wip(), ); if let Err(e) = ct.error_reported() { return Err(EvaluateConstErr::EvaluationFailure(e)); @@ -646,8 +646,10 @@ pub fn try_evaluate_const<'tcx>( // the generic arguments provided for it, then we should *not* attempt to evaluate it. return Err(EvaluateConstErr::HasGenericsOrInfers); } else { - let args = - replace_param_and_infer_args_with_placeholder(tcx, uv.args); + let args = replace_param_and_infer_args_with_placeholder( + tcx, + alias_const.args, + ); let typing_env = infcx .typing_env(tcx.erase_and_anonymize_regions(param_env)) .with_post_analysis_normalized(tcx); @@ -664,11 +666,11 @@ pub fn try_evaluate_const<'tcx>( let typing_env = infcx .typing_env(tcx.erase_and_anonymize_regions(param_env)) .with_post_analysis_normalized(tcx); - (uv.args, typing_env) + (alias_const.args, typing_env) } } Some((def_id, ty::AnonConstKind::RepeatExprCount)) => { - if uv.has_non_region_infer() { + if alias_const.has_non_region_infer() { // Diagnostics will sometimes replace the identity args of anon consts in // array repeat expr counts with inference variables so we have to handle this // even though it is not something we should ever actually encounter. @@ -704,9 +706,9 @@ pub fn try_evaluate_const<'tcx>( // logic does not go through type system normalization. If it did this would // be a backwards compatibility problem as we do not enforce "syntactic" non- // usage of generic parameters like we do here. - if uv.args.has_non_region_param() - || uv.args.has_non_region_infer() - || uv.args.has_non_region_placeholders() + if alias_const.args.has_non_region_param() + || alias_const.args.has_non_region_infer() + || alias_const.args.has_non_region_placeholders() { return Err(EvaluateConstErr::HasGenericsOrInfers); } @@ -715,19 +717,21 @@ pub fn try_evaluate_const<'tcx>( // to prevent query cycle. let typing_env = ty::TypingEnv::fully_monomorphized(); - (uv.args, typing_env) + (alias_const.args, typing_env) } }; - let uv = ty::AliasConst::new(tcx, uv.kind, args); - let erased_uv = tcx.erase_and_anonymize_regions(uv); + let alias_const = ty::AliasConst::new(tcx, alias_const.kind, args); + let erased_alias_const = tcx.erase_and_anonymize_regions(alias_const); use rustc_middle::mir::interpret::ErrorHandled; // FIXME: `def_span` will point at the definition of this const; ideally, we'd point at // where it gets used as a const generic. - let span = uv.kind.def_span(tcx); - match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, span) { - Ok(Ok(val)) => Ok(ty::Const::new_value(tcx, val, uv.type_of(tcx).skip_norm_wip())), + let span = alias_const.kind.def_span(tcx); + match tcx.const_eval_resolve_for_typeck(typing_env, erased_alias_const, span) { + Ok(Ok(val)) => { + Ok(ty::Const::new_value(tcx, val, alias_const.type_of(tcx).skip_norm_wip())) + } Ok(Err(_)) => { let e = tcx.dcx().delayed_bug( "Type system constant with non valtree'able type evaluated but no error emitted", diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index c97c5660bbade..2a28fab776a12 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -469,14 +469,14 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx if tcx.features().generic_const_exprs() // Normalize type_const items even with feature `generic_const_exprs`. - && !matches!(ct.kind(), ty::ConstKind::Alias(_, uv) if uv.kind.is_type_const(tcx)) + && !matches!(ct.kind(), ty::ConstKind::Alias(_, alias_const) if alias_const.kind.is_type_const(tcx)) || !needs_normalization(self.selcx.infcx, &ct) { return ct; } - let uv = match ct.kind() { - ty::ConstKind::Alias(_, uv) => uv, + let alias_const = match ct.kind() { + ty::ConstKind::Alias(_, alias_const) => alias_const, _ => return ct.super_fold_with(self), }; @@ -487,14 +487,16 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx // That's because we can only end up with an Alias ty::Const for a const item // if it was marked with `type const`. Using this attribute without the mgca // feature gate causes a parse error. - let ct = match uv.kind { + let ct = match alias_const.kind { ty::AliasConstKind::Projection { .. } => { - self.normalize_trait_projection(uv.into()).expect_const() + self.normalize_trait_projection(alias_const.into()).expect_const() } ty::AliasConstKind::Inherent { .. } => { - self.normalize_inherent_projection(uv.into()).expect_const() + self.normalize_inherent_projection(alias_const.into()).expect_const() + } + ty::AliasConstKind::Free { .. } => { + self.normalize_free_alias(alias_const.into()).expect_const() } - ty::AliasConstKind::Free { .. } => self.normalize_free_alias(uv.into()).expect_const(), ty::AliasConstKind::Anon { .. } => { let ct = ct.super_fold_with(self); super::with_replaced_escaping_bound_vars( diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 93cd161cf566a..e8371a3c0f257 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -272,19 +272,19 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { return Ok(constant); } - let uv = match constant.kind() { - ty::ConstKind::Alias(_, uv) => uv, + let alias_const = match constant.kind() { + ty::ConstKind::Alias(_, alias_const) => alias_const, _ => return constant.try_super_fold_with(self), }; - let constant = match uv.kind { + let constant = match alias_const.kind { ty::AliasConstKind::Anon { .. } => crate::traits::with_replaced_escaping_bound_vars( self.infcx, &mut self.universes, constant, |constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env), ), - _ => self.try_fold_free_or_assoc(uv.into())?.expect_const(), + _ => self.try_fold_free_or_assoc(alias_const.into())?.expect_const(), }; debug!(?constant, ?self.param_env); constant.try_super_fold_with(self) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index ce310dc693622..e5e87fe775e1a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -852,10 +852,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(alias_const)) => { match const_evaluatable::is_const_evaluatable( self.infcx, - uv, + alias_const, obligation.param_env, obligation.cause.span, ) { @@ -987,7 +987,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::ConstKind::Error(_) => return Ok(EvaluatedToOk), ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Alias(_, uv) => uv.type_of(self.tcx()).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => alias_const.type_of(self.tcx()).skip_norm_wip(), // FIXME(generic_const_exprs): See comment in `fulfill.rs` ty::ConstKind::Expr(_) => return Ok(EvaluatedToOk), ty::ConstKind::Placeholder(_) => { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 095bf511cc87d..a47f933f5c25e 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1064,10 +1064,11 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { let tcx = self.tcx(); match c.kind() { - ty::ConstKind::Alias(_, uv) => { + ty::ConstKind::Alias(_, alias_const) => { if !c.has_escaping_bound_vars() { // Skip type consts as mGCA doesn't support evaluatable clauses - if !uv.kind.is_type_const(tcx) && !tcx.features().generic_const_args() { + if !alias_const.kind.is_type_const(tcx) && !tcx.features().generic_const_args() + { let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( ty::ClauseKind::ConstEvaluatable(c), )); @@ -1081,15 +1082,15 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { )); } - match uv.kind { + match alias_const.kind { ty::AliasConstKind::Inherent { .. } => { - self.add_wf_preds_for_inherent_projection(uv.into()); + self.add_wf_preds_for_inherent_projection(alias_const.into()); return; // Subtree is handled by above function } ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => { - let obligations = self.nominal_obligations(def_id, uv.args); + let obligations = self.nominal_obligations(def_id, alias_const.args); self.out.extend(obligations); } } diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 826a9e6d72db4..4ad93b3d9d138 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -59,7 +59,7 @@ impl fmt::Debug for ConstKind { Infer(var) => write!(f, "{var:?}"), Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var), Placeholder(placeholder) => write!(f, "{placeholder:?}"), - Alias(is_rigid, uv) => write!(f, "Unevaluated({is_rigid:?}, {uv:?})"), + Alias(is_rigid, alias_const) => write!(f, "Unevaluated({is_rigid:?}, {ualias_const:?})"), Value(val) => write!(f, "{val:?}"), Error(_) => write!(f, "{{const error}}"), Expr(expr) => write!(f, "{expr:?}"), diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index e756caa22a71e..da7d8b5acbc1e 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -432,8 +432,8 @@ impl FlagComputation { self.add_term(term); } ty::PredicateKind::DynCompatible(_def_id) => {} - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { - self.add_const(uv); + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(alias_const)) => { + self.add_const(alias_const); } ty::PredicateKind::ConstEquate(expected, found) => { self.add_const(expected); @@ -477,9 +477,9 @@ impl FlagComputation { fn add_const_kind(&mut self, c: &ty::ConstKind) { match *c { - ty::ConstKind::Alias(is_rigid, uv) => { + ty::ConstKind::Alias(is_rigid, alias_const) => { self.add_is_rigid(is_rigid); - self.add_args(uv.args.as_slice()); + self.add_args(alias_const.args.as_slice()); self.add_flags(TypeFlags::HAS_CT_PROJECTION); } ty::ConstKind::Infer(infer) => match infer { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index f2c5ef8d52271..bca2614d27f61 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -280,7 +280,7 @@ pub trait Const>: fn new_placeholder(interner: I, param: ty::PlaceholderConst) -> Self; - fn new_alias(interner: I, is_rigid: ty::IsRigid, uv: ty::AliasConst) -> Self; + fn new_alias(interner: I, is_rigid: ty::IsRigid, alias_const: ty::AliasConst) -> Self; fn new_expr(interner: I, expr: I::ExprConst) -> Self; @@ -411,7 +411,7 @@ pub trait Term>: _ => None, }, ty::TermKind::Const(ct) => match ct.kind() { - ty::ConstKind::Alias(_, uv) => Some(uv.into()), + ty::ConstKind::Alias(_, alias_const) => Some(alias_const.into()), _ => None, }, } From 1fc3df40856804a6dd7f9c993eaad448191cf751 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 23 Jun 2026 12:15:12 +0000 Subject: [PATCH 180/278] Rename from alias const to const alias and bless test --- tests/ui/const-generics/mgca/cyclic-type-const-151251.rs | 2 +- tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr | 2 +- tests/ui/const-generics/mgca/type_const-recursive.rs | 2 +- tests/ui/const-generics/mgca/type_const-recursive.stderr | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs b/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs index b19e1af69a53d..b3dfef1cb26ca 100644 --- a/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs +++ b/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs @@ -5,6 +5,6 @@ #![expect(incomplete_features)] type const A: u8 = A; -//~^ ERROR overflow normalizing the alias const `A` +//~^ ERROR overflow normalizing the const alias `A` fn main() {} diff --git a/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr b/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr index dc2858c032612..07b4ca43eb77b 100644 --- a/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr +++ b/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow normalizing the alias const `A` +error[E0275]: overflow normalizing the const alias `A` --> $DIR/cyclic-type-const-151251.rs:7:1 | LL | type const A: u8 = A; diff --git a/tests/ui/const-generics/mgca/type_const-recursive.rs b/tests/ui/const-generics/mgca/type_const-recursive.rs index 8222071faf2a3..0791325d603be 100644 --- a/tests/ui/const-generics/mgca/type_const-recursive.rs +++ b/tests/ui/const-generics/mgca/type_const-recursive.rs @@ -3,6 +3,6 @@ type const A: u8 = A; -//~^ ERROR: overflow normalizing the alias const `A` [E0275] +//~^ ERROR: overflow normalizing the const alias `A` [E0275] fn main() {} diff --git a/tests/ui/const-generics/mgca/type_const-recursive.stderr b/tests/ui/const-generics/mgca/type_const-recursive.stderr index 892409c5f0057..f9e0965dc5412 100644 --- a/tests/ui/const-generics/mgca/type_const-recursive.stderr +++ b/tests/ui/const-generics/mgca/type_const-recursive.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow normalizing the alias const `A` +error[E0275]: overflow normalizing the const alias `A` --> $DIR/type_const-recursive.rs:5:1 | LL | type const A: u8 = A; From 857278ed7b4c02b3c113bad5eed0dceee646f2af Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 25 Jun 2026 11:20:45 +0000 Subject: [PATCH 181/278] Make sure term_kind uses same alias renames as before --- compiler/rustc_type_ir/src/term_kind.rs | 40 +++++++++---------------- compiler/rustc_type_ir/src/ty/alias.rs | 2 +- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_type_ir/src/term_kind.rs b/compiler/rustc_type_ir/src/term_kind.rs index ae69283dc94ec..89be81e1f4aaf 100644 --- a/compiler/rustc_type_ir/src/term_kind.rs +++ b/compiler/rustc_type_ir/src/term_kind.rs @@ -60,9 +60,9 @@ pub enum AliasTermKind { /// Can always be normalized away. FreeTy { def_id: I::FreeTyAliasId }, - /// An unevaluated anonymous constants. - AnonConst { def_id: I::UnevaluatedConstId }, - /// An unevaluated const coming from an associated const. + /// An anonymous constant. + AnonConst { def_id: I::AnonConstId }, + /// A const alias coming from an associated const. ProjectionConst { def_id: I::TraitAssocConstId }, /// A top level const item not part of a trait or impl. FreeConst { def_id: I::FreeConstAliasId }, @@ -79,8 +79,8 @@ impl AliasTermKind { AliasTermKind::InherentConst { .. } => "inherent associated const", AliasTermKind::OpaqueTy { .. } => "opaque type", AliasTermKind::FreeTy { .. } => "type alias", - AliasTermKind::FreeConst { .. } => "unevaluated constant", - AliasTermKind::AnonConst { .. } => "unevaluated constant", + AliasTermKind::FreeConst { .. } => "const alias", + AliasTermKind::AnonConst { .. } => "anonymous constant", } } @@ -125,12 +125,8 @@ impl From> for AliasTermKind { impl From> for AliasTermKind { fn from(value: ty::AliasConstKind) -> Self { match value { - ty::AliasConstKind::Projection { def_id } => { - AliasTermKind::ProjectionConst { def_id } - } - ty::AliasConstKind::Inherent { def_id } => { - AliasTermKind::InherentConst { def_id } - } + ty::AliasConstKind::Projection { def_id } => AliasTermKind::ProjectionConst { def_id }, + ty::AliasConstKind::Inherent { def_id } => AliasTermKind::InherentConst { def_id }, ty::AliasConstKind::Free { def_id } => AliasTermKind::FreeConst { def_id }, ty::AliasConstKind::Anon { def_id } => AliasTermKind::AnonConst { def_id }, } @@ -191,14 +187,10 @@ impl AliasTerm { pub fn expect_ct(self) -> ty::AliasConst { let kind = match self.kind { - AliasTermKind::InherentConst { def_id } => { - ty::AliasConstKind::Inherent { def_id } - } + AliasTermKind::InherentConst { def_id } => ty::AliasConstKind::Inherent { def_id }, AliasTermKind::FreeConst { def_id } => ty::AliasConstKind::Free { def_id }, AliasTermKind::AnonConst { def_id } => ty::AliasConstKind::Anon { def_id }, - AliasTermKind::ProjectionConst { def_id } => { - ty::AliasConstKind::Projection { def_id } - } + AliasTermKind::ProjectionConst { def_id } => ty::AliasConstKind::Projection { def_id }, kind @ (AliasTermKind::ProjectionTy { .. } | AliasTermKind::InherentTy { .. } | AliasTermKind::OpaqueTy { .. } @@ -215,7 +207,7 @@ impl AliasTerm { .into() }; let unevaluated_const = |kind| { - I::Const::new_unevaluated( + I::Const::new_alias( interner, is_rigid, ty::AliasConst::new(interner, kind, self.args), @@ -223,17 +215,13 @@ impl AliasTerm { .into() }; match self.kind { - AliasTermKind::FreeConst { def_id } => { - unevaluated_const(ty::AliasConstKind::Free { def_id }) - } + AliasTermKind::FreeConst { def_id } => alias_const(ty::AliasConstKind::Free { def_id }), AliasTermKind::InherentConst { def_id } => { - unevaluated_const(ty::AliasConstKind::Inherent { def_id }) - } - AliasTermKind::AnonConst { def_id } => { - unevaluated_const(ty::AliasConstKind::Anon { def_id }) + alias_const(ty::AliasConstKind::Inherent { def_id }) } + AliasTermKind::AnonConst { def_id } => alias_const(ty::AliasConstKind::Anon { def_id }), AliasTermKind::ProjectionConst { def_id } => { - unevaluated_const(ty::AliasConstKind::Projection { def_id }) + alias_const(ty::AliasConstKind::Projection { def_id }) } AliasTermKind::ProjectionTy { def_id } => alias_ty(ty::Projection { def_id }), AliasTermKind::InherentTy { def_id } => alias_ty(ty::Inherent { def_id }), diff --git a/compiler/rustc_type_ir/src/ty/alias.rs b/compiler/rustc_type_ir/src/ty/alias.rs index 59abe1ae71150..10fd4c6a25394 100644 --- a/compiler/rustc_type_ir/src/ty/alias.rs +++ b/compiler/rustc_type_ir/src/ty/alias.rs @@ -5,7 +5,7 @@ use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; -use crate::{AliasTermKind, AliasTyKind, Interner, AliasConstKind}; +use crate::{AliasConstKind, AliasTermKind, AliasTyKind, Interner}; /// Represents an alias of a type, constant, or other term-like item. /// From 58f80da065eede67f1476ecd5f30006e62dcc2fe Mon Sep 17 00:00:00 2001 From: Yilin Chen <1479826151@qq.com> Date: Fri, 26 Jun 2026 15:35:41 +0800 Subject: [PATCH 182/278] Fix inconsistent safety requirement in VecDeque::nonoverlapping_ranges --- library/alloc/src/collections/vec_deque/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 5a900db9d1ce8..065cc128101ae 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -267,7 +267,7 @@ impl VecDeque { /// /// - Ranges must not overlap: `src.abs_diff(dst) >= count`. /// - Ranges must be in bounds of the logical buffer: `src + count <= self.capacity()` and `dst + count <= self.capacity()`. - /// - `head` must be in bounds: `head < self.capacity()`. + /// - `head` must be in bounds: `head < self.capacity()`, unless `self.capacity() == 0`, in which case `head == 0`. #[cfg(not(no_global_oom_handling))] unsafe fn nonoverlapping_ranges( &mut self, From c502825eefefcf21c152991de428948f6be609da Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 26 Jun 2026 07:58:51 +0000 Subject: [PATCH 183/278] Rename Unevaluated to AliasConst in const_kind and accordingly update test and some format changes --- .../rustc_hir_analysis/src/check/wfcheck.rs | 4 +- .../src/hir_ty_lowering/mod.rs | 6 +-- compiler/rustc_middle/src/ty/consts.rs | 9 +--- compiler/rustc_middle/src/ty/mod.rs | 2 +- .../src/thir/pattern/const_to_pat.rs | 46 +++++++------------ .../rustc_next_trait_solver/src/solve/mod.rs | 2 +- .../src/solve/fulfill/derive_errors.rs | 4 +- .../src/traits/auto_trait.rs | 2 +- .../src/traits/const_evaluatable.rs | 5 +- .../src/traits/dyn_compatibility.rs | 9 ++-- .../src/traits/fulfill.rs | 9 ++-- .../src/traits/select/mod.rs | 7 +-- compiler/rustc_type_ir/src/const_kind.rs | 4 +- compiler/rustc_type_ir/src/fast_reject.rs | 4 +- compiler/rustc_type_ir/src/fold.rs | 6 +-- compiler/rustc_type_ir/src/inherent.rs | 2 +- compiler/rustc_type_ir/src/relate.rs | 5 +- compiler/rustc_type_ir/src/relate/combine.rs | 7 ++- compiler/rustc_type_ir/src/term_kind.rs | 10 ++-- .../issue_99325.main.built.after.32bit.mir | 2 +- .../issue_99325.main.built.after.64bit.mir | 2 +- tests/ui/offset-of/inside-array-length.stderr | 2 +- 22 files changed, 61 insertions(+), 88 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index b062471a1a5ab..17141de79b4bf 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1509,7 +1509,9 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: | ty::ConstKind::Bound(_, _) => unreachable!(), ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => continue, ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Alias(_, alias_const) => alias_const.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(infcx.tcx).skip_norm_wip() + } ty::ConstKind::Param(param_ct) => { param_ct.find_const_ty_from_env(wfcx.param_env) } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 812e451b379d5..0796b4f2e8644 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2775,11 +2775,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Const::new_alias( tcx, ty::IsRigid::No, - ty::AliasConst::new( - tcx, - ty::AliasConstKind::new_from_def_id(tcx, did), - args, - ), + ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, did), args), ) } Res::Def(kind @ DefKind::Ctor(ctor_of, CtorKind::Const), did) => { diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 42098eb989c0e..df25b08168609 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -194,17 +194,12 @@ impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { Const::new_placeholder(tcx, placeholder) } -<<<<<<< HEAD fn new_alias( interner: TyCtxt<'tcx>, is_rigid: ty::IsRigid, - uv: ty::AliasConst<'tcx>, + alias_const: ty::AliasConst<'tcx>, ) -> Self { - Const::new_alias(interner, is_rigid, uv) -======= - fn new_alias(interner: TyCtxt<'tcx>, alias_const: ty::AliasConst<'tcx>) -> Self { - Const::new_alias(interner, alias_const) ->>>>>>> ee149c4c59c (Rename uv to alias_const) + Const::new_alias(interner, is_rigid, alias_const) } fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 77389a76249b0..f6709e9b64328 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -705,7 +705,7 @@ impl<'tcx> Term<'tcx> { _ => false, }, ty::TermKind::Const(ct) => match ct.kind() { - ty::ConstKind::Unevaluated(ty::IsRigid::No, _) => true, + ty::ConstKind::Alias(ty::IsRigid::No, _) => true, _ => false, }, } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index f48260a014ddd..7c714fd7281d5 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -128,38 +128,26 @@ impl<'tcx> ConstToPat<'tcx> { } else { // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. - let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, alias_const, self.span) { - Ok(Ok(c)) => c, - Err(ErrorHandled::Reported(_, _)) => { - // Let's tell the use where this failing const occurs. - let mut err = - self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span }); - // We've emitted an error on the original const, it would be redundant to complain - // on its use as well. - if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() - && let ty::AliasConstKind::Projection { .. } - | ty::AliasConstKind::Inherent { .. } - | ty::AliasConstKind::Free { .. } = alias_const.kind - { - err.downgrade_to_delayed_bug(); - } - return self.mk_err(err, ty); - } - Err(ErrorHandled::TooGeneric(_)) => { - let mut e = self - .tcx - .dcx() - .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); - for arg in alias_const.args { - if let ty::GenericArgKind::Type(ty) = arg.kind() - && let ty::Param(param_ty) = ty.kind() + let valtree = + match self.tcx.const_eval_resolve_for_typeck(typing_env, alias_const, self.span) { + Ok(Ok(c)) => c, + Err(ErrorHandled::Reported(_, _)) => { + // Let's tell the use where this failing const occurs. + let mut err = + self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span }); + // We've emitted an error on the original const, it would be redundant to complain + // on its use as well. + if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() + && let ty::AliasConstKind::Projection { .. } + | ty::AliasConstKind::Inherent { .. } + | ty::AliasConstKind::Free { .. } = alias_const.kind { err.downgrade_to_delayed_bug(); } return self.mk_err(err, ty); } Err(ErrorHandled::TooGeneric(_)) => { - let mut e = self + let mut err = self .tcx .dcx() .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); @@ -171,17 +159,17 @@ impl<'tcx> ConstToPat<'tcx> { let generics = self.tcx.generics_of(def_id); let param = generics.type_param(*param_ty, self.tcx); let span = self.tcx.def_span(param.def_id); - e.span_label(span, "constant depends on this generic parameter"); + err.span_label(span, "constant depends on this generic parameter"); if let Some(ident) = self.tcx.def_ident_span(def_id) && self.tcx.sess.source_map().is_multiline(ident.between(span)) { // Display the `fn` name as well in the diagnostic, as the generic isn't // in the same line and it could be confusing otherwise. - e.span_label(ident, ""); + err.span_label(ident, ""); } } } - return self.mk_err(e, ty); + return self.mk_err(err, ty); } Ok(Err(bad_ty)) => { // The pattern cannot be turned into a valtree. diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index fcd7e00a27688..8b838f3567c84 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -207,7 +207,7 @@ where self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } - ty::ConstKind::Unevaluated(ty::IsRigid::No, alias_const) => { + ty::ConstKind::Alias(ty::IsRigid::No, alias_const) => { // We never return `NoSolution` here as `evaluate_const` emits an // error itself when failing to evaluate, so emitting an additional fulfillment // error in that case is unnecessary noise. This may change in the future once diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index c93cd2b3ef71d..098dcfb30a7b6 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -34,7 +34,9 @@ pub(super) fn fulfillment_error_for_no_solution<'tcx>( } ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => { let ct_ty = match ct.kind() { - ty::ConstKind::Alias(_, alias_const) => alias_const.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(infcx.tcx).skip_norm_wip() + } ty::ConstKind::Param(param_ct) => { param_ct.find_const_ty_from_env(obligation.param_env) } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index a948d2b8cff80..a39f498e36fd8 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -774,7 +774,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { let evaluate = |c: ty::Const<'tcx>| { - if let ty::ConstKind::Alias(_, unevaluated) = c.kind() { + if let ty::ConstKind::Alias(_, alias_const) = c.kind() { let ct = super::try_evaluate_const(selcx.infcx, c, obligation.param_env); diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 16de61f71fae0..4b54f9c3a2c26 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -44,10 +44,7 @@ pub fn is_const_evaluatable<'tcx>( let is_anon_ct = matches!( ct.kind(), - ty::ConstKind::Alias(_, ty::AliasConst { - kind: ty::AliasConstKind::Anon { .. }, - .. - }) + ty::ConstKind::Alias(_, ty::AliasConst { kind: ty::AliasConstKind::Anon { .. }, .. }) ); if !is_anon_ct { diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 82cd21ca64abb..7b45dfe48a7fe 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -886,11 +886,10 @@ impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { let ct = self.tcx.expand_abstract_consts(ct); match ct.kind() { - ty::ConstKind::Alias(_, ty::AliasConst { - kind: ty::AliasConstKind::Projection { def_id }, - args, - .. - }) if self.tcx.features().min_generic_const_args() => { + ty::ConstKind::Alias( + _, + ty::AliasConst { kind: ty::AliasConstKind::Projection { def_id }, args, .. }, + ) if self.tcx.features().min_generic_const_args() => { match self.allow_self_projections { AllowSelfProjections::Yes => { let trait_def_id = self.tcx.parent(def_id); diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index b9b74683623f1..12b9940a83f44 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -559,7 +559,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { return ProcessResult::Changed(PendingPredicateObligations::new()); } ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Alias(_, alias_const) => alias_const.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(infcx.tcx).skip_norm_wip() + } // FIXME(generic_const_exprs): we should construct an alias like // `>::Output` when this is an `Expr` representing // `lhs + rhs`. @@ -743,8 +745,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { )); } } - (_, ty::ConstKind::Alias(_, _)) - | (ty::ConstKind::Alias(_, _), _) => (), + (_, ty::ConstKind::Alias(_, _)) | (ty::ConstKind::Alias(_, _), _) => (), (_, _) => { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) @@ -764,7 +765,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let stalled_on = &mut pending_obligation.stalled_on; let mut evaluate = |c: Const<'tcx>| { - if let ty::ConstKind::Alias(_, unevaluated) = c.kind() { + if let ty::ConstKind::Alias(_, alias_const) = c.kind() { match super::try_evaluate_const( self.selcx.infcx, c, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e5e87fe775e1a..1f3d247a2f7d3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -907,8 +907,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } } - (_, ty::ConstKind::Alias(_, _)) - | (ty::ConstKind::Alias(_, _), _) => (), + (_, ty::ConstKind::Alias(_, _)) | (ty::ConstKind::Alias(_, _), _) => (), (_, _) => { if let Ok(InferOk { obligations, value: () }) = self .infcx @@ -987,7 +986,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::ConstKind::Error(_) => return Ok(EvaluatedToOk), ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Alias(_, alias_const) => alias_const.type_of(self.tcx()).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(self.tcx()).skip_norm_wip() + } // FIXME(generic_const_exprs): See comment in `fulfill.rs` ty::ConstKind::Expr(_) => return Ok(EvaluatedToOk), ty::ConstKind::Placeholder(_) => { diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 4ad93b3d9d138..0645839ef0263 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -59,7 +59,9 @@ impl fmt::Debug for ConstKind { Infer(var) => write!(f, "{var:?}"), Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var), Placeholder(placeholder) => write!(f, "{placeholder:?}"), - Alias(is_rigid, alias_const) => write!(f, "Unevaluated({is_rigid:?}, {ualias_const:?})"), + Alias(is_rigid, alias_const) => { + write!(f, "AliasConst({is_rigid:?}, {alias_const:?})") + } Value(val) => write!(f, "{val:?}"), Error(_) => write!(f, "{{const error}}"), Expr(expr) => write!(f, "{expr:?}"), diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index e74c4d2d9333c..ac296f204a914 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -500,9 +500,7 @@ impl { - true - } + ty::ConstKind::Expr(_) | ty::ConstKind::Alias(_, _) | ty::ConstKind::Error(_) => true, ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) => true, } diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 99a1028209e2d..8b1401c0609f9 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -601,9 +601,9 @@ impl TypeFolder for RigidnessFolder { } match c.kind() { - ty::ConstKind::Unevaluated(ty::IsRigid::Yes, uv) => { - let uv = uv.fold_with(self); - I::Const::new_unevaluated(self.cx, ty::IsRigid::No, uv) + ty::ConstKind::Alias(ty::IsRigid::Yes, alias_const) => { + let alias_const = alias_const.fold_with(self); + I::Const::new_alias(self.cx, ty::IsRigid::No, alias_const) } _ => c.super_fold_with(self), } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index bca2614d27f61..77e5fcac07b3f 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -424,7 +424,7 @@ pub trait Term>: _ => false, }, ty::TermKind::Const(ct) => match ct.kind() { - ty::ConstKind::Unevaluated(is_rigid, _) => is_rigid == ty::IsRigid::No, + ty::ConstKind::Alias(is_rigid, _) => is_rigid == ty::IsRigid::No, _ => false, }, } diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 6e7dea661bac1..1c532fef76ad3 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -614,10 +614,7 @@ pub fn structurally_relate_consts>( // While this is slightly incorrect, it shouldn't matter for `min_const_generics` // and is the better alternative to waiting until `generic_const_exprs` can // be stabilized. - ( - ty::ConstKind::Alias(is_rigid_a, au), - ty::ConstKind::Alias(is_rigid_b, bu), - ) => { + (ty::ConstKind::Alias(is_rigid_a, au), ty::ConstKind::Alias(is_rigid_b, bu)) => { // Users shouldn't know about this so the mismatch should be caught // during development rather than presented as type error. debug_assert_eq!(is_rigid_a, is_rigid_b, "{a:?} != {b:?}"); diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 0c3e82b4e6102..d5f9df2249df7 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -239,10 +239,9 @@ where Ok(a) } - ( - ty::ConstKind::Alias(ty::IsRigid::Yes, _), - ty::ConstKind::Alias(ty::IsRigid::Yes, _), - ) if (infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver()) => { + (ty::ConstKind::Alias(ty::IsRigid::Yes, _), ty::ConstKind::Alias(ty::IsRigid::Yes, _)) + if (infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver()) => + { structurally_relate_consts(relation, a, b) } diff --git a/compiler/rustc_type_ir/src/term_kind.rs b/compiler/rustc_type_ir/src/term_kind.rs index 89be81e1f4aaf..aed634d4f3a21 100644 --- a/compiler/rustc_type_ir/src/term_kind.rs +++ b/compiler/rustc_type_ir/src/term_kind.rs @@ -206,13 +206,9 @@ impl AliasTerm { Ty::new_alias(interner, is_rigid, ty::AliasTy::new_from_args(interner, kind, self.args)) .into() }; - let unevaluated_const = |kind| { - I::Const::new_alias( - interner, - is_rigid, - ty::AliasConst::new(interner, kind, self.args), - ) - .into() + let alias_const = |kind| { + I::Const::new_alias(interner, is_rigid, ty::AliasConst::new(interner, kind, self.args)) + .into() }; match self.kind { AliasTermKind::FreeConst { def_id } => alias_const(ty::AliasConstKind::Free { def_id }), diff --git a/tests/mir-opt/issue_99325.main.built.after.32bit.mir b/tests/mir-opt/issue_99325.main.built.after.32bit.mir index 8989903939d42..4a028248d6633 100644 --- a/tests/mir-opt/issue_99325.main.built.after.32bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.32bit.mir @@ -2,7 +2,7 @@ | User Type Annotations | 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [Unevaluated(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [AliasConst(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.64bit.mir b/tests/mir-opt/issue_99325.main.built.after.64bit.mir index 8989903939d42..4a028248d6633 100644 --- a/tests/mir-opt/issue_99325.main.built.after.64bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.64bit.mir @@ -2,7 +2,7 @@ | User Type Annotations | 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [Unevaluated(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [AliasConst(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/ui/offset-of/inside-array-length.stderr b/tests/ui/offset-of/inside-array-length.stderr index 0305603c1f547..0c430f0424ced 100644 --- a/tests/ui/offset-of/inside-array-length.stderr +++ b/tests/ui/offset-of/inside-array-length.stderr @@ -37,7 +37,7 @@ LL | fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {} = note: ...which requires caching mir of `foo::{constant#0}` for CTFE... = note: ...which requires elaborating drops for `foo::{constant#0}`... = note: ...which requires borrow-checking `foo::{constant#0}`... - = note: ...which requires normalizing `Binder { value: ConstEvaluatable(Unevaluated(No, Alias { kind: Anon { def_id: DefId(0:7 ~ inside_array_length[07d6]::foo::{constant#0}) }, args: ['^c_1, T/#1], .. })), bound_vars: [] }`... + = note: ...which requires normalizing `Binder { value: ConstEvaluatable(AliasConst(No, Alias { kind: Anon { def_id: DefId(0:7 ~ inside_array_length[07d6]::foo::{constant#0}) }, args: ['^c_1, T/#1], .. })), bound_vars: [] }`... = note: ...which again requires evaluating type-level constant, completing the cycle = note: cycle used when normalizing `inside_array_length::::foo::{constant#0}` = note: for more information, see and From c200d2fa5625ea8ddcd1e68ca65a6eaaf4b6cbfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 24 Jun 2026 14:53:00 +0200 Subject: [PATCH 184/278] fixup the refactoring errors in 156246 --- .../src/solve/eval_ctxt/mod.rs | 6 +-- .../src/solve/eval_ctxt/probe.rs | 6 +-- .../src/solve/normalizes_to.rs | 50 +++++++------------ .../src/solve/search_graph.rs | 15 ++---- .../src/solve/trait_goals.rs | 17 +++---- 5 files changed, 32 insertions(+), 62 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 05532cdb8924b..fa6de4a431727 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -462,7 +462,7 @@ where Ok(i) => Ok(i), Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution), Err(NoSolutionOrRerunNonErased::RerunNonErased(_)) => { - // check th t the opaque_accesses state mirrors the result we got. + // Check that the opaque_accesses state mirrors the result we got. assert!(opaque_accesses.should_bail().is_err()); Err(NoSolution) } @@ -1442,7 +1442,7 @@ where uv: ty::UnevaluatedConst, ) -> Result, RerunNonErased> { if self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)?; + match self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)? {} } Ok(self.delegate.evaluate_const(param_env, uv)) @@ -1515,7 +1515,7 @@ where symbol: I::Symbol, ) -> Result { if self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always(RerunReason::MayUseUnstableFeature)?; + match self.opaque_accesses.rerun_always(RerunReason::MayUseUnstableFeature)? {} } Ok(may_use_unstable_feature(&**self.delegate, param_env, symbol)) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index 1c5d6e0b14c65..d9d18bdeea7e7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -98,10 +98,8 @@ where outer.opaque_accesses.update(nested.opaque_accesses)?; - let r = match r.map_err_to_rerun()? { - Ok(i) => Ok(i), - Err(NoSolution) => Err(NoSolution), - }; + // Unwrap is unreachable, we would have returned on the line above. + let r = r.map_err_to_rerun().unwrap(); if !nested.inspect.is_noop() { let probe_kind = probe_kind(&r); diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index e0290dfbdebdf..eeba18434ccb8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -81,15 +81,10 @@ where None }, |ecx| { - ecx.probe(|&result| ProbeKind::RigidAlias { result }) - .enter(|this| { - this.structurally_instantiate_normalizes_to_term( - goal, - goal.predicate.alias, - ); - this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - .map_err(Into::into) + ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| { + this.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) }, ) } @@ -351,11 +346,9 @@ where GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous), )?; - return ecx - .evaluate_added_goals_and_make_canonical_response( - Certainty::Yes, - ) - .map_err(Into::into); + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::Yes, + ); } // Outside of coherence, we treat the associated item as rigid instead. ty::TypingMode::Typeck { .. } @@ -367,11 +360,9 @@ where goal, goal.predicate.alias, ); - return ecx - .evaluate_added_goals_and_make_canonical_response( - Certainty::Yes, - ) - .map_err(Into::into); + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::Yes, + ); } }; } @@ -401,10 +392,10 @@ where // This is not the case here and we only prefer adding an ambiguous // nested goal for consistency. ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous))?; - return then(ecx, Certainty::Yes).map_err(Into::into); + return then(ecx, Certainty::Yes); } else { ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); - return then(ecx, Certainty::Yes).map_err(Into::into); + return then(ecx, Certainty::Yes); } } else { return error_response(ecx, cx.delay_bug("missing item")); @@ -472,7 +463,7 @@ where }; ecx.instantiate_normalizes_to_term(goal, term)?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -572,7 +563,6 @@ where pred, [(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))], ) - .map_err(Into::into) } fn consider_builtin_async_fn_trait_candidates( @@ -759,8 +749,9 @@ where // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`. // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't // exist. Instead, `Pointee` should be a supertrait of `Sized`. - let alias_bound_result = - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let alias_bound_result = ecx + .probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| { let sized_predicate = ty::TraitRef::new( cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), @@ -769,12 +760,8 @@ where ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate))?; ecx.instantiate_normalizes_to_term(goal, Ty::new_unit(cx).into())?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); - - let alias_bound_result = match alias_bound_result.map_err_to_rerun()? { - Ok(i) => Ok(i), - Err(NoSolution) => Err(NoSolution), - }; + }) + .map_err_to_rerun()?; // In case the dummy alias-bound candidate does not apply, we instead treat this projection // as rigid. @@ -900,7 +887,6 @@ where // but that's already proven by the generator being WF. [], ) - .map_err(Into::into) } fn consider_builtin_fused_iterator_candidate( diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 549d5d903c045..778826ba60aee 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -4,7 +4,7 @@ use std::marker::PhantomData; use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{ - AccessedOpaques, CanonicalInput, Certainty, NoSolution, NoSolutionOrRerunNonErased, QueryResult, + AccessedOpaques, CanonicalInput, Certainty, NoSolution, QueryResult, RerunResultExt, }; use rustc_type_ir::{Interner, MayBeErased, TypingMode}; @@ -141,17 +141,8 @@ where ) -> (QueryResult, AccessedOpaques) { ensure_sufficient_stack(|| { EvalCtxt::enter_canonical(cx, search_graph, input, inspect, |ecx, goal| { - let result = ecx.compute_goal(goal); - - // if we're in `RerunNonErased`, don't even bother with inspect, - // and immediately return - let result = match result { - Ok(i) => Ok(i), - Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution), - Err(NoSolutionOrRerunNonErased::RerunNonErased(e)) => { - return Err(e.into()); - } - }; + // if we're in `RerunNonErased`, don't even bother with inspect, and immediately return + let result = ecx.compute_goal(goal).map_err_to_rerun()?; ecx.inspect.query_result(result); result.map_err(Into::into) diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 79e0af72710fb..5c002d09a75cc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -119,7 +119,7 @@ where .map(|pred| goal.with(cx, pred)), )?; - then(ecx, maximal_certainty).map_err(Into::into) + then(ecx, maximal_certainty) }) } @@ -399,7 +399,6 @@ where pred, [(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))], ) - .map_err(Into::into) } fn consider_builtin_async_fn_trait_candidates( @@ -450,7 +449,6 @@ where .chain(nested_preds.into_iter().map(|pred| goal.with(cx, pred))) .map(|goal| (GoalSource::ImplWhereBound, goal)), ) - .map_err(Into::into) } fn consider_builtin_async_fn_kind_helper_candidate( @@ -696,7 +694,7 @@ where goal.predicate.trait_ref.args.type_at(1), assume, )?; - ecx.evaluate_added_goals_and_make_canonical_response(certainty).map_err(Into::into) + ecx.evaluate_added_goals_and_make_canonical_response(certainty) }, ) } @@ -1085,7 +1083,6 @@ where ecx.try_evaluate_added_goals() }, ) - .map_err(Into::into) }) .is_ok() }; @@ -1124,11 +1121,9 @@ where return Err(NoSolution.into()); }; if matching_projections.next().is_some() { - return ecx - .evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ) - .map_err(Into::into); + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); } ecx.enter_forall_with_assumptions( target_projection, @@ -1156,7 +1151,7 @@ where Goal::new(ecx.cx(), param_env, ty::OutlivesPredicate(a_region, b_region)), )?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } From 91c04dfcad15fc8d72681f5578a1183f5f29b332 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 25 Jun 2026 18:50:36 +0200 Subject: [PATCH 185/278] cg_gcc: Fix Clippy lint fallout --- compiler/rustc_codegen_gcc/src/common.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index dd0064d34bc4a..b9b6e51d3b82c 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -58,13 +58,19 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> // or is it using a more efficient representation? match bytes.len() % 8 { 0 => { + debug_assert_eq!( + bytes.len() % 8, + 0, + "bytes length is not a multiple of 8, so bytes.as_chunks will have a remainder" + ); let context = &cx.context; let byte_type = context.new_type::(); let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 8); let elements: Vec<_> = bytes - .chunks_exact(8) - .map(|arr| { - let arr: [u8; 8] = arr.try_into().unwrap(); + .as_chunks::<8>() + .0 + .iter() + .map(|&arr| { context.new_rvalue_from_long( byte_type, // Since we are representing arbitrary byte runs as integers, we need to follow the target @@ -79,13 +85,19 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> context.new_array_constructor(None, typ, &elements) } 4 => { + debug_assert_eq!( + bytes.len() % 4, + 0, + "bytes length is not a multiple of 4, so bytes.as_chunks will have a remainder" + ); let context = &cx.context; let byte_type = context.new_type::(); let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 4); let elements: Vec<_> = bytes - .chunks_exact(4) - .map(|arr| { - let arr: [u8; 4] = arr.try_into().unwrap(); + .as_chunks::<4>() + .0 + .iter() + .map(|&arr| { context.new_rvalue_from_int( byte_type, match cx.sess().target.options.endian { From 6c52a7089765e3afcb2394a229f7c25429e3fabf Mon Sep 17 00:00:00 2001 From: teor Date: Mon, 22 Jun 2026 15:05:25 +0200 Subject: [PATCH 186/278] Limit splat AST and FnDecl to 255 args And remove redundant const fn --- compiler/rustc_ast/src/ast.rs | 10 +++-- compiler/rustc_ast_lowering/src/delegation.rs | 2 +- .../rustc_ast_passes/src/ast_validation.rs | 6 +-- compiler/rustc_hir/src/hir.rs | 41 ++++++++++--------- compiler/rustc_type_ir/src/macros.rs | 1 + 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 11a531353eba0..bd5ce5d18b839 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3059,13 +3059,15 @@ impl FnDecl { } /// The marker index for "no splatted arguments". + /// Higher values are also not supported, for performance reasons. + /// /// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `FnDeclFlags::NO_SPLATTED_ARG_INDEX`. - pub const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX; + pub const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; /// Returns a splatted argument index, if any are present. - pub fn splatted(&self) -> Option { + pub fn splatted(&self) -> Option { self.inputs.iter().enumerate().find_map(|(index, arg)| { - if index == Self::NO_SPLATTED_ARG_INDEX as usize { + if index >= usize::from(Self::NO_SPLATTED_ARG_INDEX) { // AST validation has already checked the splatted argument index is valid, so just // ignore invalid indexes here. None @@ -3073,7 +3075,7 @@ impl FnDecl { arg.attrs .iter() .any(|attr| attr.has_name(sym::splat)) - .then_some(u16::try_from(index).unwrap()) + .then_some(u8::try_from(index).unwrap()) } }) } diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index fc466246bf137..a631f8e046638 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -93,7 +93,7 @@ struct ParamInfo { pub c_variadic: bool, /// The index of the splatted parameter, if any. - pub splatted: Option, + pub splatted: Option, } const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO; diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 186a43ca1693b..06925994b052c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -412,11 +412,11 @@ impl<'a> AstValidator<'a> { }) .unzip(); - // A splatted argument at the "no splatted" marker index is not supported (this is an - // unlikely edge case). + // A splatted argument greater than or equal to the "no splatted" marker index is not + // supported. if let (Some(&splatted_arg_index), Some(&splatted_span)) = (splatted_arg_indexes.last(), splatted_spans.last()) - && splatted_arg_index == FnDecl::NO_SPLATTED_ARG_INDEX + && splatted_arg_index >= u16::from(FnDecl::NO_SPLATTED_ARG_INDEX) { self.dcx().emit_err(diagnostics::InvalidSplattedArg { splatted_arg_index, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f429d14e3a51c..6d2e68d20bf30 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4045,12 +4045,12 @@ pub struct Param<'hir> { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SplattedArgIndexError { /// The splatted argument index is invalid. - /// Currently the only unsupported index is `u16::MAX`, which is used to indicate that no argument - /// is splatted. - InvalidIndex { splatted_arg_index: u16 }, + /// A `u8::MAX` argument index used to indicate that no argument is splatted. + /// Higher values are also not supported, for performance reasons. + InvalidIndex { splatted_arg_index: u8 }, /// The splatted argument index is outside the bounds of the function arguments. - OutOfBounds { splatted_arg_index: u16, args_len: u16 }, + OutOfBounds { splatted_arg_index: u8, args_len: u16 }, } /// Contains the packed non-type fields of a function declaration. @@ -4061,9 +4061,9 @@ pub struct FnDeclFlags { flags: u8, /// Which function argument is splatted into multiple arguments in callers, if any? - /// Splatting functions with `u16::MAX` arguments is not supported, see `FnSigKind` for + /// Splatting functions with `>= u8::MAX` arguments is not supported, see `FnSigKind` for /// details. - splatted: u16, + splatted: u8, } impl fmt::Debug for FnDeclFlags { @@ -4101,13 +4101,13 @@ impl FnDeclFlags { /// Marker index for "no splatted argument". /// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`. - const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX; + const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; /// Create a new FnDeclKind with no implicit self, no lifetime elision, no C-style variadic /// argument, and no splatting. /// To modify these flags, use the `set_*` methods, for readability. // FIXME: use Default instead when that trait is const stable. - pub const fn default() -> Self { + pub fn default() -> Self { Self { flags: 0, splatted: 0 } .set_implicit_self(ImplicitSelfKind::None) .set_lifetime_elision_allowed(false) @@ -4117,7 +4117,7 @@ impl FnDeclFlags { /// Set the implicit self kind. #[must_use = "this method does not modify the receiver"] - pub const fn set_implicit_self(mut self, implicit_self: ImplicitSelfKind) -> Self { + pub fn set_implicit_self(mut self, implicit_self: ImplicitSelfKind) -> Self { self.flags &= !Self::IMPLICIT_SELF_MASK; match implicit_self { @@ -4133,7 +4133,7 @@ impl FnDeclFlags { /// Set the C-style variadic argument flag. #[must_use = "this method does not modify the receiver"] - pub const fn set_c_variadic(mut self, c_variadic: bool) -> Self { + pub fn set_c_variadic(mut self, c_variadic: bool) -> Self { if c_variadic { self.flags |= Self::C_VARIADIC_FLAG; } else { @@ -4145,7 +4145,7 @@ impl FnDeclFlags { /// Set the lifetime elision allowed flag. #[must_use = "this method does not modify the receiver"] - pub const fn set_lifetime_elision_allowed(mut self, allowed: bool) -> Self { + pub fn set_lifetime_elision_allowed(mut self, allowed: bool) -> Self { if allowed { self.flags |= Self::LIFETIME_ELISION_ALLOWED_FLAG; } else { @@ -4158,16 +4158,17 @@ impl FnDeclFlags { /// Set the splatted argument index. /// The number of function arguments is used for error checking. #[must_use = "this method does not modify the receiver"] - pub const fn set_splatted( + pub fn set_splatted( mut self, - splatted: Option, + splatted: Option, args_len: usize, ) -> Result { if let Some(splatted_arg_index) = splatted { if splatted_arg_index == Self::NO_SPLATTED_ARG_INDEX { // This index value is used as a marker for "no splatting", so it is unsupported. + // Higher values are also not supported, for performance reasons. return Err(SplattedArgIndexError::InvalidIndex { splatted_arg_index }); - } else if splatted_arg_index as usize >= args_len { + } else if usize::from(splatted_arg_index) >= args_len { return Err(SplattedArgIndexError::OutOfBounds { splatted_arg_index, args_len: args_len as u16, @@ -4184,14 +4185,14 @@ impl FnDeclFlags { /// Set "no splatted arguments" for the function declaration. #[must_use = "this method does not modify the receiver"] - pub const fn set_no_splatted_args(mut self) -> Self { + pub fn set_no_splatted_args(mut self) -> Self { self.splatted = Self::NO_SPLATTED_ARG_INDEX; self } /// Get the implicit self kind. - pub const fn implicit_self(self) -> ImplicitSelfKind { + pub fn implicit_self(self) -> ImplicitSelfKind { match self.flags & Self::IMPLICIT_SELF_MASK { 0 => ImplicitSelfKind::None, 1 => ImplicitSelfKind::Imm, @@ -4203,17 +4204,17 @@ impl FnDeclFlags { } /// Do the function arguments end with a C-style variadic argument? - pub const fn c_variadic(self) -> bool { + pub fn c_variadic(self) -> bool { self.flags & Self::C_VARIADIC_FLAG != 0 } /// Is lifetime elision allowed? - pub const fn lifetime_elision_allowed(self) -> bool { + pub fn lifetime_elision_allowed(self) -> bool { self.flags & Self::LIFETIME_ELISION_ALLOWED_FLAG != 0 } /// Get the splatted argument index, if any. - pub const fn splatted(self) -> Option { + pub fn splatted(self) -> Option { if self.splatted == Self::NO_SPLATTED_ARG_INDEX { None } else { Some(self.splatted) } } } @@ -4263,7 +4264,7 @@ impl<'hir> FnDecl<'hir> { self.fn_decl_kind.lifetime_elision_allowed() } - pub fn splatted(&self) -> Option { + pub fn splatted(&self) -> Option { self.fn_decl_kind.splatted() } diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index 857738d207b4f..ea92699958094 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -45,6 +45,7 @@ TrivialTypeTraversalImpls! { (), bool, usize, + u8, u16, u32, u64, From 5d0c6bad9418631109c36e656c6f32435136c89c Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 10 Jun 2026 12:05:50 +0200 Subject: [PATCH 187/278] Impl HIR FnSig for #[splat] --- compiler/rustc_ast_lowering/src/delegation.rs | 3 +- compiler/rustc_hir_analysis/src/check/mod.rs | 4 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 11 +- .../src/hir_ty_lowering/mod.rs | 5 +- compiler/rustc_hir_typeck/src/closure.rs | 1 + compiler/rustc_hir_typeck/src/diagnostics.rs | 1 + compiler/rustc_lint/src/foreign_modules.rs | 8 + compiler/rustc_middle/src/ty/context.rs | 2 + compiler/rustc_middle/src/ty/error.rs | 14 ++ .../src/unstable/convert/internal.rs | 1 + .../src/error_reporting/infer/mod.rs | 10 ++ .../src/error_reporting/traits/suggestions.rs | 1 + compiler/rustc_type_ir/src/error.rs | 3 +- compiler/rustc_type_ir/src/relate.rs | 4 + compiler/rustc_type_ir/src/ty_kind.rs | 155 +++++++++++++++--- compiler/rustc_type_ir/src/ty_kind/closure.rs | 2 +- src/tools/miri/src/helpers.rs | 2 + src/tools/miri/src/shims/sig.rs | 2 +- .../crates/hir-ty/src/infer/callee.rs | 2 + .../rust-analyzer/crates/hir-ty/src/lib.rs | 1 + .../rust-analyzer/crates/hir-ty/src/lower.rs | 1 + .../crates/hir-ty/src/next_solver/interner.rs | 1 + .../crates/hir-ty/src/next_solver/ty.rs | 1 + tests/ui/symbol-names/basic.legacy.stderr | 4 +- .../ui/symbol-names/issue-60925.legacy.stderr | 4 +- 25 files changed, 210 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index a631f8e046638..078ebd6987ae3 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -364,11 +364,10 @@ impl<'hir> LoweringContext<'_, 'hir> { fn param_info(&self, def_id: DefId) -> ParamInfo { let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder(); - // FIXME(splat): use `sig.splatted()` once FnSig has it ParamInfo { param_count: sig.inputs().len() + usize::from(sig.c_variadic()), c_variadic: sig.c_variadic(), - splatted: None, + splatted: sig.splatted(), } } diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index e1bbc8f0641de..9d8e0a512ab66 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -444,11 +444,13 @@ fn fn_sig_suggestion<'tcx>( predicates: impl IntoIterator, Span)>, assoc: ty::AssocItem, ) -> String { + let splatted_arg_index = sig.splatted().map(usize::from); let args = sig .inputs() .iter() .enumerate() .map(|(i, ty)| { + let splat = if splatted_arg_index == Some(i) { "#[splat] " } else { "" }; let arg_ty = match ty.kind() { ty::Param(_) if assoc.is_method() && i == 0 => "self".to_string(), ty::Ref(reg, ref_ty, mutability) if i == 0 => { @@ -477,7 +479,7 @@ fn fn_sig_suggestion<'tcx>( } } }; - Some(format!("{arg_ty}")) + Some(format!("{splat}{arg_ty}")) }) .chain(std::iter::once(if sig.c_variadic() { Some("...".to_string()) } else { None })) .flatten() diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index bf8593eb61882..b89fc81190d43 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1714,7 +1714,16 @@ fn check_fn_or_method<'tcx>( let span = tcx.def_span(def_id); let has_implicit_self = hir_decl.implicit_self().has_implicit_self(); let mut inputs = sig.inputs().iter().skip(if has_implicit_self { 1 } else { 0 }); - // FIXME(splat): use `sig.splatted()` once FnSig has it + // FIXME(splat): support the rest of closure splatting, or replace this code with an error + if let Some(mut splatted_arg_index) = sig.splatted() { + let mut inputs_count = sig.inputs().len(); + if has_implicit_self { + splatted_arg_index = splatted_arg_index.strict_sub(1); + inputs_count = inputs_count.strict_sub(1); + } + debug!(?splatted_arg_index, ?inputs_count, ?has_implicit_self, ?sig); + sig = sig.set_splatted(Some(splatted_arg_index), inputs_count).unwrap(); + } // Check that the argument is a tuple and is sized if let Some(ty) = inputs.next() { wfcx.register_bound( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index baa9fddc2a651..95a91f1444404 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -3582,11 +3582,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?output_ty); debug!(?abi, ?safety, ?decl.fn_decl_kind, input_tys_len = ?input_tys.len()); - // FIXME(splat): use `set_splatted()` once FnSig has it let fn_sig_kind = FnSigKind::default() .set_abi(abi) .set_safety(safety) - .set_c_variadic(decl.fn_decl_kind.c_variadic()); + .set_c_variadic(decl.fn_decl_kind.c_variadic()) + .set_splatted(decl.splatted(), input_tys.len()) + .unwrap(); let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, fn_sig_kind); let fn_ptr_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 41aebea1c7ebe..95e67b135e411 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -720,6 +720,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // in this binder we are creating. assert!(!expected_sig.sig.skip_binder().has_vars_bound_above(ty::INNERMOST)); let bound_sig = expected_sig.sig.map_bound(|sig| { + // Ignore splatting, it is unsupported on closures. let fn_sig_kind = FnSigKind::default() .set_abi(ExternAbi::RustCall) .set_safety(hir::Safety::Safe) diff --git a/compiler/rustc_hir_typeck/src/diagnostics.rs b/compiler/rustc_hir_typeck/src/diagnostics.rs index a9a819935287c..a5c39bd1584a1 100644 --- a/compiler/rustc_hir_typeck/src/diagnostics.rs +++ b/compiler/rustc_hir_typeck/src/diagnostics.rs @@ -100,6 +100,7 @@ impl IntoDiagArg for ReturnLikeStatementKind { } } +// FIXME(splat): add "non-splatted" to all 4 instances of this error message #[derive(Diagnostic)] #[diag("functions with the \"rust-call\" ABI must take a single non-self tuple argument")] pub(crate) struct RustCallIncorrectArgs { diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 27a21381ffce9..3010eadb61057 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -329,6 +329,14 @@ fn structurally_same_type_impl<'tcx>( let a_sig = tcx.instantiate_bound_regions_with_erased(a_poly_sig); let b_sig = tcx.instantiate_bound_regions_with_erased(b_poly_sig); + // FIXME(splat): Is splatting ever repr(C)? + // Can two splatted functions to have the same structure? + // Can a splatted and non-splatted function have the same structure? + // For now, we require splatting to match exactly. + if a_sig.splatted() != b_sig.splatted() { + return false; + } + (a_sig.abi(), a_sig.safety(), a_sig.c_variadic()) == (b_sig.abi(), b_sig.safety(), b_sig.c_variadic()) && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5e87dd7667c14..569a1d5786095 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2085,6 +2085,8 @@ impl<'tcx> TyCtxt<'tcx> { ty::Tuple(params) => *params, _ => bug!(), }; + // Ignore splatting, it is unsupported on closures. + assert!(s.splatted().is_none()); self.mk_fn_sig( params, s.output(), diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 23b5899b0cc39..208e94270a45e 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -96,6 +96,20 @@ impl<'tcx> TypeError<'tcx> { if values.found { "variadic" } else { "non-variadic" } ) .into(), + TypeError::SplatMismatch(ref values) => format!( + "expected fn with {}, found fn with {}", + if let Some(index) = values.expected { + format!("arg {index} splatted") + } else { + "no splatted arg".to_string() + }, + if let Some(index) = values.found { + format!("arg {index} splatted") + } else { + "no splatted arg".to_string() + } + ) + .into(), TypeError::ProjectionMismatched(ref values) => format!( "expected `{}`, found `{}`", tcx.alias_term_kind_def_path_str(values.expected), diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 81b32f4da10f6..181498443073d 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -312,6 +312,7 @@ impl RustcInternal for FnSig { tables: &mut Tables<'_, BridgeTys>, tcx: impl InternalCx<'tcx>, ) -> Self::T<'tcx> { + // FIXME(splat): When `#[splat]` is complete (or stable), add splatted to the public FnSig let fn_sig_kind = rustc_ty::FnSigKind::default() .set_abi(self.abi.internal(tables, tcx)) .set_safety(self.safety.internal(tables, 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 3aaf17095a6ce..9822e8cdef8b2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -823,9 +823,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // ^^^^^ let len1 = sig1.inputs().len(); let len2 = sig2.inputs().len(); + let splatted_arg_index1 = sig1.splatted().map(usize::from); + let splatted_arg_index2 = sig2.splatted().map(usize::from); if len1 == len2 { for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { self.push_comma(&mut values.0, &mut values.1, i); + if Some(i) == splatted_arg_index1 { + values.0.push("#[splat]", splatted_arg_index1 != splatted_arg_index2); + values.0.push_normal(" "); + } + if Some(i) == splatted_arg_index2 { + values.1.push("#[splat]", splatted_arg_index1 != splatted_arg_index2); + values.1.push_normal(" "); + } let (x1, x2) = self.cmp(*l, *r); (values.0).0.extend(x1.0); (values.1).0.extend(x2.0); 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 d7dfce166742b..4e3a31f0e84b5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4962,6 +4962,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let fn_sig @ ty::FnSig { .. } = fn_ty.fn_sig(tcx).skip_binder() + // FIXME(splat): this might need to change if the Fn* traits start using/supporting splat && fn_sig.abi() == ExternAbi::Rust && !fn_sig.c_variadic() && fn_sig.safety() == hir::Safety::Safe diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index c08cec21e5b1f..74abfad6e5abc 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -41,6 +41,7 @@ pub enum TypeError { ArgumentSorts(ExpectedFound, usize), Traits(ExpectedFound), VariadicMismatch(ExpectedFound), + SplatMismatch(ExpectedFound>), /// Instantiating a type variable with the given type would have /// created a cycle (because it appears somewhere within that @@ -76,7 +77,7 @@ impl TypeError { match self { CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | ArraySize(_) | ArgumentSorts(..) | Sorts(_) - | VariadicMismatch(_) | TargetFeatureCast(_) => false, + | VariadicMismatch(_) | SplatMismatch(_) | TargetFeatureCast(_) => false, Mutability | ArgumentMutability(_) diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index d64cc2450eb19..a683252f03716 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -169,6 +169,10 @@ impl Relate for ty::FnSig { return Err(TypeError::AbiMismatch(ExpectedFound::new(a.abi(), b.abi()))); }; + if a.splatted() != b.splatted() { + return Err(TypeError::SplatMismatch(ExpectedFound::new(a.splatted(), b.splatted()))); + } + let a_inputs = a.inputs(); let b_inputs = b.inputs(); if a_inputs.len() != b_inputs.len() { diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 89fac9d200214..31824b61cfb27 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -888,8 +888,19 @@ pub struct TypeAndMut { impl Eq for TypeAndMut {} +/// Error type for splatted argument index errors. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SplattedArgIndexError { + /// The splatted argument index is invalid. + /// A `u8::MAX` argument index used to indicate that no argument is splatted. + /// Higher values are also not supported, for performance reasons. + InvalidIndex { splatted_arg_index: u8 }, + + /// The splatted argument index is outside the bounds of the function arguments. + OutOfBounds { splatted_arg_index: u8, args_len: u16 }, +} + /// Contains the packed non-type fields of a function signature. -// FIXME(splat): add the splatted argument index as a u16 #[derive_where(Copy, Clone, PartialEq, Eq, Hash; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( @@ -903,6 +914,15 @@ pub struct FnSigKind { #[type_visitable(ignore)] #[type_foldable(identity)] flags: u8, + + /// Which function argument is splatted into multiple arguments in callers, if any? + /// Splatting functions with `>= u8::MAX` arguments is not supported, for performance reasons. + /// (And spending an extra byte on an edge case is not worth the perf.) + #[lift(identity)] + #[type_visitable(ignore)] + #[type_foldable(identity)] + splatted: u8, + #[type_visitable(ignore)] #[type_foldable(identity)] _marker: PhantomData I>, @@ -922,12 +942,28 @@ impl fmt::Debug for FnSigKind { if self.c_variadic() { f.field(&"CVariadic"); - }; + } + + if let Some(index) = self.splatted() { + f.field(&format!("Splatted({})", index)); + } f.finish() } } +impl Default for FnSigKind { + /// Create a new FnSigKind with the "Rust" ABI, "Unsafe" safety, and no C-style variadic or splatted arguments. + /// To modify these flags, use the `set_*` methods, for readability. + fn default() -> Self { + Self { flags: 0, splatted: 0, _marker: PhantomData } + .set_abi(ExternAbi::Rust) + .set_safety(I::Safety::unsafe_mode()) + .set_c_variadic(false) + .set_no_splatted_args() + } +} + impl FnSigKind { /// Mask for the `ExternAbi` variant, including the unwind flag. const EXTERN_ABI_MASK: u8 = 0b111111; @@ -938,19 +974,34 @@ impl FnSigKind { /// Bitflag for a trailing C-style variadic argument. const C_VARIADIC_FLAG: u8 = 1 << 7; - /// Create a new FnSigKind with the "Rust" ABI, "Unsafe" safety, and no C-style variadic argument. - /// To modify these flags, use the `set_*` methods, for readability. - // FIXME: use Default instead when that trait is const stable. - pub fn default() -> Self { - Self { flags: 0, _marker: PhantomData } - .set_abi(ExternAbi::Rust) - .set_safety(I::Safety::unsafe_mode()) - .set_c_variadic(false) - } + /// The marker index for "no splatted arguments". Higher values are also not supported, for + /// performance reasons. + /// + /// Must have the same value as `FnDeclFlags::NO_SPLATTED_ARG_INDEX` and + /// `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`. + /// + /// This is an implementation detail, which should only be used in low-level encoding. + pub const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; - /// Create a new FnSigKind with the given ABI, safety, and C-style variadic flag. - pub fn new(abi: ExternAbi, safety: I::Safety, c_variadic: bool) -> Self { - Self::default().set_abi(abi).set_safety(safety).set_c_variadic(c_variadic) + /// Create a new FnSigKind with the given ABI, safety, C-style variadic, and splatted argument + /// index. + pub fn new( + abi: ExternAbi, + safety: I::Safety, + c_variadic: bool, + splatted: Option, + args_len: usize, + ) -> Result { + Self::default() + .set_abi(abi) + .set_safety(safety) + .set_c_variadic(c_variadic) + .set_splatted(splatted, args_len) + } + + /// Create a new safe FnSigKind with the `extern "Rust"` ABI, that isn't C-style variadic or splatted. + pub fn dummy() -> Self { + Self::default().set_safety(I::Safety::safe()) } /// Set the ABI, including the unwind flag. @@ -989,6 +1040,41 @@ impl FnSigKind { self } + /// Set the splatted argument index. + /// The number of function arguments is used for error checking. + #[must_use = "this method does not modify the receiver"] + pub fn set_splatted( + mut self, + splatted: Option, + args_len: usize, + ) -> Result { + if let Some(splatted_arg_index) = splatted { + if splatted_arg_index == Self::NO_SPLATTED_ARG_INDEX { + // This index value is used as a marker for "no splatting", so it is unsupported. + // Higher values are also not supported, for performance reasons. + return Err(SplattedArgIndexError::InvalidIndex { splatted_arg_index }); + } else if usize::from(splatted_arg_index) >= args_len { + return Err(SplattedArgIndexError::OutOfBounds { + splatted_arg_index, + args_len: args_len as u16, + }); + } + + self.splatted = splatted_arg_index; + } else { + self.splatted = Self::NO_SPLATTED_ARG_INDEX; + } + + Ok(self) + } + + /// Set the splatted argument index to "no splatted arguments". + #[must_use = "this method does not modify the receiver"] + pub fn set_no_splatted_args(mut self) -> Self { + self.splatted = Self::NO_SPLATTED_ARG_INDEX; + self + } + /// Get the ABI, including the unwind flag. pub fn abi(self) -> ExternAbi { let abi_index = self.flags & Self::EXTERN_ABI_MASK; @@ -1009,6 +1095,11 @@ impl FnSigKind { pub fn c_variadic(self) -> bool { self.flags & Self::C_VARIADIC_FLAG != 0 } + + /// Get the index of the splatted argument, if any. + pub fn splatted(self) -> Option { + if self.splatted == Self::NO_SPLATTED_ARG_INDEX { None } else { Some(self.splatted) } + } } #[derive_where(Clone, Copy, PartialEq, Hash; I: Interner)] @@ -1039,8 +1130,21 @@ impl FnSig { !self.c_variadic() && self.safety().is_safe() && self.abi() == ExternAbi::Rust } + /// Set the safety flag. + #[must_use = "this method does not modify the receiver"] pub fn set_safety(self, safety: I::Safety) -> Self { - Self { fn_sig_kind: FnSigKind::new(self.abi(), safety, self.c_variadic()), ..self } + Self { fn_sig_kind: self.fn_sig_kind.set_safety(safety), ..self } + } + + /// Set the splatted argument index. + /// The number of function arguments is used for error checking. + #[must_use = "this method does not modify the receiver"] + pub fn set_splatted( + self, + splatted: Option, + args_len: usize, + ) -> Result { + Ok(Self { fn_sig_kind: self.fn_sig_kind.set_splatted(splatted, args_len)?, ..self }) } pub fn safety(self) -> I::Safety { @@ -1055,11 +1159,14 @@ impl FnSig { self.fn_sig_kind.c_variadic() } + pub fn splatted(self) -> Option { + self.fn_sig_kind.splatted() + } + + /// Create a new safe FnSig with no arguments or return type, using the `extern "Rust"` ABI, + /// that isn't C-style variadic or splatted. pub fn dummy() -> Self { - Self { - inputs_and_output: Default::default(), - fn_sig_kind: FnSigKind::new(ExternAbi::Rust, I::Safety::safe(), false), - } + Self { inputs_and_output: Default::default(), fn_sig_kind: FnSigKind::dummy() } } } @@ -1092,6 +1199,10 @@ impl ty::Binder> { self.skip_binder().c_variadic() } + pub fn splatted(self) -> Option { + self.skip_binder().splatted() + } + pub fn safety(self) -> I::Safety { self.skip_binder().safety() } @@ -1127,6 +1238,9 @@ impl fmt::Debug for FnSig { if i > 0 { write!(f, ", ")?; } + if Some(i) == fn_sig_kind.splatted().map(usize::from) { + write!(f, "#[splat] ")?; + } write!(f, "{ty:?}")?; } if fn_sig_kind.c_variadic() { @@ -1288,8 +1402,9 @@ impl FnHeader { self.fn_sig_kind.abi() } + /// Create a new safe FnHeader with the `extern "Rust"` ABI, that isn't C-style variadic or splatted. pub fn dummy() -> Self { - Self { fn_sig_kind: FnSigKind::new(ExternAbi::Rust, I::Safety::safe(), false) } + Self { fn_sig_kind: FnSigKind::dummy() } } } diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index b688a001f73fa..9453d30ce91c9 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -364,7 +364,7 @@ pub struct CoroutineClosureSignature { // Like the `fn_sig_as_fn_ptr_ty` of a regular closure, these types // never actually differ. But we save them rather than recreating them // from scratch just for good measure. - /// Always safe, RustCall, non-c-variadic + /// Always safe, RustCall, non-c-variadic, non-splatted #[type_visitable(ignore)] #[type_foldable(identity)] pub fn_sig_kind: FnSigKind, diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 730c7d9fac611..58fe7dc541fc3 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -406,6 +406,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let sig = this.tcx.mk_fn_sig( args.iter().map(|a| a.layout.ty), dest.layout.ty, + // FIXME(splat): Do we need to set splatted here? + // (Currently this also ignores c_variadic) FnSigKind::default().set_abi(caller_abi).set_safety(rustc_hir::Safety::Safe), ); let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index 68b13a6ed58a0..99673319240fe 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -274,7 +274,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { inputs_and_output.push(shim_sig.ret); let fn_sig_binder = Binder::dummy(FnSig { inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output), - // Safety does not matter for the ABI. + // Safety and splatted do not matter for the ABI. fn_sig_kind: FnSigKind::default() .set_abi(shim_sig.abi) .set_safety(rustc_hir::Safety::Safe), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index 057ba7fa868fe..a661731e60f46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -173,6 +173,8 @@ impl<'db> InferenceContext<'_, 'db> { // impl forces the closure kind to `FnOnce` i.e. `u8`. let kind_ty = autoderef.ctx().table.next_ty_var(call_expr.into()); let interner = autoderef.ctx().interner(); + + // Ignore splatting, it is unsupported on closures. let call_sig = interner.mk_fn_sig( [coroutine_closure_sig.tupled_inputs_ty], coroutine_closure_sig.to_coroutine( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index cc48ba06dbfc5..1fddfc09c666f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -464,6 +464,7 @@ pub fn callable_sig_from_fn_trait<'db>( args.tuple_fields(), ret, false, + // FIXME(splat): handle splatted arguments Safety::Safe, ExternAbi::Rust, )); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index fae63ddc2deae..d63ac7f7a0ed4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -637,6 +637,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { fn_.abi, if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, fn_.is_varargs, + // FIXME(splat): handle splatted arguments ), inputs_and_output: Tys::new_from_slice(&args), }), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index ef626fc0c8869..3c2976eccd980 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -2238,6 +2238,7 @@ impl<'db> DbInterner<'db> { self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate) } + // FIXME: add splat support when the experiment is complete pub fn mk_fn_sig( self, inputs: I, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index fe31d44207dff..d57d824e325fe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -1514,6 +1514,7 @@ impl<'db> DbInterner<'db> { TyKind::Tuple(params) => params, _ => panic!(), }; + // Ignore splatting, it is unsupported on closures. self.mk_fn_sig(params, s.output(), s.c_variadic(), safety, ExternAbi::Rust) }) } diff --git a/tests/ui/symbol-names/basic.legacy.stderr b/tests/ui/symbol-names/basic.legacy.stderr index f88fdf7509c76..59085aeb3279b 100644 --- a/tests/ui/symbol-names/basic.legacy.stderr +++ b/tests/ui/symbol-names/basic.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN5basic4main17h27f62b2d0b2beac6E) +error: symbol-name(_ZN5basic4main17h33f35fba43592008E) --> $DIR/basic.rs:8:1 | LL | #[rustc_dump_symbol_name] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: demangling(basic::main::h27f62b2d0b2beac6) +error: demangling(basic::main::h33f35fba43592008) --> $DIR/basic.rs:8:1 | LL | #[rustc_dump_symbol_name] diff --git a/tests/ui/symbol-names/issue-60925.legacy.stderr b/tests/ui/symbol-names/issue-60925.legacy.stderr index c1fc9f4b1cea1..367c2d14011ac 100644 --- a/tests/ui/symbol-names/issue-60925.legacy.stderr +++ b/tests/ui/symbol-names/issue-60925.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h1eb769490ff06e77E) +error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17hc741419c44ba4d79E) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_dump_symbol_name] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: demangling(issue_60925::foo::Foo::foo::h1eb769490ff06e77) +error: demangling(issue_60925::foo::Foo::foo::hc741419c44ba4d79) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_dump_symbol_name] From e0a35a088f667e9e7257ede4dbe4f1acac8c3629 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 10 Jun 2026 12:05:50 +0200 Subject: [PATCH 188/278] Impl HIR typeck for #[splat] --- compiler/rustc_hir_typeck/src/callee.rs | 45 +- compiler/rustc_hir_typeck/src/demand.rs | 17 +- compiler/rustc_hir_typeck/src/expr.rs | 14 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 34 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 416 +++++++++++++++--- compiler/rustc_hir_typeck/src/lib.rs | 77 +++- compiler/rustc_hir_typeck/src/writeback.rs | 2 + tests/ui/splat/splat-cannot-resolve.rs | 49 +++ tests/ui/splat/splat-cannot-resolve.stderr | 94 ++++ tests/ui/splat/splat-invalid.rs | 14 + tests/ui/splat/splat-invalid.stderr | 31 +- tests/ui/splat/splat-maybe-tuple.rs | 22 + tests/ui/splat/splat-maybe-tuple.stderr | 12 + tests/ui/splat/splat-non-tuple.rs | 90 ++++ tests/ui/splat/splat-non-tuple.stderr | 54 +++ 15 files changed, 881 insertions(+), 90 deletions(-) create mode 100644 tests/ui/splat/splat-cannot-resolve.rs create mode 100644 tests/ui/splat/splat-cannot-resolve.stderr create mode 100644 tests/ui/splat/splat-maybe-tuple.rs create mode 100644 tests/ui/splat/splat-maybe-tuple.stderr create mode 100644 tests/ui/splat/splat-non-tuple.rs create mode 100644 tests/ui/splat/splat-non-tuple.stderr diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 57ab29ac752ad..859e3463cb79f 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -58,9 +58,13 @@ pub(crate) fn check_legal_trait_for_method_call( tcx.ensure_result().coherent_trait(trait_id) } +/// State machine for typechecking a call, based on the callee type. #[derive(Debug)] enum CallStep<'tcx> { + /// Typecheck a call to a function definition or pointer. + /// Includes functions with splatted arguments. Builtin(Ty<'tcx>), + /// Deferred closure Fn* trait typechecking, when the callee is a closure. DeferredClosure(LocalDefId, ty::FnSig<'tcx>), /// Call overloading when callee implements one of the Fn* traits. Overloaded(MethodCallee<'tcx>), @@ -544,7 +548,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let (fn_sig, def_id) = match *callee_ty.kind() { + let (fn_sig, def_id, callee_generic_args) = match *callee_ty.kind() { ty::FnDef(def_id, args) => { self.enforce_context_effects(Some(call_expr.hir_id), call_expr.span, def_id, args); let fn_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args).skip_norm_wip(); @@ -573,11 +577,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit(); } } - (fn_sig, Some(def_id)) + (fn_sig, Some(def_id), Some(args)) } // FIXME(const_trait_impl): these arms should error because we can't enforce them - ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None), + ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None, None), _ => unreachable!(), }; @@ -595,12 +599,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig = self.normalize(call_expr.span, Unnormalized::new_wip(fn_sig)); self.check_argument_types_maybe_method_like( - &fn_sig, call_expr, arg_exprs, expected, def_id, + &fn_sig, + call_expr, + arg_exprs, + expected, + TupleArgumentsFlag::with_fn_sig_kind(fn_sig.fn_sig_kind, false), + def_id, + callee_generic_args, ); + // Splatting is currently incompatible with RustCall. if fn_sig.abi() == rustc_abi::ExternAbi::RustCall { let sp = arg_exprs.last().map_or(call_expr.span, |expr| expr.span); - if let Some(ty) = fn_sig.inputs().last().copied() { + if let Some(ty) = fn_sig.inputs().last().copied() + && fn_sig.splatted().is_none() + { self.register_bound( ty, self.tcx.require_lang_item(hir::LangItem::Tuple, sp), @@ -627,7 +640,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, + tuple_arguments_flag: TupleArgumentsFlag, def_id: Option, + callee_generic_args: Option>, ) { let do_check = || { self.check_argument_types( @@ -638,8 +653,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected, arg_exprs, fn_sig.c_variadic(), - TupleArgumentsFlag::DontTupleArguments, + tuple_arguments_flag, def_id, + callee_generic_args, ); }; @@ -1009,9 +1025,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_sig.output(), expected, arg_exprs, - fn_sig.c_variadic(), - TupleArgumentsFlag::TupleArguments, + fn_sig.fn_sig_kind.c_variadic(), + TupleArgumentsFlag::rust_fn_trait_call(), Some(closure_def_id.to_def_id()), + None, ); fn_sig.output() @@ -1092,6 +1109,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, method: MethodCallee<'tcx>, ) -> Ty<'tcx> { + // FIXME(splat): if we ever support splatting here, decrement the splatted index, because + // the receiver argument is removed below. + assert_eq!( + method.sig.fn_sig_kind.splatted(), + None, + "splatting is not supported on RustCall tuples", + ); self.check_argument_types( call_expr.span, call_expr, @@ -1099,9 +1123,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method.sig.output(), expected, arg_exprs, - method.sig.c_variadic(), - TupleArgumentsFlag::TupleArguments, + method.sig.fn_sig_kind.c_variadic(), + TupleArgumentsFlag::rust_fn_trait_call(), Some(method.def_id), + None, ); self.write_method_call_and_enforce_effects(call_expr.hir_id, call_expr.span, method); diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 6e4d6aa80a63a..bd8295ba3a328 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -3,11 +3,11 @@ use rustc_hir::def::Res; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, find_attr}; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_middle::bug; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AssocItem, BottomUpFolder, Ty, TypeFoldable, TypeVisitableExt}; +use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Ident, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCause; @@ -402,9 +402,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unify the method signature with our incompatible arg, to // do inference in the *opposite* direction and to find out // what our ideal rcvr ty would look like. + let Some(input_arg) = method.sig.inputs().get(idx + 1) else { + if method.sig.splatted().is_some() { + // FIXME(splat): when the arg is splatted, adjust its index, to handle the type mismatch properly + return None; + } else { + span_bug!( + self.tcx.def_span(method.def_id), + "arg index {} out of bounds for method with {} inputs", + idx + 1, + method.sig.inputs().len(), + ); + } + }; let _ = self .at(&ObligationCause::dummy(), self.param_env) - .eq(DefineOpaqueTypes::Yes, method.sig.inputs()[idx + 1], arg_ty) + .eq(DefineOpaqueTypes::Yes, *input_arg, arg_ty) .ok()?; self.select_obligations_where_possible(|errs| { // Yeet the errors, we're already reporting errors. diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 0e13f7a4bfe1c..22c211b199474 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1463,16 +1463,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(method) => { self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method); + // Handle splatted method arguments + // self is already handled as `rcvr`, so it's never splatted here + let method_inputs = &method.sig.inputs()[1..]; + let method_tuple_args_flag = + TupleArgumentsFlag::with_fn_sig_kind(method.sig.fn_sig_kind, true); + self.check_argument_types( segment.ident.span, expr, - &method.sig.inputs()[1..], + method_inputs, method.sig.output(), expected, args, - method.sig.c_variadic(), - TupleArgumentsFlag::DontTupleArguments, + method.sig.fn_sig_kind.c_variadic(), + method_tuple_args_flag, Some(method.def_id), + Some(method.args), ); self.check_call_abi(method.sig.abi(), expr.span); @@ -1495,6 +1502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false, TupleArgumentsFlag::DontTupleArguments, None, + Some(GenericArgsRef::default()), ); err_output diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index c8c6de4f99c03..bc954f5518619 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -143,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// also select obligations if it seems useful, in an effort /// to get more type information. // FIXME(-Znext-solver): A lot of the calls to this method should - // probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead. + // probably be `resolve_vars_with_obligations` or `structurally_resolve_type` instead. #[instrument(skip(self), level = "debug", ret)] pub(crate) fn resolve_vars_with_obligations>>( &self, @@ -236,6 +236,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); } + #[instrument(level = "debug", skip(self))] + pub(crate) fn write_splatted_resolution( + &self, + _hir_id: HirId, + _r: Result<() /* SplattedDef */, ErrorGuaranteed>, + ) { + // FIXME(splat): add side table and write to it here + } + #[instrument(level = "debug", skip(self))] pub(crate) fn write_method_call_and_enforce_effects( &self, @@ -248,6 +257,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_args(hir_id, method.args); } + #[instrument(level = "debug", skip(self))] + pub(crate) fn write_splatted_call( + &self, + hir_id: HirId, + span: Span, + callee_def_id: Option, + callee_generic_args: Option>, + first_tupled_arg_index: u16, + tupled_args_count: u16, + ) { + // FIXME(const_trait_impl): enforce constness using enforce_context_effects() and add + // _and_enforce_effects to this method's name + + self.write_splatted_resolution( + hir_id, + // FIXME(splat): add side table and write to it here + Ok(()), + ); + if let Some(callee_generic_args) = callee_generic_args { + self.write_args(hir_id, callee_generic_args); + } + } + fn write_args(&self, node_id: HirId, args: GenericArgsRef<'tcx>) { if !args.is_empty() { debug!("write_args({:?}, {:?}) in fcx {}", node_id, args, self.tag()); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 890110e431fa2..55c6aa2c3ad25 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -50,6 +50,19 @@ rustc_index::newtype_index! { pub(crate) struct GenericIdx {} } +/// Outcome of checking arguments that are tupled by "rust-call" or `#[splat]`. +#[derive(Debug, Clone, Eq, PartialEq)] +struct TupledArgCheckOutcome<'tcx> { + /// The error code to emit if the arguments are not compatible. + new_err_code: Option, + + /// The formal input types after checking. + untupled_formal_input_tys: Vec>, + + /// The expected input types after checking. + untupled_expected_input_tys: Option>>, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { let mut deferred_cast_checks = self.root_ctxt.deferred_cast_checks.borrow_mut(); @@ -185,13 +198,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expectation: Expectation<'tcx>, // The expressions for each provided argument provided_args: &'tcx [hir::Expr<'tcx>], - // Whether the function is variadic, for example when imported from C - // FIXME(splat): maybe change this to FnSigKind? + // Whether the function is variadic (e.g. from C) c_variadic: bool, - // Whether the arguments have been bundled in a tuple (ex: closures) + // Whether all the arguments have been bundled in a tuple (ex: closures), or one has been splatted tuple_arguments: TupleArgumentsFlag, // The DefId for the function being called, for better error messages fn_def_id: Option, + // The generics of the function being called. Only used for splatting + callee_generic_args: Option>, ) { let tcx = self.tcx; @@ -220,11 +234,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // First, let's unify the formal method signature with the expectation eagerly. - // We use this to guide coercion inference; it's output is "fudged" which means + // We use this to guide coercion inference; its output is "fudged" which means // any remaining type variables are assigned to new, unrelated variables. This // is because the inference guidance here is only speculative. + // FIXME(splat): do we need to splat arguments before this type inference? let formal_output = self.resolve_vars_with_obligations(formal_output); - let expected_input_tys: Option> = expectation + let mut expected_input_tys: Option> = expectation .only_has_type(self) .and_then(|expected_output| { // FIXME(#149379): This operation results in expected input @@ -272,45 +287,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err_code = E0061; - // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here - let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments { - let tuple_type = self.structurally_resolve_type(call_span, formal_input_tys[0]); - match tuple_type.kind() { - // We expected a tuple and got a tuple - ty::Tuple(arg_types) => { - // Argument length differs - if arg_types.len() != provided_args.len() { - err_code = E0057; - } - let expected_input_tys = match expected_input_tys { - Some(expected_input_tys) => match expected_input_tys.get(0) { - Some(ty) => match ty.kind() { - ty::Tuple(tys) => Some(tys.iter().collect()), - _ => None, - }, - None => None, - }, - None => None, - }; - (arg_types.iter().collect(), expected_input_tys) - } - _ => { - // Otherwise, there's a mismatch, so clear out what we're expecting, and set - // our input types to err_args so we don't blow up the error messages - let guar = struct_span_code_err!( - self.dcx(), - call_span, - E0059, - "cannot use call notation; the first type parameter \ - for the function trait is neither a tuple nor unit" - ) - .emit(); - (self.err_args(provided_args.len(), guar), None) - } + let mut formal_input_tys = formal_input_tys.to_vec(); + + // If the arguments should be wrapped in a tuple (ex: closures, splats), unwrap them here + if tuple_arguments.is_tupled() { + // Caller arguments are tupled before typechecking, starting at the given index. + // Tupling makes the callee and caller argument counts match. + let outcome = self.check_tupled_arguments( + call_span, + call_expr, + formal_input_tys, + provided_args, + expected_input_tys, + c_variadic, + tuple_arguments, + fn_def_id, + callee_generic_args, + ); + let TupledArgCheckOutcome { + new_err_code, + untupled_formal_input_tys, + untupled_expected_input_tys, + } = outcome; + if let Some(new_err_code) = new_err_code { + err_code = new_err_code; } - } else { - (formal_input_tys.to_vec(), expected_input_tys) - }; + formal_input_tys = untupled_formal_input_tys; + expected_input_tys = untupled_expected_input_tys; + } // If there are no external expectations at the call site, just use the types from the function defn let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys { @@ -556,6 +560,257 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Check arguments that are tupled by "rust-call" or `#[splat]`. + fn check_tupled_arguments( + &self, + // Span enclosing the call site + call_span: Span, + // Expression of the call site + call_expr: &'tcx hir::Expr<'tcx>, + // Types (as defined in the *signature* of the target function) + mut formal_input_tys: Vec>, + // The expressions for each provided argument + provided_args: &'tcx [hir::Expr<'tcx>], + // The expected input types from the context of the call site + mut expected_input_tys: Option>>, + // Whether the function is variadic (e.g. from C) + c_variadic: bool, + // Whether all the arguments have been bundled in a tuple (ex: closures). + // Splatting is handled separately. + tuple_arguments: TupleArgumentsFlag, + // The DefId for the function being called, for better error messages + fn_def_id: Option, + // The generics of the function being called. Only used for splatting + callee_generic_args: Option>, + ) -> TupledArgCheckOutcome<'tcx> { + let (first_tupled_arg_index, is_self_splatted) = tuple_arguments.tupled_arg_index(); + let Some(first_tupled_arg_index) = first_tupled_arg_index else { + // If we're not tupling any of the current arguments, we're done. + return TupledArgCheckOutcome { + new_err_code: None, + untupled_formal_input_tys: formal_input_tys, + untupled_expected_input_tys: expected_input_tys, + }; + }; + + // The argument difference can range from -1 to u16::MAX - 1, so we count the number + // of tupled arguments instead. + // (An empty argument list becomes a unit tuple in the callee.) + // 0: f() -> f(#[splat] _: ()) + // 1: f(a) -> f(#[splat] _: (A,)) + // 2: f(a, b) -> f(#[splat] _: (A, B)) + // The Fn* traits ensure this by construction, and `#[splat]` can only be applied to + // an actual argument. + let tupled_args_count = (1 + provided_args.len()).checked_sub(formal_input_tys.len()); + debug!( + ?first_tupled_arg_index, ?is_self_splatted, + ?tupled_args_count, ?tuple_arguments, ?c_variadic, + provided_args_len = ?provided_args.len(), formal_input_tys_len = ?formal_input_tys.len() + ); + + // If earlier code has modified the FnSig argument list without adjusting the splatted + // argument, indexing into the formal input types will panic. + if first_tupled_arg_index >= formal_input_tys.len() { + span_bug!( + call_span, + "splatted argument index is out of bounds: {first_tupled_arg_index:?} >= {}, \ + is_self_splatted = {is_self_splatted:?}, \ + tupled_args_count = {tupled_args_count:?}, {tuple_arguments:?}, \ + c_variadic = {c_variadic:?}, provided_args: {}", + formal_input_tys.len(), + provided_args.len(), + ); + } + + // Keep the type variable if the argument is splatted, so we can force it to be a tuple later. + let tuple_type = if tuple_arguments.is_splatted() { + let callee_tuple_type = + self.resolve_vars_with_obligations(formal_input_tys[first_tupled_arg_index]); + if callee_tuple_type.is_ty_var() + && let Some(tupled_args_count) = tupled_args_count + { + // Make the original type variable resolve to a tuple containing new type variables + let ocx = ObligationCtxt::new(self); + let origin = self.misc(call_span); + + let new_tupled_type = Ty::new_tup_from_iter( + self.tcx, + iter::repeat_with(|| self.next_ty_var(call_span)).take(tupled_args_count), + ); + + // FIXME(splat): should this be a sub/super type relationship? + let ocx_error = ocx.eq(&origin, self.param_env, callee_tuple_type, new_tupled_type); + if let Err(ocx_error) = ocx_error { + // FIXME(splat): add a test for this error and the one below, if they are reachable + struct_span_code_err!( + self.dcx(), + call_span, + // FIXME(splat): add a new error code before stabilization (and below as well) + E0277, + "cannot resolve splatted arguments; splatted type parameters \ + must be a tuple or unit type: {:?}", + ocx_error, + ) + .emit(); + } + + let type_errors = ocx.try_evaluate_obligations(); + if type_errors.is_empty() { + new_tupled_type + } else { + let guar = struct_span_code_err!( + self.dcx(), + call_span, + E0277, + "cannot resolve splatted arguments; splatted type parameters \ + must be a tuple or unit type: {:?}", + type_errors, + ) + .emit(); + Ty::new_error(self.tcx, guar) + } + } else { + // Otherwise, just let the argument type checker make a suggestion + callee_tuple_type + } + } else { + self.structurally_resolve_type(call_span, formal_input_tys[first_tupled_arg_index]) + }; + + // We expected a tuple and got a tuple (or made one ourselves). + // If it's not a tuple, we error out in the next block. + let mut err_code = None; + if let ty::Tuple(detup_formal_arg_tys) = tuple_type.kind() { + // Argument length differs + // FIXME(splat): update the error code E0057 docs when splat is stabilized + if Some(detup_formal_arg_tys.len()) != tupled_args_count { + err_code = Some(E0057); + } + if let Some(ref mut expected_input_tys) = expected_input_tys + && let Some(ty) = expected_input_tys.get(first_tupled_arg_index) + && let ty::Tuple(detup_expected_arg_tys) = ty.kind() + { + let substitute_tys = if Some(detup_expected_arg_tys.len()) == tupled_args_count { + detup_expected_arg_tys.iter() + } else { + // Just fall back to the formal argument types + detup_formal_arg_tys.iter() + }; + + expected_input_tys + .splice(first_tupled_arg_index..=first_tupled_arg_index, substitute_tys); + } else { + expected_input_tys = None; + } + // If splatting, record this call in a side-table, so MIR lowering can tuple the caller's arguments + if tuple_arguments.is_splatted() { + // FIXME(const_trait_impl): does not enforce constness yet + self.write_splatted_call( + call_expr.hir_id, + call_span, + fn_def_id, + callee_generic_args, + first_tupled_arg_index.try_into().unwrap(), + tupled_args_count.unwrap().try_into().unwrap(), + ); + } + + formal_input_tys.splice( + first_tupled_arg_index..=first_tupled_arg_index, + detup_formal_arg_tys.iter(), + ); + if let Some(ref expected_input_tys) = expected_input_tys { + assert_eq!( + formal_input_tys.len(), + expected_input_tys.len(), + "incorrectly constructed input type tuples, argument counts must match: \ + tuple_arguments: {tuple_arguments:?}", + ) + } + } + + // Otherwise, there's a mismatch during splatting or a rust-call. + // So clear out what we're expecting, and set our input types to err_args so we don't + // blow up the error messages. + let guar = + if tuple_arguments == TupleAllCallArgs && !matches!(tuple_type.kind(), ty::Tuple(_)) { + let guar = struct_span_code_err!( + self.dcx(), + call_span, + E0059, + "cannot use call notation; the first type parameter \ + for the function trait is neither a tuple nor unit" + ) + .emit(); + + Some(guar) + } else if tuple_arguments.is_splatted() { + // If we don't check argument counts here, and there's a subtle bug in the code above, + // later compilation stages can fail in unrelated places with confusing errors. + if !matches!(tuple_type.kind(), ty::Tuple(_)) { + let spans = if let Some(def_id) = fn_def_id + && let Some(hir_node) = self.tcx.hir_get_if_local(def_id) + && let Some(fn_decl) = hir_node.fn_decl() + && let Some(arg_ty) = fn_decl.inputs.get(first_tupled_arg_index) + { + let arg_def_span = arg_ty.span; + vec![call_span, arg_def_span] + } else { + vec![call_span] + }; + let guar = struct_span_code_err!( + self.dcx(), + spans, + // FIXME(splat): add a new error code before stabilization + E0277, + "cannot use splat attribute; the splatted argument type \ + must be a tuple or unit, not a {:?} ({:?})", + tuple_type.kind(), + self.structurally_resolve_type( + call_span, + formal_input_tys[first_tupled_arg_index] + ) + .kind(), + ) + .emit(); + + Some(guar) + } else if formal_input_tys.len() != provided_args.len() { + // FIXME(splat): suggest alternative argument counts, if there are any + let guar = struct_span_code_err!( + self.dcx(), + call_span, + E0057, + "this splatted function takes {} arguments, but {} {} provided", + formal_input_tys.len(), + provided_args.len(), + if provided_args.len() == 1 { "was" } else { "were" }, + ) + .emit(); + + Some(guar) + } else { + None + } + } else { + None + }; + + if let Some(guar) = guar { + TupledArgCheckOutcome { + new_err_code: err_code, + untupled_formal_input_tys: self.err_args(provided_args.len(), guar), + untupled_expected_input_tys: None, + } + } else { + TupledArgCheckOutcome { + new_err_code: err_code, + untupled_formal_input_tys: formal_input_tys, + untupled_expected_input_tys: expected_input_tys, + } + } + } + /// If `unsized_fn_params` is active, check that unsized values are place expressions. Since /// the removal of `unsized_locals` in we can't /// store them in MIR locals as temporaries. @@ -581,6 +836,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_def_id: Option, call_span: Span, call_expr: &'tcx hir::Expr<'tcx>, + // FIXME(splat): when the feature design is settled, improve the errors here tuple_arguments: TupleArgumentsFlag, ) -> ErrorGuaranteed { // Next, let's construct the error @@ -1354,8 +1610,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we're calling a method of a Fn/FnMut/FnOnce trait object implicitly // (eg invoking a closure) we want to point at the underlying callable, // not the method implicitly invoked (eg call_once). - // TupleArguments is set only when this is an implicit call (my_closure(...)) rather than explicit (my_closure.call(...)) - if tuple_arguments == TupleArguments + // TupleAllCallArgs is set only when this is an implicit call `my_closure(...)` rather + // than explicit `my_closure.call(...)`. + if tuple_arguments == TupleAllCallArgs && let Some(assoc_item) = self.tcx.opt_associated_item(def_id) // Since this is an associated item, it might point at either an impl or a trait item. // We want it to always point to the trait item. @@ -1442,25 +1699,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { deps: SmallVec<[ExpectedIdx; 4]>, } - debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + // FIXME(splat): fix the generic mismatch earlier, so it doesn't reach here + if !tuple_arguments.is_splatted() { + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + } // Gather all mismatched parameters with generics. let mut mismatched_params = Vec::>::new(); + let mut use_splat_fallback = false; if let Some(expected_idx) = expected_idx { let expected_idx = ExpectedIdx::from_usize(expected_idx); - let &(expected_generic, ref expected_param) = - ¶ms_with_generics[expected_idx]; - if let Some(expected_generic) = expected_generic { - mismatched_params.push(MismatchedParam { - idx: expected_idx, - generic: expected_generic, - param: expected_param, - deps: SmallVec::new(), - }); - } else { - // Still mark the mismatched parameter - spans.push_span_label(expected_param.span(), ""); - } - } else { + match params_with_generics.get(expected_idx) { + Some(&(Some(expected_generic), ref expected_param)) => mismatched_params + .push(MismatchedParam { + idx: expected_idx, + generic: expected_generic, + param: expected_param, + deps: SmallVec::new(), + }), + Some((None, expected_param)) => { + // Still mark the mismatched parameter + spans.push_span_label(expected_param.span(), ""); + } + None => { + if tuple_arguments.is_splatted() { + // FIXME(splat): when the arg is splatted, adjust its index, to handle the type mismatch properly + use_splat_fallback = true; + } else { + span_bug!( + self.tcx.def_span(def_id), + "arg index {} out of bounds for method with {} inputs", + expected_idx.as_usize(), + params_with_generics.len(), + ); + } + } + }; + } + + if expected_idx.is_none() || use_splat_fallback { mismatched_params.extend( params_with_generics.iter_enumerated().zip(matched_inputs).filter_map( |((idx, &(generic, ref param)), matched_idx)| { @@ -1671,15 +1947,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { provided_arg_tys: &IndexVec, Span)>, formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, is_method: bool, + is_splat: bool, ) { let Some(def_id) = callable_def_id else { return; }; if let Some((params_with_generics, _)) = self.get_hir_param_info(def_id, is_method) { - debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + // FIXME(splat): fix the generic mismatch earlier, so it doesn't reach here + if !is_splat { + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + } for (idx, (generic_param, _)) in params_with_generics.iter_enumerated() { - if matched_inputs[idx].is_none() { + if matched_inputs.get(idx).flatten_ref().is_none() { continue; } @@ -1701,7 +1981,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(other_generic_param) = other_generic_param else { return false; }; - if matched_inputs[other_idx].is_some() { + if matched_inputs.get(other_idx).flatten_ref().is_some() { return false; } other_generic_param == generic_param @@ -2004,7 +2284,11 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { self.arg_matching_ctxt.args_ctxt.call_metadata.full_call_span, format!( "{call_name} takes {}{} but {} {} supplied", - if self.c_variadic { "at least " } else { "" }, + if self.arg_matching_ctxt.args_ctxt.c_variadic { + "at least " + } else { + "" + }, potentially_plural_count( self.formal_and_expected_inputs.len(), "argument" @@ -2154,6 +2438,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { &self.provided_arg_tys, &self.formal_and_expected_inputs, self.call_metadata.is_method, + self.tuple_arguments.is_splatted(), ); if let hir::ExprKind::MethodCall(_, rcvr, _, _) = @@ -2237,7 +2522,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { format!( "this {} takes {}{} but {} {} supplied", self.call_metadata.call_name, - if self.c_variadic { "at least " } else { "" }, + if self.arg_matching_ctxt.args_ctxt.c_variadic { "at least " } else { "" }, potentially_plural_count(self.formal_and_expected_inputs.len(), "argument"), potentially_plural_count(self.provided_args.len(), "argument"), pluralize!("was", self.provided_args.len()) @@ -2614,6 +2899,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { &self.provided_arg_tys, &self.formal_and_expected_inputs, self.call_metadata.is_method, + self.arg_matching_ctxt.tuple_arguments.is_splatted(), ); } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 3941f06c3e7d3..3b3f5caa85c09 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -3,6 +3,7 @@ #![feature(iter_intersperse)] #![feature(iter_order_by)] #![feature(never_type)] +#![feature(option_reference_flattening)] #![feature(trim_prefix_suffix)] // tidy-alphabetical-end @@ -50,7 +51,7 @@ 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, Unnormalized}; +use rustc_middle::ty::{self, FnSigKind, Ty, TyCtxt, Unnormalized}; use rustc_middle::{bug, span_bug}; use rustc_session::config; use rustc_span::Span; @@ -589,12 +590,10 @@ fn report_unexpected_variant_res( .emit() } -/// Controls whether the arguments are tupled. This is used for the call -/// operator. +/// Controls whether all arguments are tupled. This is used for the call operator only. /// -/// Tupling means that all call-side arguments are packed into a tuple and -/// passed as a single parameter. For example, if tupling is enabled, this -/// function: +/// Tupling means that all call-side arguments are packed into a tuple and passed as a single +/// parameter. For example, if tupling is enabled, this function: /// ``` /// fn f(x: (isize, isize)) {} /// ``` @@ -608,10 +607,72 @@ fn report_unexpected_variant_res( /// # fn f(x: (isize, isize)) {} /// f((1, 2)); /// ``` -#[derive(Copy, Clone, Eq, PartialEq)] +/// +/// Note: splatted arguments are handled separately. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] enum TupleArgumentsFlag { + /// Arguments are typechecked unchanged. DontTupleArguments, - TupleArguments, + /// This is a call operator: all caller arguments are tupled before typechecking. + /// Set based on the "rust-call" ABI and Fn* traits. + TupleAllCallArgs, + /// The `self` method argument is splatted, so `Self` should be tupled before typechecking. + TupleSplattedSelfArg, + /// A non-self argument is splatted, so that argument should be tupled before typechecking. + TupleSplattedArg(u8), +} + +impl TupleArgumentsFlag { + /// Returns the TupleArgumentsFlag for a known RustCall function. + fn rust_fn_trait_call() -> Self { + Self::TupleAllCallArgs + } + + /// Returns the appropriate TupleArgumentsFlag for the given FnSigKind and method flag. + fn with_fn_sig_kind<'tcx>(fn_sig_kind: FnSigKind<'tcx>, is_method: bool) -> Self { + if let Some(splatted_arg_index) = fn_sig_kind.splatted() { + if is_method { + if let Some(splatted_arg_index) = splatted_arg_index.checked_sub(1) { + return Self::TupleSplattedArg(splatted_arg_index); + } else { + // In `check_argument_types`, this is effectively `TupleSplattedArg(-1)` + return Self::TupleSplattedSelfArg; + } + } + + return Self::TupleSplattedArg(splatted_arg_index); + } + + Self::DontTupleArguments + } + + /// Returns true if the arguments are tupled through "rust-call" or splatting. + fn is_tupled(self) -> bool { + match self { + Self::DontTupleArguments => false, + Self::TupleAllCallArgs | Self::TupleSplattedSelfArg | Self::TupleSplattedArg(_) => true, + } + } + + /// Returns true if the arguments are tupled through splatting. + /// (But false if they are "rust-call" or not tupled.) + fn is_splatted(self) -> bool { + match self { + Self::TupleSplattedSelfArg | Self::TupleSplattedArg(_) => true, + Self::DontTupleArguments | Self::TupleAllCallArgs => false, + } + } + + /// Returns the tupled argument index, and whether the `self` argument is splatted. + /// Returns `None` if the arguments are not tupled, or if the `self` argument is splatted. + fn tupled_arg_index(self) -> (Option, bool /* is_self_splatted */) { + match self { + Self::TupleSplattedArg(index) => (Some(usize::from(index)), false), + Self::TupleAllCallArgs => (Some(0), false), + Self::TupleSplattedSelfArg => (None, true), + Self::DontTupleArguments => (None, false), + } + } } fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! { diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 298fee525fc55..ad08daa7a4141 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -663,6 +663,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.type_dependent_defs_mut().insert(hir_id, def); } + // FIXME(splat): add side table and write to it here + // Resolve any borrowings for the node with id `node_id` self.visit_adjustments(span, hir_id); diff --git a/tests/ui/splat/splat-cannot-resolve.rs b/tests/ui/splat/splat-cannot-resolve.rs new file mode 100644 index 0000000000000..1c22a53f82916 --- /dev/null +++ b/tests/ui/splat/splat-cannot-resolve.rs @@ -0,0 +1,49 @@ +//! Test that using `#[splat]` on un-resolvable types is an error. + +#![allow(incomplete_features)] +#![allow(unconditional_recursion)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn tuple(#[splat] t: impl Sized) -> impl Sized { + //~^ ERROR cannot resolve opaque type + tuple(tuple((t, ()))) +} + +fn tuple_trait(#[splat] t: impl std::marker::Tuple) -> impl std::marker::Tuple { + //~^ ERROR cannot resolve opaque type + tuple_trait(tuple_trait((t, ()))) +} + +trait Trait { + type MaybeTup; + type Tup: std::marker::Tuple; +} + +fn ambig(#[splat] t: Trait::MaybeTup) {} +//~^ ERROR ambiguous associated type +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +fn ambig_tup(#[splat] t: Trait::Tup) {} +//~^ ERROR ambiguous associated type +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a + +fn main() { + tuple(); + tuple_trait(); + ambig(); + ambig_tup(); + + tuple(1); + tuple_trait(1); + ambig(1); + ambig_tup(1); + + tuple(1, 2.0); + tuple_trait(1, 2.0); + ambig(1, 2.0); + ambig_tup(1, 2.0); +} diff --git a/tests/ui/splat/splat-cannot-resolve.stderr b/tests/ui/splat/splat-cannot-resolve.stderr new file mode 100644 index 0000000000000..f91267d37dbc8 --- /dev/null +++ b/tests/ui/splat/splat-cannot-resolve.stderr @@ -0,0 +1,94 @@ +error[E0223]: ambiguous associated type + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - fn ambig(#[splat] t: Trait::MaybeTup) {} +LL + fn ambig(#[splat] t: ::MaybeTup) {} + | + +error[E0223]: ambiguous associated type + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - fn ambig_tup(#[splat] t: Trait::Tup) {} +LL + fn ambig_tup(#[splat] t: ::Tup) {} + | + +error[E0720]: cannot resolve opaque type + --> $DIR/splat-cannot-resolve.rs:8:37 + | +LL | fn tuple(#[splat] t: impl Sized) -> impl Sized { + | ^^^^^^^^^^ + +error[E0720]: cannot resolve opaque type + --> $DIR/splat-cannot-resolve.rs:13:56 + | +LL | fn tuple_trait(#[splat] t: impl std::marker::Tuple) -> impl std::marker::Tuple { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(); + | ^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(); + | ^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(1); + | ^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(1); + | ^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(1, 2.0); + | ^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(1, 2.0); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0223, E0277, E0720. +For more information about an error, try `rustc --explain E0223`. diff --git a/tests/ui/splat/splat-invalid.rs b/tests/ui/splat/splat-invalid.rs index 314003459ef72..2c4bf57ef972a 100644 --- a/tests/ui/splat/splat-invalid.rs +++ b/tests/ui/splat/splat-invalid.rs @@ -30,4 +30,18 @@ extern "C" { fn bar_2(#[splat] _: (u32, i8)); } +trait FooTrait { + fn has_splat(#[splat] _: ()); + + fn no_splat(_: (u32, f64)); +} + +struct Foo; + +impl FooTrait for Foo { + fn has_splat(_: ()) {} //~ ERROR method `has_splat` has an incompatible type for trait + + fn no_splat(#[splat] _: (u32, f64)) {} //~ ERROR method `no_splat` has an incompatible type for trait +} + fn main() {} diff --git a/tests/ui/splat/splat-invalid.stderr b/tests/ui/splat/splat-invalid.stderr index d88cbf4ff0b6d..74c54c00cd285 100644 --- a/tests/ui/splat/splat-invalid.stderr +++ b/tests/ui/splat/splat-invalid.stderr @@ -73,5 +73,34 @@ LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} | = help: remove `#[splat]` or remove `...` -error: aborting due to 9 previous errors +error[E0053]: method `has_splat` has an incompatible type for trait + --> $DIR/splat-invalid.rs:42:5 + | +LL | fn has_splat(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^ expected fn with arg 0 splatted, found fn with no splatted arg + | +note: type in trait + --> $DIR/splat-invalid.rs:34:5 + | +LL | fn has_splat(#[splat] _: ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn(#[splat] ())` + found signature `fn(())` + +error[E0053]: method `no_splat` has an incompatible type for trait + --> $DIR/splat-invalid.rs:44:5 + | +LL | fn no_splat(#[splat] _: (u32, f64)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn with no splatted arg, found fn with arg 0 splatted + | +note: type in trait + --> $DIR/splat-invalid.rs:36:5 + | +LL | fn no_splat(_: (u32, f64)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn((_, _))` + found signature `fn(#[splat] (_, _))` + +error: aborting due to 11 previous errors +For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-maybe-tuple.rs b/tests/ui/splat/splat-maybe-tuple.rs new file mode 100644 index 0000000000000..a74af66e9a874 --- /dev/null +++ b/tests/ui/splat/splat-maybe-tuple.rs @@ -0,0 +1,22 @@ +//! Test that using `#[splat]` on maybe-tuple generic function arguments is an error, +//! but only when the generics aren't tuples. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(unused)] + +fn unbound_generic_arg(#[splat] t: T) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +fn main() { + unbound_generic_arg(); + unbound_generic_arg::<()>(); + + unbound_generic_arg(1); + unbound_generic_arg::<(u32,)>(1); + + unbound_generic_arg(1, 2.0); + unbound_generic_arg::<(u32, f32)>(1, 2.0); + + // The error comes from this call + unbound_generic_arg::(1); +} diff --git a/tests/ui/splat/splat-maybe-tuple.stderr b/tests/ui/splat/splat-maybe-tuple.stderr new file mode 100644 index 0000000000000..9abacdd657b93 --- /dev/null +++ b/tests/ui/splat/splat-maybe-tuple.stderr @@ -0,0 +1,12 @@ +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-maybe-tuple.rs:8:39 + | +LL | fn unbound_generic_arg(#[splat] t: T) {} + | ^ +... +LL | unbound_generic_arg::(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/splat/splat-non-tuple.rs b/tests/ui/splat/splat-non-tuple.rs new file mode 100644 index 0000000000000..35841a770b226 --- /dev/null +++ b/tests/ui/splat/splat-non-tuple.rs @@ -0,0 +1,90 @@ +//! Test that using `#[splat]` on non-tuple function arguments is an error. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(unused)] + +fn primitive_arg(#[splat] x: u32) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +enum NotATuple { + A(u32), + B(i8), +} + +fn enum_arg(#[splat] y: NotATuple) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a NotATuple + +trait FooTrait { + fn tuple_1(#[splat] _: (u32,)); //~ NOTE type in trait + + // Ambiguous case, self could be a tuple or a non-tuple + fn tuple_4(#[splat] self, _: (u32, i8, (), f32)); +} + +struct Foo; + +fn struct_arg(#[splat] z: Foo) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Foo + +impl Foo { + fn tuple_2_self( + // FIXME(splat): ERROR cannot use splat attribute; the splatted argument type must be a... + #[splat] self, + (a, b): (u32, i8), + ) -> u32 { + a + } +} + +impl FooTrait for Foo { + fn tuple_1(_: (u32,)) {} + //~^ ERROR method `tuple_1` has an incompatible type for trait + //~| NOTE expected fn with arg 0 splatted, found fn with no splatted arg + //~| NOTE expected signature `fn(#[splat] (_,))` + //~| NOTE found signature `fn((_,))` + + fn tuple_4( + // FIXME(splat): ERROR cannot use splat attribute; the splatted argument type must be a... + #[splat] self, + _: (u32, i8, (), f32), + ) { + } +} + +struct TupleStruct(u32, i8); + +fn tuple_struct_arg(#[splat] z: TupleStruct) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a TupleStruct + +impl TupleStruct { + fn tuple_2( + #[splat] self, // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + (a, b): (f32, f64), + ) -> f32 { + a + } +} + +impl FooTrait for TupleStruct { + fn tuple_1(#[splat] _: (u32,)) {} + + fn tuple_4( + #[splat] self, // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + _: (u32, i8, (), f32), + ) { + } +} + +fn main() { + // FIXME(splat): is it enough for just the definitions/callees to error, + // or should the callers also error? + primitive_arg(1u32); + enum_arg(NotATuple::A(1u32)); + + let foo = Foo; + struct_arg(foo); + foo.tuple_2_self((1u32, 2i8)); + + let tuple_struct = TupleStruct(1u32, 2i8); + tuple_struct_arg(tuple_struct); + tuple_struct.tuple_2((1f32, 2f64)); + TupleStruct::tuple_1(1u32); + tuple_struct.tuple_4((1u32, 2i8, (), 3f32)); +} diff --git a/tests/ui/splat/splat-non-tuple.stderr b/tests/ui/splat/splat-non-tuple.stderr new file mode 100644 index 0000000000000..64f7148d8dbd2 --- /dev/null +++ b/tests/ui/splat/splat-non-tuple.stderr @@ -0,0 +1,54 @@ +error[E0053]: method `tuple_1` has an incompatible type for trait + --> $DIR/splat-non-tuple.rs:38:5 + | +LL | fn tuple_1(_: (u32,)) {} + | ^^^^^^^^^^^^^^^^^^^^^ expected fn with arg 0 splatted, found fn with no splatted arg + | +note: type in trait + --> $DIR/splat-non-tuple.rs:17:5 + | +LL | fn tuple_1(#[splat] _: (u32,)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn(#[splat] (_,))` + found signature `fn((_,))` + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-non-tuple.rs:7:30 + | +LL | fn primitive_arg(#[splat] x: u32) {} + | ^^^ +... +LL | primitive_arg(1u32); + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a NotATuple (NotATuple) + --> $DIR/splat-non-tuple.rs:14:25 + | +LL | fn enum_arg(#[splat] y: NotATuple) {} + | ^^^^^^^^^ +... +LL | enum_arg(NotATuple::A(1u32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Foo (Foo) + --> $DIR/splat-non-tuple.rs:25:27 + | +LL | fn struct_arg(#[splat] z: Foo) {} + | ^^^ +... +LL | struct_arg(foo); + | ^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a TupleStruct (TupleStruct) + --> $DIR/splat-non-tuple.rs:54:33 + | +LL | fn tuple_struct_arg(#[splat] z: TupleStruct) {} + | ^^^^^^^^^^^ +... +LL | tuple_struct_arg(tuple_struct); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0053, E0277. +For more information about an error, try `rustc --explain E0053`. From 472c197cef731dc9632fc1a56e9b6753d7f2dd9d Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 5 Jun 2026 19:09:38 +0200 Subject: [PATCH 189/278] Remove a leftover direct call to fn_ctxt.label_generic_mismatches --- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 55c6aa2c3ad25..9a773cc7808d4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2431,15 +2431,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { format!("arguments to this {call_name} are incorrect"), ); - self.fn_ctxt.label_generic_mismatches( - &mut err, - self.fn_def_id, - &self.matched_inputs, - &self.provided_arg_tys, - &self.formal_and_expected_inputs, - self.call_metadata.is_method, - self.tuple_arguments.is_splatted(), - ); + self.label_generic_mismatches(&mut err); if let hir::ExprKind::MethodCall(_, rcvr, _, _) = self.arg_matching_ctxt.args_ctxt.call_ctxt.call_expr.kind From 50c5d853a0d8594d995d2d537765599fe64b7122 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Wed, 3 Jun 2026 15:44:02 +0530 Subject: [PATCH 190/278] Fix ICE in `label_fn_like` when splatted arg index is out of bounds --- tests/ui/splat/splat-overload-at-home-fail.rs | 45 +++++++++++++++++++ .../splat/splat-overload-at-home-fail.stderr | 40 +++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 tests/ui/splat/splat-overload-at-home-fail.rs create mode 100644 tests/ui/splat/splat-overload-at-home-fail.stderr diff --git a/tests/ui/splat/splat-overload-at-home-fail.rs b/tests/ui/splat/splat-overload-at-home-fail.rs new file mode 100644 index 0000000000000..730b46ac0f9ee --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home-fail.rs @@ -0,0 +1,45 @@ +// tests/ui/splat/splat-overload-at-home-fail.rs + +// ignore-tidy-linelength +//! Test error cases for `#[splat]` "overloading at home" example code. +//! Splatted calls that don't match any registered MethodArgs impl should fail. +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +struct Foo; + +trait MethodArgs: std::marker::Tuple { + fn call_method(self, _this: &Foo); +} + +impl MethodArgs for () { + fn call_method(self, _this: &Foo) {} +} + +impl MethodArgs for (i32,) { + fn call_method(self, _this: &Foo) {} +} + +impl MethodArgs for (i32, String) { + fn call_method(self, _this: &Foo) {} +} + +impl Foo { + fn method(&self, #[splat] args: T) { + args.call_method(self) + } +} + +fn main() { + let foo = Foo; + + // No impl for (f32,) — wrong type + foo.method(42f32); + //~^ ERROR mismatched types + + // No impl for (i32,i32) - wrong type + foo.method(42i32, 42i32); + //~^ ERROR mismatched types + + } diff --git a/tests/ui/splat/splat-overload-at-home-fail.stderr b/tests/ui/splat/splat-overload-at-home-fail.stderr new file mode 100644 index 0000000000000..8a0ba449af213 --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home-fail.stderr @@ -0,0 +1,40 @@ +error[E0308]: mismatched types + --> $DIR/splat-overload-at-home-fail.rs:38:16 + | +LL | foo.method(42f32); + | ------ ^^^^^ expected `i32`, found `f32` + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/splat-overload-at-home-fail.rs:29:8 + | +LL | fn method(&self, #[splat] args: T) { + | ^^^^^^ ---------------- +help: change the type of the numeric literal from `f32` to `i32` + | +LL - foo.method(42f32); +LL + foo.method(42i32); + | + +error[E0308]: mismatched types + --> $DIR/splat-overload-at-home-fail.rs:42:23 + | +LL | foo.method(42i32, 42i32); + | ------ ^^^^^ expected `String`, found `i32` + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/splat-overload-at-home-fail.rs:29:8 + | +LL | fn method(&self, #[splat] args: T) { + | ^^^^^^ +help: try using a conversion method + | +LL | foo.method(42i32, 42i32.to_string()); + | ++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From 9e48cd9e1f45447f6560777e460aac71503b1561 Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 12 Jun 2026 14:04:35 +0200 Subject: [PATCH 191/278] Impl MIR lowering side-tables for #[splat] --- .../rustc_codegen_cranelift/src/abi/mod.rs | 1 + .../src/debuginfo/type_names.rs | 1 + compiler/rustc_codegen_ssa/src/mir/block.rs | 1 + compiler/rustc_codegen_ssa/src/mir/mod.rs | 1 + .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 17 ++++---- compiler/rustc_hir_typeck/src/writeback.rs | 5 ++- compiler/rustc_middle/src/ty/mod.rs | 3 +- compiler/rustc_middle/src/ty/print/pretty.rs | 15 ++++++- .../rustc_middle/src/ty/typeck_results.rs | 35 +++++++++++++++++ compiler/rustc_mir_build/src/builder/mod.rs | 1 + tests/ui/splat/splat-fn-tuple-generic-fail.rs | 27 +++++++++++++ .../splat/splat-fn-tuple-generic-fail.stderr | 39 +++++++++++++++++++ 12 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 tests/ui/splat/splat-fn-tuple-generic-fail.rs create mode 100644 tests/ui/splat/splat-fn-tuple-generic-fail.stderr diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 8f965a5ef7b12..012951098fa15 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -271,6 +271,7 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ .map(|local| { let arg_ty = fx.monomorphize(fx.mir.local_decls[local].ty); + // FIXME(splat): un-tuple splatted arguments in codegen, for performance // Adapted from https://github.com/rust-lang/rust/blob/145155dc96757002c7b2e9de8489416e2fdbbd57/src/librustc_codegen_llvm/mir/mod.rs#L442-L482 if Some(local) == fx.mir.spread_arg { // This argument (e.g. the last argument in the "rust-call" ABI) diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 9b8d05c9f72c0..5ece363fcd8d9 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -375,6 +375,7 @@ fn push_debuginfo_type_name<'tcx>( output.push_str("fn("); } + // FIXME(splat): should debuginfo be de-tupled in the callee (and caller)? if !sig.inputs().is_empty() { for ¶meter_type in sig.inputs() { push_debuginfo_type_name(tcx, parameter_type, true, output, visited); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 115c50edf4e9f..00cfe1c845d7a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1202,6 +1202,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; // Split the rust-call tupled arguments off. + // FIXME(splat): un-tuple splatted arguments in codegen, for performance let (first_args, untuple) = if sig.abi() == ExternAbi::RustCall && let Some((tup, args)) = args.split_last() { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 67c9a85ca408f..68fed6c867fa5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -433,6 +433,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let arg_decl = &mir.local_decls[local]; let arg_ty = fx.monomorphize(arg_decl.ty); + // FIXME(splat): re-tuple splatted arguments that were un-tupled in the ABI if Some(local) == mir.spread_arg { // This argument (e.g., the last argument in the "rust-call" ABI) // is a tuple that was spread at the ABI level and now we have diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index bc954f5518619..db2fb43522e14 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -27,8 +27,8 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, - SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, Unnormalized, - UserArgs, UserSelfTy, + SizedTraitKind, SplattedDef, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, + Unnormalized, UserArgs, UserSelfTy, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -239,10 +239,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self))] pub(crate) fn write_splatted_resolution( &self, - _hir_id: HirId, - _r: Result<() /* SplattedDef */, ErrorGuaranteed>, + hir_id: HirId, + r: Result, ) { - // FIXME(splat): add side table and write to it here + self.typeck_results.borrow_mut().splatted_defs_mut().insert(hir_id, r); } #[instrument(level = "debug", skip(self))] @@ -272,8 +272,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_splatted_resolution( hir_id, - // FIXME(splat): add side table and write to it here - Ok(()), + Ok(SplattedDef { + def_id: callee_def_id, + arg_index: first_tupled_arg_index, + arg_count: tupled_args_count, + }), ); if let Some(callee_generic_args) = callee_generic_args { self.write_args(hir_id, callee_generic_args); diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index ad08daa7a4141..3d50befe042f1 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -663,7 +663,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.type_dependent_defs_mut().insert(hir_id, def); } - // FIXME(splat): add side table and write to it here + // Export splatted function call resolutions. + if let Some(def) = self.fcx.typeck_results.borrow_mut().splatted_defs_mut().remove(hir_id) { + self.typeck_results.splatted_defs_mut().insert(hir_id, def); + } // Resolve any borrowings for the node with id `node_id` self.visit_adjustments(span, hir_id); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3ea798ee45fb2..f081e98127cf4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -113,7 +113,8 @@ pub use self::sty::{ pub use self::trait_def::TraitDef; pub use self::typeck_results::{ CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity, - Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind, + Rust2024IncompatiblePatInfo, SplattedDef, TypeckResults, UserType, UserTypeAnnotationIndex, + UserTypeKind, }; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; use crate::metadata::{AmbigModChild, ModChild}; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c1f96c3a94fdd..89b43480b3fc3 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1435,6 +1435,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p.pretty_print_fn_sig( tys, false, + // FIXME(splat): support splatted arguments here? + None, proj.skip_binder().term.as_type().expect("Return type was a const"), )?; resugared = true; @@ -1538,10 +1540,19 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { &mut self, inputs: &[Ty<'tcx>], c_variadic: bool, + splatted: Option, output: Ty<'tcx>, ) -> Result<(), PrintError> { write!(self, "(")?; - self.comma_sep(inputs.iter().copied())?; + let splatted_arg_index = splatted.map(usize::from); + let mut input_iter = inputs.iter().copied(); + if let Some(index) = splatted_arg_index { + self.comma_sep((&mut input_iter).take(usize::from(index)))?; + write!(self, ", #[splat]")?; + self.comma_sep(input_iter)?; + } else { + self.comma_sep(input_iter)?; + } if c_variadic { if !inputs.is_empty() { write!(self, ", ")?; @@ -3150,7 +3161,7 @@ define_print! { } write!(p, "fn")?; - p.pretty_print_fn_sig(self.inputs(), self.c_variadic(), self.output())?; + p.pretty_print_fn_sig(self.inputs(), self.c_variadic(), self.splatted(), self.output())?; } ty::TraitRef<'tcx> { diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 1287047581196..77820beef0228 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -36,6 +36,9 @@ pub struct TypeckResults<'tcx> { /// method calls, including those of overloaded operators. type_dependent_defs: ItemLocalMap>, + /// Resolved definitions for splatted function calls. + splatted_defs: ItemLocalMap>, + /// Resolved field indices for field accesses in expressions (`S { field }`, `obj.field`) /// or patterns (`S { field }`). The index is often useful by itself, but to learn more /// about the field you also need definition of the variant to which the field @@ -229,6 +232,7 @@ impl<'tcx> TypeckResults<'tcx> { TypeckResults { hir_owner, type_dependent_defs: Default::default(), + splatted_defs: Default::default(), field_indices: Default::default(), user_provided_types: Default::default(), user_provided_sigs: Default::default(), @@ -287,6 +291,21 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.type_dependent_defs } } + pub fn splatted_defs(&self) -> LocalTableInContext<'_, Result> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.splatted_defs } + } + + pub fn splatted_def(&self, id: HirId) -> Option { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.splatted_defs.get(&id.local_id).cloned().and_then(|r| r.ok()) + } + + pub fn splatted_defs_mut( + &mut self, + ) -> LocalTableInContextMut<'_, Result> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.splatted_defs } + } + pub fn field_indices(&self) -> LocalTableInContext<'_, FieldIdx> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.field_indices } } @@ -407,6 +426,10 @@ impl<'tcx> TypeckResults<'tcx> { matches!(self.type_dependent_defs().get(expr.hir_id), Some(Ok((DefKind::AssocFn, _)))) } + pub fn is_splatted_call(&self, expr: &hir::Expr<'_>) -> bool { + matches!(self.splatted_defs().get(expr.hir_id), Some(Ok(SplattedDef { .. }))) + } + /// Returns the computed binding mode for a `PatKind::Binding` pattern /// (after match ergonomics adjustments). pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> BindingMode { @@ -569,6 +592,18 @@ impl<'tcx> TypeckResults<'tcx> { } } +/// A resolved splatted function call. +#[derive(Debug, Copy, Clone, PartialEq, Eq, StableHash, TyEncodable, TyDecodable)] +pub struct SplattedDef { + /// The function DefId, if available (FnPtrs don't have DefIds) + pub def_id: Option, + /// The index of the first argument in the callee's splatted tuple, and the index of the + /// splatted tuple argument in the caller. + pub arg_index: u16, + /// The number of arguments in the splatted tuple. + pub arg_count: u16, +} + /// Validate that the given HirId (respectively its `local_id` part) can be /// safely used as a key in the maps of a TypeckResults. For that to be /// the case, the HirId must have the same `owner` as all the other IDs in diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 854a378bb993d..378e2e618fb93 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -547,6 +547,7 @@ fn construct_fn<'tcx>( body.spread_arg = if abi == ExternAbi::RustCall { // RustCall pseudo-ABI untuples the last argument. + // FIXME(splat): splat can untuple any argument, set spread_arg here Some(Local::new(arguments.len())) } else { None diff --git a/tests/ui/splat/splat-fn-tuple-generic-fail.rs b/tests/ui/splat/splat-fn-tuple-generic-fail.rs new file mode 100644 index 0000000000000..8a8651e75c9ff --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic-fail.rs @@ -0,0 +1,27 @@ +//! Test failing use of `#[splat]` on tuple trait arguments of generic functions. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn splat_generic_tuple(#[splat] _t: T) {} + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + + // Calling with un-splatted arguments might look like it works, but the actual generic type is + // a tuple inside another tuple. Aren't generics great? + splat_generic_tuple((1, 2)); + splat_generic_tuple((1u32, 2i8)); + + // FIXME(splat): Make the splat generic handling code handle tuples inside tuples + // (if we want to support tupled calls) + splat_generic_tuple::<(((u32, i8)))>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<(((u32, i8)))>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + + splat_generic_tuple::<((u32, i8))>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<((u32, i8))>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + + splat_generic_tuple::<(u32, i8)>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<(u32, i8)>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided +} diff --git a/tests/ui/splat/splat-fn-tuple-generic-fail.stderr b/tests/ui/splat/splat-fn-tuple-generic-fail.stderr new file mode 100644 index 0000000000000..7fd4a0719b493 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic-fail.stderr @@ -0,0 +1,39 @@ +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:19:5 + | +LL | splat_generic_tuple::<(((u32, i8)))>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:20:5 + | +LL | splat_generic_tuple::<(((u32, i8)))>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:22:5 + | +LL | splat_generic_tuple::<((u32, i8))>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:23:5 + | +LL | splat_generic_tuple::<((u32, i8))>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:25:5 + | +LL | splat_generic_tuple::<(u32, i8)>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:26:5 + | +LL | splat_generic_tuple::<(u32, i8)>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0057`. From 2bb9dabcc247dc1c47eb14c655c0958cbb5d3671 Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 12 Jun 2026 14:27:09 +0200 Subject: [PATCH 192/278] Impl actual MIR lowering for #[splat] --- compiler/rustc_mir_build/src/thir/cx/expr.rs | 151 ++++++++++++++++-- tests/ui/splat/splat-assoc-fn-tuple-simple.rs | 22 +++ tests/ui/splat/splat-fn-ptr-tuple.rs | 52 ++++++ tests/ui/splat/splat-fn-ptr-tuple.stderr | 24 +++ tests/ui/splat/splat-fn-tuple-generic.rs | 23 +++ tests/ui/splat/splat-fn-tuple-simple.rs | 32 ++++ tests/ui/splat/splat-generics-everywhere.rs | 68 ++++++++ tests/ui/splat/splat-method-tuple-simple.rs | 40 +++++ tests/ui/splat/splat-overload-at-home.rs | 52 ++++++ tests/ui/splat/splat-trait-tuple.rs | 45 ++++++ 10 files changed, 494 insertions(+), 15 deletions(-) create mode 100644 tests/ui/splat/splat-assoc-fn-tuple-simple.rs create mode 100644 tests/ui/splat/splat-fn-ptr-tuple.rs create mode 100644 tests/ui/splat/splat-fn-ptr-tuple.stderr create mode 100644 tests/ui/splat/splat-fn-tuple-generic.rs create mode 100644 tests/ui/splat/splat-fn-tuple-simple.rs create mode 100644 tests/ui/splat/splat-generics-everywhere.rs create mode 100644 tests/ui/splat/splat-method-tuple-simple.rs create mode 100644 tests/ui/splat/splat-overload-at-home.rs create mode 100644 tests/ui/splat/splat-trait-tuple.rs diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b71793c157621..c286c0819aea2 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -18,8 +18,8 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, }; use rustc_middle::ty::{ - self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, TyCtxt, - UpvarArgs, + self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, SplattedDef, Ty, + TyCtxt, UpvarArgs, }; use rustc_middle::{bug, span_bug}; use rustc_span::{DesugaringKind, Span}; @@ -369,19 +369,26 @@ impl<'tcx> ThirBuildCx<'tcx> { let kind = match expr.kind { // Here comes the interesting stuff: hir::ExprKind::MethodCall(segment, receiver, args, fn_span) => { - // Rewrite a.b(c) into UFCS form like Trait::b(a, c) - let expr = self.method_callee(expr, segment.ident.span, None); - info!("Using method span: {:?}", expr.span); - let args = std::iter::once(receiver) - .chain(args.iter()) - .map(|expr| self.mirror_expr(expr)) - .collect(); - ExprKind::Call { - ty: expr.ty, - fun: self.thir.exprs.push(expr), - args, - from_hir_call: true, - fn_span, + if self.typeck_results.is_splatted_call(expr) { + // The callee has a splatted tuple argument. + // rewrite `receiver.f(a, u, v)` into `receiver.f(a, #[splat] (u, v))` + self.convert_splatted_callee(expr, fn_span, args, Some(receiver)) + } else { + // Rewrite a.b(c) into UFCS form like Trait::b(a, c) + let expr = self.method_callee(expr, segment.ident.span, None); + info!("Using method span: {:?}", expr.span); + + let args = std::iter::once(receiver) + .chain(args.iter()) + .map(|expr| self.mirror_expr(expr)) + .collect(); + ExprKind::Call { + ty: expr.ty, + fun: self.thir.exprs.push(expr), + args, + from_hir_call: true, + fn_span, + } } } @@ -412,6 +419,10 @@ impl<'tcx> ThirBuildCx<'tcx> { from_hir_call: true, fn_span: expr.span, } + } else if self.typeck_results.is_splatted_call(expr) { + // The callee has a splatted tuple argument. + // rewrite `f(a, u, v)` into `f(a, #[splat] (u, v))` + self.convert_splatted_callee(expr, fun.span, args, None) } else { // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. let adt_data = if let hir::ExprKind::Path(ref qpath) = fun.kind @@ -1205,6 +1216,116 @@ impl<'tcx> ThirBuildCx<'tcx> { } } + fn splatted_callee( + &mut self, + expr: &hir::Expr<'_>, + span: Span, + ) -> (Expr<'tcx>, u16 /* arg_index */, u16 /* arg_count */) { + let SplattedDef { def_id, arg_index, arg_count } = + self.typeck_results.splatted_def(expr.hir_id).unwrap_or_else(|| { + span_bug!(expr.span, "no splatted def for function or method callee") + }); + let def_id = def_id.unwrap_or_else(|| { + span_bug!(expr.span, "no splatted def for function or method callee") + }); + let def_kind = self.tcx.def_kind(def_id); + let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(def_kind, def_id)); + debug!( + "splatted_callee: user_ty={:?} def_kind={:?} def_id={:?} arg_index={:?} arg_count={:?}", + user_ty, def_kind, def_id, arg_index, arg_count + ); + + ( + Expr { + temp_scope_id: expr.hir_id.local_id, + ty: Ty::new_fn_def(self.tcx, def_id, self.typeck_results.node_args(expr.hir_id)), + span, + kind: ExprKind::ZstLiteral { user_ty }, + }, + arg_index, + arg_count, + ) + } + + /// The callee has a splatted tuple argument. + /// Rewrite a splatted call `receiver.f(a, u, v)` into `receiver.f(a, #[splat] (u, v))`. + /// The receiver is optional. + fn convert_splatted_callee( + &mut self, + expr: &hir::Expr<'_>, + fn_span: Span, + args: &'tcx [hir::Expr<'tcx>], + receiver: Option<&'tcx hir::Expr<'tcx>>, + ) -> ExprKind<'tcx> { + let tcx = self.tcx; + + // The callee has a splatted tuple argument. + let (func, tupled_arg_index, tupled_args_count) = self.splatted_callee(expr, fn_span); + let tupled_arg_index = usize::from(tupled_arg_index); + let tupled_args_count = usize::from(tupled_args_count); + + // Splatting an empty tuple is permitted: `a.f() -> Trait::f(a, #[splat] ())`. + // In that case, the tupled arg index is one past the end of the args. + if tupled_arg_index + tupled_args_count > args.len() { + span_bug!( + expr.span, + "splatted arg index out of bounds of function args: {:?} + {:?} > {:?} for function call: receiver {:?}, args {:?}", + tupled_arg_index, + tupled_args_count, + args.len(), + receiver, + args, + ); + } + + info!("Using splatted function span: {:?}", func.span); + + // Split into non-tupled and tupled arguments + let initial_non_tupled_args = + args.iter().take(tupled_arg_index).map(|e| self.mirror_expr(e)).collect_vec(); + let tupled_args = if tupled_arg_index == args.len() || tupled_args_count == 0 { + // Splatting an empty tuple, in the ABI this gets ignored + Default::default() + } else { + &args[tupled_arg_index..(tupled_arg_index + tupled_args_count)] + }; + let final_non_tupled_args = args + .iter() + .skip(tupled_arg_index + tupled_args_count) + .map(|e| self.mirror_expr(e)) + .collect_vec(); + + let tupled_arg_tys = tupled_args.iter().map(|e| self.typeck_results.expr_ty_adjusted(e)); + + let temp_scope_id = + if receiver.is_some() { func.temp_scope_id } else { expr.hir_id.local_id }; + let tupled_args = Expr { + ty: Ty::new_tup_from_iter(tcx, tupled_arg_tys), + temp_scope_id, + span: expr.span, + kind: ExprKind::Tuple { fields: self.mirror_exprs(tupled_args) }, + }; + + let tupled_args = self.thir.exprs.push(tupled_args); + + let mut args = + if let Some(receiver) = receiver { vec![self.mirror_expr(receiver)] } else { vec![] }; + args.extend(initial_non_tupled_args); + args.push(tupled_args); + args.extend(final_non_tupled_args); + + // We need the tupled arguments in HIR/MIR for type checking, but codegen can + // de-tuple them for performance + let fn_span = if receiver.is_some() { func.span } else { expr.span }; + ExprKind::Call { + ty: func.ty, + fun: self.thir.exprs.push(func), + args: args.into_boxed_slice(), + from_hir_call: true, + fn_span, + } + } + fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> ArmId { let arm = Arm { pattern: self.pattern_from_hir(&arm.pat), diff --git a/tests/ui/splat/splat-assoc-fn-tuple-simple.rs b/tests/ui/splat/splat-assoc-fn-tuple-simple.rs new file mode 100644 index 0000000000000..d2681c0d2574c --- /dev/null +++ b/tests/ui/splat/splat-assoc-fn-tuple-simple.rs @@ -0,0 +1,22 @@ +//@ run-pass +//! Test using `#[splat]` on associated function tuple arguments (no receivers). + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo; + +impl Foo { + fn tuple_1(#[splat] (_a,): (u32,)) {} + + fn tuple_3(#[splat] (_a, _b, _c): (u32, i32, i8)) {} +} + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::tuple_1((1u32,)); + + Foo::tuple_1(1u32); + Foo::tuple_3(1u32, 2i32, 3i8); +} diff --git a/tests/ui/splat/splat-fn-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-tuple.rs new file mode 100644 index 0000000000000..b4a6a3beae58f --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple.rs @@ -0,0 +1,52 @@ +//@ failure-status: 101 + +//@ normalize-stderr: "(.*)internal compiler error:([^:]+):\d{1,}:\d{1,}:(.*)" -> "$1internal compiler error:$2:LL:CC:$3" +//@ normalize-stderr: "thread.*panicked at compiler.*" -> "" +//@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" +//@ normalize-stderr: "note: compiler flags.*\n\n" -> "" +//@ normalize-stderr: " +\d{1,}: .*\n" -> "" +//@ normalize-stderr: " + at .*\n" -> "" +//@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" +//@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" + +//! Test using `#[splat]` on tuple arguments of simple functions. +//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. + +#![allow(incomplete_features)] +#![feature(splat)] + +fn tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +fn splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in + // MIR lowering + // FIXME(rustfmt): the attribute gets deleted by rustfmt + // Functions + #[rustfmt::skip] + let fn_ptr: fn(#[splat] (u32, i8)) = tuple_args; + fn_ptr(1, 2); //~ ERROR no splatted def for function or method callee + fn_ptr(1u32, 2i8); + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //fn_ptr((1, 2)); // ERROR this splatted function takes 2 arguments, but 1 was provided + + #[rustfmt::skip] + let fn_ptr: fn(#[splat] (u32, i8), f64) = splat_non_terminal_arg; + fn_ptr(1, 2, 3.5); + fn_ptr(1u32, 2i8, 3.5f64); + + // Function pointers + #[rustfmt::skip] + let fn_ptr: *const fn(#[splat] (u32, i8)) = tuple_args as *const fn(#[splat] (u32, i8)); + (*fn_ptr)(1, 2); + (*fn_ptr)(1u32, 2i8); + + #[rustfmt::skip] + let fn_ptr: *const fn(#[splat] (u32, i8), f64) = + splat_non_terminal_arg as *const fn(#[splat] (u32, i8), f64); + (*fn_ptr)(1, 2, 3.5); + (*fn_ptr)(1u32, 2i8, 3.5f64); +} diff --git a/tests/ui/splat/splat-fn-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-tuple.stderr new file mode 100644 index 0000000000000..700d7639241a3 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple.stderr @@ -0,0 +1,24 @@ + compiler error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: no splatted def for function or method callee + --> $DIR/splat-fn-ptr-tuple.rs:29:5 + | +LL | fn_ptr(1, 2); + | ^^^^^^^^^^^^ + + + +Box +stack backtrace: + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc {version} running on {platform} + +query stack during panic: +#0 [thir_body] building THIR for `main` +#1 [check_unsafety] unsafety-checking `main` +#2 [analysis] running analysis passes on crate `splat_fn_ptr_tuple` +end of query stack +error: aborting due to 1 previous error + diff --git a/tests/ui/splat/splat-fn-tuple-generic.rs b/tests/ui/splat/splat-fn-tuple-generic.rs new file mode 100644 index 0000000000000..b7e3615f62c45 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic.rs @@ -0,0 +1,23 @@ +//@ run-pass +//! Test using `#[splat]` on tuple trait arguments of generic functions. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn splat_generic_tuple(#[splat] _t: T) {} + +fn main() { + // Calling with un-splatted arguments might look like it works, but the actual generic type is + // a tuple inside another tuple. Aren't generics great? + splat_generic_tuple((1, 2)); + splat_generic_tuple((1u32, 2i8)); + + // Generic tuple trait implementers are resolved during caller typeck. + splat_generic_tuple::<(u32, i8)>(1u32, 2i8); + splat_generic_tuple(1u32, 2i8); + splat_generic_tuple(1, 2); + + splat_generic_tuple::<()>(); + splat_generic_tuple(); +} diff --git a/tests/ui/splat/splat-fn-tuple-simple.rs b/tests/ui/splat/splat-fn-tuple-simple.rs new file mode 100644 index 0000000000000..c7234a15b9d55 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-simple.rs @@ -0,0 +1,32 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of simple functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +fn tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +fn splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + tuple_args(1, 2); + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //tuple_args((1, 2)); + + tuple_args(1, 2); + tuple_args(1u32, 2i8); + + splat_non_terminal_arg(1, 2, 3.5); + splat_non_terminal_arg(1u32, 2i8, 3.5f64); + + #[expect(unused_variables, reason = "FIXME(splat or lint): this is obviously used")] + let fn_ptr = tuple_args; + fn_ptr(1, 2); + fn_ptr(1u32, 2i8); + + #[expect(unused_variables, reason = "FIXME(splat or lint): this is obviously used")] + let fn_ptr = splat_non_terminal_arg; + fn_ptr(1, 2, 3.5); + fn_ptr(1u32, 2i8, 3.5f64); +} diff --git a/tests/ui/splat/splat-generics-everywhere.rs b/tests/ui/splat/splat-generics-everywhere.rs new file mode 100644 index 0000000000000..16093fe338357 --- /dev/null +++ b/tests/ui/splat/splat-generics-everywhere.rs @@ -0,0 +1,68 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with generics in various positions. + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo(T); + +// FIXME(splat): also add assoc/method with splatted generic tuple traits +// also add generics inside the splatted tuple +impl Foo { + fn new(t: T) -> Self { + Self(t) + } + + fn assoc(_u: U, #[splat] _s: ()) {} + + fn method(&self, _v: V, #[splat] _s: (u32, f64)) {} + + fn lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +// FIXME(splat): also add generics to the trait +// also add assoc/method with splatted generic tuple traits +// also add generics inside the splatted tuple +trait BarTrait { + fn trait_assoc(w: W, #[splat] _s: ()); + + fn trait_method(&self, x: X, #[splat] _s: (u32, f64)); + + fn trait_lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn trait_const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +impl BarTrait for Foo { + fn trait_assoc(_w: W, #[splat] _s: ()) {} + + fn trait_method(&self, _x: X, #[splat] _s: (u32, f64)) {} + + fn trait_lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn trait_const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +// FIXME(splat): +// - add `T: Tuple` generics tests +// - add const fn generics tests + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::::assoc(("u",)); + + Foo::::assoc("u"); + Foo::::trait_assoc("w"); + + let foo = Foo::new("t"); + foo.method("v", 1u32, 2.3); + foo.lifetime(1u32, 2.3, "asdf"); + foo.const_generic(1u32, 2.3, [1, 2, 3]); + + foo.trait_method("x", 42u32, 9.8); + foo.trait_lifetime(1u32, 2.3, "asdf"); + foo.trait_const_generic(1u32, 2.3, [1, 2, 3]); +} diff --git a/tests/ui/splat/splat-method-tuple-simple.rs b/tests/ui/splat/splat-method-tuple-simple.rs new file mode 100644 index 0000000000000..887c9515bcdae --- /dev/null +++ b/tests/ui/splat/splat-method-tuple-simple.rs @@ -0,0 +1,40 @@ +//@ run-pass +//! Test using `#[splat]` on method tuple arguments (with receivers). + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo; + +impl Foo { + fn tuple_2(&self, #[splat] (a, _b): (u32, i8)) -> u32 { + a + } + + fn tuple_4(&self, #[splat] (a, _b, _c, _d): (u32, i8, (), f32)) -> u32 { + a + } +} + +#[expect(dead_code)] +struct TupleStruct(u32, i8); + +impl TupleStruct { + fn tuple_2(&self, #[splat] (a, _b): (u32, i8)) -> u32 { + a + } +} + +fn main() { + let foo = Foo; + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //foo.tuple_2((1, 2)); + + foo.tuple_2(1u32, 2i8); + foo.tuple_4(1u32, 2i8, (), 3f32); + + let tuple_struct = TupleStruct(1u32, 2i8); + tuple_struct.tuple_2(1u32, 2i8); +} diff --git a/tests/ui/splat/splat-overload-at-home.rs b/tests/ui/splat/splat-overload-at-home.rs new file mode 100644 index 0000000000000..621f0e04f67e1 --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home.rs @@ -0,0 +1,52 @@ +//@ run-pass +// ignore-tidy-linelength +//! Test using `#[splat]` on some "overloading at home" example code. +//! + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +struct Foo; + +trait MethodArgs: std::marker::Tuple { + fn call_method(self, _this: &Foo); +} +impl MethodArgs for () { + fn call_method(self, _this: &Foo) {} +} +impl MethodArgs for (i32,) { + fn call_method(self, _this: &Foo) {} +} +impl MethodArgs for (i32, String) { + fn call_method(self, _this: &Foo) {} +} + +impl Foo { + fn method(&self, #[splat] args: T) { + args.call_method(self) + } +} + +fn main() { + let foo = Foo; + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //foo.method(()); + //foo.method((42i32,)); + + // Generic tuple trait implementers work without explicit tuple type parameters. + foo.method::<()>(); + foo.method(); + + foo.method::<(i32,)>(42i32); + foo.method::<(i32,)>(42); + foo.method(42i32); + foo.method(42); + + foo.method::<(i32, String)>(42i32, "asdf".to_owned()); + foo.method::<(i32, String)>(42, "asdf".to_owned()); + foo.method(42i32, "asdf".to_owned()); + foo.method(42, "asdf".to_owned()); +} diff --git a/tests/ui/splat/splat-trait-tuple.rs b/tests/ui/splat/splat-trait-tuple.rs new file mode 100644 index 0000000000000..a5b74a40cffd9 --- /dev/null +++ b/tests/ui/splat/splat-trait-tuple.rs @@ -0,0 +1,45 @@ +//@ run-pass +//! Test using `#[splat]` on trait assoc function/method tuple arguments. + +#![allow(incomplete_features)] +#![feature(splat)] + +trait FooTrait { + fn tuple_1_trait(#[splat] _: (u32,)); + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)); +} + +struct Foo; + +impl FooTrait for Foo { + // Currently, splat attributes on impls must match traits. This provides better UX. + fn tuple_1_trait(#[splat] _: (u32,)) {} + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)) {} +} + +#[expect(dead_code)] +struct TupleStruct(u32, i8); + +impl FooTrait for TupleStruct { + fn tuple_1_trait(#[splat] _: (u32,)) {} + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)) {} +} + +fn main() { + let foo = Foo; + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::tuple_1_trait((1u32,)); + //foo.tuple_2_trait((1, 3.5)); + + Foo::tuple_1_trait(1u32); + foo.tuple_2_trait(1, 3.5); + + let tuple_struct = TupleStruct(1u32, 2i8); + TupleStruct::tuple_1_trait(1u32); + tuple_struct.tuple_2_trait(1, 3.5) +} From 50c658c0cbd9c79f4e519aeae4d4b90136dbf04f Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 11 Mar 2026 17:34:32 +1000 Subject: [PATCH 193/278] Impl TypeInfo for splat --- .../src/const_eval/type_info.rs | 18 ++++- .../rustc_const_eval/src/interpret/call.rs | 1 + compiler/rustc_span/src/symbol.rs | 2 + library/core/src/mem/type_info.rs | 18 ++++- library/coretests/tests/mem/fn_ptr.rs | 66 +++++++++++++++++++ 5 files changed, 103 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 3d8d9592459c4..8f58e39d419ce 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -7,7 +7,7 @@ use rustc_ast::Mutability; use rustc_hir::LangItem; use rustc_middle::span_bug; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{self, Const, FnHeader, FnSigTys, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::{self, Const, FnHeader, FnSigKind, FnSigTys, ScalarInt, Ty, TyCtxt}; use rustc_span::{Symbol, sym}; use crate::const_eval::CompileTimeMachine; @@ -436,6 +436,22 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { sym::variadic => { self.write_scalar(Scalar::from_bool(fn_sig_kind.c_variadic()), &field_place)?; } + sym::is_splatted => { + self.write_scalar( + Scalar::from_bool(fn_sig_kind.splatted().is_some()), + &field_place, + )?; + } + sym::splatted_index => { + self.write_scalar( + Scalar::from_u8( + // Currently the same encoding as FnSigKind.splatted + // FIXME(splat): make these two fields into a single Option, or choose a stable encoding + fn_sig_kind.splatted().unwrap_or(FnSigKind::NO_SPLATTED_ARG_INDEX), + ), + &field_place, + )?; + } other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 44420b7148478..e2fe8c3a79690 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -681,6 +681,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // Special handling for the closure ABI: untuple the last argument. + // FIXME(splat): un-tuple splatted arguments that were tupled in typecheck let args: Cow<'_, [FnArg<'tcx, M::Provenance>]> = if caller_abi == ExternAbi::RustCall && !args.is_empty() { // Untuple diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 827e33dcd6632..b3344d150d999 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1141,6 +1141,7 @@ symbols! { irrefutable_let_patterns, is, is_auto, + is_splatted, is_val_statically_known, isa_attribute, isize, @@ -1996,6 +1997,7 @@ symbols! { speed, spirv, splat, + splatted_index, spotlight, sqrtf16, sqrtf32, diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 9ec36561839b5..7614adbcc532b 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -215,7 +215,7 @@ pub struct Variant { pub name: &'static str, /// All fields of the variant. pub fields: &'static [Field], - /// Whether the enum variant fields is non-exhaustive. + /// Whether the enum variant fields are non-exhaustive. pub non_exhaustive: bool, } @@ -342,6 +342,22 @@ pub struct FnPtr { /// Vardiadic function, e.g. extern "C" fn add(n: usize, mut args: ...); pub variadic: bool, + + // FIXME(splat): should these fields be private, or merged into an Option? + /// Is any function argument splatted? + pub is_splatted: bool, + + /// The index of the splatted function argument in `inputs`, only valid if `is_splatted` is true. + /// e.g. in `fn overload(a: u8, #[splat] b: (f32, usize))` the index is 1, and it can be called + /// as `overload(a, 1.0, 2)`. + pub splatted_index: u8, +} + +impl FnPtr { + /// Returns the splatted function argument index, or `None` if no argument is splatted. + pub const fn splatted(&self) -> Option { + if self.is_splatted { Some(self.splatted_index) } else { None } + } } #[derive(Debug, Default)] diff --git a/library/coretests/tests/mem/fn_ptr.rs b/library/coretests/tests/mem/fn_ptr.rs index 1d50a2552a193..6e7e170917f55 100644 --- a/library/coretests/tests/mem/fn_ptr.rs +++ b/library/coretests/tests/mem/fn_ptr.rs @@ -5,6 +5,7 @@ const STRING_TY: TypeId = const { TypeId::of::() }; const U8_TY: TypeId = const { TypeId::of::() }; const _U8_REF_TY: TypeId = const { TypeId::of::<&u8>() }; const UNIT_TY: TypeId = const { TypeId::of::<()>() }; +const TUPLE_STRING_U8_TY: TypeId = const { TypeId::of::<(String, u8)>() }; #[test] fn test_fn_ptrs() { @@ -14,6 +15,8 @@ fn test_fn_ptrs() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -31,6 +34,8 @@ fn test_ref() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -61,6 +66,8 @@ fn test_unsafe() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -75,6 +82,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -87,6 +96,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -99,6 +110,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -114,6 +127,8 @@ fn test_inputs() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -128,6 +143,8 @@ fn test_inputs() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -145,6 +162,8 @@ fn test_output() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of:: u8>().kind }) else { panic!(); @@ -160,6 +179,8 @@ fn test_variadic() { inputs: [ty1], output, variadic: true, + is_splatted: false, + splatted_index: _, }) = &(const { Type::of::().kind }) else { panic!(); @@ -167,3 +188,48 @@ fn test_variadic() { assert_eq!(output, &UNIT_TY); assert_eq!(*ty1, U8_TY); } + +#[test] +fn test_splat() { + #[rustfmt::skip] + let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { + panic!(); + }; + let FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: [ty1], + output, + variadic: false, + is_splatted: true, + splatted_index: 0, + } = fn_ptr_ty + else { + panic!(); + }; + assert_eq!(output, &UNIT_TY); + assert_eq!(*ty1, TUPLE_STRING_U8_TY); + assert_eq!(fn_ptr_ty.splatted(), Some(0)); +} + +#[test] +fn test_not_splat() { + let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { + panic!(); + }; + let FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: [ty1], + output, + variadic: false, + is_splatted: false, + splatted_index: _, + } = fn_ptr_ty + else { + panic!(); + }; + assert_eq!(output, &UNIT_TY); + assert_eq!(*ty1, TUPLE_STRING_U8_TY); + assert_eq!(fn_ptr_ty.splatted(), None); +} From dc3889a9f6d163a49cf87c05a50d90d408a4eb32 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Wed, 3 Jun 2026 15:44:38 +0530 Subject: [PATCH 194/278] Add UI tests for `#[splat]` unhappy path, generics, fn colors - const, async, and unsafe functions - const generics, complex types, and where clauses (including impl Tuple) --- tests/ui/splat/splat-async-fn-tuple-fail.rs | 17 ++++++ .../ui/splat/splat-async-fn-tuple-fail.stderr | 27 +++++++++ tests/ui/splat/splat-async-fn-tuple.rs | 18 ++++++ .../ui/splat/splat-const-fn-tuple-generic.rs | 34 +++++++++++ tests/ui/splat/splat-const-fn-tuple.rs | 17 ++++++ .../ui/splat/splat-generics-complex-types.rs | 26 ++++++++ tests/ui/splat/splat-generics-everywhere.rs | 4 -- tests/ui/splat/splat-generics-inside-tuple.rs | 29 +++++++++ tests/ui/splat/splat-invalid-trait-impl.rs | 27 +++++++++ .../ui/splat/splat-invalid-trait-impl.stderr | 60 +++++++++++++++++++ tests/ui/splat/splat-overload-at-home-fail.rs | 6 +- .../splat/splat-overload-at-home-fail.stderr | 8 +-- tests/ui/splat/splat-unsafe-fn-tuple-fail.rs | 16 +++++ .../splat/splat-unsafe-fn-tuple-fail.stderr | 20 +++++++ tests/ui/splat/splat-unsafe-fn-tuple.rs | 19 ++++++ tests/ui/splat/splat-where-clause.rs | 44 ++++++++++++++ 16 files changed, 359 insertions(+), 13 deletions(-) create mode 100644 tests/ui/splat/splat-async-fn-tuple-fail.rs create mode 100644 tests/ui/splat/splat-async-fn-tuple-fail.stderr create mode 100644 tests/ui/splat/splat-async-fn-tuple.rs create mode 100644 tests/ui/splat/splat-const-fn-tuple-generic.rs create mode 100644 tests/ui/splat/splat-const-fn-tuple.rs create mode 100644 tests/ui/splat/splat-generics-complex-types.rs create mode 100644 tests/ui/splat/splat-generics-inside-tuple.rs create mode 100644 tests/ui/splat/splat-invalid-trait-impl.rs create mode 100644 tests/ui/splat/splat-invalid-trait-impl.stderr create mode 100644 tests/ui/splat/splat-unsafe-fn-tuple-fail.rs create mode 100644 tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr create mode 100644 tests/ui/splat/splat-unsafe-fn-tuple.rs create mode 100644 tests/ui/splat/splat-where-clause.rs diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.rs b/tests/ui/splat/splat-async-fn-tuple-fail.rs new file mode 100644 index 0000000000000..a1f67dfe606d6 --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple-fail.rs @@ -0,0 +1,17 @@ +//@ edition:2024 +//! Test that using `#[splat]` incorrectly on async functions gives errors. + +#![allow(incomplete_features)] +#![feature(splat)] + +async fn async_wrong_type(#[splat] _x: u32) {} +//~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} +//~^ ERROR multiple `#[splat]`s are not allowed in the same function + +fn main() { + async_wrong_type(1u32); + async_multi_splat(1u32, 2i8, 3u32, 4i8); + //~^ ERROR this splatted function takes 3 arguments, but 4 were provided +} diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.stderr b/tests/ui/splat/splat-async-fn-tuple-fail.stderr new file mode 100644 index 0000000000000..e643b29c88c8a --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple-fail.stderr @@ -0,0 +1,27 @@ +error: multiple `#[splat]`s are not allowed in the same function + --> $DIR/splat-async-fn-tuple-fail.rs:10:28 + | +LL | async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `#[splat]` from all but one argument + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-async-fn-tuple-fail.rs:7:40 + | +LL | async fn async_wrong_type(#[splat] _x: u32) {} + | ^^^ +... +LL | async_wrong_type(1u32); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 3 arguments, but 4 were provided + --> $DIR/splat-async-fn-tuple-fail.rs:15:5 + | +LL | async_multi_splat(1u32, 2i8, 3u32, 4i8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0057, E0277. +For more information about an error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/splat-async-fn-tuple.rs b/tests/ui/splat/splat-async-fn-tuple.rs new file mode 100644 index 0000000000000..81e56193bd4ab --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple.rs @@ -0,0 +1,18 @@ +//@ run-pass +//@ edition:2024 +//! Test using `#[splat]` on tuple arguments of async functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +async fn async_tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +async fn async_splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + let _ = async_tuple_args(1u32, 2i8); + let _ = async_tuple_args(1, 2); + + let _ = async_splat_non_terminal_arg(1u32, 2i8, 3.5f64); + let _ = async_splat_non_terminal_arg(1, 2, 3.5); +} diff --git a/tests/ui/splat/splat-const-fn-tuple-generic.rs b/tests/ui/splat/splat-const-fn-tuple-generic.rs new file mode 100644 index 0000000000000..646bf7e9b9f19 --- /dev/null +++ b/tests/ui/splat/splat-const-fn-tuple-generic.rs @@ -0,0 +1,34 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of const functions with generics. + +#![allow(incomplete_features)] +#![feature(splat)] + +// Generic type in first position +const fn const_generic_first(#[splat] _: (T, u32)) {} + +// Generic type in second position +const fn const_generic_second(#[splat] _: (u32, T)) {} + +// Multiple generic types +const fn const_generic_both(#[splat] _: (T, U)) {} + +// Generic with extra non-splatted arg +const fn const_generic_extra(#[splat] _: (T, u32), _extra: i32) {} + +fn main() { + const_generic_first(1i8, 2u32); + const_generic_first(true, 2u32); + const_generic_first(1u64, 2u32); + + const_generic_second(1u32, 2i8); + const_generic_second(1u32, true); + const_generic_second(1u32, 2u64); + + const_generic_both(1u32, 2i8); + const_generic_both(true, 2u64); + const_generic_both(1i8, false); + + const_generic_extra(1i8, 2u32, 42i32); + const_generic_extra(true, 2u32, 42i32); +} diff --git a/tests/ui/splat/splat-const-fn-tuple.rs b/tests/ui/splat/splat-const-fn-tuple.rs new file mode 100644 index 0000000000000..f4ae2e124a72d --- /dev/null +++ b/tests/ui/splat/splat-const-fn-tuple.rs @@ -0,0 +1,17 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of const functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +const fn sum(#[splat] (a, b): (u32, u32)) -> u32 { + a + b +} + +const RESULT: u32 = sum(1, 2); + +fn main() { + assert_eq!(RESULT, 3); + assert_eq!(sum(12, 18) , 30); + assert_eq!(sum(1, 2), 3); +} diff --git a/tests/ui/splat/splat-generics-complex-types.rs b/tests/ui/splat/splat-generics-complex-types.rs new file mode 100644 index 0000000000000..7d4490c18da75 --- /dev/null +++ b/tests/ui/splat/splat-generics-complex-types.rs @@ -0,0 +1,26 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with complex generic types inside the splatted tuple. + +#![allow(incomplete_features)] +#![feature(splat)] + +// Vec and Option inside splatted tuple +fn nested_generic(#[splat] _: (Vec, Option)) {} + +// Box inside splatted tuple +fn box_generic(#[splat] _: (Box, u32)) {} + +// Multiple complex generics +fn multi_generic(#[splat] _: (Vec, Option, Box)) {} + +fn main() { + nested_generic(vec![1u32, 2u32], Some(2i8)); + nested_generic(vec![1, 2, 3], None::); + nested_generic::(vec![], Some("hello")); + + box_generic(Box::new(1u32), 42u32); + box_generic(Box::new("hello"), 1u32); + + multi_generic(vec![1u32], Some(2i8), Box::new(3.0f64)); + multi_generic::(vec![], None::, Box::new("hello")); +} diff --git a/tests/ui/splat/splat-generics-everywhere.rs b/tests/ui/splat/splat-generics-everywhere.rs index 16093fe338357..be4f917ce21df 100644 --- a/tests/ui/splat/splat-generics-everywhere.rs +++ b/tests/ui/splat/splat-generics-everywhere.rs @@ -45,10 +45,6 @@ impl BarTrait for Foo { fn trait_const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} } -// FIXME(splat): -// - add `T: Tuple` generics tests -// - add const fn generics tests - fn main() { // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? // Add a tupled test for each call if they are. diff --git a/tests/ui/splat/splat-generics-inside-tuple.rs b/tests/ui/splat/splat-generics-inside-tuple.rs new file mode 100644 index 0000000000000..dce26e55fa2d9 --- /dev/null +++ b/tests/ui/splat/splat-generics-inside-tuple.rs @@ -0,0 +1,29 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with generics inside the splatted tuple. +#![allow(incomplete_features)] +#![feature(splat)] + +fn generic_second(#[splat] _s: (u32, T)) {} + +fn generic_first(#[splat] _s: (T, u32)) {} + +fn generic_both(#[splat] _s: (T, U)) {} + +fn generic_triple(#[splat] _s: (T, U, V)) {} + +fn main() { + generic_second(1u32, 2i8); + generic_second(1u32, 2.0f64); + generic_second(1u32, "hello"); + + generic_first(1i8, 2u32); + generic_first(2.0f64, 2u32); + generic_first("hello", 2u32); + + generic_both(1u32, 2i8); + generic_both("hello", 2.0f64); + generic_both(true, "world"); + + generic_triple(1u32, 2.0f64, "hello"); + generic_triple(true, 42i32, 3.14f32); +} diff --git a/tests/ui/splat/splat-invalid-trait-impl.rs b/tests/ui/splat/splat-invalid-trait-impl.rs new file mode 100644 index 0000000000000..fe0d5ddc5a1c8 --- /dev/null +++ b/tests/ui/splat/splat-invalid-trait-impl.rs @@ -0,0 +1,27 @@ +//! Test that `#[splat]` trait impls with mismatched tuple element types are rejected. +#![allow(incomplete_features)] +#![feature(splat)] + +trait FooTrait { + fn method(#[splat] _: (u32, i8)); +} + +struct Foo; +struct Foo1; +struct Foo2; + +impl FooTrait for Foo { + fn method(#[splat] _: (u32, f32)) {} + //~^ ERROR method `method` has an incompatible type for trait +} + +impl FooTrait for Foo1 { + fn method(#[splat] _: (f32, i8)) {} + //~^ ERROR method `method` has an incompatible type for trait +} + +impl FooTrait for Foo2 { + fn method(#[splat] _: (f32, f64)) {} + //~^ ERROR method `method` has an incompatible type for trait +} +fn main() {} diff --git a/tests/ui/splat/splat-invalid-trait-impl.stderr b/tests/ui/splat/splat-invalid-trait-impl.stderr new file mode 100644 index 0000000000000..684cc65d7a326 --- /dev/null +++ b/tests/ui/splat/splat-invalid-trait-impl.stderr @@ -0,0 +1,60 @@ +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:14:27 + | +LL | fn method(#[splat] _: (u32, f32)) {} + | ^^^^^^^^^^ expected `i8`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (_, i8))` + found signature `fn(#[splat] (_, f32))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (u32, f32)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:19:27 + | +LL | fn method(#[splat] _: (f32, i8)) {} + | ^^^^^^^^^ expected `u32`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (u32, _))` + found signature `fn(#[splat] (f32, _))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (f32, i8)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:24:27 + | +LL | fn method(#[splat] _: (f32, f64)) {} + | ^^^^^^^^^^ expected `u32`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (u32, i8))` + found signature `fn(#[splat] (f32, f64))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (f32, f64)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-overload-at-home-fail.rs b/tests/ui/splat/splat-overload-at-home-fail.rs index 730b46ac0f9ee..8e6a375e0eb96 100644 --- a/tests/ui/splat/splat-overload-at-home-fail.rs +++ b/tests/ui/splat/splat-overload-at-home-fail.rs @@ -1,6 +1,3 @@ -// tests/ui/splat/splat-overload-at-home-fail.rs - -// ignore-tidy-linelength //! Test error cases for `#[splat]` "overloading at home" example code. //! Splatted calls that don't match any registered MethodArgs impl should fail. #![allow(incomplete_features)] @@ -41,5 +38,4 @@ fn main() { // No impl for (i32,i32) - wrong type foo.method(42i32, 42i32); //~^ ERROR mismatched types - - } +} diff --git a/tests/ui/splat/splat-overload-at-home-fail.stderr b/tests/ui/splat/splat-overload-at-home-fail.stderr index 8a0ba449af213..fcedccf28c359 100644 --- a/tests/ui/splat/splat-overload-at-home-fail.stderr +++ b/tests/ui/splat/splat-overload-at-home-fail.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/splat-overload-at-home-fail.rs:38:16 + --> $DIR/splat-overload-at-home-fail.rs:35:16 | LL | foo.method(42f32); | ------ ^^^^^ expected `i32`, found `f32` @@ -7,7 +7,7 @@ LL | foo.method(42f32); | arguments to this method are incorrect | note: method defined here - --> $DIR/splat-overload-at-home-fail.rs:29:8 + --> $DIR/splat-overload-at-home-fail.rs:26:8 | LL | fn method(&self, #[splat] args: T) { | ^^^^^^ ---------------- @@ -18,7 +18,7 @@ LL + foo.method(42i32); | error[E0308]: mismatched types - --> $DIR/splat-overload-at-home-fail.rs:42:23 + --> $DIR/splat-overload-at-home-fail.rs:39:23 | LL | foo.method(42i32, 42i32); | ------ ^^^^^ expected `String`, found `i32` @@ -26,7 +26,7 @@ LL | foo.method(42i32, 42i32); | arguments to this method are incorrect | note: method defined here - --> $DIR/splat-overload-at-home-fail.rs:29:8 + --> $DIR/splat-overload-at-home-fail.rs:26:8 | LL | fn method(&self, #[splat] args: T) { | ^^^^^^ diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs new file mode 100644 index 0000000000000..3f29b9d292d22 --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs @@ -0,0 +1,16 @@ +//! Test that using `#[splat]` incorrectly on unsafe functions gives errors. + +#![allow(incomplete_features)] +#![feature(splat)] + +unsafe fn unsafe_wrong_type(#[splat] _x: u32) {} +//~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} +//~^ ERROR multiple `#[splat]`s are not allowed in the same function + +fn main() { + unsafe { + unsafe_wrong_type(1u32); + } +} diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr new file mode 100644 index 0000000000000..67935e170671a --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr @@ -0,0 +1,20 @@ +error: multiple `#[splat]`s are not allowed in the same function + --> $DIR/splat-unsafe-fn-tuple-fail.rs:9:30 + | +LL | unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `#[splat]` from all but one argument + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-unsafe-fn-tuple-fail.rs:6:42 + | +LL | unsafe fn unsafe_wrong_type(#[splat] _x: u32) {} + | ^^^ +... +LL | unsafe_wrong_type(1u32); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/splat/splat-unsafe-fn-tuple.rs b/tests/ui/splat/splat-unsafe-fn-tuple.rs new file mode 100644 index 0000000000000..0b9b3510ad50b --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple.rs @@ -0,0 +1,19 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of unsafe functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +unsafe fn unsafe_tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +unsafe fn unsafe_splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + unsafe { + unsafe_tuple_args(1u32, 2i8); + unsafe_tuple_args(1, 2); + + unsafe_splat_non_terminal_arg(1u32, 2i8, 3.5f64); + unsafe_splat_non_terminal_arg(1, 2, 3.5); + } +} diff --git a/tests/ui/splat/splat-where-clause.rs b/tests/ui/splat/splat-where-clause.rs new file mode 100644 index 0000000000000..42a23fe67b216 --- /dev/null +++ b/tests/ui/splat/splat-where-clause.rs @@ -0,0 +1,44 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments with where clause bounds. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn where_splat(#[splat] _t: T) where T: std::marker::Tuple {} + +fn where_splat_with_extra(#[splat] _t: T, _extra: u32) where T: std::marker::Tuple {} + +fn impl_tuple_splat(#[splat] _t: impl std::marker::Tuple) {} + +fn impl_tuple_splat_with_extra(#[splat] _t: impl std::marker::Tuple, _extra: u32) {} + +fn main() { + // empty tuple + where_splat(); + + // single element + where_splat(1u32); + where_splat(1); + + // two elements + where_splat(1u32, 2i8); + where_splat(1, 2); + + // three elements + where_splat(1u32, 2i8, 3.0f64); + where_splat(1, 2, 3.0); + + // with extra non-splatted arg + where_splat_with_extra(1u32, 2i8, 42u32); + where_splat_with_extra(1, 2, 42); + + // impl Trait syntax variants + impl_tuple_splat(); + impl_tuple_splat(1u32); + impl_tuple_splat(1u32, 2i8); + impl_tuple_splat(1, 2, 3.0); + + impl_tuple_splat_with_extra(1u32, 2i8, 42u32); + impl_tuple_splat_with_extra(1, 2, 42); +} From 633b89bd53b73dee02050af2e0b23b8fdef858c1 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Mon, 22 Jun 2026 11:50:13 +0530 Subject: [PATCH 195/278] Add failing test for #[splat] with dyn AsRef where T: Tuple --- tests/ui/splat/splat-dyn-asref-tuple-fail.rs | 14 +++++++++++ .../splat/splat-dyn-asref-tuple-fail.stderr | 24 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/ui/splat/splat-dyn-asref-tuple-fail.rs create mode 100644 tests/ui/splat/splat-dyn-asref-tuple-fail.stderr diff --git a/tests/ui/splat/splat-dyn-asref-tuple-fail.rs b/tests/ui/splat/splat-dyn-asref-tuple-fail.rs new file mode 100644 index 0000000000000..40d702346b7a3 --- /dev/null +++ b/tests/ui/splat/splat-dyn-asref-tuple-fail.rs @@ -0,0 +1,14 @@ +//! Test that `#[splat]` on `&dyn AsRef` where `T: Tuple` is an error. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} +//~^ ERROR cannot use splat attribute + +fn main() { + let s: String = "hello".to_owned(); + dyn_asref_splat::(&s); + //~^ ERROR `String` is not a tuple +} diff --git a/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr b/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr new file mode 100644 index 0000000000000..cba1ab28181a4 --- /dev/null +++ b/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr @@ -0,0 +1,24 @@ +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a &'?3 dyn [Binder { value: Trait(std::convert::AsRef), bound_vars: [] }] + '?3 (&'?3 dyn [Binder { value: Trait(std::convert::AsRef), bound_vars: [] }] + '?3) + --> $DIR/splat-dyn-asref-tuple-fail.rs:7:36 + | +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} + | ^^^^^^^^^^^^^ +... +LL | dyn_asref_splat::(&s); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `String` is not a tuple + --> $DIR/splat-dyn-asref-tuple-fail.rs:12:23 + | +LL | dyn_asref_splat::(&s); + | ^^^^^^ the nightly-only, unstable trait `std::marker::Tuple` is not implemented for `String` + | +note: required by a bound in `dyn_asref_splat` + --> $DIR/splat-dyn-asref-tuple-fail.rs:7:60 + | +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} + | ^^^^^^^^^^^^^^^^^^ required by this bound in `dyn_asref_splat` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 3173c8b900d8007bdb546ee0ac762aef78da2b88 Mon Sep 17 00:00:00 2001 From: teor Date: Mon, 22 Jun 2026 16:13:45 +0200 Subject: [PATCH 196/278] Add splat UI tests for 255 splatted arg limit --- tests/ui/splat/splat-255-limit-fail.rs | 97 +++++++++++++ tests/ui/splat/splat-255-limit-fail.stderr | 34 +++++ tests/ui/splat/splat-255-limit-pass.rs | 156 +++++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 tests/ui/splat/splat-255-limit-fail.rs create mode 100644 tests/ui/splat/splat-255-limit-fail.stderr create mode 100644 tests/ui/splat/splat-255-limit-pass.rs diff --git a/tests/ui/splat/splat-255-limit-fail.rs b/tests/ui/splat/splat-255-limit-fail.rs new file mode 100644 index 0000000000000..422988ffc3f01 --- /dev/null +++ b/tests/ui/splat/splat-255-limit-fail.rs @@ -0,0 +1,97 @@ +// ignore-tidy-linelength +//! Test `#[splat]` fails over the 255th argument index (or higher). +//! FIXME(splat): The 255 argument limit is a temporary performance hack. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(dead_code)] + +type A = (); + +// These functions are deliberately formatted with 17 arguments in 15 lines, to show they have 255 +// arguments. +#[rustfmt::skip] +fn s_255_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 +) {} + +#[rustfmt::skip] +fn s_256_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 +) {} + +#[rustfmt::skip] +fn s_255_non_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_256_non_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 + _: A, +) {} + +fn main() {} diff --git a/tests/ui/splat/splat-255-limit-fail.stderr b/tests/ui/splat/splat-255-limit-fail.stderr new file mode 100644 index 0000000000000..51cad1b3cf588 --- /dev/null +++ b/tests/ui/splat/splat-255-limit-fail.stderr @@ -0,0 +1,34 @@ +error: `#[splat]` is not supported on argument index 255 + --> $DIR/splat-255-limit-fail.rs:30:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 256 + --> $DIR/splat-255-limit-fail.rs:51:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 255 + --> $DIR/splat-255-limit-fail.rs:71:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 256 + --> $DIR/splat-255-limit-fail.rs:93:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: aborting due to 4 previous errors + diff --git a/tests/ui/splat/splat-255-limit-pass.rs b/tests/ui/splat/splat-255-limit-pass.rs new file mode 100644 index 0000000000000..37301afcec561 --- /dev/null +++ b/tests/ui/splat/splat-255-limit-pass.rs @@ -0,0 +1,156 @@ +//@ run-pass +// ignore-tidy-linelength +//! Test `#[splat]` on the 255th argument index (or lower). +//! FIXME(splat): The 255 argument limit is a temporary performance hack. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(dead_code)] + +type A = (); + +// These functions are deliberately formatted with 17 arguments in 15 lines, to show they have 255 +// arguments. +#[rustfmt::skip] +fn s_253_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), +) {} + +#[rustfmt::skip] +fn s_254_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), +) {} + +#[rustfmt::skip] +fn s_254_non_terminal_272_args( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_253_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_254_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_255_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_256_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, +) {} + +fn main() {} From 18185d0a6dfdfbd6e89e8c62a4e40dfa32bc6773 Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 23 Jun 2026 14:55:04 +0200 Subject: [PATCH 197/278] UI test arg diffs greater than u8::MAX --- tests/ui/splat/splat-255-limit-fail.rs | 108 ++++++++++++++++++++- tests/ui/splat/splat-255-limit-fail.stderr | 47 ++++++++- tests/ui/splat/splat-255-limit-pass.rs | 51 +++++++++- 3 files changed, 195 insertions(+), 11 deletions(-) diff --git a/tests/ui/splat/splat-255-limit-fail.rs b/tests/ui/splat/splat-255-limit-fail.rs index 422988ffc3f01..31767f11d999b 100644 --- a/tests/ui/splat/splat-255-limit-fail.rs +++ b/tests/ui/splat/splat-255-limit-fail.rs @@ -8,8 +8,28 @@ type A = (); -// These functions are deliberately formatted with 17 arguments in 15 lines, to show they have 255 -// arguments. +// These types and functions are deliberately formatted with 17 arguments in 15 lines, to show they +// have ~255 arguments. +#[rustfmt::skip] +type Tuple256 = ( + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, +); + #[rustfmt::skip] fn s_255_terminal( _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, @@ -94,4 +114,86 @@ fn s_256_non_terminal( _: A, ) {} -fn main() {} +// It's only the splatted index that's constrained to 255, not the argument count of the caller or callee. +fn more_than_255_splatted_args(#[splat] _t: Tuple256) {} + +fn main() { + let a = (); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 255 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + /* missing: a, */ + ); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 257 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, /* unexpected: */ a, + ); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 512 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + /* unexpected: */ + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + ); +} diff --git a/tests/ui/splat/splat-255-limit-fail.stderr b/tests/ui/splat/splat-255-limit-fail.stderr index 51cad1b3cf588..da62da8aa605b 100644 --- a/tests/ui/splat/splat-255-limit-fail.stderr +++ b/tests/ui/splat/splat-255-limit-fail.stderr @@ -1,5 +1,5 @@ error: `#[splat]` is not supported on argument index 255 - --> $DIR/splat-255-limit-fail.rs:30:5 + --> $DIR/splat-255-limit-fail.rs:50:5 | LL | #[splat] (_a, _b): (u32, i8), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here @@ -7,7 +7,7 @@ LL | #[splat] (_a, _b): (u32, i8), = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list error: `#[splat]` is not supported on argument index 256 - --> $DIR/splat-255-limit-fail.rs:51:5 + --> $DIR/splat-255-limit-fail.rs:71:5 | LL | #[splat] (_a, _b): (u32, i8), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here @@ -15,7 +15,7 @@ LL | #[splat] (_a, _b): (u32, i8), = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list error: `#[splat]` is not supported on argument index 255 - --> $DIR/splat-255-limit-fail.rs:71:5 + --> $DIR/splat-255-limit-fail.rs:91:5 | LL | #[splat] (_a, _b): (u32, i8), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here @@ -23,12 +23,49 @@ LL | #[splat] (_a, _b): (u32, i8), = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list error: `#[splat]` is not supported on argument index 256 - --> $DIR/splat-255-limit-fail.rs:93:5 + --> $DIR/splat-255-limit-fail.rs:113:5 | LL | #[splat] (_a, _b): (u32, i8), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here | = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list -error: aborting due to 4 previous errors +error[E0057]: this splatted function takes 256 arguments, but 255 were provided + --> $DIR/splat-255-limit-fail.rs:124:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | /* missing: a, */ +LL | | ); + | |_____^ + +error[E0057]: this splatted function takes 256 arguments, but 257 were provided + --> $DIR/splat-255-limit-fail.rs:144:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | a, /* unexpected: */ a, +LL | | ); + | |_____^ + +error[E0057]: this splatted function takes 256 arguments, but 512 were provided + --> $DIR/splat-255-limit-fail.rs:164:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | a, +LL | | ); + | |_____^ + +error: aborting due to 7 previous errors +For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/splat-255-limit-pass.rs b/tests/ui/splat/splat-255-limit-pass.rs index 37301afcec561..9713616830f9b 100644 --- a/tests/ui/splat/splat-255-limit-pass.rs +++ b/tests/ui/splat/splat-255-limit-pass.rs @@ -9,8 +9,28 @@ type A = (); -// These functions are deliberately formatted with 17 arguments in 15 lines, to show they have 255 -// arguments. +// These types and functions are deliberately formatted with 17 arguments in 15 lines, to show they +// have ~255 arguments. +#[rustfmt::skip] +type Tuple256 = ( + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, +); + #[rustfmt::skip] fn s_253_terminal( _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, @@ -132,6 +152,7 @@ fn s_0_initial_255_args( _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, ) {} +// It's only the splatted index that's constrained to 255, not the argument count of the caller or callee. #[rustfmt::skip] fn s_0_initial_256_args( #[splat] (_a, _b): (u32, i8), @@ -153,4 +174,28 @@ fn s_0_initial_256_args( _: A, ) {} -fn main() {} +fn more_than_255_splatted_args(#[splat] _t: Tuple256) {} + +fn main() { + let a = (); + + #[rustfmt::skip] + more_than_255_splatted_args( + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + ); +} From 810d2dbad580fb11eb8bdac4c199f6f0f8c88712 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 25 May 2026 22:39:36 +0200 Subject: [PATCH 198/278] Update `doc_cfg` hide/show syntax --- .../rustc_attr_parsing/src/attributes/doc.rs | 170 ++++++++++++++---- .../rustc_attr_parsing/src/diagnostics.rs | 17 +- .../rustc_hir/src/attrs/data_structures.rs | 99 ++++++++-- .../rustc_hir/src/attrs/pretty_printing.rs | 2 +- src/librustdoc/clean/cfg.rs | 14 +- src/librustdoc/json/conversions.rs | 1 + 6 files changed, 247 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 988df2b200f86..2aa9769b46be5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -1,25 +1,29 @@ use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; use rustc_errors::{Applicability, msg}; use rustc_feature::AttributeStability; use rustc_hir::Target; use rustc_hir::attrs::{ - AttributeKind, CfgEntry, CfgHideShow, CfgInfo, DocAttribute, DocInline, HideOrShow, + AttributeKind, CfgEntry, CfgHideShow, DocAttribute, DocCfgHideShow, DocCfgHideShowValue, + DocInline, HideOrShow, }; use rustc_session::errors::feature_err; use rustc_span::{Span, Symbol, edition, sym}; -use thin_vec::ThinVec; use super::prelude::{ALL_TARGETS, AllowedTargets}; use super::{AcceptMapping, AttributeParser, template}; use crate::context::{AcceptContext, FinalizeContext}; use crate::diagnostics::{ AttrCrateLevelOnly, DocAliasDuplicated, DocAutoCfgExpectsHideOrShow, - DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowUnexpectedItem, DocAutoCfgWrongLiteral, - DocTestLiteral, DocTestTakesList, DocTestUnknown, DocUnknownAny, DocUnknownInclude, - DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs, - IllFormedAttributeInput, MalformedDoc, + DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowNoIdentBeforeValues, + DocAutoCfgHideShowUnexpectedItem, DocAutoCfgHideShowUnexpectedItemAfterValues, + DocAutoCfgHideShowValuesMix, DocAutoCfgWrongLiteral, DocTestLiteral, DocTestTakesList, + DocTestUnknown, DocUnknownAny, DocUnknownInclude, DocUnknownPasses, DocUnknownPlugins, + DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs, IllFormedAttributeInput, MalformedDoc, +}; +use crate::parser::{ + ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser, }; -use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser}; use crate::session_diagnostics::{ DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttrNotCrateLevel, DocAttributeNotAttribute, DocKeywordNotKeyword, UnusedDuplicate, @@ -304,6 +308,81 @@ impl DocParser { } } + // Parses the `doc(auto_cfg(hide/show(..., values())))` attribute. + fn parse_auto_cfg_values( + &self, + cx: &mut AcceptContext<'_, '_>, + list: &MetaItemListParser, + values: &mut Option, + ) { + let mut cfg_values = DocCfgHideShow::new(); + + let mut values_set = FxHashSet::default(); + for item in list.mixed() { + match item { + // If it's a string literal, all good. + MetaItemOrLitParser::Lit(MetaItemLit { + kind: LitKind::Str(symbol, _), + span, + .. + }) => match &mut cfg_values.values { + DocCfgHideShowValue::Any(any_span) => { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowValuesMix { value_span: *span }, + *any_span, + ); + } + DocCfgHideShowValue::List(symbols) => { + if values_set.insert(symbol) { + symbols.push((*symbol, *span)); + } + } + }, + // If it's any other kind of literal, then it's wrong and we emit a lint. + MetaItemOrLitParser::Lit(lit) => cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowUnexpectedItem { attr_name: lit.symbol }, + lit.span, + ), + // If it's a list, then only `any()` and `none()` are allowed and they must not + // contain any item. + MetaItemOrLitParser::MetaItemParser(sub_item) => { + if let Some(ident) = sub_item.ident() + && [sym::any, sym::none].contains(&ident.name) + && let ArgParser::List(list) = sub_item.args() + && list.mixed().count() == 0 + { + if ident.name == sym::any { + if let DocCfgHideShowValue::List(values) = &cfg_values.values + && let Some((_, span)) = values.first() + { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowValuesMix { value_span: *span }, + sub_item.span(), + ); + } else { + cfg_values.values = DocCfgHideShowValue::Any(sub_item.span()); + } + } else { + cfg_values.only_key = Some(sub_item.span()); + } + } else { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowUnexpectedItem { + attr_name: sub_item.ident().unwrap().name, + }, + sub_item.span(), + ); + } + } + } + } + *values = Some(cfg_values); + } + fn parse_auto_cfg( &mut self, cx: &mut AcceptContext<'_, '_>, @@ -315,7 +394,7 @@ impl DocParser { self.attribute.auto_cfg_change.push((true, path.span())); } ArgParser::List(list) => { - for meta in list.mixed() { + 'main: for meta in list.mixed() { let MetaItemOrLitParser::MetaItemParser(item) = meta else { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, @@ -324,6 +403,8 @@ impl DocParser { ); continue; }; + // Only `hide` and `show` are allowed in `auto_cfg` if it's a list, and both + // must be a list. let (kind, attr_name) = match item.path().word_sym() { Some(sym::hide) => (HideOrShow::Hide, sym::hide), Some(sym::show) => (HideOrShow::Show, sym::show), @@ -345,8 +426,10 @@ impl DocParser { continue; }; - let mut cfg_hide_show = CfgHideShow { kind, values: ThinVec::new() }; + let mut cfg_hide_show = CfgHideShow { kind, values: FxIndexMap::default() }; + let mut cfg_names = FxHashSet::default(); + let mut values = None; for item in list.mixed() { let MetaItemOrLitParser::MetaItemParser(sub_item) = item else { cx.emit_lint( @@ -354,47 +437,60 @@ impl DocParser { DocAutoCfgHideShowUnexpectedItem { attr_name }, item.span(), ); - continue; + continue 'main; }; match sub_item.args() { - a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => { + ArgParser::NoArgs if values.is_none() => { let Some(name) = sub_item.path().word_sym() else { - // FIXME: remove this method once merged and uncomment the line - // below instead. - // cx.expected_identifier(sub_item.path().span()); + cx.adcx().expected_identifier(sub_item.path().span()); + continue 'main; + }; + cfg_names.insert(name); + } + // The only accepted list is `values()`. + ArgParser::List(list) if values.is_none() => { + let Some(sym::values) = sub_item.path().word_sym() else { + cx.adcx().expected_identifier(sub_item.path().span()); + continue 'main; + }; + if cfg_names.is_empty() { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - MalformedDoc, - sub_item.path().span(), - ); - continue; - }; - if let Ok(CfgEntry::NameValue { name, value, .. }) = - super::cfg::parse_name_value( - name, - sub_item.path().span(), - a.as_name_value(), + DocAutoCfgHideShowNoIdentBeforeValues, sub_item.span(), - cx, - ) - { - cfg_hide_show.values.push(CfgInfo { - name, - name_span: sub_item.path().span(), - // If `value` is `Some`, `a.name_value()` will always return - // `Some` as well. - value: value - .map(|v| (v, a.as_name_value().unwrap().value_span)), - }) + ); + continue 'main; } + self.parse_auto_cfg_values(cx, list, &mut values); } - _ => { + // No `name = value` is allowed. + ArgParser::NameValue(_) => { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, DocAutoCfgHideShowUnexpectedItem { attr_name }, sub_item.span(), ); - continue; + } + // If `values()` was already used, no item should come after it. + _ => { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowUnexpectedItemAfterValues, + sub_item.span(), + ); + } + } + } + + let values = values.unwrap_or(DocCfgHideShow::new_with_only_key(item.span())); + #[allow(rustc::potential_query_instability)] + for cfg_name in &cfg_names { + match cfg_hide_show.values.entry(*cfg_name) { + IndexEntry::Vacant(v) => { + v.insert(values.clone()); + } + IndexEntry::Occupied(mut o) => { + o.get_mut().merge_with(&values); } } } diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index d8b7144aa01ba..50667952b814d 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -169,11 +169,22 @@ pub(crate) struct DocAutoCfgExpectsHideOrShow; pub(crate) struct AmbiguousDeriveHelpers; #[derive(Diagnostic)] -#[diag("`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items")] +#[diag("`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or `values(...)`")] pub(crate) struct DocAutoCfgHideShowUnexpectedItem { pub attr_name: Symbol, } +#[derive(Diagnostic)] +#[diag("`any()` was used when other values were provided")] +pub(crate) struct DocAutoCfgHideShowValuesMix { + #[label("value declared here")] + pub value_span: Span, +} + +#[derive(Diagnostic)] +#[diag("unexpected item after `values()`")] +pub(crate) struct DocAutoCfgHideShowUnexpectedItemAfterValues; + #[derive(Diagnostic)] #[diag("`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items")] pub(crate) struct DocAutoCfgHideShowExpectsList { @@ -239,6 +250,10 @@ pub(crate) struct DocUnknownAny { #[diag("expected boolean for `#[doc(auto_cfg = ...)]`")] pub(crate) struct DocAutoCfgWrongLiteral; +#[derive(Diagnostic)] +#[diag("there must be at least one identifier before `values(...)`")] +pub(crate) struct DocAutoCfgHideShowNoIdentBeforeValues; + #[derive(Diagnostic)] #[diag("`#[doc(test(...)]` takes a list of attributes")] pub(crate) struct DocTestTakesList; diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 7c125c3a8983e..e35a9d68a4d00 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -515,19 +515,96 @@ pub enum HideOrShow { Show, } +#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq)] +pub enum DocCfgHideShowValue { + Any(Span), + List(ThinVec<(Symbol, Span)>), +} + #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] -pub struct CfgInfo { - pub name: Symbol, - pub name_span: Span, - pub value: Option<(Symbol, Span)>, +pub struct DocCfgHideShow { + /// If `Some`, then `cfg` without values (like `cfg(windows)`) will be shown/hidden. + /// The `Span` comes from where this value was set. + pub only_key: Option, + /// The values of this `cfg` to shown/hidden. + pub values: DocCfgHideShowValue, } -impl CfgInfo { - pub fn span_for_name_and_value(&self) -> Span { - if let Some((_, value_span)) = self.value { - self.name_span.with_hi(value_span.hi()) - } else { - self.name_span +impl DocCfgHideShow { + pub fn new() -> Self { + Self { only_key: None, values: DocCfgHideShowValue::List(ThinVec::new()) } + } + + pub fn new_with_only_key(span: Span) -> Self { + Self { only_key: Some(span), values: DocCfgHideShowValue::List(ThinVec::new()) } + } + + pub fn merge_with(&mut self, other: &Self) { + if self.only_key.is_none() + && let Some(span) = other.only_key + { + self.only_key = Some(span); + } + match (&mut self.values, &other.values) { + (DocCfgHideShowValue::Any(_), DocCfgHideShowValue::Any(_)) => { + // Nothing to do. + } + (_, DocCfgHideShowValue::Any(span)) => { + // We "upgrade" the list values to "all". + self.values = DocCfgHideShowValue::Any(*span); + } + (DocCfgHideShowValue::List(values), DocCfgHideShowValue::List(other_values)) => { + // Having duplicates is not an issue, we simply ignore them. Would be more + // convenient to have a `set` type though. T_T + for (other_symbol, other_span) in other_values { + if !values.iter().any(|(symbol, _)| symbol == other_symbol) { + values.push((*other_symbol, *other_span)); + } + } + } + (DocCfgHideShowValue::Any(_), DocCfgHideShowValue::List(_)) => { + // Nothing to do here either, we already accept all values. + } + } + } + + pub fn update_with(&mut self, other: &Self) { + if self.only_key.is_none() + && let Some(span) = other.only_key + { + self.only_key = Some(span); + } + match (&mut self.values, &other.values) { + (DocCfgHideShowValue::Any(_), _) => {} + (DocCfgHideShowValue::List(_), DocCfgHideShowValue::Any(span)) => { + self.values = DocCfgHideShowValue::Any(*span); + } + (DocCfgHideShowValue::List(values), DocCfgHideShowValue::List(other_values)) => { + // Having duplicates is not an issue, we simply ignore them. Would be more + // convenient to have a `set` type though. T_T + for (other_symbol, other_span) in other_values { + if !values.iter().any(|(symbol, _)| symbol == other_symbol) { + values.push((*other_symbol, *other_span)); + } + } + } + } + } + + pub fn remove_overlap(&mut self, other: &Self) { + if other.only_key.is_some() { + self.only_key = None; + } + match (&mut self.values, &other.values) { + (_, DocCfgHideShowValue::Any(_)) => { + self.values = DocCfgHideShowValue::List(ThinVec::new()); + } + (DocCfgHideShowValue::Any(_), DocCfgHideShowValue::List(values)) => { + self.values = DocCfgHideShowValue::List(values.clone()); + } + (DocCfgHideShowValue::List(current), DocCfgHideShowValue::List(values)) => { + current.retain(|(name, _)| !values.iter().any(|(other, _)| other == name)); + } } } } @@ -535,7 +612,7 @@ impl CfgInfo { #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub struct CfgHideShow { pub kind: HideOrShow, - pub values: ThinVec, + pub values: FxIndexMap, } #[derive(Clone, Debug, Default, StableHash, Decodable, PrintAttribute)] diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index 9d14f9de3078d..d826aa363f349 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -81,7 +81,7 @@ impl PrintAttribute for ThinVec { p.word("]"); } } -impl PrintAttribute for FxIndexMap { +impl PrintAttribute for FxIndexMap { fn should_render(&self) -> bool { self.is_empty() || self[0].should_render() } diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 170f1439ecc9c..8062db16018c6 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -12,7 +12,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; use rustc_hir as hir; use rustc_hir::Attribute; -use rustc_hir::attrs::{self, AttributeKind, CfgEntry, CfgHideShow, HideOrShow}; +use rustc_hir::attrs::{ + self, AttributeKind, CfgEntry, CfgHideShow, DocCfgHideShowValue, HideOrShow, +}; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{DUMMY_SP, Span}; @@ -685,7 +687,7 @@ impl<'a> From<&'a attrs::CfgInfo> for NameValueCfg { pub(crate) struct CfgInfo { /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active /// `doc(auto_cfg(show(...)))` cfgs. - hidden_cfg: FxHashSet, + hidden_cfg: FxHashMap, /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while /// taking into account the `hidden_cfg` information. current_cfg: Cfg, @@ -700,10 +702,10 @@ pub(crate) struct CfgInfo { impl Default for CfgInfo { fn default() -> Self { Self { - hidden_cfg: FxHashSet::from_iter([ - NameValueCfg::new(sym::test), - NameValueCfg::new(sym::doc), - NameValueCfg::new(sym::doctest), + hidden_cfg: FxHashMap::from_iter([ + (sym::test, DocCfgHideShowValue::All), + (sym::doc, DocCfgHideShowValue::All), + (sym::doctest, DocCfgHideShowValue::All), ]), current_cfg: Cfg(CfgEntry::Bool(true, DUMMY_SP)), auto_cfg_active: true, diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 4de9c46900a76..644d6f2acdb98 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -1138,6 +1138,7 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) // characters surrounding the string. out.push_str(&format!(" = {:?}", value.as_str())); } + out.push(')'); } out.push_str(")))]"); ret.push(Attribute::Other(out)); From dc7a2ce9f07509409ead44baa7680d7582a1f365 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 11 Jun 2026 17:58:14 +0200 Subject: [PATCH 199/278] Update `alloc` and `core` to new `doc_cfg` syntax --- library/alloc/src/lib.rs | 5 ++++- library/core/src/lib.rs | 31 ++++++++++--------------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index b0e8039263ffe..f8cab52633fe1 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -65,7 +65,10 @@ issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", test(no_crate_inject, attr(allow(unused_variables, duplicate_features), deny(warnings))) )] -#![doc(auto_cfg(hide(no_global_oom_handling, no_rc, no_sync, target_has_atomic = "ptr")))] +#![doc(auto_cfg( + hide(no_global_oom_handling, no_rc, no_sync), + hide(target_has_atomic, values("ptr")), +))] #![doc(rust_logo)] #![feature(rustdoc_internals)] #![no_std] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index a26304c46ecea..61b6741219f7e 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -51,27 +51,16 @@ test(attr(allow(dead_code, deprecated, unused_variables, unused_mut, duplicate_features))) )] #![doc(rust_logo)] -#![doc(auto_cfg(hide( - no_fp_fmt_parse, - target_pointer_width = "16", - target_pointer_width = "32", - target_pointer_width = "64", - target_has_atomic = "8", - target_has_atomic = "16", - target_has_atomic = "32", - target_has_atomic = "64", - target_has_atomic = "ptr", - target_has_atomic_primitive_alignment = "8", - target_has_atomic_primitive_alignment = "16", - target_has_atomic_primitive_alignment = "32", - target_has_atomic_primitive_alignment = "64", - target_has_atomic_primitive_alignment = "ptr", - target_has_atomic_load_store = "8", - target_has_atomic_load_store = "16", - target_has_atomic_load_store = "32", - target_has_atomic_load_store = "64", - target_has_atomic_load_store = "ptr", -)))] +#![doc(auto_cfg( + hide(no_fp_fmt_parse), + hide(target_pointer_width, values("16", "32", "64")), + hide( + target_has_atomic, + target_has_atomic_primitive_alignment, + target_has_atomic_load_store, + values("8", "16", "32", "64", "ptr"), + ), +))] #![no_core] #![rustc_coherence_is_core] #![rustc_preserve_ub_checks] From f22107c1c7efe24af676f9ec53f321cac6a1d3e7 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 13 Jun 2026 01:06:37 +0200 Subject: [PATCH 200/278] Update rustdoc to use new `doc_cfg` syntax --- src/librustdoc/clean/cfg.rs | 141 +++++++++++------- src/librustdoc/json/conversions.rs | 53 +++++-- tests/rustdoc-html/doc-cfg/decl-macro.rs | 6 +- tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs | 2 +- .../doc-cfg/trait-impls-manual.rs | 6 +- tests/rustdoc-html/doc-cfg/trait-impls.rs | 6 +- tests/rustdoc-html/doc_auto_cfg.rs | 10 +- tests/rustdoc-ui/cfg-hide-show-conflict.rs | 4 +- .../rustdoc-ui/cfg-hide-show-conflict.stderr | 12 +- tests/rustdoc-ui/doc-cfg-2.rs | 4 +- tests/rustdoc-ui/doc-cfg-2.stderr | 22 +-- tests/rustdoc-ui/doc-cfg.rs | 2 +- tests/rustdoc-ui/doc-cfg.stderr | 13 +- tests/rustdoc-ui/lints/doc_cfg_hide-2.rs | 4 + tests/rustdoc-ui/lints/doc_cfg_hide-2.stderr | 11 ++ tests/rustdoc-ui/lints/doc_cfg_hide.rs | 1 - tests/rustdoc-ui/lints/doc_cfg_hide.stderr | 8 +- 17 files changed, 183 insertions(+), 122 deletions(-) create mode 100644 tests/rustdoc-ui/lints/doc_cfg_hide-2.rs create mode 100644 tests/rustdoc-ui/lints/doc_cfg_hide-2.stderr diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 8062db16018c6..8721523f83f58 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -8,12 +8,12 @@ use std::sync::Arc; use std::{fmt, mem, ops}; use itertools::Either; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; use rustc_hir as hir; use rustc_hir::Attribute; use rustc_hir::attrs::{ - self, AttributeKind, CfgEntry, CfgHideShow, DocCfgHideShowValue, HideOrShow, + AttributeKind, CfgEntry, CfgHideShow, DocCfgHideShow, DocCfgHideShowValue, HideOrShow, }; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{Symbol, sym}; @@ -55,15 +55,24 @@ fn is_any_cfg(cfg: &CfgEntry) -> bool { } } -fn strip_hidden(cfg: &CfgEntry, hidden: &FxHashSet) -> Option { +fn strip_hidden(cfg: &CfgEntry, hidden: &FxHashMap) -> Option { match cfg { CfgEntry::Bool(..) => Some(cfg.clone()), - CfgEntry::NameValue { .. } => { - if !hidden.contains(&NameValueCfg::from(cfg)) { - Some(cfg.clone()) - } else { - None + CfgEntry::NameValue { name, value, .. } => { + let mut is_stripped = false; + if let Some(values) = hidden.get(name) { + if let Some(value) = value { + match &values.values { + DocCfgHideShowValue::Any(_) => is_stripped = true, + DocCfgHideShowValue::List(values) => { + is_stripped = values.iter().any(|(v, _)| v == value); + } + } + } else { + is_stripped = values.only_key.is_some(); + } } + if !is_stripped { Some(cfg.clone()) } else { None } } CfgEntry::Not(cfg, _) => { if let Some(cfg) = strip_hidden(cfg, hidden) { @@ -655,39 +664,12 @@ fn human_readable_target_env(env: Symbol) -> Option<&'static str> { }) } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -struct NameValueCfg { - name: Symbol, - value: Option, -} - -impl NameValueCfg { - fn new(name: Symbol) -> Self { - Self { name, value: None } - } -} - -impl<'a> From<&'a CfgEntry> for NameValueCfg { - fn from(cfg: &'a CfgEntry) -> Self { - match cfg { - CfgEntry::NameValue { name, value, .. } => NameValueCfg { name: *name, value: *value }, - _ => NameValueCfg { name: sym::empty, value: None }, - } - } -} - -impl<'a> From<&'a attrs::CfgInfo> for NameValueCfg { - fn from(cfg: &'a attrs::CfgInfo) -> Self { - Self { name: cfg.name, value: cfg.value.map(|(value, _)| value) } - } -} - /// This type keeps track of (doc) cfg information as we go down the item tree. #[derive(Clone, Debug)] pub(crate) struct CfgInfo { /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active /// `doc(auto_cfg(show(...)))` cfgs. - hidden_cfg: FxHashMap, + hidden_cfg: FxHashMap, /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while /// taking into account the `hidden_cfg` information. current_cfg: Cfg, @@ -703,9 +685,9 @@ impl Default for CfgInfo { fn default() -> Self { Self { hidden_cfg: FxHashMap::from_iter([ - (sym::test, DocCfgHideShowValue::All), - (sym::doc, DocCfgHideShowValue::All), - (sym::doctest, DocCfgHideShowValue::All), + (sym::test, DocCfgHideShow::new_with_only_key(DUMMY_SP)), + (sym::doc, DocCfgHideShow::new_with_only_key(DUMMY_SP)), + (sym::doctest, DocCfgHideShow::new_with_only_key(DUMMY_SP)), ]), current_cfg: Cfg(CfgEntry::Bool(true, DUMMY_SP)), auto_cfg_active: true, @@ -718,7 +700,9 @@ fn show_hide_show_conflict_error( tcx: TyCtxt<'_>, item_span: rustc_span::Span, previous: rustc_span::Span, + errors: &mut usize, ) { + *errors += 1; let mut diag = tcx.sess.dcx().struct_span_err( item_span, format!( @@ -729,6 +713,48 @@ fn show_hide_show_conflict_error( diag.emit(); } +fn check_if_no_overlap( + tcx: TyCtxt<'_>, + attrs: &FxHashMap, + cfg_name: Symbol, + info: &DocCfgHideShow, +) -> bool { + let mut errors = 0; + if let Some(other) = attrs.get(&cfg_name) { + match (&other.only_key, &info.only_key) { + (Some(previous_span), Some(span)) => { + show_hide_show_conflict_error(tcx, *span, *previous_span, &mut errors); + } + _ => {} + } + match (&other.values, &info.values) { + (DocCfgHideShowValue::Any(previous_span), DocCfgHideShowValue::Any(span)) => { + show_hide_show_conflict_error(tcx, *span, *previous_span, &mut errors); + } + (DocCfgHideShowValue::Any(previous_span), DocCfgHideShowValue::List(items)) => { + // If the list is empty, then it's just the default so no problem there. + if let Some((_, span)) = items.first() { + show_hide_show_conflict_error(tcx, *span, *previous_span, &mut errors); + } + } + (DocCfgHideShowValue::List(previous_items), DocCfgHideShowValue::Any(span)) => { + // If the list is empty, then it's just the default so no problem there. + if let Some((_, previous_span)) = previous_items.first() { + show_hide_show_conflict_error(tcx, *span, *previous_span, &mut errors); + } + } + (DocCfgHideShowValue::List(previous_items), DocCfgHideShowValue::List(items)) => { + for (previous_name, previous_span) in previous_items { + if let Some((_, span)) = items.iter().find(|(name, _)| name == previous_name) { + show_hide_show_conflict_error(tcx, *span, *previous_span, &mut errors); + } + } + } + } + } + errors == 0 +} + /// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument. /// /// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and @@ -740,25 +766,34 @@ fn handle_auto_cfg_hide_show( tcx: TyCtxt<'_>, cfg_info: &mut CfgInfo, attr: &CfgHideShow, - new_show_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, - new_hide_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, + new_show_attrs: &mut FxHashMap, + new_hide_attrs: &mut FxHashMap, ) { - for value in &attr.values { - let simple = NameValueCfg::from(value); + for (cfg_name, value) in &attr.values { if attr.kind == HideOrShow::Show { - if let Some(span) = new_hide_attrs.get(&(simple.name, simple.value)) { - show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span); - } else { - new_show_attrs.insert((simple.name, simple.value), value.span_for_name_and_value()); + if check_if_no_overlap(tcx, new_hide_attrs, *cfg_name, value) { + new_show_attrs + .entry(*cfg_name) + .and_modify(|entry| entry.update_with(value)) + .or_insert_with(|| value.clone()); + cfg_info + .hidden_cfg + .entry(*cfg_name) + .and_modify(|entry| entry.remove_overlap(value)) + .or_insert_with(|| value.clone()); } - cfg_info.hidden_cfg.remove(&simple); } else { - if let Some(span) = new_show_attrs.get(&(simple.name, simple.value)) { - show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span); - } else { - new_hide_attrs.insert((simple.name, simple.value), value.span_for_name_and_value()); + if check_if_no_overlap(tcx, new_show_attrs, *cfg_name, value) { + new_hide_attrs + .entry(*cfg_name) + .and_modify(|entry| entry.update_with(value)) + .or_insert_with(|| value.clone()); + cfg_info + .hidden_cfg + .entry(*cfg_name) + .and_modify(|entry| entry.update_with(value)) + .or_insert_with(|| value.clone()); } - cfg_info.hidden_cfg.insert(simple); } } } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 644d6f2acdb98..9ec7e4bd49ceb 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -7,7 +7,9 @@ use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; -use rustc_hir::attrs::{self, DeprecatedSince, DocAttribute, DocInline, HideOrShow}; +use rustc_hir::attrs::{ + self, DeprecatedSince, DocAttribute, DocCfgHideShowValue, DocInline, HideOrShow, +}; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::DefId; use rustc_hir::{HeaderSafety, Safety, find_attr}; @@ -1122,25 +1124,46 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) for sub_cfg in cfg { ret.push(Attribute::Other(format!("#[doc(cfg({sub_cfg}))]"))); } - for (auto_cfg, _) in auto_cfg { - let kind = match auto_cfg.kind { - HideOrShow::Hide => "hide", - HideOrShow::Show => "show", - }; - let mut out = format!("#[doc(auto_cfg({kind}("); - for (pos, value) in auto_cfg.values.iter().enumerate() { - if pos > 0 { + if !auto_cfg.is_empty() { + let mut out = format!("#[doc(auto_cfg("); + for (index, (auto_cfg, _)) in auto_cfg.iter().enumerate() { + let kind = match auto_cfg.kind { + HideOrShow::Hide => "hide", + HideOrShow::Show => "show", + }; + if index > 0 { out.push_str(", "); } - out.push_str(value.name.as_str()); - if let Some((value, _)) = value.value { - // We use `as_str` and debug display to have characters escaped and `"` - // characters surrounding the string. - out.push_str(&format!(" = {:?}", value.as_str())); + out.push_str(&format!("{kind}(")); + for (name, cfgs) in &auto_cfg.values { + out.push_str(&format!("{name}, values(")); + let mut pos = 0; + if cfgs.only_key.is_some() { + out.push_str("none()"); + pos += 1; + } + match &cfgs.values { + DocCfgHideShowValue::Any(_) => { + out.push_str(&format!("{}any()", if pos > 0 { ", " } else { "" })); + } + DocCfgHideShowValue::List(values) => { + for (value, _) in values { + // We use `as_str` and debug display to have characters escaped + // and `"` characters surrounding the string. + out.push_str(&format!( + "{}{:?}", + if pos > 0 { ", " } else { "" }, + value.as_str() + )); + pos += 1; + } + } + } + out.push_str(")"); } out.push(')'); } - out.push_str(")))]"); + out.push_str("))]"); ret.push(Attribute::Other(out)); } for (change, _) in auto_cfg_change { diff --git a/tests/rustdoc-html/doc-cfg/decl-macro.rs b/tests/rustdoc-html/doc-cfg/decl-macro.rs index e97da8647a6ab..ce4cd36d1367e 100644 --- a/tests/rustdoc-html/doc-cfg/decl-macro.rs +++ b/tests/rustdoc-html/doc-cfg/decl-macro.rs @@ -45,7 +45,7 @@ pub mod auto_cfg_disabled { } #[cfg(feature = "routing")] -#[doc(auto_cfg(hide(feature = "routing")))] +#[doc(auto_cfg(hide(feature, values("routing"))))] pub mod auto_cfg_hidden { //@ count 'foo/macro.hidden_cfg_macro.html' '//*[@class="stab portability"]' 0 #[macro_export] @@ -55,9 +55,9 @@ pub mod auto_cfg_hidden { } #[cfg(feature = "routing")] -#[doc(auto_cfg(hide(feature = "routing")))] +#[doc(auto_cfg(hide(feature, values("routing"))))] pub mod auto_cfg_shown { - #[doc(auto_cfg(show(feature = "routing")))] + #[doc(auto_cfg(show(feature, values("routing"))))] pub mod inner { //@ has 'foo/macro.shown_cfg_macro.html' '//*[@class="stab portability"]' 'Available on crate feature routing only.' #[macro_export] diff --git a/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs b/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs index e919206d3a478..72c33d209c7a1 100644 --- a/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs +++ b/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs @@ -1,7 +1,7 @@ #![crate_name = "oud"] #![feature(doc_cfg)] -#![doc(auto_cfg(hide(feature = "solecism")))] +#![doc(auto_cfg(hide(feature, values("solecism"))))] //@ has 'oud/struct.Solecism.html' //@ count - '//*[@class="stab portability"]' 0 diff --git a/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs b/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs index fbd96cc46d800..4329d8e06dfc5 100644 --- a/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs +++ b/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs @@ -3,7 +3,7 @@ #![feature(doc_cfg)] #![doc(auto_cfg(hide( - target_pointer_width = "64", + target_pointer_width, values("64"), )))] #![crate_name = "foo"] @@ -36,7 +36,7 @@ pub struct X; //@count - '//*[@id="impl-Trait-for-X"]' 1 //@count - '//*[@id="impl-Trait-for-X"]/*[@class="item-info"]' 0 #[doc(cfg(any(target_pointer_width = "64", target_arch = "wasm32")))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp { impl super::Trait for super::X { fn f(&self) {} } } @@ -64,7 +64,7 @@ pub struct Y; //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]' 1 //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]/*[@class="item-info"]' 0 #[doc(cfg(any(target_pointer_width = "64", target_arch = "wasm32")))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp4 { impl super::Y { pub fn plain_auto() {} } } diff --git a/tests/rustdoc-html/doc-cfg/trait-impls.rs b/tests/rustdoc-html/doc-cfg/trait-impls.rs index 581d171123d00..9ea6490ae8e21 100644 --- a/tests/rustdoc-html/doc-cfg/trait-impls.rs +++ b/tests/rustdoc-html/doc-cfg/trait-impls.rs @@ -3,7 +3,7 @@ #![feature(doc_cfg)] #![doc(auto_cfg(hide( - target_pointer_width = "64", + target_pointer_width, values("64"), )))] #![crate_name = "foo"] @@ -36,7 +36,7 @@ pub struct X; //@count - '//*[@id="impl-Trait-for-X"]' 1 //@count - '//*[@id="impl-Trait-for-X"]/*[@class="item-info"]' 0 #[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp { impl super::Trait for super::X { fn f(&self) {} } } @@ -64,7 +64,7 @@ pub struct Y; //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]' 1 //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]/*[@class="item-info"]' 0 #[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp4 { impl super::Y { pub fn plain_auto() {} } } diff --git a/tests/rustdoc-html/doc_auto_cfg.rs b/tests/rustdoc-html/doc_auto_cfg.rs index a1903e1a0ca3d..a33c5830157fb 100644 --- a/tests/rustdoc-html/doc_auto_cfg.rs +++ b/tests/rustdoc-html/doc_auto_cfg.rs @@ -2,7 +2,7 @@ #![crate_name = "foo"] #![feature(doc_cfg)] -#![doc(auto_cfg(hide(feature = "hidden")))] +#![doc(auto_cfg(hide(feature, values("hidden"))))] //@ has 'foo/index.html' //@ !has - '//*[@class="stab portability"]' 'Non-moustache' @@ -17,18 +17,18 @@ pub mod m { pub struct A; //@ has 'foo/m/inner/index.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' - #[doc(auto_cfg(show(feature = "hidden")))] + #[doc(auto_cfg(show(feature, values("hidden"))))] pub mod inner { //@ has 'foo/m/inner/struct.B.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' pub struct B; //@ count 'foo/m/inner/struct.A.html' '//*[@class="stab portability"]' 0 - #[doc(auto_cfg(hide(feature = "hidden")))] + #[doc(auto_cfg(hide(feature, values("hidden"))))] pub struct A; } //@ has 'foo/m/struct.B.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' - #[doc(auto_cfg(show(feature = "hidden")))] + #[doc(auto_cfg(show(feature, values("hidden"))))] pub struct B; } @@ -61,7 +61,7 @@ pub mod n { // Should re-enable `auto_cfg` and make `moustache` listed. //@ has 'foo/n/struct.Z.html' '//*[@class="stab portability"]' \ // 'Available on non-crate feature moustache only.' - #[doc(auto_cfg(hide(feature = "hidden")))] + #[doc(auto_cfg(hide(feature, values("hidden"))))] pub struct Z; } diff --git a/tests/rustdoc-ui/cfg-hide-show-conflict.rs b/tests/rustdoc-ui/cfg-hide-show-conflict.rs index 8e98b95c85bb9..b2f7728704c26 100644 --- a/tests/rustdoc-ui/cfg-hide-show-conflict.rs +++ b/tests/rustdoc-ui/cfg-hide-show-conflict.rs @@ -1,3 +1,3 @@ #![feature(doc_cfg)] -#![doc(auto_cfg(hide(target_os = "linux")))] -#![doc(auto_cfg(show(windows, target_os = "linux")))] //~ ERROR +#![doc(auto_cfg(hide(target_os, values("linux"))))] +#![doc(auto_cfg(show(windows), show(target_os, values("linux"))))] //~ ERROR diff --git a/tests/rustdoc-ui/cfg-hide-show-conflict.stderr b/tests/rustdoc-ui/cfg-hide-show-conflict.stderr index 22231e82cd7bf..b43b8104cdc12 100644 --- a/tests/rustdoc-ui/cfg-hide-show-conflict.stderr +++ b/tests/rustdoc-ui/cfg-hide-show-conflict.stderr @@ -1,14 +1,14 @@ error: same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item - --> $DIR/cfg-hide-show-conflict.rs:3:31 + --> $DIR/cfg-hide-show-conflict.rs:3:55 | -LL | #![doc(auto_cfg(show(windows, target_os = "linux")))] - | ^^^^^^^^^^^^^^^^^^^ +LL | #![doc(auto_cfg(show(windows), show(target_os, values("linux"))))] + | ^^^^^^^ | note: first change was here - --> $DIR/cfg-hide-show-conflict.rs:2:22 + --> $DIR/cfg-hide-show-conflict.rs:2:40 | -LL | #![doc(auto_cfg(hide(target_os = "linux")))] - | ^^^^^^^^^^^^^^^^^^^ +LL | #![doc(auto_cfg(hide(target_os, values("linux"))))] + | ^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doc-cfg-2.rs b/tests/rustdoc-ui/doc-cfg-2.rs index f615e96bbc6b5..4661bf5445796 100644 --- a/tests/rustdoc-ui/doc-cfg-2.rs +++ b/tests/rustdoc-ui/doc-cfg-2.rs @@ -13,7 +13,5 @@ // Shouldn't lint #[doc(auto_cfg(hide(windows)))] #[doc(auto_cfg(hide(feature = "windows")))] -//~^ WARN unexpected `cfg` condition name: `feature` -#[doc(auto_cfg(hide(foo)))] -//~^ WARN unexpected `cfg` condition name: `foo` +//~^ ERROR `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values()` pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg-2.stderr b/tests/rustdoc-ui/doc-cfg-2.stderr index 164e755de8ad7..41e2d853aba54 100644 --- a/tests/rustdoc-ui/doc-cfg-2.stderr +++ b/tests/rustdoc-ui/doc-cfg-2.stderr @@ -30,19 +30,19 @@ note: the lint level is defined here LL | #![deny(invalid_doc_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values()` --> $DIR/doc-cfg-2.rs:8:21 | LL | #[doc(auto_cfg(hide(true)))] | ^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values()` --> $DIR/doc-cfg-2.rs:9:21 | LL | #[doc(auto_cfg(hide(42)))] | ^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values()` --> $DIR/doc-cfg-2.rs:10:21 | LL | #[doc(auto_cfg(hide("a")))] @@ -60,23 +60,11 @@ error: expected boolean for `#[doc(auto_cfg = ...)]` LL | #[doc(auto_cfg = "a")] | ^^^ -warning: unexpected `cfg` condition name: `feature` +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values()` --> $DIR/doc-cfg-2.rs:15:21 | LL | #[doc(auto_cfg(hide(feature = "windows")))] | ^^^^^^^^^^^^^^^^^^^ - | - = help: to expect this configuration use `--check-cfg=cfg(feature, values("windows"))` - = note: see for more information about checking conditional configuration - -warning: unexpected `cfg` condition name: `foo` - --> $DIR/doc-cfg-2.rs:17:21 - | -LL | #[doc(auto_cfg(hide(foo)))] - | ^^^ - | - = help: to expect this configuration use `--check-cfg=cfg(foo)` - = note: see for more information about checking conditional configuration -error: aborting due to 6 previous errors; 4 warnings emitted +error: aborting due to 7 previous errors; 2 warnings emitted diff --git a/tests/rustdoc-ui/doc-cfg.rs b/tests/rustdoc-ui/doc-cfg.rs index abaea97192808..5f8b5a29a4402 100644 --- a/tests/rustdoc-ui/doc-cfg.rs +++ b/tests/rustdoc-ui/doc-cfg.rs @@ -6,5 +6,5 @@ //~| ERROR #[doc(cfg())] //~ ERROR #[doc(cfg(foo, bar))] //~ ERROR -#[doc(auto_cfg(hide(foo::bar)))] +#[doc(auto_cfg(hide(foo::bar)))] //~ ERROR pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr index f3af95528b2d2..35d14f913b725 100644 --- a/tests/rustdoc-ui/doc-cfg.stderr +++ b/tests/rustdoc-ui/doc-cfg.stderr @@ -62,6 +62,15 @@ LL - #[doc(cfg(foo, bar))] LL + #[doc(cfg(any(foo, bar)))] | -error: aborting due to 4 previous errors +error[E0565]: malformed `doc` attribute input + --> $DIR/doc-cfg.rs:9:1 + | +LL | #[doc(auto_cfg(hide(foo::bar)))] + | ^^^^^^^^^^^^^^^^^^^^--------^^^^ + | | + | expected a valid identifier here + +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0805`. +Some errors have detailed explanations: E0565, E0805. +For more information about an error, try `rustc --explain E0565`. diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide-2.rs b/tests/rustdoc-ui/lints/doc_cfg_hide-2.rs new file mode 100644 index 0000000000000..f1ee59e64c93c --- /dev/null +++ b/tests/rustdoc-ui/lints/doc_cfg_hide-2.rs @@ -0,0 +1,4 @@ +#![feature(doc_cfg)] + +#![deny(invalid_doc_attributes)] +#![doc(auto_cfg(hide(not(windows))))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide-2.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide-2.stderr new file mode 100644 index 0000000000000..68503ebf132fa --- /dev/null +++ b/tests/rustdoc-ui/lints/doc_cfg_hide-2.stderr @@ -0,0 +1,11 @@ +error[E0565]: malformed `doc` attribute input + --> $DIR/doc_cfg_hide-2.rs:4:1 + | +LL | #![doc(auto_cfg(hide(not(windows))))] + | ^^^^^^^^^^^^^^^^^^^^^---^^^^^^^^^^^^^ + | | + | expected a valid identifier here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0565`. diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.rs b/tests/rustdoc-ui/lints/doc_cfg_hide.rs index 6c190f9befac8..28c0c4a89cebd 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.rs +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.rs @@ -2,4 +2,3 @@ #![feature(doc_cfg)] #![doc(auto_cfg(hide = "test"))] //~ ERROR #![doc(auto_cfg(hide))] //~ ERROR -#![doc(auto_cfg(hide(not(windows))))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index a5ec8fdf5d34e..4b984feb3d5a1 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -16,11 +16,5 @@ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items LL | #![doc(auto_cfg(hide))] | ^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc_cfg_hide.rs:5:22 - | -LL | #![doc(auto_cfg(hide(not(windows))))] - | ^^^^^^^^^^^^ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors From 3c92a3ce4824637f31931fd135c53aac468b79a7 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 20 May 2026 14:12:08 +0200 Subject: [PATCH 201/278] Move `doc-cfg` tests into the right location --- tests/rustdoc-html/{ => doc-cfg}/cfg-bool.rs | 0 tests/rustdoc-html/{doc_auto_cfg.rs => doc-cfg/doc-auto-cfg-2.rs} | 0 .../rustdoc-html/{ => doc-cfg}/doc-auto-cfg-public-in-private.rs | 0 tests/rustdoc-html/{ => doc-cfg}/doc-auto-cfg.rs | 0 tests/rustdoc-html/{ => doc-cfg}/doc_auto_cfg_reexports.rs | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename tests/rustdoc-html/{ => doc-cfg}/cfg-bool.rs (100%) rename tests/rustdoc-html/{doc_auto_cfg.rs => doc-cfg/doc-auto-cfg-2.rs} (100%) rename tests/rustdoc-html/{ => doc-cfg}/doc-auto-cfg-public-in-private.rs (100%) rename tests/rustdoc-html/{ => doc-cfg}/doc-auto-cfg.rs (100%) rename tests/rustdoc-html/{ => doc-cfg}/doc_auto_cfg_reexports.rs (100%) diff --git a/tests/rustdoc-html/cfg-bool.rs b/tests/rustdoc-html/doc-cfg/cfg-bool.rs similarity index 100% rename from tests/rustdoc-html/cfg-bool.rs rename to tests/rustdoc-html/doc-cfg/cfg-bool.rs diff --git a/tests/rustdoc-html/doc_auto_cfg.rs b/tests/rustdoc-html/doc-cfg/doc-auto-cfg-2.rs similarity index 100% rename from tests/rustdoc-html/doc_auto_cfg.rs rename to tests/rustdoc-html/doc-cfg/doc-auto-cfg-2.rs diff --git a/tests/rustdoc-html/doc-auto-cfg-public-in-private.rs b/tests/rustdoc-html/doc-cfg/doc-auto-cfg-public-in-private.rs similarity index 100% rename from tests/rustdoc-html/doc-auto-cfg-public-in-private.rs rename to tests/rustdoc-html/doc-cfg/doc-auto-cfg-public-in-private.rs diff --git a/tests/rustdoc-html/doc-auto-cfg.rs b/tests/rustdoc-html/doc-cfg/doc-auto-cfg.rs similarity index 100% rename from tests/rustdoc-html/doc-auto-cfg.rs rename to tests/rustdoc-html/doc-cfg/doc-auto-cfg.rs diff --git a/tests/rustdoc-html/doc_auto_cfg_reexports.rs b/tests/rustdoc-html/doc-cfg/doc_auto_cfg_reexports.rs similarity index 100% rename from tests/rustdoc-html/doc_auto_cfg_reexports.rs rename to tests/rustdoc-html/doc-cfg/doc_auto_cfg_reexports.rs From c383e5c2db59daac4dedc1064530ba83df82b858 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 13 Jun 2026 20:10:47 +0200 Subject: [PATCH 202/278] Add more regression tests for `doc(auto_cfg())` --- .../doc-cfg/hide-inheritance-key-only.rs | 26 ++++ .../rustdoc-html/doc-cfg/hide-inheritance.rs | 135 ++++++++++++++++++ tests/rustdoc-ui/doc-cfg-2.rs | 2 +- tests/rustdoc-ui/doc-cfg-2.stderr | 8 +- tests/rustdoc-ui/doc-cfg-3.rs | 7 + tests/rustdoc-ui/doc-cfg-3.stderr | 22 +++ tests/rustdoc-ui/doc-cfg-4.rs | 7 + tests/rustdoc-ui/doc-cfg-4.stderr | 20 +++ 8 files changed, 222 insertions(+), 5 deletions(-) create mode 100644 tests/rustdoc-html/doc-cfg/hide-inheritance-key-only.rs create mode 100644 tests/rustdoc-html/doc-cfg/hide-inheritance.rs create mode 100644 tests/rustdoc-ui/doc-cfg-3.rs create mode 100644 tests/rustdoc-ui/doc-cfg-3.stderr create mode 100644 tests/rustdoc-ui/doc-cfg-4.rs create mode 100644 tests/rustdoc-ui/doc-cfg-4.stderr diff --git a/tests/rustdoc-html/doc-cfg/hide-inheritance-key-only.rs b/tests/rustdoc-html/doc-cfg/hide-inheritance-key-only.rs new file mode 100644 index 0000000000000..d3842b4b9591c --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/hide-inheritance-key-only.rs @@ -0,0 +1,26 @@ +// This test ensures that `auto_cfg(hide(key))` does not hide `key = value` and that +// `auto_cfg(hide(key, values(none())))` does the same. + +#![feature(doc_cfg)] +#![crate_name = "foo"] + +#![doc(auto_cfg(hide(meow)))] +#![doc(auto_cfg(hide(another_meow, values(none()))))] + +//@ has foo/fn.foo.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-another_meow' +#[cfg(not(meow))] +#[cfg(not(another_meow))] +#[cfg(not(blob))] +pub fn foo() {} + +//@ has foo/fn.bar.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-another_meow=lol' +#[cfg(not(meow = "lol"))] +#[cfg(not(another_meow = "lol"))] +#[cfg(not(blob = "lol"))] +pub fn bar() {} diff --git a/tests/rustdoc-html/doc-cfg/hide-inheritance.rs b/tests/rustdoc-html/doc-cfg/hide-inheritance.rs new file mode 100644 index 0000000000000..20cf6e3b7114f --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/hide-inheritance.rs @@ -0,0 +1,135 @@ +// This test ensures that using `auto_cfg(show(key))` works correctly. + +#![feature(doc_cfg)] +#![crate_name = "foo"] + +#![doc(auto_cfg(hide(meow)))] +#![doc(auto_cfg(hide(meow, values("lol"))))] + +//@ has foo/fn.foo.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' +#[cfg(not(meow))] +#[cfg(not(blob))] +pub fn foo() {} + +//@ has foo/fn.bar.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' +#[cfg(not(meow = "lol"))] +#[cfg(not(blob = "lol"))] +pub fn bar() {} + +//@ has foo/fn.babar.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' +#[cfg(not(meow = "lola"))] +#[cfg(not(blob = "lola"))] +pub fn babar() {} + +pub mod sub { + // We show again `meow`, however `meow="lol"` should still be hidden. + #![doc(auto_cfg(show(meow)))] + + //@ has foo/sub/fn.foo.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' + #[cfg(not(meow))] + #[cfg(not(blob))] + pub fn foo() {} + + //@ has foo/sub/fn.bar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' + #[cfg(not(meow = "lol"))] + #[cfg(not(blob = "lol"))] + pub fn bar() {} + + //@ has foo/sub/fn.babar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' + #[cfg(not(meow = "lola"))] + #[cfg(not(blob = "lola"))] + pub fn babar() {} +} + +pub mod sub2 { + // We show again `meow = "lol`, however `meow` should still be hidden. + #![doc(auto_cfg(show(meow, values("lol"))))] + + //@ has foo/sub2/fn.foo.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' + #[cfg(not(meow))] + #[cfg(not(blob))] + pub fn foo() {} + + //@ has foo/sub2/fn.bar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' + #[cfg(not(meow = "lol"))] + #[cfg(not(blob = "lol"))] + pub fn bar() {} + + //@ has foo/sub2/fn.babar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' + #[cfg(not(meow = "lola"))] + #[cfg(not(blob = "lola"))] + pub fn babar() {} +} + +pub mod sub3 { + // We show again `meow = "lol`, but by using `any()` this time. + #![doc(auto_cfg(show(meow, values(any()))))] + + //@ has foo/sub3/fn.foo.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' + #[cfg(not(meow))] + #[cfg(not(blob))] + pub fn foo() {} + + //@ has foo/sub3/fn.bar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' + #[cfg(not(meow = "lol"))] + #[cfg(not(blob = "lol"))] + pub fn bar() {} + + //@ has foo/sub3/fn.babar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' + #[cfg(not(meow = "lola"))] + #[cfg(not(blob = "lola"))] + pub fn babar() {} +} + +// This test the mix of values and `none()`. +#[doc(auto_cfg( + hide(bla, values(none(), "tic")), + hide(alb, values(none())), +))] +pub mod sub4 { + //@ has foo/sub4/fn.foo.html + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=tic' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + #[cfg(not(bla))] + #[cfg(not(bla = "tic"))] + #[cfg(not(alb))] + pub fn foo() {} + + //@ has foo/sub4/fn.foo2.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=tic' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + #[doc(auto_cfg( + show(bla, values(none(), "tic")), + show(alb, values(none())), + ))] + #[cfg(not(bla))] + #[cfg(not(bla = "tic"))] + #[cfg(not(alb))] + pub fn foo2() {} +} diff --git a/tests/rustdoc-ui/doc-cfg-2.rs b/tests/rustdoc-ui/doc-cfg-2.rs index 4661bf5445796..ba9e22e755203 100644 --- a/tests/rustdoc-ui/doc-cfg-2.rs +++ b/tests/rustdoc-ui/doc-cfg-2.rs @@ -13,5 +13,5 @@ // Shouldn't lint #[doc(auto_cfg(hide(windows)))] #[doc(auto_cfg(hide(feature = "windows")))] -//~^ ERROR `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values()` +//~^ ERROR `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg-2.stderr b/tests/rustdoc-ui/doc-cfg-2.stderr index 41e2d853aba54..aeb842c338c6b 100644 --- a/tests/rustdoc-ui/doc-cfg-2.stderr +++ b/tests/rustdoc-ui/doc-cfg-2.stderr @@ -30,19 +30,19 @@ note: the lint level is defined here LL | #![deny(invalid_doc_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values()` +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:8:21 | LL | #[doc(auto_cfg(hide(true)))] | ^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values()` +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:9:21 | LL | #[doc(auto_cfg(hide(42)))] | ^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values()` +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:10:21 | LL | #[doc(auto_cfg(hide("a")))] @@ -60,7 +60,7 @@ error: expected boolean for `#[doc(auto_cfg = ...)]` LL | #[doc(auto_cfg = "a")] | ^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values()` +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:15:21 | LL | #[doc(auto_cfg(hide(feature = "windows")))] diff --git a/tests/rustdoc-ui/doc-cfg-3.rs b/tests/rustdoc-ui/doc-cfg-3.rs new file mode 100644 index 0000000000000..39ae86086c882 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-3.rs @@ -0,0 +1,7 @@ +// Checks that you cannot have `any()` and values at the same time. + +#![deny(invalid_doc_attributes)] +#![feature(doc_cfg)] + +#![doc(auto_cfg(hide(target_os, values(any(), "linux"))))] //~ ERROR +#![doc(auto_cfg(hide(target_os, values("linux", any()))))] //~ ERROR diff --git a/tests/rustdoc-ui/doc-cfg-3.stderr b/tests/rustdoc-ui/doc-cfg-3.stderr new file mode 100644 index 0000000000000..b9cd74388ecd9 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-3.stderr @@ -0,0 +1,22 @@ +error: `any()` was used when other values were provided + --> $DIR/doc-cfg-3.rs:6:40 + | +LL | #![doc(auto_cfg(hide(target_os, values(any(), "linux"))))] + | ^^^^^ ------- value declared here + | +note: the lint level is defined here + --> $DIR/doc-cfg-3.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `any()` was used when other values were provided + --> $DIR/doc-cfg-3.rs:7:49 + | +LL | #![doc(auto_cfg(hide(target_os, values("linux", any()))))] + | ------- ^^^^^ + | | + | value declared here + +error: aborting due to 2 previous errors + diff --git a/tests/rustdoc-ui/doc-cfg-4.rs b/tests/rustdoc-ui/doc-cfg-4.rs new file mode 100644 index 0000000000000..eddbe7d84ff52 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-4.rs @@ -0,0 +1,7 @@ +// Checks that you cannot have any item inside `any()` and + +#![deny(invalid_doc_attributes)] +#![feature(doc_cfg)] + +#![doc(auto_cfg(hide(target_os, values(any("linux")))))] //~ ERROR +#![doc(auto_cfg(hide(target_os, values(none("linux")))))] //~ ERROR diff --git a/tests/rustdoc-ui/doc-cfg-4.stderr b/tests/rustdoc-ui/doc-cfg-4.stderr new file mode 100644 index 0000000000000..5de5e3b806594 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-4.stderr @@ -0,0 +1,20 @@ +error: `#![doc(auto_cfg(any(...)))]` only accepts identifiers or `values(...)` + --> $DIR/doc-cfg-4.rs:6:40 + | +LL | #![doc(auto_cfg(hide(target_os, values(any("linux")))))] + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/doc-cfg-4.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `#![doc(auto_cfg(none(...)))]` only accepts identifiers or `values(...)` + --> $DIR/doc-cfg-4.rs:7:40 + | +LL | #![doc(auto_cfg(hide(target_os, values(none("linux")))))] + | ^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From a3ad0686c4b81a09b7627910d963b112f94dfb6f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 13 Jun 2026 20:25:02 +0200 Subject: [PATCH 203/278] Update syntax information for `doc(auto_cfg(hide/show()))` in the rustdoc book --- src/doc/rustdoc/src/unstable-features.md | 50 ++++++++++++++++++++++-- tests/rustdoc-ui/doc-cfg-4.rs | 2 +- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index f16f375a5a84b..a6fb56e85ef59 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -850,12 +850,49 @@ pub mod futures { Then, the `unix` cfg will never be displayed into the documentation. -Rustdoc currently hides `doc` and `doctest` attributes by default and reserves the right to change the list of "hidden by default" attributes. +The syntax of `hide` is as follows: you can list as many `cfg` name as you want: + +```rust,ignore (nightly) +#[doc(auto_cfg(hide(feature, target_os)))] +``` + +With the above example, it means that `#[cfg(feature)]` and `#[cfg(target_os)]` won't be displayed in the docs. However, `#[cfg(target_os = "linux)]` or `#[cfg(feature = "something")]` will be displayed because only the key without values was marked as hidden. if you want to hide some values, you can do: + +```rust,ignore (nightly) +#[doc(auto_cfg(hide(feature, target_os, values("something", "linux"))))] +``` + +In this case, `#[cfg(feature = "linux")]`, `#[cfg(feature = "something")]`, `#[cfg(target_os = "something")]` and `#[cfg(target_os = "linux")]` will be hidden. All listed keys will be impacted by `values(...)`. You can split them by having two `hide`: + +```rust,ignore (nightly) +#[doc(auto_cfg( + hide(feature, values("something")), + hide(target_os, values("linux"))))] +``` + +Now, only `#[cfg(feature = "something")]` and `#[cfg(target_os = "linux")]` will be hidden. If you want to hide all values of a key, you can use `any()`: + +```rust,ignore (nightly) +#[doc(auto_cfg(hide(feature, values(any()))))] +``` + +If you want to hide when there is no value you can use `none()`: + +```rust,ignore (nightly) +#[doc(auto_cfg(hide(feature, values("something", none()))))] +``` + +If the previous example, both `#[cfg(feature)]` and `#[cfg(feature = "something")]` will be hidden. + +Rustdoc currently hides `test`, `doc` and `doctest` attributes by default and reserves the right to change the list of "hidden by default" attributes. The attribute accepts only a list of identifiers or key/value items. So you can write: ```rust,ignore (nightly) -#[doc(auto_cfg(hide(unix, doctest, feature = "something")))] +#[doc(auto_cfg( + hide(unix, doctest), + hide(feature, values("something")), +))] #[doc(auto_cfg(hide()))] ``` @@ -865,7 +902,7 @@ But you cannot write: #[doc(auto_cfg(hide(not(unix))))] ``` -So if we use `doc(auto_cfg(hide(unix)))`, it means it will hide all mentions of `unix`: +So if we use `doc(auto_cfg(hide(unix)))`, it means it will hide all mentions of `unix` without a value: ```rust,ignore (nightly) #[cfg(unix)] // nothing displayed @@ -919,6 +956,8 @@ The reason behind this is that `doc(auto_cfg = ...)` enables or disables the fea This attribute does the opposite of `#[doc(auto_cfg(hide(...)))]`: if you used `#[doc(auto_cfg(hide(...)))]` and want to revert its effect on an item and its descendants, you can use `#[doc(auto_cfg(show(...)))]`. It only applies to `#[doc(auto_cfg = true)]`, not to `#[doc(cfg(...))]`. +It follows the same syntax rules as for `#[doc(auto_cfg(hide(...)))]`. + For example: ```rust,ignore (nightly) @@ -936,7 +975,10 @@ pub mod futures { The attribute accepts only a list of identifiers or key/value items. So you can write: ```rust,ignore (nightly) -#[doc(auto_cfg(show(unix, doctest, feature = "something")))] +#[doc(auto_cfg( + show(unix, doctest), + show(feature, values("something")), +))] #[doc(auto_cfg(show()))] ``` diff --git a/tests/rustdoc-ui/doc-cfg-4.rs b/tests/rustdoc-ui/doc-cfg-4.rs index eddbe7d84ff52..75d0e30f76af9 100644 --- a/tests/rustdoc-ui/doc-cfg-4.rs +++ b/tests/rustdoc-ui/doc-cfg-4.rs @@ -1,4 +1,4 @@ -// Checks that you cannot have any item inside `any()` and +// Checks that you cannot have any item inside `any()` and `none()`. #![deny(invalid_doc_attributes)] #![feature(doc_cfg)] From 9653fd0ff54d35ae754c3cb5fb1ed0f734e60c52 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 17 Jun 2026 17:06:13 +0200 Subject: [PATCH 204/278] Add rustdoc-json test for `doc_cfg` --- tests/rustdoc-json/doc_cfg.rs | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/rustdoc-json/doc_cfg.rs diff --git a/tests/rustdoc-json/doc_cfg.rs b/tests/rustdoc-json/doc_cfg.rs new file mode 100644 index 0000000000000..0033968305bd0 --- /dev/null +++ b/tests/rustdoc-json/doc_cfg.rs @@ -0,0 +1,5 @@ +#![feature(doc_cfg)] + +//@ is "$.index[?(@.name=='f')].attrs" '[{"other": "#[doc(auto_cfg(hide(bar, values(none())), hide(blob, values(\"a\", \"14\"))))]"}]' +#[doc(auto_cfg(hide(bar, values(none())), hide(blob, values("a", "14")),))] +pub fn f() {} From 702445c767598123165fbfaaeae13b18528768d2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 24 Jun 2026 18:30:22 +0200 Subject: [PATCH 205/278] Remove doc_cfg show/hide conflict error and correctly handle `values(any())` --- .../rustc_attr_parsing/src/attributes/doc.rs | 18 +- .../rustc_hir/src/attrs/data_structures.rs | 106 +++------ src/doc/rustdoc/src/unstable-features.md | 42 ++-- src/librustdoc/clean/cfg.rs | 221 ++++++++---------- src/librustdoc/json/conversions.rs | 33 ++- .../rustdoc-html/doc-cfg/hide-inheritance.rs | 46 +++- tests/rustdoc-ui/cfg-hide-show-conflict.rs | 3 - 7 files changed, 228 insertions(+), 241 deletions(-) delete mode 100644 tests/rustdoc-ui/cfg-hide-show-conflict.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 2aa9769b46be5..23eef6334ccee 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -325,17 +325,17 @@ impl DocParser { kind: LitKind::Str(symbol, _), span, .. - }) => match &mut cfg_values.values { - DocCfgHideShowValue::Any(any_span) => { + }) => match &mut cfg_values { + DocCfgHideShow::Any(any_span) => { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, DocAutoCfgHideShowValuesMix { value_span: *span }, *any_span, ); } - DocCfgHideShowValue::List(symbols) => { + DocCfgHideShow::List(symbols) => { if values_set.insert(symbol) { - symbols.push((*symbol, *span)); + symbols.push(DocCfgHideShowValue::new(*symbol, *span)); } } }, @@ -354,19 +354,19 @@ impl DocParser { && list.mixed().count() == 0 { if ident.name == sym::any { - if let DocCfgHideShowValue::List(values) = &cfg_values.values - && let Some((_, span)) = values.first() + if let DocCfgHideShow::List(values) = &cfg_values + && let Some(value) = values.first() { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - DocAutoCfgHideShowValuesMix { value_span: *span }, + DocAutoCfgHideShowValuesMix { value_span: value.span }, sub_item.span(), ); } else { - cfg_values.values = DocCfgHideShowValue::Any(sub_item.span()); + cfg_values.merge_with(&DocCfgHideShow::Any(sub_item.span())); } } else { - cfg_values.only_key = Some(sub_item.span()); + cfg_values.push_none(sub_item.span()); } } else { cx.emit_lint( diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index e35a9d68a4d00..acae158acb2b4 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -515,98 +515,68 @@ pub enum HideOrShow { Show, } -#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq)] -pub enum DocCfgHideShowValue { - Any(Span), - List(ThinVec<(Symbol, Span)>), +#[derive(Clone, Copy, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq)] +pub struct DocCfgHideShowValue { + pub span: Span, + /// If `value` is `None`, then it's a `none()` value. + pub value: Option, } -#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] -pub struct DocCfgHideShow { - /// If `Some`, then `cfg` without values (like `cfg(windows)`) will be shown/hidden. - /// The `Span` comes from where this value was set. - pub only_key: Option, - /// The values of this `cfg` to shown/hidden. - pub values: DocCfgHideShowValue, +impl DocCfgHideShowValue { + pub fn new(value: Symbol, span: Span) -> Self { + Self { span, value: Some(value) } + } + + pub fn new_none(span: Span) -> Self { + Self { span, value: None } + } +} + +#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq)] +pub enum DocCfgHideShow { + Any(Span), + List(ThinVec), } impl DocCfgHideShow { pub fn new() -> Self { - Self { only_key: None, values: DocCfgHideShowValue::List(ThinVec::new()) } + Self::List(ThinVec::new()) } pub fn new_with_only_key(span: Span) -> Self { - Self { only_key: Some(span), values: DocCfgHideShowValue::List(ThinVec::new()) } + let mut values = ThinVec::with_capacity(1); + values.push(DocCfgHideShowValue { span, value: None }); + Self::List(values) } - pub fn merge_with(&mut self, other: &Self) { - if self.only_key.is_none() - && let Some(span) = other.only_key + pub fn push_none(&mut self, span: Span) { + if let Self::List(values) = self + && !values.iter().any(|v| v.value.is_none()) { - self.only_key = Some(span); + values.push(DocCfgHideShowValue { span, value: None }); } - match (&mut self.values, &other.values) { - (DocCfgHideShowValue::Any(_), DocCfgHideShowValue::Any(_)) => { + } + + pub fn merge_with(&mut self, other: &Self) { + match (self, other) { + (Self::Any(_), Self::Any(_) | Self::List(_)) => { // Nothing to do. } - (_, DocCfgHideShowValue::Any(span)) => { + (s, Self::Any(span)) => { // We "upgrade" the list values to "all". - self.values = DocCfgHideShowValue::Any(*span); - } - (DocCfgHideShowValue::List(values), DocCfgHideShowValue::List(other_values)) => { - // Having duplicates is not an issue, we simply ignore them. Would be more - // convenient to have a `set` type though. T_T - for (other_symbol, other_span) in other_values { - if !values.iter().any(|(symbol, _)| symbol == other_symbol) { - values.push((*other_symbol, *other_span)); - } - } + *s = Self::Any(*span); } - (DocCfgHideShowValue::Any(_), DocCfgHideShowValue::List(_)) => { - // Nothing to do here either, we already accept all values. - } - } - } - - pub fn update_with(&mut self, other: &Self) { - if self.only_key.is_none() - && let Some(span) = other.only_key - { - self.only_key = Some(span); - } - match (&mut self.values, &other.values) { - (DocCfgHideShowValue::Any(_), _) => {} - (DocCfgHideShowValue::List(_), DocCfgHideShowValue::Any(span)) => { - self.values = DocCfgHideShowValue::Any(*span); - } - (DocCfgHideShowValue::List(values), DocCfgHideShowValue::List(other_values)) => { + (Self::List(values), Self::List(other_values)) => { // Having duplicates is not an issue, we simply ignore them. Would be more // convenient to have a `set` type though. T_T - for (other_symbol, other_span) in other_values { - if !values.iter().any(|(symbol, _)| symbol == other_symbol) { - values.push((*other_symbol, *other_span)); + for other in other_values { + if !values.iter().any(|value| value.value == other.value) { + values.push(*other); } } } } } - - pub fn remove_overlap(&mut self, other: &Self) { - if other.only_key.is_some() { - self.only_key = None; - } - match (&mut self.values, &other.values) { - (_, DocCfgHideShowValue::Any(_)) => { - self.values = DocCfgHideShowValue::List(ThinVec::new()); - } - (DocCfgHideShowValue::Any(_), DocCfgHideShowValue::List(values)) => { - self.values = DocCfgHideShowValue::List(values.clone()); - } - (DocCfgHideShowValue::List(current), DocCfgHideShowValue::List(values)) => { - current.retain(|(name, _)| !values.iter().any(|(other, _)| other == name)); - } - } - } } #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index a6fb56e85ef59..cc6cf2f8a648c 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -867,26 +867,40 @@ In this case, `#[cfg(feature = "linux")]`, `#[cfg(feature = "something")]`, `#[c ```rust,ignore (nightly) #[doc(auto_cfg( hide(feature, values("something")), - hide(target_os, values("linux"))))] + hide(target_os, values("linux")), +))] +``` + +Now, only `#[cfg(feature = "something")]` and `#[cfg(target_os = "linux")]` will be hidden. If you want to hide a key and all its values, you can use `any()`: + +```rust,ignore (nightly) +#[doc(auto_cfg( + hide(feature, values(any())), +))] ``` -Now, only `#[cfg(feature = "something")]` and `#[cfg(target_os = "linux")]` will be hidden. If you want to hide all values of a key, you can use `any()`: +If you want to hide only when there is no value you can use `none()`: ```rust,ignore (nightly) -#[doc(auto_cfg(hide(feature, values(any()))))] +#[doc(auto_cfg( + hide(feature, values("something", none())), +))] ``` -If you want to hide when there is no value you can use `none()`: +So now, if you want to forbid all values for a key, but allow the key itself, you can do: ```rust,ignore (nightly) -#[doc(auto_cfg(hide(feature, values("something", none()))))] +#[doc(auto_cfg( + hide(feature, values(any())), // We completely hide "feature". + show(feature), // We show again "feature" (but not any value). +))] ``` If the previous example, both `#[cfg(feature)]` and `#[cfg(feature = "something")]` will be hidden. Rustdoc currently hides `test`, `doc` and `doctest` attributes by default and reserves the right to change the list of "hidden by default" attributes. -The attribute accepts only a list of identifiers or key/value items. So you can write: +The attribute accepts only a list of identifiers and `values()`. So you can write: ```rust,ignore (nightly) #[doc(auto_cfg( @@ -916,14 +930,6 @@ However, it only impacts the `unix` cfg, not the feature: #[cfg(feature = "unix")] // `feature = "unix"` is displayed ``` -If `cfg_auto(show(...))` and `cfg_auto(hide(...))` are used to show/hide a same `cfg` on a same item, it'll emit an error. Example: - -```rust,ignore (nightly) -#[doc(auto_cfg(hide(unix)))] -#[doc(auto_cfg(show(unix)))] // Error! -pub fn foo() {} -``` - Using this attribute will re-enable `auto_cfg` if it was disabled at this location: ```rust,ignore (nightly) @@ -941,14 +947,6 @@ pub mod module { } ``` -However, using `doc(auto_cfg = ...)` and `doc(auto_cfg(...))` on the same item will emit an error: - -```rust,ignore (nightly) -#[doc(auto_cfg = false)] -#[doc(auto_cfg(hide(unix)))] // error -pub fn foo() {} -``` - The reason behind this is that `doc(auto_cfg = ...)` enables or disables the feature, whereas `doc(auto_cfg(...))` enables it unconditionally, making the first attribute to appear useless as it will be overidden by the next `doc(auto_cfg)` attribute. ### `#[doc(auto_cfg(show(...)))]` diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 8721523f83f58..2950e3b563c1c 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -32,6 +32,87 @@ mod tests; #[cfg_attr(test, derive(PartialEq))] pub(crate) struct Cfg(CfgEntry); +// Similar to `hir::DocCfgHideShow` but allows to handle both `show` and `hide` as with the `except` +// field in `Any` variant. +#[derive(Clone, Debug)] +enum DocCfgHide { + Any { except: ThinVec }, + List(ThinVec), +} + +impl DocCfgHide { + fn new() -> Self { + Self::List([DocCfgHideShowValue::new_none(DUMMY_SP)].into()) + } + + fn contains(&self, value: Option) -> bool { + match self { + // Contains any values except the ones listed in `except`. + Self::Any { except } => !except.iter().any(|e| e.value == value), + Self::List(values) => values.iter().any(|v| v.value == value), + } + } + + fn merge_with(&mut self, other: &DocCfgHideShow) { + match (self, other) { + (Self::Any { except }, DocCfgHideShow::Any(_)) => { + except.clear(); + } + (s, DocCfgHideShow::Any(_)) => { + // We "upgrade" the list values to "all". + *s = Self::Any { except: ThinVec::new() }; + } + (Self::Any { except }, DocCfgHideShow::List(values)) => { + for other in values { + if let Some(index) = except.iter().position(|value| value.value == other.value) + { + except.remove(index); + } + } + } + (Self::List(values), DocCfgHideShow::List(other_values)) => { + for other in other_values { + if !values.iter().any(|value| value.value == other.value) { + values.push(*other); + } + } + } + } + } + + fn remove(&mut self, other: &DocCfgHideShow) { + match (self, other) { + (s, DocCfgHideShow::Any(_)) => { + *s = Self::List(ThinVec::new()); + } + (Self::Any { except }, DocCfgHideShow::List(other_values)) => { + for other in other_values { + if !except.iter().any(|value| value.value == other.value) { + except.push(*other); + } + } + } + (Self::List(values), DocCfgHideShow::List(other_values)) => { + for other in other_values { + if let Some(index) = values.iter().position(|value| value.value == other.value) + { + values.remove(index); + } + } + } + } + } +} + +impl From<&DocCfgHideShow> for DocCfgHide { + fn from(from: &DocCfgHideShow) -> Self { + match from { + DocCfgHideShow::Any(_) => Self::Any { except: ThinVec::new() }, + DocCfgHideShow::List(values) => Self::List(values.clone()), + } + } +} + /// Whether the configuration consists of just `Cfg` or `Not`. fn is_simple_cfg(cfg: &CfgEntry) -> bool { match cfg { @@ -55,24 +136,15 @@ fn is_any_cfg(cfg: &CfgEntry) -> bool { } } -fn strip_hidden(cfg: &CfgEntry, hidden: &FxHashMap) -> Option { +fn strip_hidden(cfg: &CfgEntry, hidden: &FxHashMap) -> Option { match cfg { CfgEntry::Bool(..) => Some(cfg.clone()), CfgEntry::NameValue { name, value, .. } => { - let mut is_stripped = false; - if let Some(values) = hidden.get(name) { - if let Some(value) = value { - match &values.values { - DocCfgHideShowValue::Any(_) => is_stripped = true, - DocCfgHideShowValue::List(values) => { - is_stripped = values.iter().any(|(v, _)| v == value); - } - } - } else { - is_stripped = values.only_key.is_some(); - } + if hidden.get(name).is_some_and(|values| values.contains(*value)) { + None + } else { + Some(cfg.clone()) } - if !is_stripped { Some(cfg.clone()) } else { None } } CfgEntry::Not(cfg, _) => { if let Some(cfg) = strip_hidden(cfg, hidden) { @@ -669,7 +741,7 @@ fn human_readable_target_env(env: Symbol) -> Option<&'static str> { pub(crate) struct CfgInfo { /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active /// `doc(auto_cfg(show(...)))` cfgs. - hidden_cfg: FxHashMap, + hidden_cfg: FxHashMap, /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while /// taking into account the `hidden_cfg` information. current_cfg: Cfg, @@ -685,9 +757,9 @@ impl Default for CfgInfo { fn default() -> Self { Self { hidden_cfg: FxHashMap::from_iter([ - (sym::test, DocCfgHideShow::new_with_only_key(DUMMY_SP)), - (sym::doc, DocCfgHideShow::new_with_only_key(DUMMY_SP)), - (sym::doctest, DocCfgHideShow::new_with_only_key(DUMMY_SP)), + (sym::test, DocCfgHide::new()), + (sym::doc, DocCfgHide::new()), + (sym::doctest, DocCfgHide::new()), ]), current_cfg: Cfg(CfgEntry::Bool(true, DUMMY_SP)), auto_cfg_active: true, @@ -696,104 +768,24 @@ impl Default for CfgInfo { } } -fn show_hide_show_conflict_error( - tcx: TyCtxt<'_>, - item_span: rustc_span::Span, - previous: rustc_span::Span, - errors: &mut usize, -) { - *errors += 1; - let mut diag = tcx.sess.dcx().struct_span_err( - item_span, - format!( - "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item" - ), - ); - diag.span_note(previous, "first change was here"); - diag.emit(); -} - -fn check_if_no_overlap( - tcx: TyCtxt<'_>, - attrs: &FxHashMap, - cfg_name: Symbol, - info: &DocCfgHideShow, -) -> bool { - let mut errors = 0; - if let Some(other) = attrs.get(&cfg_name) { - match (&other.only_key, &info.only_key) { - (Some(previous_span), Some(span)) => { - show_hide_show_conflict_error(tcx, *span, *previous_span, &mut errors); - } - _ => {} - } - match (&other.values, &info.values) { - (DocCfgHideShowValue::Any(previous_span), DocCfgHideShowValue::Any(span)) => { - show_hide_show_conflict_error(tcx, *span, *previous_span, &mut errors); - } - (DocCfgHideShowValue::Any(previous_span), DocCfgHideShowValue::List(items)) => { - // If the list is empty, then it's just the default so no problem there. - if let Some((_, span)) = items.first() { - show_hide_show_conflict_error(tcx, *span, *previous_span, &mut errors); - } - } - (DocCfgHideShowValue::List(previous_items), DocCfgHideShowValue::Any(span)) => { - // If the list is empty, then it's just the default so no problem there. - if let Some((_, previous_span)) = previous_items.first() { - show_hide_show_conflict_error(tcx, *span, *previous_span, &mut errors); - } - } - (DocCfgHideShowValue::List(previous_items), DocCfgHideShowValue::List(items)) => { - for (previous_name, previous_span) in previous_items { - if let Some((_, span)) = items.iter().find(|(name, _)| name == previous_name) { - show_hide_show_conflict_error(tcx, *span, *previous_span, &mut errors); - } - } - } - } - } - errors == 0 -} - /// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument. /// -/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and -/// `auto_cfg(show(...))` on the same item and emits an error if it's the case. -/// /// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs` /// and in `new_hide_attrs` arguments. -fn handle_auto_cfg_hide_show( - tcx: TyCtxt<'_>, - cfg_info: &mut CfgInfo, - attr: &CfgHideShow, - new_show_attrs: &mut FxHashMap, - new_hide_attrs: &mut FxHashMap, -) { +fn handle_auto_cfg_hide_show(cfg_info: &mut CfgInfo, attr: &CfgHideShow) { for (cfg_name, value) in &attr.values { if attr.kind == HideOrShow::Show { - if check_if_no_overlap(tcx, new_hide_attrs, *cfg_name, value) { - new_show_attrs - .entry(*cfg_name) - .and_modify(|entry| entry.update_with(value)) - .or_insert_with(|| value.clone()); - cfg_info - .hidden_cfg - .entry(*cfg_name) - .and_modify(|entry| entry.remove_overlap(value)) - .or_insert_with(|| value.clone()); - } + cfg_info + .hidden_cfg + .entry(*cfg_name) + .and_modify(|entry| entry.remove(value)) + .or_insert_with(|| value.into()); } else { - if check_if_no_overlap(tcx, new_show_attrs, *cfg_name, value) { - new_hide_attrs - .entry(*cfg_name) - .and_modify(|entry| entry.update_with(value)) - .or_insert_with(|| value.clone()); - cfg_info - .hidden_cfg - .entry(*cfg_name) - .and_modify(|entry| entry.update_with(value)) - .or_insert_with(|| value.clone()); - } + cfg_info + .hidden_cfg + .entry(*cfg_name) + .and_modify(|entry| entry.merge_with(value)) + .or_insert_with(|| value.into()); } } } @@ -828,9 +820,6 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator false } - let mut new_show_attrs = FxHashMap::default(); - let mut new_hide_attrs = FxHashMap::default(); - let mut doc_cfg = attrs .clone() .filter_map(|attr| match attr { @@ -881,13 +870,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator return None; } for (value, _) in &d.auto_cfg { - handle_auto_cfg_hide_show( - tcx, - cfg_info, - value, - &mut new_show_attrs, - &mut new_hide_attrs, - ); + handle_auto_cfg_hide_show(cfg_info, value); } } } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr { diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 9ec7e4bd49ceb..882220a0cae03 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::attrs::{ - self, DeprecatedSince, DocAttribute, DocCfgHideShowValue, DocInline, HideOrShow, + self, DeprecatedSince, DocAttribute, DocCfgHideShow, DocInline, HideOrShow, }; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::DefId; @@ -1137,25 +1137,20 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) out.push_str(&format!("{kind}(")); for (name, cfgs) in &auto_cfg.values { out.push_str(&format!("{name}, values(")); - let mut pos = 0; - if cfgs.only_key.is_some() { - out.push_str("none()"); - pos += 1; - } - match &cfgs.values { - DocCfgHideShowValue::Any(_) => { - out.push_str(&format!("{}any()", if pos > 0 { ", " } else { "" })); + match cfgs { + DocCfgHideShow::Any(_) => { + out.push_str("any()"); } - DocCfgHideShowValue::List(values) => { - for (value, _) in values { - // We use `as_str` and debug display to have characters escaped - // and `"` characters surrounding the string. - out.push_str(&format!( - "{}{:?}", - if pos > 0 { ", " } else { "" }, - value.as_str() - )); - pos += 1; + DocCfgHideShow::List(values) => { + for (pos, value) in values.iter().enumerate() { + let separator = if pos > 0 { ", " } else { "" }; + if let Some(value) = &value.value { + // We use `as_str` and debug display to have characters escaped + // and `"` characters surrounding the string. + out.push_str(&format!("{separator}{:?}", value.as_str())); + } else { + out.push_str(&format!("{separator}none()")); + } } } } diff --git a/tests/rustdoc-html/doc-cfg/hide-inheritance.rs b/tests/rustdoc-html/doc-cfg/hide-inheritance.rs index 20cf6e3b7114f..3f0e2ffca2136 100644 --- a/tests/rustdoc-html/doc-cfg/hide-inheritance.rs +++ b/tests/rustdoc-html/doc-cfg/hide-inheritance.rs @@ -85,7 +85,7 @@ pub mod sub3 { //@ has foo/sub3/fn.foo.html //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' - //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' #[cfg(not(meow))] #[cfg(not(blob))] pub fn foo() {} @@ -133,3 +133,47 @@ pub mod sub4 { #[cfg(not(alb))] pub fn foo2() {} } + +// This test the mix of `any()` and values. +#[doc(auto_cfg( + hide(alb, values(any())), + hide(bla, values(any())), + show(bla, values("top")), +))] +pub mod sub5 { + //@ has foo/sub5/fn.foo.html + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=top' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=a' + #[cfg(not(alb))] + #[cfg(not(bla = "top"))] + #[cfg(not(bla = "a"))] + pub fn foo() {} + + //@ has foo/sub5/fn.foo2.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=top' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=a' + #[doc(auto_cfg( + show(alb, values(none())), + hide(bla, values("top")), + ))] + #[cfg(not(alb))] + #[cfg(not(bla = "top"))] + #[cfg(not(bla = "a"))] + pub fn foo2() {} + + //@ has foo/sub5/fn.foo3.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=top' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=a' + #[doc(auto_cfg( + show(alb, values(any())), + show(bla, values(any())), + hide(bla, values(none(), "top")), + ))] + #[cfg(not(alb))] + #[cfg(not(bla = "top"))] + #[cfg(not(bla = "a"))] + pub fn foo3() {} +} diff --git a/tests/rustdoc-ui/cfg-hide-show-conflict.rs b/tests/rustdoc-ui/cfg-hide-show-conflict.rs deleted file mode 100644 index b2f7728704c26..0000000000000 --- a/tests/rustdoc-ui/cfg-hide-show-conflict.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![feature(doc_cfg)] -#![doc(auto_cfg(hide(target_os, values("linux"))))] -#![doc(auto_cfg(show(windows), show(target_os, values("linux"))))] //~ ERROR From 4d9ae094fb1f588b75b297c735bc0f2c2c070fc5 Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 26 Jun 2026 09:54:10 +0200 Subject: [PATCH 206/278] Fix output normtn in splat-fn-ptr-tuple.rs --- tests/ui/splat/splat-fn-ptr-tuple.rs | 5 +++-- tests/ui/splat/splat-fn-ptr-tuple.stderr | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/splat/splat-fn-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-tuple.rs index b4a6a3beae58f..297dbc0457794 100644 --- a/tests/ui/splat/splat-fn-ptr-tuple.rs +++ b/tests/ui/splat/splat-fn-ptr-tuple.rs @@ -1,13 +1,14 @@ //@ failure-status: 101 -//@ normalize-stderr: "(.*)internal compiler error:([^:]+):\d{1,}:\d{1,}:(.*)" -> "$1internal compiler error:$2:LL:CC:$3" -//@ normalize-stderr: "thread.*panicked at compiler.*" -> "" +//@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" +//@ normalize-stderr: "thread.*panicked at .*compiler.*" -> "" //@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" //@ normalize-stderr: "note: compiler flags.*\n\n" -> "" //@ normalize-stderr: " +\d{1,}: .*\n" -> "" //@ normalize-stderr: " + at .*\n" -> "" //@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" //@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" +//@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" //! Test using `#[splat]` on tuple arguments of simple functions. //! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. diff --git a/tests/ui/splat/splat-fn-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-tuple.stderr index 700d7639241a3..a69175bd5e886 100644 --- a/tests/ui/splat/splat-fn-ptr-tuple.stderr +++ b/tests/ui/splat/splat-fn-ptr-tuple.stderr @@ -1,5 +1,4 @@ - compiler error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: no splatted def for function or method callee - --> $DIR/splat-fn-ptr-tuple.rs:29:5 +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: no splatted def for function or method callee | LL | fn_ptr(1, 2); | ^^^^^^^^^^^^ From 5d41c454727a71f2d962101bd590d209eaf2b2b0 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Fri, 26 Jun 2026 14:54:55 +0200 Subject: [PATCH 207/278] Update Enzyme submodule --- src/tools/enzyme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/enzyme b/src/tools/enzyme index 7c0141f133a35..a8668c7ca3579 160000 --- a/src/tools/enzyme +++ b/src/tools/enzyme @@ -1 +1 @@ -Subproject commit 7c0141f133a3592daa12cc6cc07f297a5222a42e +Subproject commit a8668c7ca3579c3304d628bca518bafc0fcc62d1 From b049d2bc02eae9506e200c1c7e7d60876f50b050 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Wed, 24 Jun 2026 16:18:11 +0800 Subject: [PATCH 208/278] make use of rigidness marker in fast_reject --- compiler/rustc_type_ir/src/fast_reject.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 14ebffb70b8bf..097527a23703b 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -256,13 +256,12 @@ impl { + ty::Param(_) | ty::Alias(ty::IsRigid::Yes, _) => { if INSTANTIATE_RHS_WITH_INFER { return true; } } - // FIXME(#155345): we should fast-path for rigid aliases here. - ty::Error(_) | ty::Alias(..) | ty::Bound(..) => return true, + ty::Error(_) | ty::Alias(ty::IsRigid::No, _) | ty::Bound(..) => return true, ty::Infer(var) => return self.var_and_ty_may_unify(var, lhs), // These types only unify with inference variables or their own @@ -339,12 +338,22 @@ impl self.var_and_ty_may_unify(var, rhs), + // Since we ensure that the rhs is not non-rigid alias, + // lhs rigid alias can only unify with it if it's a rigid alias of the same kind. + ty::Alias(ty::IsRigid::Yes, lhs_alias) => { + INSTANTIATE_LHS_WITH_INFER + || match rhs.kind() { + ty::Alias(ty::IsRigid::Yes, rhs_alias) => { + lhs_alias.kind == rhs_alias.kind + && self.args_may_unify_inner(lhs_alias.args, rhs_alias.args, depth) + } + _ => false, + } + } // As we're walking the whole type, it may encounter projections // inside of binders and what not, so we're just going to assume that - // projections can unify with other stuff. - // - // Looking forward to lazy normalization this is the safer strategy anyways. - ty::Alias(..) => true, + // non-rigid alias can unify with anything. + ty::Alias(ty::IsRigid::No, _) => true, ty::Int(_) | ty::Uint(_) From 8b90f5c45e247662d6cf9bd64d9b2e84988feb16 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 26 Jun 2026 15:51:44 +0200 Subject: [PATCH 209/278] Remove `FIXME` comment that is not needed anymore --- library/core/src/fmt/num_buffer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/core/src/fmt/num_buffer.rs b/library/core/src/fmt/num_buffer.rs index af8acda6633c5..00170346289cd 100644 --- a/library/core/src/fmt/num_buffer.rs +++ b/library/core/src/fmt/num_buffer.rs @@ -74,7 +74,6 @@ impl NumBuffer { #[stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] pub const fn new() -> Self { - // FIXME: Once const generics feature is working, use `T::BUF_SIZE` instead of 40. NumBuffer { buf: T::DEFAULT, phantom: core::marker::PhantomData } } } From eb78d13769ac4ae9a9e1b324c7f46331085405ac Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 26 Jun 2026 16:05:13 +0200 Subject: [PATCH 210/278] Replace `ilog(10)` with `ilog10()` --- library/core/src/fmt/num.rs | 2 +- library/core/src/fmt/num_buffer.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 6c639a8b092b3..050822da8f12a 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -361,7 +361,7 @@ macro_rules! impl_Display { #[cfg(feature = "optimize_for_size")] fn ${concat($fmt_fn, _small)}(n: $T, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const MAX_DEC_N: usize = $T::MAX.ilog(10) as usize + 1; + const MAX_DEC_N: usize = $T::MAX.ilog10() as usize + 1; let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; let offset = ${concat($fmt_fn, _in_buf_small)}(n, &mut buf); diff --git a/library/core/src/fmt/num_buffer.rs b/library/core/src/fmt/num_buffer.rs index 00170346289cd..624a953062332 100644 --- a/library/core/src/fmt/num_buffer.rs +++ b/library/core/src/fmt/num_buffer.rs @@ -17,13 +17,13 @@ macro_rules! impl_NumBufferTrait { #[stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] impl NumBufferTrait for $signed { // `+ 2` and not `+ 1` to include the `-` character. - const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $signed::MAX.ilog(10) as usize + 2]; - type Buf = [MaybeUninit; $signed::MAX.ilog(10) as usize + 2]; + const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $signed::MAX.ilog10() as usize + 2]; + type Buf = [MaybeUninit; $signed::MAX.ilog10() as usize + 2]; } #[stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] impl NumBufferTrait for $unsigned { - const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $unsigned::MAX.ilog(10) as usize + 1]; - type Buf = [MaybeUninit; $unsigned::MAX.ilog(10) as usize + 1]; + const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $unsigned::MAX.ilog10() as usize + 1]; + type Buf = [MaybeUninit; $unsigned::MAX.ilog10() as usize + 1]; } )* } From 3c00af96ba6cd3de91a96066d8b5037f11747482 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Thu, 5 Feb 2026 12:55:37 +0100 Subject: [PATCH 211/278] Add supertrait item shadowing for type-level path resolution --- .../src/hir_ty_lowering/errors.rs | 6 +- .../src/hir_ty_lowering/mod.rs | 76 ++++++++++++++++++- .../supertrait-shadowing/assoc-const2.rs | 32 ++++++++ .../assoc-const2.run.stdout | 2 + .../supertrait-shadowing/assoc-type.rs | 47 ++++++++++++ .../assoc-type.run.stdout | 4 + 6 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 tests/ui/methods/supertrait-shadowing/assoc-const2.rs create mode 100644 tests/ui/methods/supertrait-shadowing/assoc-const2.run.stdout create mode 100644 tests/ui/methods/supertrait-shadowing/assoc-type.rs create mode 100644 tests/ui/methods/supertrait-shadowing/assoc-type.run.stdout diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 39402cfc35dce..32678e8a5a12f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -409,9 +409,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub(super) fn report_ambiguous_assoc_item( &self, - bound1: ty::PolyTraitRef<'tcx>, - bound2: ty::PolyTraitRef<'tcx>, - matching_candidates: impl Iterator>, + matching_candidates: &[ty::PolyTraitRef<'tcx>], qself: AssocItemQSelf, assoc_tag: ty::AssocTag, assoc_ident: Ident, @@ -443,7 +441,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // predicates!). // FIXME: Turn this into a structured, translatable & more actionable suggestion. let mut where_bounds = vec![]; - for bound in [bound1, bound2].into_iter().chain(matching_candidates) { + for &bound in matching_candidates { let bound_id = bound.def_id(); let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind( tcx, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index baa9fddc2a651..5785de6c07845 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -24,6 +24,7 @@ use std::{assert_matches, slice}; use rustc_abi::FIRST_VARIANT; use rustc_ast::LitKind; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::sso::SsoHashSet; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, StashKey, @@ -1262,6 +1263,69 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) } + /// When there are multiple traits which contain an identically named + /// associated item, this function eliminates any traits which are a + /// supertrait of another candidate trait. + /// + /// This implements RFC #3624. + fn collapse_candidates_to_subtrait_pick( + &self, + matching_candidates: &[ty::PolyTraitRef<'tcx>], + ) -> Option> { + if !self.tcx().features().supertrait_item_shadowing() { + return None; + } + + let mut child_trait = matching_candidates[0]; + let mut supertraits: SsoHashSet<_> = + traits::supertrait_def_ids(self.tcx(), child_trait.def_id()).collect(); + + let mut remaining_candidates: Vec<_> = matching_candidates[1..].iter().copied().collect(); + while !remaining_candidates.is_empty() { + let mut made_progress = false; + let mut next_round = vec![]; + + for remaining_trait in remaining_candidates { + if supertraits.contains(&remaining_trait.def_id()) { + made_progress = true; + continue; + } + + // This pick is not a supertrait of the `child_pick`. + // Check if it's a subtrait of the `child_pick`, instead. + // If it is, then it must have been a subtrait of every + // other pick we've eliminated at this point. It will + // take over at this point. + let remaining_trait_supertraits: SsoHashSet<_> = + traits::supertrait_def_ids(self.tcx(), remaining_trait.def_id()).collect(); + if remaining_trait_supertraits.contains(&child_trait.def_id()) { + child_trait = remaining_trait; + supertraits = remaining_trait_supertraits; + made_progress = true; + continue; + } + + // `child_pick` is not a supertrait of this pick. + // Don't bail here, since we may be comparing two supertraits + // of a common subtrait. These two supertraits won't be related + // at all, but we will pick them up next round when we find their + // child as we continue iterating in this round. + next_round.push(remaining_trait); + } + + if made_progress { + // If we've made progress, iterate again. + remaining_candidates = next_round; + } else { + // Otherwise, we must have at least two candidates which + // are not related to each other at all.; + return None; + } + } + + Some(child_trait) + } + /// Search for a single trait bound whose trait defines the associated item given by /// `assoc_ident`. /// @@ -1296,10 +1360,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; if let Some(bound2) = matching_candidates.next() { + let all_matching_candidates: Vec<_> = + [bound1, bound2].into_iter().chain(matching_candidates).collect(); + if let Some(bound) = + self.collapse_candidates_to_subtrait_pick(&all_matching_candidates) + { + return Ok(bound); + } + return Err(self.report_ambiguous_assoc_item( - bound1, - bound2, - matching_candidates, + &all_matching_candidates, qself, assoc_tag, assoc_ident, diff --git a/tests/ui/methods/supertrait-shadowing/assoc-const2.rs b/tests/ui/methods/supertrait-shadowing/assoc-const2.rs new file mode 100644 index 0000000000000..ebd0875eac8ea --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/assoc-const2.rs @@ -0,0 +1,32 @@ +//@ run-pass +//@ check-run-results + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![allow(dead_code)] + +trait A { + const CONST: i32; +} +impl A for T { + const CONST: i32 = 1; +} + +trait B: A { + type const CONST: i32; +} +impl B for T { + type const CONST: i32 = 2; +} + +trait C: B {} +impl C for T {} + +fn main() { + println!("{}", i32::CONST); + generic::(); +} + +fn generic>() { + println!("{}", T::CONST); +} diff --git a/tests/ui/methods/supertrait-shadowing/assoc-const2.run.stdout b/tests/ui/methods/supertrait-shadowing/assoc-const2.run.stdout new file mode 100644 index 0000000000000..51993f072d583 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/assoc-const2.run.stdout @@ -0,0 +1,2 @@ +2 +2 diff --git a/tests/ui/methods/supertrait-shadowing/assoc-type.rs b/tests/ui/methods/supertrait-shadowing/assoc-type.rs new file mode 100644 index 0000000000000..7434fa7523119 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/assoc-type.rs @@ -0,0 +1,47 @@ +//@ run-pass +//@ check-run-results + +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + type T; +} +impl A for T { + type T = i8; +} + +trait B: A { + type T; +} +impl B for T { + type T = i16; +} + +trait C: B {} +impl C for T {} + +fn main() { + generic::(); + generic2::(); + generic3::(); + generic4::(); +} + +fn generic() { + println!("{}", size_of::()); +} + +fn generic2>() { + println!("{}", size_of::()); +} + +fn generic3>() { + println!("{}", size_of::()); +} + +fn generic4>() { + println!("{}", size_of::()); +} diff --git a/tests/ui/methods/supertrait-shadowing/assoc-type.run.stdout b/tests/ui/methods/supertrait-shadowing/assoc-type.run.stdout new file mode 100644 index 0000000000000..6f9071418f37e --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/assoc-type.run.stdout @@ -0,0 +1,4 @@ +2 +1 +2 +2 From 1efd5a62d141b4ea013f6bfd875677f7b8dc3b88 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Wed, 3 Jun 2026 17:29:02 +0100 Subject: [PATCH 212/278] Address review feedback --- .../src/hir_ty_lowering/mod.rs | 16 ++++++++++------ compiler/rustc_hir_typeck/src/method/probe.rs | 4 ++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 5785de6c07845..2d0dab03f2c46 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1267,6 +1267,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// associated item, this function eliminates any traits which are a /// supertrait of another candidate trait. /// + /// This is the type-level analogue of + /// `rustc_hir_typeck::method::probe::ProbeContext::collapse_candidates_to_subtrait_pick`; + /// keep both implementations in sync. + /// /// This implements RFC #3624. fn collapse_candidates_to_subtrait_pick( &self, @@ -1291,8 +1295,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { continue; } - // This pick is not a supertrait of the `child_pick`. - // Check if it's a subtrait of the `child_pick`, instead. + // This candidate is not a supertrait of the `child_trait`. + // Check if it's a subtrait of the `child_trait`, instead. // If it is, then it must have been a subtrait of every // other pick we've eliminated at this point. It will // take over at this point. @@ -1305,7 +1309,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { continue; } - // `child_pick` is not a supertrait of this pick. + // Neither `child_trait` or the current candidate are + // supertraits of each other. // Don't bail here, since we may be comparing two supertraits // of a common subtrait. These two supertraits won't be related // at all, but we will pick them up next round when we find their @@ -1318,7 +1323,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { remaining_candidates = next_round; } else { // Otherwise, we must have at least two candidates which - // are not related to each other at all.; + // are not related to each other at all. return None; } } @@ -1362,8 +1367,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let Some(bound2) = matching_candidates.next() { let all_matching_candidates: Vec<_> = [bound1, bound2].into_iter().chain(matching_candidates).collect(); - if let Some(bound) = - self.collapse_candidates_to_subtrait_pick(&all_matching_candidates) + if let Some(bound) = self.collapse_candidates_to_subtrait_pick(&all_matching_candidates) { return Ok(bound); } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index e8921841c4405..ce187aa1aff14 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -2359,6 +2359,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// multiple conflicting picks if there is one pick whose trait container is a subtrait /// of the trait containers of all of the other picks. /// + /// This is the method-probe analogue of + /// `rustc_hir_analysis::hir_ty_lowering::HirTyLowerer::collapse_candidates_to_subtrait_pick`; + /// keep both implementations in sync. + /// /// This implements RFC #3624. fn collapse_candidates_to_subtrait_pick( &self, From e4ef1cff8961155a42c9a47e02d144a925d685a2 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Wed, 3 Jun 2026 17:33:44 +0100 Subject: [PATCH 213/278] Move supertrait shadowing tests to tests/ui/supertrait-shadowing --- tests/ui/{methods => }/supertrait-shadowing/assoc-const.rs | 0 tests/ui/{methods => }/supertrait-shadowing/assoc-const2.rs | 0 .../ui/{methods => }/supertrait-shadowing/assoc-const2.run.stdout | 0 tests/ui/{methods => }/supertrait-shadowing/assoc-type.rs | 0 tests/ui/{methods => }/supertrait-shadowing/assoc-type.run.stdout | 0 .../supertrait-shadowing/auxiliary/shadowed_stability.rs | 0 tests/ui/{methods => }/supertrait-shadowing/common-ancestor-2.rs | 0 .../{methods => }/supertrait-shadowing/common-ancestor-2.stderr | 0 tests/ui/{methods => }/supertrait-shadowing/common-ancestor-3.rs | 0 .../{methods => }/supertrait-shadowing/common-ancestor-3.stderr | 0 tests/ui/{methods => }/supertrait-shadowing/common-ancestor.rs | 0 .../ui/{methods => }/supertrait-shadowing/common-ancestor.stderr | 0 tests/ui/{methods => }/supertrait-shadowing/definition-site.rs | 0 .../ui/{methods => }/supertrait-shadowing/definition-site.stderr | 0 .../supertrait-shadowing/false-subtrait-after-inference.rs | 0 .../supertrait-shadowing/false-subtrait-after-inference.stderr | 0 .../ui/{methods => }/supertrait-shadowing/no-common-ancestor-2.rs | 0 .../supertrait-shadowing/no-common-ancestor-2.stderr | 0 tests/ui/{methods => }/supertrait-shadowing/no-common-ancestor.rs | 0 .../{methods => }/supertrait-shadowing/no-common-ancestor.stderr | 0 tests/ui/{methods => }/supertrait-shadowing/out-of-scope.rs | 0 .../supertrait-shadowing/trivially-false-subtrait.rs | 0 tests/ui/{methods => }/supertrait-shadowing/type-dependent.rs | 0 .../{methods => }/supertrait-shadowing/unstable.off_normal.stderr | 0 .../supertrait-shadowing/unstable.off_shadowing.stderr | 0 .../{methods => }/supertrait-shadowing/unstable.on_normal.stderr | 0 tests/ui/{methods => }/supertrait-shadowing/unstable.rs | 0 27 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{methods => }/supertrait-shadowing/assoc-const.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/assoc-const2.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/assoc-const2.run.stdout (100%) rename tests/ui/{methods => }/supertrait-shadowing/assoc-type.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/assoc-type.run.stdout (100%) rename tests/ui/{methods => }/supertrait-shadowing/auxiliary/shadowed_stability.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/common-ancestor-2.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/common-ancestor-2.stderr (100%) rename tests/ui/{methods => }/supertrait-shadowing/common-ancestor-3.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/common-ancestor-3.stderr (100%) rename tests/ui/{methods => }/supertrait-shadowing/common-ancestor.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/common-ancestor.stderr (100%) rename tests/ui/{methods => }/supertrait-shadowing/definition-site.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/definition-site.stderr (100%) rename tests/ui/{methods => }/supertrait-shadowing/false-subtrait-after-inference.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/false-subtrait-after-inference.stderr (100%) rename tests/ui/{methods => }/supertrait-shadowing/no-common-ancestor-2.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/no-common-ancestor-2.stderr (100%) rename tests/ui/{methods => }/supertrait-shadowing/no-common-ancestor.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/no-common-ancestor.stderr (100%) rename tests/ui/{methods => }/supertrait-shadowing/out-of-scope.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/trivially-false-subtrait.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/type-dependent.rs (100%) rename tests/ui/{methods => }/supertrait-shadowing/unstable.off_normal.stderr (100%) rename tests/ui/{methods => }/supertrait-shadowing/unstable.off_shadowing.stderr (100%) rename tests/ui/{methods => }/supertrait-shadowing/unstable.on_normal.stderr (100%) rename tests/ui/{methods => }/supertrait-shadowing/unstable.rs (100%) diff --git a/tests/ui/methods/supertrait-shadowing/assoc-const.rs b/tests/ui/supertrait-shadowing/assoc-const.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/assoc-const.rs rename to tests/ui/supertrait-shadowing/assoc-const.rs diff --git a/tests/ui/methods/supertrait-shadowing/assoc-const2.rs b/tests/ui/supertrait-shadowing/assoc-const2.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/assoc-const2.rs rename to tests/ui/supertrait-shadowing/assoc-const2.rs diff --git a/tests/ui/methods/supertrait-shadowing/assoc-const2.run.stdout b/tests/ui/supertrait-shadowing/assoc-const2.run.stdout similarity index 100% rename from tests/ui/methods/supertrait-shadowing/assoc-const2.run.stdout rename to tests/ui/supertrait-shadowing/assoc-const2.run.stdout diff --git a/tests/ui/methods/supertrait-shadowing/assoc-type.rs b/tests/ui/supertrait-shadowing/assoc-type.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/assoc-type.rs rename to tests/ui/supertrait-shadowing/assoc-type.rs diff --git a/tests/ui/methods/supertrait-shadowing/assoc-type.run.stdout b/tests/ui/supertrait-shadowing/assoc-type.run.stdout similarity index 100% rename from tests/ui/methods/supertrait-shadowing/assoc-type.run.stdout rename to tests/ui/supertrait-shadowing/assoc-type.run.stdout diff --git a/tests/ui/methods/supertrait-shadowing/auxiliary/shadowed_stability.rs b/tests/ui/supertrait-shadowing/auxiliary/shadowed_stability.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/auxiliary/shadowed_stability.rs rename to tests/ui/supertrait-shadowing/auxiliary/shadowed_stability.rs diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs b/tests/ui/supertrait-shadowing/common-ancestor-2.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs rename to tests/ui/supertrait-shadowing/common-ancestor-2.rs diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr b/tests/ui/supertrait-shadowing/common-ancestor-2.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr rename to tests/ui/supertrait-shadowing/common-ancestor-2.stderr diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs b/tests/ui/supertrait-shadowing/common-ancestor-3.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs rename to tests/ui/supertrait-shadowing/common-ancestor-3.rs diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr b/tests/ui/supertrait-shadowing/common-ancestor-3.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr rename to tests/ui/supertrait-shadowing/common-ancestor-3.stderr diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor.rs b/tests/ui/supertrait-shadowing/common-ancestor.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/common-ancestor.rs rename to tests/ui/supertrait-shadowing/common-ancestor.rs diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor.stderr b/tests/ui/supertrait-shadowing/common-ancestor.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/common-ancestor.stderr rename to tests/ui/supertrait-shadowing/common-ancestor.stderr diff --git a/tests/ui/methods/supertrait-shadowing/definition-site.rs b/tests/ui/supertrait-shadowing/definition-site.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/definition-site.rs rename to tests/ui/supertrait-shadowing/definition-site.rs diff --git a/tests/ui/methods/supertrait-shadowing/definition-site.stderr b/tests/ui/supertrait-shadowing/definition-site.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/definition-site.stderr rename to tests/ui/supertrait-shadowing/definition-site.stderr diff --git a/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.rs b/tests/ui/supertrait-shadowing/false-subtrait-after-inference.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.rs rename to tests/ui/supertrait-shadowing/false-subtrait-after-inference.rs diff --git a/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.stderr b/tests/ui/supertrait-shadowing/false-subtrait-after-inference.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.stderr rename to tests/ui/supertrait-shadowing/false-subtrait-after-inference.stderr diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs b/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs rename to tests/ui/supertrait-shadowing/no-common-ancestor-2.rs diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr b/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr rename to tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs b/tests/ui/supertrait-shadowing/no-common-ancestor.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs rename to tests/ui/supertrait-shadowing/no-common-ancestor.rs diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr b/tests/ui/supertrait-shadowing/no-common-ancestor.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr rename to tests/ui/supertrait-shadowing/no-common-ancestor.stderr diff --git a/tests/ui/methods/supertrait-shadowing/out-of-scope.rs b/tests/ui/supertrait-shadowing/out-of-scope.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/out-of-scope.rs rename to tests/ui/supertrait-shadowing/out-of-scope.rs diff --git a/tests/ui/methods/supertrait-shadowing/trivially-false-subtrait.rs b/tests/ui/supertrait-shadowing/trivially-false-subtrait.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/trivially-false-subtrait.rs rename to tests/ui/supertrait-shadowing/trivially-false-subtrait.rs diff --git a/tests/ui/methods/supertrait-shadowing/type-dependent.rs b/tests/ui/supertrait-shadowing/type-dependent.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/type-dependent.rs rename to tests/ui/supertrait-shadowing/type-dependent.rs diff --git a/tests/ui/methods/supertrait-shadowing/unstable.off_normal.stderr b/tests/ui/supertrait-shadowing/unstable.off_normal.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.off_normal.stderr rename to tests/ui/supertrait-shadowing/unstable.off_normal.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.off_shadowing.stderr b/tests/ui/supertrait-shadowing/unstable.off_shadowing.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.off_shadowing.stderr rename to tests/ui/supertrait-shadowing/unstable.off_shadowing.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.on_normal.stderr b/tests/ui/supertrait-shadowing/unstable.on_normal.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.on_normal.stderr rename to tests/ui/supertrait-shadowing/unstable.on_normal.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.rs b/tests/ui/supertrait-shadowing/unstable.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.rs rename to tests/ui/supertrait-shadowing/unstable.rs From af3eaeeedd1812692940f6d8947ad3573680c432 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Thu, 4 Jun 2026 17:03:02 +0100 Subject: [PATCH 214/278] Address review feedback for tests --- tests/ui/README.md | 4 + tests/ui/supertrait-shadowing/assoc-const.rs | 15 ++- tests/ui/supertrait-shadowing/assoc-const2.rs | 32 ------- .../assoc-const2.run.stdout | 2 - .../supertrait-shadowing/assoc-type-fail.rs | 47 +++++++++ .../assoc-type-fail.stderr | 37 ++++++++ .../assoc-type-predicates.rs | 45 +++++++++ .../assoc-type-predicates.stderr | 32 +++++++ tests/ui/supertrait-shadowing/assoc-type.rs | 30 +++--- .../assoc-type.run.stdout | 4 - .../supertrait-shadowing/common-ancestor-2.rs | 32 ++++++- .../common-ancestor-2.stderr | 46 +++++++-- .../supertrait-shadowing/common-ancestor-3.rs | 41 +++++++- .../common-ancestor-3.stderr | 86 +++++++++++++++-- .../supertrait-shadowing/common-ancestor.rs | 25 ++++- .../common-ancestor.stderr | 40 ++++++-- .../supertrait-shadowing/definition-site.rs | 10 ++ .../definition-site.stderr | 60 +++++++++++- .../no-common-ancestor-2.rs | 39 +++++++- .../no-common-ancestor-2.stderr | 95 +++++++++++++++++-- .../no-common-ancestor.rs | 25 ++++- .../no-common-ancestor.stderr | 59 +++++++++++- tests/ui/supertrait-shadowing/out-of-scope.rs | 23 ++++- .../ui/supertrait-shadowing/type-dependent.rs | 29 +++++- 24 files changed, 744 insertions(+), 114 deletions(-) delete mode 100644 tests/ui/supertrait-shadowing/assoc-const2.rs delete mode 100644 tests/ui/supertrait-shadowing/assoc-const2.run.stdout create mode 100644 tests/ui/supertrait-shadowing/assoc-type-fail.rs create mode 100644 tests/ui/supertrait-shadowing/assoc-type-fail.stderr create mode 100644 tests/ui/supertrait-shadowing/assoc-type-predicates.rs create mode 100644 tests/ui/supertrait-shadowing/assoc-type-predicates.stderr delete mode 100644 tests/ui/supertrait-shadowing/assoc-type.run.stdout diff --git a/tests/ui/README.md b/tests/ui/README.md index 6ffef1a1cd699..d342f393d0550 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1351,6 +1351,10 @@ Generic collection of tests for suggestions, when no more specific directories a **FIXME**: Some overlap with `tests/ui/did_you_mean/`, that directory should probably be moved under here. +## `tests/ui/supertrait-shadowing/` + +Tests for supertrait item shadowing (RFC 3624). + ## `tests/ui/svh/`: Strict Version Hash Tests on the *Strict Version Hash* (SVH, also known as the "crate hash"). diff --git a/tests/ui/supertrait-shadowing/assoc-const.rs b/tests/ui/supertrait-shadowing/assoc-const.rs index 01d045b9fb74f..be0d990284b0c 100644 --- a/tests/ui/supertrait-shadowing/assoc-const.rs +++ b/tests/ui/supertrait-shadowing/assoc-const.rs @@ -1,6 +1,7 @@ //@ run-pass #![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] #![allow(dead_code)] trait A { @@ -11,12 +12,20 @@ impl A for T { } trait B: A { - const CONST: i32; + type const CONST: i32; } impl B for T { - const CONST: i32 = 2; + type const CONST: i32 = 2; } +trait C: B {} +impl C for T {} + fn main() { - assert_eq!(i32::CONST, 2) + assert_eq!(i32::CONST, 2); + generic::(); +} + +fn generic>() { + assert_eq!(T::CONST, 2); } diff --git a/tests/ui/supertrait-shadowing/assoc-const2.rs b/tests/ui/supertrait-shadowing/assoc-const2.rs deleted file mode 100644 index ebd0875eac8ea..0000000000000 --- a/tests/ui/supertrait-shadowing/assoc-const2.rs +++ /dev/null @@ -1,32 +0,0 @@ -//@ run-pass -//@ check-run-results - -#![feature(supertrait_item_shadowing)] -#![feature(min_generic_const_args)] -#![allow(dead_code)] - -trait A { - const CONST: i32; -} -impl A for T { - const CONST: i32 = 1; -} - -trait B: A { - type const CONST: i32; -} -impl B for T { - type const CONST: i32 = 2; -} - -trait C: B {} -impl C for T {} - -fn main() { - println!("{}", i32::CONST); - generic::(); -} - -fn generic>() { - println!("{}", T::CONST); -} diff --git a/tests/ui/supertrait-shadowing/assoc-const2.run.stdout b/tests/ui/supertrait-shadowing/assoc-const2.run.stdout deleted file mode 100644 index 51993f072d583..0000000000000 --- a/tests/ui/supertrait-shadowing/assoc-const2.run.stdout +++ /dev/null @@ -1,2 +0,0 @@ -2 -2 diff --git a/tests/ui/supertrait-shadowing/assoc-type-fail.rs b/tests/ui/supertrait-shadowing/assoc-type-fail.rs new file mode 100644 index 0000000000000..5df4edb7e96e2 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-fail.rs @@ -0,0 +1,47 @@ +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + type Assoc; +} +impl A for X { + type Assoc = i8; +} + +trait B: A { + type Assoc; +} +impl B for X { + type Assoc = i16; +} + +trait C: B {} +impl C for X {} + +fn main() { + b_unbound::(); + c_unbound::(); + + b_assoc_is_a::(); + //~^ ERROR type mismatch resolving `::Assoc == i8` + c_assoc_is_a::(); + //~^ ERROR type mismatch resolving `::Assoc == i8` +} + +fn b_unbound() { + let _ = size_of::(); +} + +fn c_unbound() { + let _ = size_of::(); +} + +fn b_assoc_is_a>() { + let _ = size_of::(); +} + +fn c_assoc_is_a>() { + let _ = size_of::(); +} diff --git a/tests/ui/supertrait-shadowing/assoc-type-fail.stderr b/tests/ui/supertrait-shadowing/assoc-type-fail.stderr new file mode 100644 index 0000000000000..36a9bd082aaf6 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-fail.stderr @@ -0,0 +1,37 @@ +error[E0271]: type mismatch resolving `::Assoc == i8` + --> $DIR/assoc-type-fail.rs:27:20 + | +LL | b_assoc_is_a::(); + | ^^^ type mismatch resolving `::Assoc == i8` + | +note: expected this to be `i8` + --> $DIR/assoc-type-fail.rs:17:18 + | +LL | type Assoc = i16; + | ^^^ +note: required by a bound in `b_assoc_is_a` + --> $DIR/assoc-type-fail.rs:41:22 + | +LL | fn b_assoc_is_a>() { + | ^^^^^^^^^^ required by this bound in `b_assoc_is_a` + +error[E0271]: type mismatch resolving `::Assoc == i8` + --> $DIR/assoc-type-fail.rs:29:20 + | +LL | c_assoc_is_a::(); + | ^^^ type mismatch resolving `::Assoc == i8` + | +note: expected this to be `i8` + --> $DIR/assoc-type-fail.rs:17:18 + | +LL | type Assoc = i16; + | ^^^ +note: required by a bound in `c_assoc_is_a` + --> $DIR/assoc-type-fail.rs:45:22 + | +LL | fn c_assoc_is_a>() { + | ^^^^^^^^^^ required by this bound in `c_assoc_is_a` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/supertrait-shadowing/assoc-type-predicates.rs b/tests/ui/supertrait-shadowing/assoc-type-predicates.rs new file mode 100644 index 0000000000000..55d2b84a50f9a --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-predicates.rs @@ -0,0 +1,45 @@ +//@ normalize-stderr: "assoc_type_predicates\[[^\]]+\]" -> "assoc_type_predicates[HASH]" + +#![feature(rustc_attrs)] +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +trait A { + type Assoc; +} +impl A for T { + type Assoc = i8; +} + +trait B: A { + type Assoc; +} +impl B for T { + type Assoc = i16; +} + +trait C: B {} +impl C for T {} + +#[rustc_dump_predicates] +fn a_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE A::Assoc + +#[rustc_dump_predicates] +fn b_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE B::Assoc + +#[rustc_dump_predicates] +fn c_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE B::Assoc + +fn main() {} diff --git a/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr b/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr new file mode 100644 index 0000000000000..be765e737a6e1 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr @@ -0,0 +1,32 @@ +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:25:1 + | +LL | fn a_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:4 ~ assoc_type_predicates[HASH]::A::Assoc) }, args: [T/#0], .. }, Term::Ty(i8)), bound_vars: [] } + +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:32:1 + | +LL | fn b_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:9 ~ assoc_type_predicates[HASH]::B::Assoc) }, args: [T/#0], .. }, Term::Ty(i16)), bound_vars: [] } + +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:39:1 + | +LL | fn c_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:9 ~ assoc_type_predicates[HASH]::B::Assoc) }, args: [T/#0], .. }, Term::Ty(i16)), bound_vars: [] } + +error: aborting due to 3 previous errors + diff --git a/tests/ui/supertrait-shadowing/assoc-type.rs b/tests/ui/supertrait-shadowing/assoc-type.rs index 7434fa7523119..9e9799397c571 100644 --- a/tests/ui/supertrait-shadowing/assoc-type.rs +++ b/tests/ui/supertrait-shadowing/assoc-type.rs @@ -7,17 +7,17 @@ use std::mem::size_of; trait A { - type T; + type Assoc; } impl A for T { - type T = i8; + type Assoc = i8; } trait B: A { - type T; + type Assoc; } impl B for T { - type T = i16; + type Assoc = i16; } trait C: B {} @@ -28,20 +28,26 @@ fn main() { generic2::(); generic3::(); generic4::(); + generic5::(); } -fn generic() { - println!("{}", size_of::()); +fn generic() { + assert_eq!(size_of::(), 2); } -fn generic2>() { - println!("{}", size_of::()); +fn generic2>() { + assert_eq!(size_of::(), 1); } -fn generic3>() { - println!("{}", size_of::()); +fn generic3>() { + assert_eq!(size_of::(), 2); } -fn generic4>() { - println!("{}", size_of::()); +fn generic4>() { + assert_eq!(size_of::(), 2); +} + +fn generic5() { + assert_eq!(size_of::<::Assoc>(), 1); + assert_eq!(size_of::<::Assoc>(), 2); } diff --git a/tests/ui/supertrait-shadowing/assoc-type.run.stdout b/tests/ui/supertrait-shadowing/assoc-type.run.stdout deleted file mode 100644 index 6f9071418f37e..0000000000000 --- a/tests/ui/supertrait-shadowing/assoc-type.run.stdout +++ /dev/null @@ -1,4 +0,0 @@ -2 -1 -2 -2 diff --git a/tests/ui/supertrait-shadowing/common-ancestor-2.rs b/tests/ui/supertrait-shadowing/common-ancestor-2.rs index ce56c25df19ea..908c5d2bf2199 100644 --- a/tests/ui/supertrait-shadowing/common-ancestor-2.rs +++ b/tests/ui/supertrait-shadowing/common-ancestor-2.rs @@ -1,33 +1,59 @@ //@ run-pass #![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] #![warn(resolving_to_items_shadowing_supertrait_items)] #![warn(shadowing_supertrait_items)] #![allow(dead_code)] +use std::mem::size_of; + trait A { fn hello(&self) -> &'static str { "A" } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; } -impl A for T {} trait B { fn hello(&self) -> &'static str { "B" } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; } -impl B for T {} trait C: A + B { fn hello(&self) -> &'static str { //~^ WARN trait item `hello` from `C` shadows identically named item "C" } + type Assoc; + //~^ WARN trait item `Assoc` from `C` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `C` shadows identically named item +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; } -impl C for T {} fn main() { assert_eq!(().hello(), "C"); //~^ WARN trait item `hello` from `C` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 4); + assert_eq!(T::CONST, 3); } diff --git a/tests/ui/supertrait-shadowing/common-ancestor-2.stderr b/tests/ui/supertrait-shadowing/common-ancestor-2.stderr index b0f61b46b6911..646726b835041 100644 --- a/tests/ui/supertrait-shadowing/common-ancestor-2.stderr +++ b/tests/ui/supertrait-shadowing/common-ancestor-2.stderr @@ -1,11 +1,11 @@ warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-2.rs:23:5 + --> $DIR/common-ancestor-2.rs:36:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: items from several supertraits are shadowed: `B` and `A` - --> $DIR/common-ancestor-2.rs:9:5 + --> $DIR/common-ancestor-2.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,24 +13,54 @@ LL | fn hello(&self) -> &'static str { LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor-2.rs:5:9 + --> $DIR/common-ancestor-2.rs:6:9 | LL | #![warn(shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +warning: trait item `Assoc` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-2.rs:40:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-2.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-2.rs:42:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-2.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-2.rs:31:19 + --> $DIR/common-ancestor-2.rs:51:19 | LL | assert_eq!(().hello(), "C"); | ^^^^^ | note: item from `C` shadows a supertrait item - --> $DIR/common-ancestor-2.rs:23:5 + --> $DIR/common-ancestor-2.rs:36:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: items from several supertraits are shadowed: `A` and `B` - --> $DIR/common-ancestor-2.rs:9:5 + --> $DIR/common-ancestor-2.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -38,10 +68,10 @@ LL | fn hello(&self) -> &'static str { LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor-2.rs:4:9 + --> $DIR/common-ancestor-2.rs:5:9 | LL | #![warn(resolving_to_items_shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 2 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui/supertrait-shadowing/common-ancestor-3.rs b/tests/ui/supertrait-shadowing/common-ancestor-3.rs index b29f3c8d014ec..ade23ca88aec2 100644 --- a/tests/ui/supertrait-shadowing/common-ancestor-3.rs +++ b/tests/ui/supertrait-shadowing/common-ancestor-3.rs @@ -1,31 +1,51 @@ //@ run-pass #![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] #![warn(resolving_to_items_shadowing_supertrait_items)] #![warn(shadowing_supertrait_items)] #![allow(dead_code)] +use std::mem::size_of; + trait A { fn hello(&self) -> &'static str { "A" } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; } -impl A for T {} trait B { fn hello(&self) -> &'static str { "B" } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; } -impl B for T {} trait C: A + B { fn hello(&self) -> &'static str { //~^ WARN trait item `hello` from `C` shadows identically named item "C" } + type Assoc; + //~^ WARN trait item `Assoc` from `C` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `C` shadows identically named item +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; } -impl C for T {} // `D` extends `C` which extends `B` and `A` @@ -34,10 +54,23 @@ trait D: C { //~^ WARN trait item `hello` from `D` shadows identically named item "D" } + type Assoc; + //~^ WARN trait item `Assoc` from `D` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `D` shadows identically named item +} +impl D for T { + type Assoc = i64; + type const CONST: i32 = 4; } -impl D for T {} fn main() { assert_eq!(().hello(), "D"); //~^ WARN trait item `hello` from `D` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 8); + assert_eq!(T::CONST, 4); } diff --git a/tests/ui/supertrait-shadowing/common-ancestor-3.stderr b/tests/ui/supertrait-shadowing/common-ancestor-3.stderr index 28fe7f72f94c3..62132832da4ba 100644 --- a/tests/ui/supertrait-shadowing/common-ancestor-3.stderr +++ b/tests/ui/supertrait-shadowing/common-ancestor-3.stderr @@ -1,11 +1,11 @@ warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:23:5 + --> $DIR/common-ancestor-3.rs:36:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: items from several supertraits are shadowed: `B` and `A` - --> $DIR/common-ancestor-3.rs:9:5 + --> $DIR/common-ancestor-3.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,19 +13,49 @@ LL | fn hello(&self) -> &'static str { LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor-3.rs:5:9 + --> $DIR/common-ancestor-3.rs:6:9 | LL | #![warn(shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +warning: trait item `Assoc` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:40:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:42:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + warning: trait item `hello` from `D` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:33:5 + --> $DIR/common-ancestor-3.rs:53:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: items from several supertraits are shadowed: `C`, `B`, and `A` - --> $DIR/common-ancestor-3.rs:9:5 + --> $DIR/common-ancestor-3.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,19 +66,55 @@ LL | fn hello(&self) -> &'static str { LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +warning: trait item `Assoc` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:57:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:59:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + warning: trait item `hello` from `D` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:41:19 + --> $DIR/common-ancestor-3.rs:68:19 | LL | assert_eq!(().hello(), "D"); | ^^^^^ | note: item from `D` shadows a supertrait item - --> $DIR/common-ancestor-3.rs:33:5 + --> $DIR/common-ancestor-3.rs:53:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: items from several supertraits are shadowed: `A`, `B`, and `C` - --> $DIR/common-ancestor-3.rs:9:5 + --> $DIR/common-ancestor-3.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -59,10 +125,10 @@ LL | fn hello(&self) -> &'static str { LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor-3.rs:4:9 + --> $DIR/common-ancestor-3.rs:5:9 | LL | #![warn(resolving_to_items_shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 3 warnings emitted +warning: 7 warnings emitted diff --git a/tests/ui/supertrait-shadowing/common-ancestor.rs b/tests/ui/supertrait-shadowing/common-ancestor.rs index b288d6e22b8c3..903f85957870b 100644 --- a/tests/ui/supertrait-shadowing/common-ancestor.rs +++ b/tests/ui/supertrait-shadowing/common-ancestor.rs @@ -1,26 +1,47 @@ //@ run-pass #![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] #![warn(resolving_to_items_shadowing_supertrait_items)] #![warn(shadowing_supertrait_items)] #![allow(dead_code)] +use std::mem::size_of; + trait A { fn hello(&self) -> &'static str { "A" } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; } -impl A for T {} trait B: A { fn hello(&self) -> &'static str { //~^ WARN trait item `hello` from `B` shadows identically named item "B" } + type Assoc; + //~^ WARN trait item `Assoc` from `B` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `B` shadows identically named item +} +impl B for T { + type Assoc = i16; + type const CONST: i32 = 2; } -impl B for T {} fn main() { assert_eq!(().hello(), "B"); //~^ WARN trait item `hello` from `B` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 2); + assert_eq!(T::CONST, 2); } diff --git a/tests/ui/supertrait-shadowing/common-ancestor.stderr b/tests/ui/supertrait-shadowing/common-ancestor.stderr index 9afedeab5e2c2..9b13537cf800c 100644 --- a/tests/ui/supertrait-shadowing/common-ancestor.stderr +++ b/tests/ui/supertrait-shadowing/common-ancestor.stderr @@ -1,41 +1,65 @@ warning: trait item `hello` from `B` shadows identically named item from supertrait - --> $DIR/common-ancestor.rs:16:5 + --> $DIR/common-ancestor.rs:24:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: item from `A` is shadowed by a subtrait item - --> $DIR/common-ancestor.rs:9:5 + --> $DIR/common-ancestor.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor.rs:5:9 + --> $DIR/common-ancestor.rs:6:9 | LL | #![warn(shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +warning: trait item `Assoc` from `B` shadows identically named item from supertrait + --> $DIR/common-ancestor.rs:28:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: item from `A` is shadowed by a subtrait item + --> $DIR/common-ancestor.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `B` shadows identically named item from supertrait + --> $DIR/common-ancestor.rs:30:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: item from `A` is shadowed by a subtrait item + --> $DIR/common-ancestor.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + warning: trait item `hello` from `B` shadows identically named item from supertrait - --> $DIR/common-ancestor.rs:24:19 + --> $DIR/common-ancestor.rs:39:19 | LL | assert_eq!(().hello(), "B"); | ^^^^^ | note: item from `B` shadows a supertrait item - --> $DIR/common-ancestor.rs:16:5 + --> $DIR/common-ancestor.rs:24:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: item from `A` is shadowed by a subtrait item - --> $DIR/common-ancestor.rs:9:5 + --> $DIR/common-ancestor.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor.rs:4:9 + --> $DIR/common-ancestor.rs:5:9 | LL | #![warn(resolving_to_items_shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 2 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui/supertrait-shadowing/definition-site.rs b/tests/ui/supertrait-shadowing/definition-site.rs index 248df032736f6..3309ee97e9f00 100644 --- a/tests/ui/supertrait-shadowing/definition-site.rs +++ b/tests/ui/supertrait-shadowing/definition-site.rs @@ -3,16 +3,26 @@ trait SuperSuper { fn method(); + const CONST: i32; + type Assoc; } trait Super: SuperSuper { fn method(); //~^ ERROR trait item `method` from `Super` shadows identically named item + const CONST: i32; + //~^ ERROR trait item `CONST` from `Super` shadows identically named item + type Assoc; + //~^ ERROR trait item `Assoc` from `Super` shadows identically named item } trait Sub: Super { fn method(); //~^ ERROR trait item `method` from `Sub` shadows identically named item + const CONST: i32; + //~^ ERROR trait item `CONST` from `Sub` shadows identically named item + type Assoc; + //~^ ERROR trait item `Assoc` from `Sub` shadows identically named item } fn main() {} diff --git a/tests/ui/supertrait-shadowing/definition-site.stderr b/tests/ui/supertrait-shadowing/definition-site.stderr index 1e35a753a9ebc..faf01cc711c6a 100644 --- a/tests/ui/supertrait-shadowing/definition-site.stderr +++ b/tests/ui/supertrait-shadowing/definition-site.stderr @@ -1,5 +1,5 @@ error: trait item `method` from `Super` shadows identically named item from supertrait - --> $DIR/definition-site.rs:9:5 + --> $DIR/definition-site.rs:11:5 | LL | fn method(); | ^^^^^^^^^^^^ @@ -15,8 +15,32 @@ note: the lint level is defined here LL | #![deny(shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: trait item `CONST` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:13:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:6:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + +error: trait item `Assoc` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:7:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + error: trait item `method` from `Sub` shadows identically named item from supertrait - --> $DIR/definition-site.rs:14:5 + --> $DIR/definition-site.rs:20:5 | LL | fn method(); | ^^^^^^^^^^^^ @@ -30,5 +54,35 @@ LL | fn method(); LL | fn method(); | ^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: trait item `CONST` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:22:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:6:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + +error: trait item `Assoc` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:24:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:7:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +error: aborting due to 6 previous errors diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs b/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs index b49476c7a4f68..957aabf5a51f8 100644 --- a/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs +++ b/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs @@ -1,25 +1,43 @@ #![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] + +use std::mem::size_of; trait A { fn hello(&self) -> &'static str { "A" } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; } -impl A for T {} trait B { fn hello(&self) -> &'static str { "B" } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; } -impl B for T {} trait C: A + B { fn hello(&self) -> &'static str { "C" } + type Assoc; + type const CONST: i32; +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; } -impl C for T {} // Since `D` is not a subtrait of `C`, // we have no obvious lower bound. @@ -28,10 +46,23 @@ trait D: B { fn hello(&self) -> &'static str { "D" } + type Assoc; + type const CONST: i32; +} +impl D for T { + type Assoc = i64; + type const CONST: i32 = 4; } -impl D for T {} fn main() { ().hello(); //~^ ERROR multiple applicable items in scope + check::<()>(); +} + +fn check() { + let _ = size_of::(); + //~^ ERROR ambiguous associated type `Assoc` in bounds of `T` + let _ = T::CONST; + //~^ ERROR multiple applicable items in scope } diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr b/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr index 745f61d00d660..987adcfbbe959 100644 --- a/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr +++ b/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr @@ -1,26 +1,26 @@ error[E0034]: multiple applicable items in scope - --> $DIR/no-common-ancestor-2.rs:35:8 + --> $DIR/no-common-ancestor-2.rs:58:8 | LL | ().hello(); | ^^^^^ multiple `hello` found | note: candidate #1 is defined in an impl of the trait `A` for the type `T` - --> $DIR/no-common-ancestor-2.rs:4:5 + --> $DIR/no-common-ancestor-2.rs:7:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: candidate #2 is defined in an impl of the trait `B` for the type `T` - --> $DIR/no-common-ancestor-2.rs:11:5 + --> $DIR/no-common-ancestor-2.rs:19:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: candidate #3 is defined in an impl of the trait `C` for the type `T` - --> $DIR/no-common-ancestor-2.rs:18:5 + --> $DIR/no-common-ancestor-2.rs:31:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: candidate #4 is defined in an impl of the trait `D` for the type `T` - --> $DIR/no-common-ancestor-2.rs:28:5 + --> $DIR/no-common-ancestor-2.rs:46:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,6 +45,87 @@ LL - ().hello(); LL + D::hello(&()); | -error: aborting due to 1 previous error +error[E0221]: ambiguous associated type `Assoc` in bounds of `T` + --> $DIR/no-common-ancestor-2.rs:64:23 + | +LL | type Assoc; + | ---------- ambiguous `Assoc` from `A` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `B` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `C` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `D` +... +LL | let _ = size_of::(); + | ^^^^^^^^ ambiguous associated type `Assoc` + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor-2.rs:66:16 + | +LL | let _ = T::CONST; + | ^^^^^ multiple `CONST` found + | +note: candidate #1 is defined in the trait `A` + --> $DIR/no-common-ancestor-2.rs:11:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `B` + --> $DIR/no-common-ancestor-2.rs:23:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #3 is defined in the trait `C` + --> $DIR/no-common-ancestor-2.rs:35:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ +note: candidate #4 is defined in the trait `D` + --> $DIR/no-common-ancestor-2.rs:50:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ +help: use fully-qualified syntax to disambiguate + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0034`. +Some errors have detailed explanations: E0034, E0221. +For more information about an error, try `rustc --explain E0034`. diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor.rs b/tests/ui/supertrait-shadowing/no-common-ancestor.rs index 03c822eb7375e..73209c015f8f5 100644 --- a/tests/ui/supertrait-shadowing/no-common-ancestor.rs +++ b/tests/ui/supertrait-shadowing/no-common-ancestor.rs @@ -1,20 +1,41 @@ #![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] + +use std::mem::size_of; trait A { fn hello(&self) -> &'static str { "A" } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; } -impl A for T {} trait B { fn hello(&self) -> &'static str { "B" } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; } -impl B for T {} fn main() { ().hello(); //~^ ERROR multiple applicable items in scope + check::<()>(); +} + +fn check() { + let _ = size_of::(); + //~^ ERROR ambiguous associated type `Assoc` in bounds of `T` + let _ = T::CONST; + //~^ ERROR multiple applicable items in scope } diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor.stderr b/tests/ui/supertrait-shadowing/no-common-ancestor.stderr index 29cf7ff1dbb11..78fd8f4670577 100644 --- a/tests/ui/supertrait-shadowing/no-common-ancestor.stderr +++ b/tests/ui/supertrait-shadowing/no-common-ancestor.stderr @@ -1,16 +1,16 @@ error[E0034]: multiple applicable items in scope - --> $DIR/no-common-ancestor.rs:18:8 + --> $DIR/no-common-ancestor.rs:31:8 | LL | ().hello(); | ^^^^^ multiple `hello` found | note: candidate #1 is defined in an impl of the trait `A` for the type `T` - --> $DIR/no-common-ancestor.rs:4:5 + --> $DIR/no-common-ancestor.rs:7:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: candidate #2 is defined in an impl of the trait `B` for the type `T` - --> $DIR/no-common-ancestor.rs:11:5 + --> $DIR/no-common-ancestor.rs:19:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,6 +25,55 @@ LL - ().hello(); LL + B::hello(&()); | -error: aborting due to 1 previous error +error[E0221]: ambiguous associated type `Assoc` in bounds of `T` + --> $DIR/no-common-ancestor.rs:37:23 + | +LL | type Assoc; + | ---------- ambiguous `Assoc` from `A` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `B` +... +LL | let _ = size_of::(); + | ^^^^^^^^ ambiguous associated type `Assoc` + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor.rs:39:16 + | +LL | let _ = T::CONST; + | ^^^^^ multiple `CONST` found + | +note: candidate #1 is defined in the trait `A` + --> $DIR/no-common-ancestor.rs:11:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `B` + --> $DIR/no-common-ancestor.rs:23:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +help: use fully-qualified syntax to disambiguate + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0034`. +Some errors have detailed explanations: E0034, E0221. +For more information about an error, try `rustc --explain E0034`. diff --git a/tests/ui/supertrait-shadowing/out-of-scope.rs b/tests/ui/supertrait-shadowing/out-of-scope.rs index 4112634399b78..7b2322843f1c3 100644 --- a/tests/ui/supertrait-shadowing/out-of-scope.rs +++ b/tests/ui/supertrait-shadowing/out-of-scope.rs @@ -1,23 +1,42 @@ //@ run-pass +#![feature(min_generic_const_args)] #![allow(dead_code)] +use std::mem::size_of; + mod out_of_scope { pub trait Subtrait: super::Supertrait { fn hello(&self) -> &'static str { "subtrait" } + type Assoc; + type const CONST: i32; + } + impl Subtrait for T { + type Assoc = i16; + type const CONST: i32 = 2; } - impl Subtrait for T {} } trait Supertrait { fn hello(&self) -> &'static str { "supertrait" } + type Assoc; + const CONST: i32; +} +impl Supertrait for T { + type Assoc = i8; + const CONST: i32 = 1; } -impl Supertrait for T {} fn main() { assert_eq!(().hello(), "supertrait"); + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 1); + assert_eq!(T::CONST, 1); } diff --git a/tests/ui/supertrait-shadowing/type-dependent.rs b/tests/ui/supertrait-shadowing/type-dependent.rs index 4a5af1d598812..75272d101dd29 100644 --- a/tests/ui/supertrait-shadowing/type-dependent.rs +++ b/tests/ui/supertrait-shadowing/type-dependent.rs @@ -1,28 +1,51 @@ //@ run-pass -// Makes sure we can shadow with type-dependent method syntax. +// Makes sure we can shadow with type-dependent associated item syntax. +#![feature(min_generic_const_args)] #![feature(supertrait_item_shadowing)] #![allow(dead_code)] +use std::mem::size_of; + trait A { fn hello() -> &'static str { "A" } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; } -impl A for T {} trait B: A { fn hello() -> &'static str { "B" } + type Assoc; + type const CONST: i32; +} +impl B for T { + type Assoc = i16; + type const CONST: i32 = 2; } -impl B for T {} fn foo() -> &'static str { T::hello() } +fn assoc() -> usize { + size_of::() +} + +fn konst() -> i32 { + T::CONST +} + fn main() { assert_eq!(foo::<()>(), "B"); + assert_eq!(assoc::<()>(), 2); + assert_eq!(konst::<()>(), 2); } From 9260f2ee472d875b7e9731d109fc76ba2b4f0202 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Fri, 26 Jun 2026 22:59:23 +0600 Subject: [PATCH 215/278] fix: don't produce invalid `Trait` in "consider further restricting this bound" suggestion when a bound already has generic args (e.g. `impl Pair`) the "consider further restricting this bound" suggestion would generate invalid syntax by appending a new `` producing `Pair`. the fix detects when the insertion point immediately follows a `>` and merges into the existing arg list instead producing the valid `Pair`. --- compiler/rustc_middle/src/ty/diagnostics.rs | 27 ++++++++++++++----- ...restrict-bound-already-has-generic-args.rs | 25 +++++++++++++++++ ...rict-bound-already-has-generic-args.stderr | 24 +++++++++++++++++ 3 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 tests/ui/suggestions/restrict-bound-already-has-generic-args.rs create mode 100644 tests/ui/suggestions/restrict-bound-already-has-generic-args.stderr diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index fadf1ee2ee384..ec9b73d415f93 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -398,15 +398,28 @@ pub fn suggest_constraining_type_params<'a>( } }; let constraint = constraint.join(" + "); - let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| { - let suggestion = if span_to_replace.is_some() { - constraint.clone() - } else if constraint.starts_with('<') { - constraint.clone() + let mut suggest_restrict = |span: Span, bound_list_non_empty, open_paren_sp| { + let (span, suggestion) = if span_to_replace.is_some() { + (span, constraint.clone()) + } else if let Some(rest) = constraint.strip_prefix('<') { + // `constraint` adds generic args; check whether the bound already has some. + // Naively inserting `` after `Foo` yields invalid `Foo`. + if span.lo() > BytePos(0) + && tcx + .sess + .source_map() + .span_to_snippet(span.with_lo(span.lo() - BytePos(1))) + .as_deref() + == Ok(">") + { + (span.with_lo(span.lo() - BytePos(1)), format!(", {rest}")) + } else { + (span, constraint.clone()) + } } else if bound_list_non_empty { - format!(" + {constraint}") + (span, format!(" + {constraint}")) } else { - format!(" {constraint}") + (span, format!(" {constraint}")) }; if let Some(open_paren_sp) = open_paren_sp { diff --git a/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs b/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs new file mode 100644 index 0000000000000..912d92fdc762f --- /dev/null +++ b/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs @@ -0,0 +1,25 @@ +// Regression test for https://github.com/rust-lang/rust/issues/142803. + +trait Pair { + type Left; + type Right; + + fn split(self) -> (Self::Left, Self::Right); +} + +impl Pair for (A, B) { + type Left = A; + type Right = B; + + fn split(self) -> (Self::Left, Self::Right) { + self + } +} + +fn frob(pair: impl Pair) -> impl Pair { + //~^ ERROR type mismatch + let (left, right) = pair.split(); + (left, right) +} + +fn main() {} diff --git a/tests/ui/suggestions/restrict-bound-already-has-generic-args.stderr b/tests/ui/suggestions/restrict-bound-already-has-generic-args.stderr new file mode 100644 index 0000000000000..a284f2adb366c --- /dev/null +++ b/tests/ui/suggestions/restrict-bound-already-has-generic-args.stderr @@ -0,0 +1,24 @@ +error[E0271]: type mismatch resolving `<(A, as Pair>::Right) as Pair>::Right == B` + --> $DIR/restrict-bound-already-has-generic-args.rs:19:45 + | +LL | fn frob(pair: impl Pair) -> impl Pair { + | - expected this type parameter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<(A, as Pair>::Right) as Pair>::Right == B` +... +LL | (left, right) + | ------------- return type was inferred to be `(A, as Pair>::Right)` here + | +note: expected this to be `B` + --> $DIR/restrict-bound-already-has-generic-args.rs:12:18 + | +LL | type Right = B; + | ^ + = note: expected type parameter `B` + found associated type ` as Pair>::Right` +help: consider further restricting this bound + | +LL | fn frob(pair: impl Pair) -> impl Pair { + | +++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. From 247e6e1ad788079a8f78875032207e98f7f7015d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 26 Jun 2026 18:12:18 +0200 Subject: [PATCH 216/278] Add missing links in integer docs --- library/core/src/num/int_macros.rs | 46 ++++++++++++++--------------- library/core/src/num/uint_macros.rs | 4 +-- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 34f16184e4ec3..ade9d294e8592 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -983,7 +983,7 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is when one divides `MIN / -1` on a signed type (where - /// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value + /// [`MIN`](Self::MIN) is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value /// that is too large to represent in the type. /// /// # Examples @@ -1050,7 +1050,7 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is when one divides `MIN / -1` on a signed type (where - /// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value + /// [`MIN`](Self::MIN) is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value /// that is too large to represent in the type. /// /// # Examples @@ -1223,7 +1223,7 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is `x % y` for `MIN / -1` on a - /// signed type (where `MIN` is the negative minimal value), which is invalid due to implementation artifacts. + /// signed type (where [`MIN`](Self::MIN) is the negative minimal value), which is invalid due to implementation artifacts. /// /// # Examples /// @@ -1289,7 +1289,7 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is `x % y` for `MIN / -1` on a - /// signed type (where `MIN` is the negative minimal value), which is invalid due to implementation artifacts. + /// signed type (where [`MIN`](Self::MIN) is the negative minimal value), which is invalid due to implementation artifacts. /// /// # Examples /// @@ -2259,8 +2259,8 @@ macro_rules! int_impl { /// boundary of the type. /// /// The only case where such wrapping can occur is when one divides `MIN / -1` on a signed type (where - /// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value - /// that is too large to represent in the type. In such a case, this function returns `MIN` itself. + /// [`MIN`](Self::MIN) is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value + /// that is too large to represent in the type. In such a case, this function returns [`MIN`](Self::MIN) itself. /// /// # Panics /// @@ -2284,9 +2284,9 @@ macro_rules! int_impl { /// Wrapping Euclidean division. Computes `self.div_euclid(rhs)`, /// wrapping around at the boundary of the type. /// - /// Wrapping will only occur in `MIN / -1` on a signed type (where `MIN` is the negative minimal value + /// Wrapping will only occur in `MIN / -1` on a signed type (where [`MIN`](Self::MIN) is the negative minimal value /// for the type). This is equivalent to `-MIN`, a positive value that is too large to represent in the - /// type. In this case, this method returns `MIN` itself. + /// type. In this case, this method returns [`MIN`](Self::MIN) itself. /// /// # Panics /// @@ -2311,7 +2311,7 @@ macro_rules! int_impl { /// boundary of the type. /// /// Such wrap-around never actually occurs mathematically; implementation artifacts make `x % y` - /// invalid for `MIN / -1` on a signed type (where `MIN` is the negative minimal value). In such a case, + /// invalid for `MIN / -1` on a signed type (where [`MIN`](Self::MIN) is the negative minimal value). In such a case, /// this function returns `0`. /// /// # Panics @@ -2336,8 +2336,8 @@ macro_rules! int_impl { /// Wrapping Euclidean remainder. Computes `self.rem_euclid(rhs)`, wrapping around /// at the boundary of the type. /// - /// Wrapping will only occur in `MIN % -1` on a signed type (where `MIN` is the negative minimal value - /// for the type). In this case, this method returns 0. + /// Wrapping will only occur in `MIN % -1` on a signed type (where [`MIN`](Self::MIN) is + /// the negative minimal value for the type). In this case, this method returns 0. /// /// # Panics /// @@ -2361,9 +2361,9 @@ macro_rules! int_impl { /// Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary /// of the type. /// - /// The only case where such wrapping can occur is when one negates `MIN` on a signed type (where `MIN` + /// The only case where such wrapping can occur is when one negates [`MIN`](Self::MIN) on a signed type (where [`MIN`](Self::MIN) /// is the negative minimal value for the type); this is a positive value that is too large to represent - /// in the type. In such a case, this function returns `MIN` itself. + /// in the type. In such a case, this function returns [`MIN`](Self::MIN) itself. /// /// # Examples /// @@ -2460,7 +2460,7 @@ macro_rules! int_impl { /// /// The only case where such wrapping can occur is when one takes the absolute value of the negative /// minimal value for the type; this is a positive value that is too large to represent in the type. In - /// such a case, this function returns `MIN` itself. + /// such a case, this function returns [`MIN`](Self::MIN) itself. /// /// # Examples /// @@ -2772,7 +2772,7 @@ macro_rules! int_impl { /// /// # Examples /// - /// Please note that this example is shared among integer types, which is why `i32` is used. + /// Please note that this example is shared among integer types, which is why [`i32`] is used. /// /// ``` /// #![feature(signed_bigint_helpers)] @@ -2950,7 +2950,7 @@ macro_rules! int_impl { /// Negates self, overflowing if this is equal to the minimum value. /// /// Returns a tuple of the negated version of self along with a boolean indicating whether an overflow - /// happened. If `self` is the minimum value (e.g., `i32::MIN` for values of type `i32`), then the + /// happened. If `self` is the minimum value (e.g., [`i32::MIN`] for values of type [`i32`]), then the /// minimum value will be returned again and `true` will be returned for an overflow happening. /// /// # Examples @@ -3020,7 +3020,7 @@ macro_rules! int_impl { /// /// Returns a tuple of the absolute version of self along with a boolean indicating whether an overflow /// happened. If self is the minimum value - #[doc = concat!("(e.g., ", stringify!($SelfT), "::MIN for values of type ", stringify!($SelfT), "),")] + #[doc = concat!("(e.g., [`", stringify!($SelfT), "::MIN`] for values of type [`", stringify!($SelfT), "`]),")] /// then the minimum value will be returned again and true will be returned /// for an overflow happening. /// @@ -3177,7 +3177,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3215,7 +3215,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` and + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] and /// `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3262,7 +3262,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3304,7 +3304,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3441,8 +3441,8 @@ macro_rules! int_impl { /// rounded down. /// /// This method might not be optimized owing to implementation details; - /// `ilog2` can produce results more efficiently for base 2, and `ilog10` - /// can produce results more efficiently for base 10. + /// [`ilog2`][Self::ilog2] can produce results more efficiently for base 2, + /// and [`ilog10`](Self::ilog10) can produce results more efficiently for base 10. /// /// # Panics /// diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 4053134d89d43..cfb5eb54fb6bc 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1721,8 +1721,8 @@ macro_rules! uint_impl { /// rounded down. /// /// This method might not be optimized owing to implementation details; - /// `ilog2` can produce results more efficiently for base 2, and `ilog10` - /// can produce results more efficiently for base 10. + /// [`ilog2`](Self::ilog2) can produce results more efficiently for base 2, + /// and [`ilog10`](Self::ilog10) can produce results more efficiently for base 10. /// /// # Panics /// From 53cc2f81d4533150ad42b75533362ca599c36303 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 26 Jun 2026 21:04:44 +0100 Subject: [PATCH 217/278] std: fix xous dns ipv6 parsing off-by-one the ipv6 arm read the 16-byte address from offset+1 instead of offset, unlike the ipv4 arm. this mis-parsed every ipv6 result and let the slice reach offset+17 while the bounds check only guards offset+16, so a malformed dns response could index past the 4096-byte buffer and panic. --- library/std/src/sys/net/connection/xous/dns.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/net/connection/xous/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs index b139376f59768..0ed15b28c36e1 100644 --- a/library/std/src/sys/net/connection/xous/dns.rs +++ b/library/std/src/sys/net/connection/xous/dns.rs @@ -43,9 +43,8 @@ impl Iterator for LookupHost { return None; } let mut new_addr = [0u8; 16]; - for (src, octet) in self.data.0[(self.offset + 1)..(self.offset + 16 + 1)] - .iter() - .zip(new_addr.iter_mut()) + for (src, octet) in + self.data.0[self.offset..(self.offset + 16)].iter().zip(new_addr.iter_mut()) { *octet = *src; } From 87f3254ca008c05b1cf1b55a73f95b67717bdc1a Mon Sep 17 00:00:00 2001 From: Peter Lyons Kehl Date: Sun, 21 Jun 2026 18:59:01 -0700 Subject: [PATCH 218/278] Cross-referencing tuple_trait tracking issue, source and the Unstable Book. --- library/core/src/marker.rs | 2 +- src/doc/unstable-book/src/library-features/tuple-trait.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 src/doc/unstable-book/src/library-features/tuple-trait.md diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 744bd41d1139e..45b8b266a2671 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1066,7 +1066,7 @@ pub const trait Destruct: PointeeSized {} /// /// The implementation of this trait is built-in and cannot be implemented /// for any user type. -#[unstable(feature = "tuple_trait", issue = "none")] +#[unstable(feature = "tuple_trait", issue = "157987")] #[lang = "tuple_trait"] #[diagnostic::on_unimplemented(message = "`{Self}` is not a tuple")] #[rustc_deny_explicit_impl] diff --git a/src/doc/unstable-book/src/library-features/tuple-trait.md b/src/doc/unstable-book/src/library-features/tuple-trait.md new file mode 100644 index 0000000000000..5521850f12bb6 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/tuple-trait.md @@ -0,0 +1,5 @@ +# `tuple_trait` + +The tracking issue for this feature is [#157987]. + +[#157987]: https://github.com/rust-lang/rust/issues/157987 From 2592b4176d62f5310957cc5595d2ab0747326d3c Mon Sep 17 00:00:00 2001 From: Emmanuel Ugwu Date: Fri, 26 Jun 2026 22:34:26 +0100 Subject: [PATCH 219/278] Add test to confirm usage of unstable foreign type error Signed-off-by: Emmanuel Ugwu --- tests/ui/lint/auxiliary/lint_stability.rs | 6 ++++++ tests/ui/lint/lint-stability.rs | 7 +++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/ui/lint/auxiliary/lint_stability.rs b/tests/ui/lint/auxiliary/lint_stability.rs index 99c29dcdda677..bc2d47aebbc29 100644 --- a/tests/ui/lint/auxiliary/lint_stability.rs +++ b/tests/ui/lint/auxiliary/lint_stability.rs @@ -1,5 +1,6 @@ #![crate_name="lint_stability"] #![crate_type = "lib"] +#![feature(extern_types)] #![feature(staged_api)] #![feature(associated_type_defaults)] #![stable(feature = "lint_stability", since = "1.0.0")] @@ -186,3 +187,8 @@ macro_rules! macro_test_arg { macro_rules! macro_test_arg_nested { ($func:ident) => (macro_test_arg!($func())); } + +extern "C" { + #[unstable(feature = "unstable_test_feature", issue = "none")] + pub type UnstableForeignType; +} diff --git a/tests/ui/lint/lint-stability.rs b/tests/ui/lint/lint-stability.rs index 21009b1e61db4..c8712397189c7 100644 --- a/tests/ui/lint/lint-stability.rs +++ b/tests/ui/lint/lint-stability.rs @@ -9,10 +9,6 @@ #![feature(extern_types)] #![stable(feature = "rust1", since = "1.0.0")] -extern "C" { - #[unstable(feature = "fn_static", issue = "none")] - type Ty; -} #[macro_use] extern crate lint_stability; @@ -22,6 +18,9 @@ mod cross_crate { use lint_stability::*; + fn test_foreign_type(_: &mut UnstableForeignType) { //~ ERROR use of unstable library feature + } + fn test() { type Foo = MethodTester; let foo = MethodTester; From e7da124cc26ff87439806f7b72312a96577eb02d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 16 Jun 2026 08:12:59 +1000 Subject: [PATCH 220/278] Improve documentation on lint passes To explain some non-obvious things that took me some time to work out. --- compiler/rustc_lint/src/context.rs | 48 ++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 348f1f55d3215..70638f3648552 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -46,20 +46,38 @@ type LateLintPassFactory = Box Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync>; /// Information about the registered lints. +// +// About the pass factories: these should only be called once, but since we +// want to avoid locks or interior mutability, we don't enforce this. Lints +// should, in theory, be compatible with being constructed more than once, +// though not necessarily in a sane manner. This is safe though. pub struct LintStore { /// Registered lints. lints: Vec<&'static Lint>, - /// Constructor functions for each variety of lint pass. + /// This lint pass kind is softly deprecated. It misses expanded code and has caused a few + /// errors in the past. Currently, it is only used in Clippy. New implementations + /// should avoid using this interface, as it might be removed in the future. /// - /// These should only be called once, but since we want to avoid locks or - /// interior mutability, we don't enforce this (and lints should, in theory, - /// be compatible with being constructed more than once, though not - /// necessarily in a sane manner. This is safe though.) + /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838) + /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518) pub pre_expansion_passes: Vec, + + /// These lint passes run on AST nodes. pub early_passes: Vec, + + /// These lint passes run on HIR nodes. Each one processes an entire crate. They don't benefit + /// from incremental compilation. `late_module_passes` should be used in preference where + /// possible; only use `late_passes` for lints that implement `check_crate` and/or + /// `check_crate_post` and accumulate cross-module state. + /// + /// The exception is Clippy, which uses `late_passes` for all late lint passes. It needs + /// `check_crate`/`check_crate_post` for some of its lints and uses late lint passes throughout + /// for consistency. This is ok because Clippy isn't wired for incremental compilation. pub late_passes: Vec, - /// This is unique in that we construct them per-module, so not once. + + /// These lint passes run on HIR nodes, and are constructed per-module (i.e. multiple times). + /// They benefit from incremental compilation. pub late_module_passes: Vec, /// Lints indexed by name. @@ -166,24 +184,22 @@ impl LintStore { self.lint_groups.keys().copied() } - pub fn register_early_pass(&mut self, pass: EarlyLintPassFactory) { - self.early_passes.push(pass); - } - - /// This lint pass is softly deprecated. It misses expanded code and has caused a few - /// errors in the past. Currently, it is only used in Clippy. New implementations - /// should avoid using this interface, as it might be removed in the future. - /// - /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838) - /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518) + /// See the comment on `LintStore::pre_expansion_passes`. pub fn register_pre_expansion_pass(&mut self, pass: EarlyLintPassFactory) { self.pre_expansion_passes.push(pass); } + /// See the comment on `LintStore::early_passes`. + pub fn register_early_pass(&mut self, pass: EarlyLintPassFactory) { + self.early_passes.push(pass); + } + + /// See the comment on `LintStore::late_passes`. pub fn register_late_pass(&mut self, pass: LateLintPassFactory) { self.late_passes.push(pass); } + /// See the comment on `LintStore::late_module_passes`. pub fn register_late_mod_pass(&mut self, pass: LateLintPassFactory) { self.late_module_passes.push(pass); } From e983dd7aff5c6d24583e1f4492c8a4db52b3f4f3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 16 Jun 2026 11:14:36 +1000 Subject: [PATCH 221/278] Fix a comment `EarlyLintPassObjects` is an old name. --- compiler/rustc_lint/src/passes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 5c101048b8010..12cf58907566d 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -203,7 +203,7 @@ macro_rules! expand_combined_early_lint_pass_methods { /// Combines multiple lints passes into a single lint pass, at compile time, /// for maximum speed. Each `check_foo` method in `$methods` within this pass /// simply calls `check_foo` once per `$pass`. Compare with -/// `EarlyLintPassObjects`, which is similar, but combines lint passes at +/// `RuntimeCombinedEarlyLintPass`, which is similar, but combines lint passes at /// runtime. #[macro_export] macro_rules! declare_combined_early_lint_pass { From 969db62833ad1c623110de0b71baf40a2028813d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 18 Jun 2026 15:17:00 +1000 Subject: [PATCH 222/278] Rename `*CombinedModuleLateLintPass` As `*CombinedLateLintModPass`, because that matches things like `late_lint_mod`. --- compiler/rustc_lint/src/lib.rs | 12 ++++++------ src/doc/rustc-dev-guide/src/diagnostics/lintstore.md | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 9b87da79ee155..4d8194ba3e140 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -154,7 +154,7 @@ pub fn provide(providers: &mut Providers) { } fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { - late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new()); + late_lint_mod(tcx, module_def_id, BuiltinCombinedLateLintModPass::new()); } early_lint_methods!( @@ -207,7 +207,7 @@ early_lint_methods!( late_lint_methods!( declare_combined_late_lint_pass, [ - BuiltinCombinedModuleLateLintPass, + BuiltinCombinedLateLintModPass, [ ForLoopsOverFallibles: ForLoopsOverFallibles, DefaultCouldBeDerived: DefaultCouldBeDerived, @@ -279,7 +279,7 @@ late_lint_methods!( late_lint_methods!( declare_combined_late_lint_pass, [ - InternalCombinedModuleLateLintPass, + InternalCombinedLateLintModPass, [ DefaultHashTypes: DefaultHashTypes, QueryStability: QueryStability, @@ -317,7 +317,7 @@ fn register_builtins(store: &mut LintStore) { store.register_lints(&BuiltinCombinedPreExpansionLintPass::lint_vec()); store.register_lints(&BuiltinCombinedEarlyLintPass::lint_vec()); - store.register_lints(&BuiltinCombinedModuleLateLintPass::lint_vec()); + store.register_lints(&BuiltinCombinedLateLintModPass::lint_vec()); store.register_lints(&foreign_modules::lint_vec()); store.register_lints(&hardwired::lint_vec()); @@ -700,8 +700,8 @@ fn register_internals(store: &mut LintStore) { store.register_lints(&InternalCombinedEarlyLintPass::lint_vec()); store.register_early_pass(Box::new(|| Box::new(InternalCombinedEarlyLintPass::new()))); - store.register_lints(&InternalCombinedModuleLateLintPass::lint_vec()); - store.register_late_mod_pass(Box::new(|_| Box::new(InternalCombinedModuleLateLintPass::new()))); + store.register_lints(&InternalCombinedLateLintModPass::lint_vec()); + store.register_late_mod_pass(Box::new(|_| Box::new(InternalCombinedLateLintModPass::new()))); store.register_group( false, diff --git a/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md b/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md index 11a7a573f38b6..baae63e96bdc7 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md @@ -97,7 +97,7 @@ The best way for drivers to get access to this is by overriding the Within the compiler, for performance reasons, we usually do not register dozens of lint passes. Instead, we have a single lint pass of each variety (e.g., -`BuiltinCombinedModuleLateLintPass`) which will internally call all of the +`BuiltinCombinedLateLintModPass`) which will internally call all of the individual lint passes; this is because then we get the benefits of static over dynamic dispatch for each of the (often empty) trait methods. From cd8d703f1ff9afa26cc88e8bf1496e81c79774e2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 18 Jun 2026 15:30:20 +1000 Subject: [PATCH 223/278] Rename some lint pass things We generally write `lint_pass`/`LintPass`. This commit renames some things that only use `pass`/`Pass`. And also some `s/module/mod` changes, too. --- compiler/rustc_lint/src/context.rs | 46 ++++++++++++------------ compiler/rustc_lint/src/early.rs | 4 +-- compiler/rustc_lint/src/late.rs | 4 +-- compiler/rustc_lint/src/lib.rs | 6 ++-- src/tools/clippy/clippy_lints/src/lib.rs | 8 +++-- 5 files changed, 36 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 70638f3648552..040236297270a 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -61,24 +61,24 @@ pub struct LintStore { /// /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838) /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518) - pub pre_expansion_passes: Vec, + pub pre_expansion_lint_passes: Vec, /// These lint passes run on AST nodes. - pub early_passes: Vec, + pub early_lint_passes: Vec, /// These lint passes run on HIR nodes. Each one processes an entire crate. They don't benefit - /// from incremental compilation. `late_module_passes` should be used in preference where - /// possible; only use `late_passes` for lints that implement `check_crate` and/or + /// from incremental compilation. `late_lint_mod_passes` should be used in preference where + /// possible; only use `late_lint_passes` for lints that implement `check_crate` and/or /// `check_crate_post` and accumulate cross-module state. /// - /// The exception is Clippy, which uses `late_passes` for all late lint passes. It needs + /// The exception is Clippy, which uses `late_lint_passes` for all late lint passes. It needs /// `check_crate`/`check_crate_post` for some of its lints and uses late lint passes throughout /// for consistency. This is ok because Clippy isn't wired for incremental compilation. - pub late_passes: Vec, + pub late_lint_passes: Vec, /// These lint passes run on HIR nodes, and are constructed per-module (i.e. multiple times). /// They benefit from incremental compilation. - pub late_module_passes: Vec, + pub late_lint_mod_passes: Vec, /// Lints indexed by name. by_name: UnordMap, @@ -154,10 +154,10 @@ impl LintStore { pub fn new() -> LintStore { LintStore { lints: vec![], - pre_expansion_passes: vec![], - early_passes: vec![], - late_passes: vec![], - late_module_passes: vec![], + pre_expansion_lint_passes: vec![], + early_lint_passes: vec![], + late_lint_passes: vec![], + late_lint_mod_passes: vec![], by_name: Default::default(), lint_groups: Default::default(), } @@ -184,24 +184,24 @@ impl LintStore { self.lint_groups.keys().copied() } - /// See the comment on `LintStore::pre_expansion_passes`. - pub fn register_pre_expansion_pass(&mut self, pass: EarlyLintPassFactory) { - self.pre_expansion_passes.push(pass); + /// See the comment on `LintStore::pre_expansion_lint_passes`. + pub fn register_pre_expansion_lint_pass(&mut self, pass: EarlyLintPassFactory) { + self.pre_expansion_lint_passes.push(pass); } - /// See the comment on `LintStore::early_passes`. - pub fn register_early_pass(&mut self, pass: EarlyLintPassFactory) { - self.early_passes.push(pass); + /// See the comment on `LintStore::early_lint_passes`. + pub fn register_early_lint_pass(&mut self, pass: EarlyLintPassFactory) { + self.early_lint_passes.push(pass); } - /// See the comment on `LintStore::late_passes`. - pub fn register_late_pass(&mut self, pass: LateLintPassFactory) { - self.late_passes.push(pass); + /// See the comment on `LintStore::late_lint_passes`. + pub fn register_late_lint_pass(&mut self, pass: LateLintPassFactory) { + self.late_lint_passes.push(pass); } - /// See the comment on `LintStore::late_module_passes`. - pub fn register_late_mod_pass(&mut self, pass: LateLintPassFactory) { - self.late_module_passes.push(pass); + /// See the comment on `LintStore::late_lint_mod_passes`. + pub fn register_late_lint_mod_pass(&mut self, pass: LateLintPassFactory) { + self.late_lint_mod_passes.push(pass); } /// Helper method for register_early/late_pass diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 93d150c79d0df..9c123f67abd5f 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -329,11 +329,11 @@ pub fn check_ast_node<'a>( let context = if pre_expansion { let builtin_lints = crate::BuiltinCombinedPreExpansionLintPass::new(); - let passes = &lint_store.pre_expansion_passes; + let passes = &lint_store.pre_expansion_lint_passes; run_passes(check_node, context, builtin_lints, passes) } else { let builtin_lints = crate::BuiltinCombinedEarlyLintPass::new(); - let passes = &lint_store.early_passes; + let passes = &lint_store.early_lint_passes; run_passes(check_node, context, builtin_lints, passes) }; diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index fea6b2ab2f9bd..c17fe6f2e510f 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -355,7 +355,7 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( // `builtin_lints` directly rather than bundling it up into the // `RuntimeCombinedLateLintPass`. let mut passes: Vec<_> = unerased_lint_store(tcx.sess) - .late_module_passes + .late_lint_mod_passes .iter() .map(|mk_pass| mk_pass(tcx)) .filter(|pass| is_lint_pass_required(skippable_lints, &pass.get_lints())) @@ -403,7 +403,7 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { // Note: `passes` is often empty after filtering. let passes: Vec<_> = unerased_lint_store(tcx.sess) - .late_passes + .late_lint_passes .iter() .map(|mk_pass| mk_pass(tcx)) .filter(|pass| is_lint_pass_required(skippable_lints, &pass.get_lints())) diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 4d8194ba3e140..72c9ba0e5afbf 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -698,10 +698,12 @@ fn register_builtins(store: &mut LintStore) { fn register_internals(store: &mut LintStore) { store.register_lints(&InternalCombinedEarlyLintPass::lint_vec()); - store.register_early_pass(Box::new(|| Box::new(InternalCombinedEarlyLintPass::new()))); + store.register_early_lint_pass(Box::new(|| Box::new(InternalCombinedEarlyLintPass::new()))); store.register_lints(&InternalCombinedLateLintModPass::lint_vec()); - store.register_late_mod_pass(Box::new(|_| Box::new(InternalCombinedLateLintModPass::new()))); + store.register_late_lint_mod_pass(Box::new(|_| { + Box::new(InternalCombinedLateLintModPass::new()) + })); store.register_group( false, diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index d002df267027d..913be07ef326c 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -454,7 +454,9 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. // Due to the architecture of the compiler, currently `cfg_attr` attributes on crate // level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. - store.register_pre_expansion_pass(Box::new(move || Box::new(attrs::EarlyAttributes::new(conf)))); + store.register_pre_expansion_lint_pass( + Box::new(move || Box::new(attrs::EarlyAttributes::new(conf))) + ); let format_args_storage = FormatArgsStorage::default(); let attr_storage = AttrStorage::default(); @@ -462,12 +464,12 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co { let format_args = format_args_storage.clone(); let attrs = attr_storage.clone(); - store.early_passes.push(Box::new(move || { + store.early_lint_passes.push(Box::new(move || { Box::new(CombinedEarlyLintPass::new(conf, format_args.clone(), attrs.clone())) })); } - store.late_passes.push(Box::new(move |tcx: TyCtxt<'_>| { + store.late_lint_passes.push(Box::new(move |tcx: TyCtxt<'_>| { let skippable_lints = tcx.skippable_lints(()); let is_active = |lints: &rustc_lint::LintVec| is_lint_pass_required(skippable_lints, lints); Box::new(CombinedLateLintPass::new( From 879aba106a8cb86267ef498fc7e64346b90b5514 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 18 Jun 2026 15:43:57 +1000 Subject: [PATCH 224/278] Make `LintStore::*_lint_passes` non-`pub` There are `register_*_lint_pass` methods, might as well go through them. --- compiler/rustc_lint/src/context.rs | 8 ++++---- src/tools/clippy/clippy_lints/src/lib.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 040236297270a..e992f1ed385e7 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -61,10 +61,10 @@ pub struct LintStore { /// /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838) /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518) - pub pre_expansion_lint_passes: Vec, + pub(crate) pre_expansion_lint_passes: Vec, /// These lint passes run on AST nodes. - pub early_lint_passes: Vec, + pub(crate) early_lint_passes: Vec, /// These lint passes run on HIR nodes. Each one processes an entire crate. They don't benefit /// from incremental compilation. `late_lint_mod_passes` should be used in preference where @@ -74,11 +74,11 @@ pub struct LintStore { /// The exception is Clippy, which uses `late_lint_passes` for all late lint passes. It needs /// `check_crate`/`check_crate_post` for some of its lints and uses late lint passes throughout /// for consistency. This is ok because Clippy isn't wired for incremental compilation. - pub late_lint_passes: Vec, + pub(crate) late_lint_passes: Vec, /// These lint passes run on HIR nodes, and are constructed per-module (i.e. multiple times). /// They benefit from incremental compilation. - pub late_lint_mod_passes: Vec, + pub(crate) late_lint_mod_passes: Vec, /// Lints indexed by name. by_name: UnordMap, diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 913be07ef326c..a65b27f550995 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -464,12 +464,12 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co { let format_args = format_args_storage.clone(); let attrs = attr_storage.clone(); - store.early_lint_passes.push(Box::new(move || { + store.register_early_lint_pass(Box::new(move || { Box::new(CombinedEarlyLintPass::new(conf, format_args.clone(), attrs.clone())) })); } - store.late_lint_passes.push(Box::new(move |tcx: TyCtxt<'_>| { + store.register_late_lint_pass(Box::new(move |tcx: TyCtxt<'_>| { let skippable_lints = tcx.skippable_lints(()); let is_active = |lints: &rustc_lint::LintVec| is_lint_pass_required(skippable_lints, lints); Box::new(CombinedLateLintPass::new( From b80ff2c0216c1965ed84ba0ec60df299da3d121e Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 26 Jun 2026 21:54:38 -0400 Subject: [PATCH 225/278] Update cargo submodules --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index a595d0da21f22..a335d47ff8036 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit a595d0da21f228b7fdae64d3d5c0e527ea66bb59 +Subproject commit a335d47ff8036918d3d548dabd513dc0444096a9 From ca28237682237165e6313fd77d7cb0bd1ba5ae0e Mon Sep 17 00:00:00 2001 From: zedddie Date: Sat, 27 Jun 2026 03:00:38 +0200 Subject: [PATCH 226/278] move batch --- .../resolve-method-with-missing-assoc-type.rs} | 0 .../resolve-method-with-missing-assoc-type.stderr} | 0 .../{issues/issue-27949.rs => binop/binop-subtyping-lifetimes.rs} | 0 .../issue-27889.rs => borrowck/enum-variants-share-field-name.rs} | 0 .../clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs} | 0 .../derive-copy-clone-non-copy-field-diagnostic.stderr} | 0 .../dropflag-reinit-in-loop.rs} | 0 .../region-leak-rc-and-mut-ptr.rs} | 0 .../lifetime-errors/trait-method-return-lifetime-mismatch.rs} | 0 .../lifetime-errors/trait-method-return-lifetime-mismatch.stderr} | 0 .../issue-2761.rs => macros/assert-with-custom-message.rs} | 0 .../issue-27815.rs => structs/non-struct-in-struct-position.rs} | 0 .../non-struct-in-struct-position.stderr} | 0 .../issue-27842.rs => tuple/tuple-bracket-index-suggest-dot.rs} | 0 .../tuple-bracket-index-suggest-dot.stderr} | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-28344.rs => associated-types/resolve-method-with-missing-assoc-type.rs} (100%) rename tests/ui/{issues/issue-28344.stderr => associated-types/resolve-method-with-missing-assoc-type.stderr} (100%) rename tests/ui/{issues/issue-27949.rs => binop/binop-subtyping-lifetimes.rs} (100%) rename tests/ui/{issues/issue-27889.rs => borrowck/enum-variants-share-field-name.rs} (100%) rename tests/ui/{issues/issue-27340.rs => derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs} (100%) rename tests/ui/{issues/issue-27340.stderr => derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr} (100%) rename tests/ui/{issues/issue-27401-dropflag-reinit.rs => drop/dropflag-reinit-in-loop.rs} (100%) rename tests/ui/{issues/issue-28279.rs => higher-ranked/region-leak-rc-and-mut-ptr.rs} (100%) rename tests/ui/{issues/issue-27942.rs => lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs} (100%) rename tests/ui/{issues/issue-27942.stderr => lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr} (100%) rename tests/ui/{issues/issue-2761.rs => macros/assert-with-custom-message.rs} (100%) rename tests/ui/{issues/issue-27815.rs => structs/non-struct-in-struct-position.rs} (100%) rename tests/ui/{issues/issue-27815.stderr => structs/non-struct-in-struct-position.stderr} (100%) rename tests/ui/{issues/issue-27842.rs => tuple/tuple-bracket-index-suggest-dot.rs} (100%) rename tests/ui/{issues/issue-27842.stderr => tuple/tuple-bracket-index-suggest-dot.stderr} (100%) diff --git a/tests/ui/issues/issue-28344.rs b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs similarity index 100% rename from tests/ui/issues/issue-28344.rs rename to tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs diff --git a/tests/ui/issues/issue-28344.stderr b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr similarity index 100% rename from tests/ui/issues/issue-28344.stderr rename to tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr diff --git a/tests/ui/issues/issue-27949.rs b/tests/ui/binop/binop-subtyping-lifetimes.rs similarity index 100% rename from tests/ui/issues/issue-27949.rs rename to tests/ui/binop/binop-subtyping-lifetimes.rs diff --git a/tests/ui/issues/issue-27889.rs b/tests/ui/borrowck/enum-variants-share-field-name.rs similarity index 100% rename from tests/ui/issues/issue-27889.rs rename to tests/ui/borrowck/enum-variants-share-field-name.rs diff --git a/tests/ui/issues/issue-27340.rs b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs similarity index 100% rename from tests/ui/issues/issue-27340.rs rename to tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs diff --git a/tests/ui/issues/issue-27340.stderr b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr similarity index 100% rename from tests/ui/issues/issue-27340.stderr rename to tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr diff --git a/tests/ui/issues/issue-27401-dropflag-reinit.rs b/tests/ui/drop/dropflag-reinit-in-loop.rs similarity index 100% rename from tests/ui/issues/issue-27401-dropflag-reinit.rs rename to tests/ui/drop/dropflag-reinit-in-loop.rs diff --git a/tests/ui/issues/issue-28279.rs b/tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs similarity index 100% rename from tests/ui/issues/issue-28279.rs rename to tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs diff --git a/tests/ui/issues/issue-27942.rs b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs similarity index 100% rename from tests/ui/issues/issue-27942.rs rename to tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs diff --git a/tests/ui/issues/issue-27942.stderr b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr similarity index 100% rename from tests/ui/issues/issue-27942.stderr rename to tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr diff --git a/tests/ui/issues/issue-2761.rs b/tests/ui/macros/assert-with-custom-message.rs similarity index 100% rename from tests/ui/issues/issue-2761.rs rename to tests/ui/macros/assert-with-custom-message.rs diff --git a/tests/ui/issues/issue-27815.rs b/tests/ui/structs/non-struct-in-struct-position.rs similarity index 100% rename from tests/ui/issues/issue-27815.rs rename to tests/ui/structs/non-struct-in-struct-position.rs diff --git a/tests/ui/issues/issue-27815.stderr b/tests/ui/structs/non-struct-in-struct-position.stderr similarity index 100% rename from tests/ui/issues/issue-27815.stderr rename to tests/ui/structs/non-struct-in-struct-position.stderr diff --git a/tests/ui/issues/issue-27842.rs b/tests/ui/tuple/tuple-bracket-index-suggest-dot.rs similarity index 100% rename from tests/ui/issues/issue-27842.rs rename to tests/ui/tuple/tuple-bracket-index-suggest-dot.rs diff --git a/tests/ui/issues/issue-27842.stderr b/tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr similarity index 100% rename from tests/ui/issues/issue-27842.stderr rename to tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr From f6ac8642ff0b935254e2074225511af54a343f07 Mon Sep 17 00:00:00 2001 From: zedddie Date: Sat, 27 Jun 2026 03:21:21 +0200 Subject: [PATCH 227/278] bless batch --- .../resolve-method-with-missing-assoc-type.rs | 3 +++ .../resolve-method-with-missing-assoc-type.stderr | 8 ++++---- tests/ui/binop/binop-subtyping-lifetimes.rs | 13 +++++++------ tests/ui/borrowck/enum-variants-share-field-name.rs | 5 +++-- .../derive-copy-clone-non-copy-field-diagnostic.rs | 4 ++++ ...rive-copy-clone-non-copy-field-diagnostic.stderr | 4 ++-- tests/ui/drop/dropflag-reinit-in-loop.rs | 8 ++++---- .../ui/higher-ranked/region-leak-rc-and-mut-ptr.rs | 4 ++++ .../trait-method-return-lifetime-mismatch.rs | 2 ++ .../trait-method-return-lifetime-mismatch.stderr | 12 ++++++------ tests/ui/macros/assert-with-custom-message.rs | 1 + tests/ui/structs/non-struct-in-struct-position.rs | 3 +++ .../ui/structs/non-struct-in-struct-position.stderr | 8 ++++---- tests/ui/tuple/tuple-bracket-index-suggest-dot.rs | 4 ++++ .../ui/tuple/tuple-bracket-index-suggest-dot.stderr | 6 +++--- 15 files changed, 54 insertions(+), 31 deletions(-) diff --git a/tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs index 883a8397e5a9e..2c892fae7b930 100644 --- a/tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs +++ b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Test we don't ICE on item resolution when associated type is omitted. //@ edition:2015 + use std::ops::BitXor; fn main() { diff --git a/tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr index 0dcb585033d07..b942ad8d26556 100644 --- a/tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr +++ b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr @@ -1,5 +1,5 @@ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-28344.rs:5:17 + --> $DIR/resolve-method-with-missing-assoc-type.rs:8:17 | LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); | ^^^^^^ @@ -13,7 +13,7 @@ LL | let x: u8 = ::bitor(0 as u8, 0 as u8); | ++++ + error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be specified - --> $DIR/issue-28344.rs:5:17 + --> $DIR/resolve-method-with-missing-assoc-type.rs:8:17 | LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); | ^^^^^^ @@ -24,7 +24,7 @@ LL | let x: u8 = BitXor::::bitor(0 as u8, 0 as u8); | +++++++++++++++++++++++ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-28344.rs:10:13 + --> $DIR/resolve-method-with-missing-assoc-type.rs:13:13 | LL | let g = BitXor::bitor; | ^^^^^^ @@ -37,7 +37,7 @@ LL | let g = ::bitor; | ++++ + error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be specified - --> $DIR/issue-28344.rs:10:13 + --> $DIR/resolve-method-with-missing-assoc-type.rs:13:13 | LL | let g = BitXor::bitor; | ^^^^^^ diff --git a/tests/ui/binop/binop-subtyping-lifetimes.rs b/tests/ui/binop/binop-subtyping-lifetimes.rs index e10c60085072f..6456768fed939 100644 --- a/tests/ui/binop/binop-subtyping-lifetimes.rs +++ b/tests/ui/binop/binop-subtyping-lifetimes.rs @@ -1,10 +1,11 @@ +//! Regression test for . +//! +//! At one time, the `==` operator (and other binary operators) did not +//! support subtyping during type checking, and would therefore require +//! LHS and RHS to be exactly identical--i.e. to have the same lifetimes. +//! +//! This was fixed in 1a7fb7dc78439a704f024609ce3dc0beb1386552. //@ run-pass -// -// At one time, the `==` operator (and other binary operators) did not -// support subtyping during type checking, and would therefore require -// LHS and RHS to be exactly identical--i.e. to have the same lifetimes. -// -// This was fixed in 1a7fb7dc78439a704f024609ce3dc0beb1386552. #[derive(Copy, Clone)] struct Input<'a> { diff --git a/tests/ui/borrowck/enum-variants-share-field-name.rs b/tests/ui/borrowck/enum-variants-share-field-name.rs index 8737f03db1a59..d357d805ea22a 100644 --- a/tests/ui/borrowck/enum-variants-share-field-name.rs +++ b/tests/ui/borrowck/enum-variants-share-field-name.rs @@ -1,8 +1,9 @@ +//! Regression test for . +//! Test that a field can have the same name in different variants +//! of an enum, and borrowck won't treat them as the same value. //@ check-pass #![allow(unused_assignments)] #![allow(unused_variables)] -// Test that a field can have the same name in different variants -// of an enum pub enum Foo { X { foo: u32 }, diff --git a/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs index 9966c24a7441e..6053b603cc8fa 100644 --- a/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs +++ b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs @@ -1,3 +1,7 @@ +//! Regression test for . +//! Test anon fields in tuple-syntax structs which don't implement trait +//! get nice error message mentioning the type of field and its span. + struct Foo; #[derive(Copy, Clone)] struct Bar(Foo); diff --git a/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr index 3b4ad58b1f080..a997fc0aa0d98 100644 --- a/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr +++ b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr @@ -1,5 +1,5 @@ error[E0204]: the trait `Copy` cannot be implemented for this type - --> $DIR/issue-27340.rs:3:8 + --> $DIR/derive-copy-clone-non-copy-field-diagnostic.rs:7:8 | LL | #[derive(Copy, Clone)] | ---- in this derive macro expansion @@ -7,7 +7,7 @@ LL | struct Bar(Foo); | ^^^ --- this field does not implement `Copy` error[E0277]: the trait bound `Foo: Clone` is not satisfied - --> $DIR/issue-27340.rs:3:12 + --> $DIR/derive-copy-clone-non-copy-field-diagnostic.rs:7:12 | LL | #[derive(Copy, Clone)] | ----- in this derive macro expansion diff --git a/tests/ui/drop/dropflag-reinit-in-loop.rs b/tests/ui/drop/dropflag-reinit-in-loop.rs index b89497241e096..04fb8eaee965e 100644 --- a/tests/ui/drop/dropflag-reinit-in-loop.rs +++ b/tests/ui/drop/dropflag-reinit-in-loop.rs @@ -1,9 +1,9 @@ +//! Regression test for . +//! Check that when a `let`-binding occurs in a loop, its associated +//! drop-flag is reinitialized (to indicate "needs-drop" at the end of +//! the owning variable's scope). //@ run-pass -// Check that when a `let`-binding occurs in a loop, its associated -// drop-flag is reinitialized (to indicate "needs-drop" at the end of -// the owning variable's scope). - struct A<'a>(&'a mut i32); impl<'a> Drop for A<'a> { diff --git a/tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs b/tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs index 23814b284e905..754e3ce2167a7 100644 --- a/tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs +++ b/tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs @@ -1,4 +1,8 @@ +//! Regression test for . +//! Region variables escaped comparison for common supertype, which led +//! `Rc` and `*mut Fn(&T)` to break. //@ check-pass + #![allow(dead_code)] use std::rc::Rc; diff --git a/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs index 1c2e6456937bb..91a18bfbf4390 100644 --- a/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs +++ b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs @@ -1,3 +1,5 @@ +//! Regression test for . +//! Test internal compiler structs do not leak into error message. //@ dont-require-annotations: NOTE pub trait Resources<'a> {} diff --git a/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr index 671875aa7e2ac..bab0f4c180958 100644 --- a/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr +++ b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-27942.rs:7:25 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:25 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch @@ -7,18 +7,18 @@ LL | fn select(&self) -> BufferViewHandle; = note: expected trait `Resources<'_>` found trait `Resources<'a>` note: the lifetime `'a` as defined here... - --> $DIR/issue-27942.rs:5:18 + --> $DIR/trait-method-return-lifetime-mismatch.rs:7:18 | LL | pub trait Buffer<'a, R: Resources<'a>> { | ^^ note: ...does not necessarily outlive the anonymous lifetime defined here - --> $DIR/issue-27942.rs:7:15 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:15 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^ error[E0308]: mismatched types - --> $DIR/issue-27942.rs:7:25 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:25 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch @@ -26,12 +26,12 @@ LL | fn select(&self) -> BufferViewHandle; = note: expected trait `Resources<'_>` found trait `Resources<'a>` note: the anonymous lifetime defined here... - --> $DIR/issue-27942.rs:7:15 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:15 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^ note: ...does not necessarily outlive the lifetime `'a` as defined here - --> $DIR/issue-27942.rs:5:18 + --> $DIR/trait-method-return-lifetime-mismatch.rs:7:18 | LL | pub trait Buffer<'a, R: Resources<'a>> { | ^^ diff --git a/tests/ui/macros/assert-with-custom-message.rs b/tests/ui/macros/assert-with-custom-message.rs index 6df7a5118b820..78a4a2d61cb9b 100644 --- a/tests/ui/macros/assert-with-custom-message.rs +++ b/tests/ui/macros/assert-with-custom-message.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ run-fail //@ error-pattern:custom message //@ needs-subprocess diff --git a/tests/ui/structs/non-struct-in-struct-position.rs b/tests/ui/structs/non-struct-in-struct-position.rs index 9e53014f45522..782808b8e8520 100644 --- a/tests/ui/structs/non-struct-in-struct-position.rs +++ b/tests/ui/structs/non-struct-in-struct-position.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! Test usage of struct literal syntax with non-structs doesn't ICE. + mod A {} fn main() { diff --git a/tests/ui/structs/non-struct-in-struct-position.stderr b/tests/ui/structs/non-struct-in-struct-position.stderr index 43f78ccf6395a..667ebe7387d07 100644 --- a/tests/ui/structs/non-struct-in-struct-position.stderr +++ b/tests/ui/structs/non-struct-in-struct-position.stderr @@ -1,23 +1,23 @@ error[E0574]: expected struct, variant or union type, found module `A` - --> $DIR/issue-27815.rs:4:13 + --> $DIR/non-struct-in-struct-position.rs:7:13 | LL | let u = A { x: 1 }; | ^ not a struct, variant or union type error[E0574]: expected struct, variant or union type, found builtin type `u32` - --> $DIR/issue-27815.rs:5:13 + --> $DIR/non-struct-in-struct-position.rs:8:13 | LL | let v = u32 { x: 1 }; | ^^^ not a struct, variant or union type error[E0574]: expected struct, variant or union type, found module `A` - --> $DIR/issue-27815.rs:7:9 + --> $DIR/non-struct-in-struct-position.rs:10:9 | LL | A { x: 1 } => {} | ^ not a struct, variant or union type error[E0574]: expected struct, variant or union type, found builtin type `u32` - --> $DIR/issue-27815.rs:9:9 + --> $DIR/non-struct-in-struct-position.rs:12:9 | LL | u32 { x: 1 } => {} | ^^^ not a struct, variant or union type diff --git a/tests/ui/tuple/tuple-bracket-index-suggest-dot.rs b/tests/ui/tuple/tuple-bracket-index-suggest-dot.rs index 060d3b34e09d9..a72be060529c8 100644 --- a/tests/ui/tuple/tuple-bracket-index-suggest-dot.rs +++ b/tests/ui/tuple/tuple-bracket-index-suggest-dot.rs @@ -1,3 +1,7 @@ +//! Regression test for . +//! Test we suggest the use of dot syntax when user is trying to index +//! tuple via square brackets. + fn main() { let tup = (0, 1, 2); // the case where we show a suggestion diff --git a/tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr b/tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr index f388fdf85cde5..f6e6528572661 100644 --- a/tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr +++ b/tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr @@ -1,5 +1,5 @@ error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer})` - --> $DIR/issue-27842.rs:4:16 + --> $DIR/tuple-bracket-index-suggest-dot.rs:8:16 | LL | let _ = tup[0]; | ^^^ help: to access tuple element `0`, use: `.0` @@ -7,7 +7,7 @@ LL | let _ = tup[0]; = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer})` - --> $DIR/issue-27842.rs:9:16 + --> $DIR/tuple-bracket-index-suggest-dot.rs:13:16 | LL | let _ = tup[i]; | ^^^ @@ -15,7 +15,7 @@ LL | let _ = tup[i]; = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error[E0608]: cannot index into a value of type `({integer},)` - --> $DIR/issue-27842.rs:14:16 + --> $DIR/tuple-bracket-index-suggest-dot.rs:18:16 | LL | let _ = tup[3]; | ^^^ From 67f85db8263b605eabf6b6bb54ffb29661054cfe Mon Sep 17 00:00:00 2001 From: Yukang Date: Sat, 27 Jun 2026 12:53:52 +0800 Subject: [PATCH 228/278] Add proc macro for unused assignments and corresponding test --- .../ui/liveness/auxiliary/aux_issue_147648.rs | 23 +++++++++++++++++++ .../unused-assignments-from-macro-147648.rs | 18 +++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/ui/liveness/auxiliary/aux_issue_147648.rs create mode 100644 tests/ui/liveness/unused-assignments-from-macro-147648.rs diff --git a/tests/ui/liveness/auxiliary/aux_issue_147648.rs b/tests/ui/liveness/auxiliary/aux_issue_147648.rs new file mode 100644 index 0000000000000..db188b50e285d --- /dev/null +++ b/tests/ui/liveness/auxiliary/aux_issue_147648.rs @@ -0,0 +1,23 @@ +#![feature(proc_macro_quote)] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro_derive(UnusedAssign)] +pub fn generate(ts: TokenStream) -> TokenStream { + let mut ts = ts.into_iter(); + let _pub = ts.next(); + let _struct = ts.next(); + let name = ts.next().unwrap(); + let TokenTree::Group(fields) = ts.next().unwrap() else { panic!() }; + let mut fields = fields.stream().into_iter(); + let field = fields.next().unwrap(); + + quote! { + impl Drop for $name { + fn drop(&mut self) { + let Self { $field } = self; + } + } + } +} diff --git a/tests/ui/liveness/unused-assignments-from-macro-147648.rs b/tests/ui/liveness/unused-assignments-from-macro-147648.rs new file mode 100644 index 0000000000000..92165c76d1d82 --- /dev/null +++ b/tests/ui/liveness/unused-assignments-from-macro-147648.rs @@ -0,0 +1,18 @@ +//@ check-pass +//@ proc-macro: aux_issue_147648.rs + +#![deny(unused_assignments)] +#![allow(dead_code)] +#![allow(unused_variables)] + +extern crate aux_issue_147648; +use aux_issue_147648::UnusedAssign; + +#[derive(UnusedAssign)] +pub struct MyError { + source_code: (), +} + +fn main() { + let _error = MyError { source_code: () }; +} From d69c87aed2c5234c0d78569b448d1dc17fdee331 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sat, 27 Jun 2026 01:36:56 -0400 Subject: [PATCH 229/278] Upgrade `jsonsocck` and `jsondoclint` to edition 2024. --- src/tools/jsondocck/Cargo.toml | 2 +- src/tools/jsondoclint/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/jsondocck/Cargo.toml b/src/tools/jsondocck/Cargo.toml index 80fc26cbe6680..f2ae68f7fbd69 100644 --- a/src/tools/jsondocck/Cargo.toml +++ b/src/tools/jsondocck/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsondocck" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] jsonpath-rust = "1.0.0" diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml index cc8ecefd530b4..848c0b37ae94e 100644 --- a/src/tools/jsondoclint/Cargo.toml +++ b/src/tools/jsondoclint/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsondoclint" version = "0.1.0" -edition = "2021" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From f9a88c6b46c32fa7752a4349e0fca807d4cf5b48 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sat, 27 Jun 2026 05:39:48 +0000 Subject: [PATCH 230/278] Prepare for merging from rust-lang/rust This updates the rust-version file to 16761606d606b6ec4d0c88fc9251670742ad9fd2. --- 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 10d09bd1f1ebd..d7711535dbf29 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -40557f6225e337d68c8d4f086557ce54135f5dd9 +16761606d606b6ec4d0c88fc9251670742ad9fd2 From 480ddb0c4c92343c43eb1d2428b382f0385fedf9 Mon Sep 17 00:00:00 2001 From: Yukang Date: Sat, 27 Jun 2026 13:46:09 +0800 Subject: [PATCH 231/278] Add regression test for unexpected pointer dereference issue --- .../unexpected-pointer-deref-issue-154568.rs | 22 +++++++++++++++++++ ...expected-pointer-deref-issue-154568.stderr | 20 +++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.rs create mode 100644 tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.stderr diff --git a/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.rs b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.rs new file mode 100644 index 0000000000000..ca97b25fa64fa --- /dev/null +++ b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.rs @@ -0,0 +1,22 @@ +// Regression test for #154568 +//@ compile-flags: -Znext-solver=globally + +trait Role { + type Inner; +} + +struct HandshakeCallback(C); +struct Handshake(R::Inner); + +fn main() { + let callback = HandshakeCallback(()); + let handshake = Handshake(callback.0.clone()); + //~^ ERROR type annotations needed + match &handshake { + hs if (|| { + let borrowed_inner = &hs.0; + borrowed_inner == &callback.0 + })() => println!(), + _ => {} + } +} diff --git a/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.stderr b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.stderr new file mode 100644 index 0000000000000..3c5a00744a636 --- /dev/null +++ b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.stderr @@ -0,0 +1,20 @@ +error[E0283]: type annotations needed for `Handshake<_>` + --> $DIR/unexpected-pointer-deref-issue-154568.rs:13:9 + | +LL | let handshake = Handshake(callback.0.clone()); + | ^^^^^^^^^ ----------------------------- type must be known at this point + | + = note: the type must implement `Role` +note: required by a bound in `Handshake` + --> $DIR/unexpected-pointer-deref-issue-154568.rs:9:21 + | +LL | struct Handshake(R::Inner); + | ^^^^ required by this bound in `Handshake` +help: consider giving `handshake` an explicit type, where the type for type parameter `R` is specified + | +LL | let handshake: Handshake = Handshake(callback.0.clone()); + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. From 4bbd8fcbfb100f3c25885804eb5096bfd260eb2e Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 8 Jun 2026 17:14:28 +0000 Subject: [PATCH 232/278] Use infer tys for synthetic params when lowering const paths point to fns --- .../src/hir_ty_lowering/mod.rs | 22 ++++++++++++++++++- .../mgca/bad-impl-trait-with-apit.rs | 13 +++++++++++ .../mgca/bad-impl-trait-with-apit.stderr | 9 ++++++++ .../mgca/synth-gen-arg-ice-158152.rs | 1 + .../mgca/synth-gen-arg-ice-158152.stderr | 13 ++++++++--- 5 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 tests/ui/const-generics/mgca/bad-impl-trait-with-apit.rs create mode 100644 tests/ui/const-generics/mgca/bad-impl-trait-with-apit.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 95a91f1444404..64b31b5efee31 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2866,7 +2866,27 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { did, path.segments.last().unwrap(), ); - ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args)) + + if self.tcx().generics_of(did).own_synthetic_params_count() == 0 { + ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args)) + } else { + let tcx = self.tcx(); + let generics = tcx.generics_of(did); + + // Use infer tys for synthetic params; otherwise the impl header's trait ref may + // contain callee-owned synthetic params and fail when instantiated with impl args. + // See issue #155834 + let args = args.iter().enumerate().map(|(index, arg)| { + let param = generics.param_at(index, tcx); + if param.kind.is_synthetic() { + self.ty_infer(Some(param), span).into() + } else { + arg + } + }); + + ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args)) + } } // Exhaustive match to be clear about what exactly we're considering to be diff --git a/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.rs b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.rs new file mode 100644 index 0000000000000..c509c52951197 --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.rs @@ -0,0 +1,13 @@ +// Regression test for issue #155834 + +#![expect(incomplete_features)] +#![feature(min_generic_const_args)] + +trait Trait {} + +impl<'t> Trait for [(); N] {} +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for implementations + +fn N(arg: impl Trait) {} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.stderr b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.stderr new file mode 100644 index 0000000000000..a5caa077c7620 --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.stderr @@ -0,0 +1,9 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations + --> $DIR/bad-impl-trait-with-apit.rs:8:25 + | +LL | impl<'t> Trait for [(); N] {} + | ^ not allowed in type signatures + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs index 11d970534f513..53cf964b8f5a5 100644 --- a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs +++ b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs @@ -4,6 +4,7 @@ trait A {} trait Trait {} impl A<[usize; fn_item]> for () {} +//~^ ERROR: the placeholder `_` is not allowed within types on item signatures for implementations fn fn_item(_: impl Trait) {} //~^ ERROR: type provided when a constant was expected diff --git a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr index 0cb59587ee20b..a68c0a1cb409d 100644 --- a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr +++ b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr @@ -1,5 +1,11 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations + --> $DIR/synth-gen-arg-ice-158152.rs:6:16 + | +LL | impl A<[usize; fn_item]> for () {} + | ^^^^^^^ not allowed in type signatures + error[E0747]: type provided when a constant was expected - --> $DIR/synth-gen-arg-ice-158152.rs:8:26 + --> $DIR/synth-gen-arg-ice-158152.rs:9:26 | LL | fn fn_item(_: impl Trait) {} | ^^^^^ @@ -9,6 +15,7 @@ help: if this generic argument was intended as a const parameter, surround it wi LL | fn fn_item(_: impl Trait<{ usize }>) {} | + + -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0747`. +Some errors have detailed explanations: E0121, E0747. +For more information about an error, try `rustc --explain E0121`. From 1af87c1bfbc66a35da470ca74a09460a39819de0 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 27 Jun 2026 09:16:28 +0200 Subject: [PATCH 233/278] Fix doc comment on get_debug_as_hex. --- library/core/src/fmt/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 838dab90a6de0..9a601c757cd39 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -518,7 +518,7 @@ impl FormattingOptions { pub const fn get_precision(&self) -> Option { if self.flags & flags::PRECISION_FLAG != 0 { Some(self.precision) } else { None } } - /// Returns the current precision. + /// Returns the current `x?` or `X?` flag. #[unstable(feature = "formatting_options", issue = "118117")] pub const fn get_debug_as_hex(&self) -> Option { if self.flags & flags::DEBUG_LOWER_HEX_FLAG != 0 { From 36cd1368a8989da36c36ba9a4409b1aac4566934 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 27 Jun 2026 09:18:52 +0200 Subject: [PATCH 234/278] Fix doc comment on FormattingOptions::new(). --- library/core/src/fmt/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 838dab90a6de0..26d1b37ffb65f 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -332,9 +332,7 @@ mod flags { } impl FormattingOptions { - /// Construct a new `FormatterBuilder` with the supplied `Write` trait - /// object for output that is equivalent to the `{}` formatting - /// specifier: + /// Construct a new `FormattingOptions` representing the plain `{}` formatting specifier: /// /// - no flags, /// - filled with spaces, From 5fad8a6a2313aa0c8d90197b55a0c644396fbc20 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Sat, 27 Jun 2026 14:26:29 +0700 Subject: [PATCH 235/278] Remove redundant dyn-compatibility check. This check is already handled in `rustc_trait_selection::traits::fulfill::FulfillProcessor::process_obligation`. --- .../rustc_trait_selection/src/traits/select/mod.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e33311ed07719..515567f174e37 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -771,12 +771,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(EvaluatedToOkModuloRegions) } - ty::PredicateKind::DynCompatible(trait_def_id) => { - if self.tcx().is_dyn_compatible(trait_def_id) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) - } + ty::PredicateKind::DynCompatible(_) => { + bug!( + "Obligation {obligation:?} should have been handled by fulfillment already." + ) } ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { From 74830a7fcbf7867b4945736fd401852fd60756f0 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Tue, 23 Jun 2026 19:17:19 +0300 Subject: [PATCH 236/278] Move `check_target_feature` into the attribute parser Use `TargetFeatureParser` to reject `#[target_feature]` on functions marked as lang items (via `#[lang = "..."]` or `#[panic_handler]`), replacing `check_target_feature` in `rustc_passes`. --- .../src/attributes/codegen_attrs.rs | 34 ++++++++++++------ .../src/attributes/link_attrs.rs | 2 +- .../rustc_attr_parsing/src/attributes/mod.rs | 21 +++++++---- .../src/session_diagnostics.rs | 20 +++++++++++ compiler/rustc_passes/src/check_attr.rs | 35 +------------------ compiler/rustc_passes/src/diagnostics.rs | 20 ----------- .../start_lang_item_with_target_feature.rs | 2 +- ...start_lang_item_with_target_feature.stderr | 12 ++++--- .../panic-handler-with-target-feature.rs | 2 +- .../panic-handler-with-target-feature.stderr | 12 ++++--- 10 files changed, 77 insertions(+), 83 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 7beee7e341b90..e3865345cae84 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -10,7 +10,7 @@ use crate::attributes::AttributeSafety; use crate::session_diagnostics::{ EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, - SanitizeInvalidStatic, + SanitizeInvalidStatic, TargetFeatureOnLangItem, }; use crate::target_checking::Policy::AllowSilent; @@ -524,15 +524,6 @@ impl CombineAttributeParser for TargetFeatureParser { was_forced: false, }; const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]); - const STABILITY: AttributeStability = AttributeStability::Stable; - - fn extend( - cx: &mut AcceptContext<'_, '_>, - args: &ArgParser, - ) -> impl IntoIterator { - parse_tf_attribute(cx, args) - } - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), @@ -544,6 +535,29 @@ impl CombineAttributeParser for TargetFeatureParser { Warn(Target::MacroDef), Warn(Target::MacroCall), ]); + const STABILITY: AttributeStability = AttributeStability::Stable; + + fn extend( + cx: &mut AcceptContext<'_, '_>, + args: &ArgParser, + ) -> impl IntoIterator { + parse_tf_attribute(cx, args) + } + + fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + // `#[target_feature]` is incompatible with lang item functions, + // except on WASM where calling target-feature functions is safe (see #84988). + if !cx.sess().target.is_like_wasm && !cx.sess().opts.actually_rustdoc { + // `#[panic_handler]` is checked first so it takes priority in the diagnostic. + let lang_kind = cx + .all_attrs + .iter() + .find_map(|a| [sym::panic_handler, sym::lang].into_iter().find(|&s| a.word_is(s))); + if let Some(kind) = lang_kind { + cx.emit_err(TargetFeatureOnLangItem { attr_span, kind, item_span: cx.target_span }); + } + } + } } pub(crate) struct ForceTargetFeatureParser; diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 534058f1233b3..2c640ab5385b9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -576,7 +576,7 @@ impl NoArgsAttributeParser for FfiPureParser { const STABILITY: AttributeStability = unstable!(ffi_pure); const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure; - fn finalize_check(attr_span: Span, cx: &FinalizeContext<'_, '_>) { + fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { // `#[ffi_const]` functions cannot be `#[ffi_pure]`. if cx.all_attrs.iter().any(|a| a.word_is(sym::ffi_const)) { cx.emit_err(BothFfiConstAndPure { attr_span }); diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 222daa5a421b1..d3ddd79a97619 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -154,7 +154,7 @@ pub(crate) trait SingleAttributeParser: 'static { /// reject incompatible combinations. `attr_span` is the span of this attribute. /// /// Defaults to a no-op. - fn finalize_check(_attr_span: Span, _cx: &FinalizeContext<'_, '_>) {} + fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} } /// Use in combination with [`SingleAttributeParser`]. @@ -187,7 +187,7 @@ impl AttributeParser for Single { fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { let (kind, span) = self.1?; - T::finalize_check(span, cx); + T::finalize_check(cx, span); Some(kind) } } @@ -275,7 +275,7 @@ pub(crate) trait NoArgsAttributeParser: 'static { /// `attr_span` is the span of this attribute. /// /// Defaults to a no-op. - fn finalize_check(_attr_span: Span, _cx: &FinalizeContext<'_, '_>) {} + fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} } pub(crate) struct WithoutArgs(PhantomData); @@ -299,8 +299,8 @@ impl SingleAttributeParser for WithoutArgs { Some(T::CREATE(cx.attr_span)) } - fn finalize_check(attr_span: Span, cx: &FinalizeContext<'_, '_>) { - T::finalize_check(attr_span, cx) + fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + T::finalize_check(cx, attr_span) } } @@ -335,6 +335,14 @@ pub(crate) trait CombineAttributeParser: 'static { cx: &mut AcceptContext<'_, '_>, args: &ArgParser, ) -> impl IntoIterator; + + /// Optional cross-attribute validation, run once during finalization after all + /// attributes on the item have been parsed. Has access to the sibling attributes via + /// [`FinalizeContext::all_attrs`], so it can reject incompatible combinations. + /// `attr_span` is the span of the first attribute that was encountered. + /// + /// Defaults to a no-op. + fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} } /// Use in combination with [`CombineAttributeParser`]. @@ -367,8 +375,9 @@ impl AttributeParser for Combine { const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS; const SAFETY: AttributeSafety = T::SAFETY; - fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { + fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { if let Some(first_span) = self.first_span { + T::finalize_check(cx, first_span); Some(T::CONVERT(self.items, first_span)) } else { None diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 3e54e91d14783..63e33e0ae4882 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -80,6 +80,26 @@ pub(crate) struct DocAttributeNotAttribute { pub attribute: Symbol, } +#[derive(Diagnostic)] +#[diag( + "`#[target_feature]` cannot be applied to a {$kind -> + [panic_handler] `#[panic_handler]` + *[other] lang item + } function" +)] +pub(crate) struct TargetFeatureOnLangItem { + #[primary_span] + pub attr_span: Span, + pub kind: Symbol, + #[label( + "{$kind -> + [panic_handler] `#[panic_handler]` + *[other] lang item + } function is not allowed to have `#[target_feature]`" + )] + pub item_span: Span, +} + #[derive(Diagnostic)] #[diag("missing 'since'", code = E0542)] pub(crate) struct MissingSince { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 6241bf48fd08e..7ae6005e9bc98 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -204,9 +204,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::Deprecated { span: attr_span, .. } => { self.check_deprecated(hir_id, *attr_span, target) } - AttributeKind::TargetFeature { attr_span, .. } => { - self.check_target_feature(hir_id, *attr_span, target, attrs) - } AttributeKind::RustcDumpObjectLifetimeDefaults => { self.check_dump_object_lifetime_defaults(hir_id); } @@ -406,6 +403,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::ShouldPanic { .. } => (), AttributeKind::Splat(..) => (), AttributeKind::Stability { .. } => (), + AttributeKind::TargetFeature { .. } => {} AttributeKind::TestRunner(..) => (), AttributeKind::ThreadLocal => (), AttributeKind::TypeLengthLimit { .. } => (), @@ -799,37 +797,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Checks if the `#[target_feature]` attribute on `item` is valid. - fn check_target_feature( - &self, - hir_id: HirId, - attr_span: Span, - target: Target, - attrs: &[Attribute], - ) { - match target { - Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) - | Target::Fn => { - // `#[target_feature]` is not allowed in lang items. - if let Some(lang_item) = find_attr!(attrs, Lang(lang ) => lang) - // Calling functions with `#[target_feature]` is - // not unsafe on WASM, see #84988 - && !self.tcx.sess.target.is_like_wasm - && !self.tcx.sess.opts.actually_rustdoc - { - let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); - - self.dcx().emit_err(diagnostics::LangItemWithTargetFeature { - attr_span, - name: lang_item.name(), - sig_span: sig.span, - }); - } - } - _ => {} - } - } - fn check_doc_alias_value(&self, span: Span, hir_id: HirId, target: Target, alias: Symbol) { if let Some(location) = match target { Target::AssocTy => { diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 18482dc570d0b..92562cc462b87 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -362,26 +362,6 @@ pub(crate) struct LangItemWithTrackCaller { pub sig_span: Span, } -#[derive(Diagnostic)] -#[diag( - "{$name -> - [panic_impl] `#[panic_handler]` - *[other] `{$name}` lang item - } function is not allowed to have `#[target_feature]`" -)] -pub(crate) struct LangItemWithTargetFeature { - #[primary_span] - pub attr_span: Span, - pub name: Symbol, - #[label( - "{$name -> - [panic_impl] `#[panic_handler]` - *[other] `{$name}` lang item - } function is not allowed to have `#[target_feature]`" - )] - pub sig_span: Span, -} - #[derive(Diagnostic)] #[diag("duplicate diagnostic item in crate `{$crate_name}`: `{$name}`")] pub(crate) struct DuplicateDiagnosticItemInCrate { diff --git a/tests/ui/lang-items/start_lang_item_with_target_feature.rs b/tests/ui/lang-items/start_lang_item_with_target_feature.rs index 19036819d3d80..61f34d0321202 100644 --- a/tests/ui/lang-items/start_lang_item_with_target_feature.rs +++ b/tests/ui/lang-items/start_lang_item_with_target_feature.rs @@ -18,7 +18,7 @@ pub trait Sized: MetaSized {} #[lang = "start"] #[target_feature(enable = "avx2")] -//~^ ERROR `start` lang item function is not allowed to have `#[target_feature]` +//~^ ERROR #[target_feature]` cannot be applied to a lang item function fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { 0 } diff --git a/tests/ui/lang-items/start_lang_item_with_target_feature.stderr b/tests/ui/lang-items/start_lang_item_with_target_feature.stderr index ce0b1d7557445..8611aa7c7628d 100644 --- a/tests/ui/lang-items/start_lang_item_with_target_feature.stderr +++ b/tests/ui/lang-items/start_lang_item_with_target_feature.stderr @@ -1,11 +1,13 @@ -error: `start` lang item function is not allowed to have `#[target_feature]` +error: `#[target_feature]` cannot be applied to a lang item function --> $DIR/start_lang_item_with_target_feature.rs:20:1 | -LL | #[target_feature(enable = "avx2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "avx2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { - | ------------------------------------------------------------------------------------------- `start` lang item function is not allowed to have `#[target_feature]` +LL | / fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { +LL | | 0 +LL | | } + | |_- lang item function is not allowed to have `#[target_feature]` error: aborting due to 1 previous error diff --git a/tests/ui/panic-handler/panic-handler-with-target-feature.rs b/tests/ui/panic-handler/panic-handler-with-target-feature.rs index aec00c637bedb..6e81516a91628 100644 --- a/tests/ui/panic-handler/panic-handler-with-target-feature.rs +++ b/tests/ui/panic-handler/panic-handler-with-target-feature.rs @@ -8,7 +8,7 @@ use core::panic::PanicInfo; #[panic_handler] #[target_feature(enable = "avx2")] -//~^ ERROR `#[panic_handler]` function is not allowed to have `#[target_feature]` +//~^ ERROR `#[target_feature]` cannot be applied to a `#[panic_handler]` function fn panic(info: &PanicInfo) -> ! { unimplemented!(); } diff --git a/tests/ui/panic-handler/panic-handler-with-target-feature.stderr b/tests/ui/panic-handler/panic-handler-with-target-feature.stderr index ddf0ae77a0a1a..93cf164618ed9 100644 --- a/tests/ui/panic-handler/panic-handler-with-target-feature.stderr +++ b/tests/ui/panic-handler/panic-handler-with-target-feature.stderr @@ -1,11 +1,13 @@ -error: `#[panic_handler]` function is not allowed to have `#[target_feature]` +error: `#[target_feature]` cannot be applied to a `#[panic_handler]` function --> $DIR/panic-handler-with-target-feature.rs:10:1 | -LL | #[target_feature(enable = "avx2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "avx2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | fn panic(info: &PanicInfo) -> ! { - | ------------------------------- `#[panic_handler]` function is not allowed to have `#[target_feature]` +LL | / fn panic(info: &PanicInfo) -> ! { +LL | | unimplemented!(); +LL | | } + | |_- `#[panic_handler]` function is not allowed to have `#[target_feature]` error: aborting due to 1 previous error From fc35c230b9d1d339d2f428d8101ca636f52d8205 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 23 Jun 2026 08:04:59 +0000 Subject: [PATCH 237/278] add crashtests --- tests/crashes/147719.rs | 16 ++++++++++++++++ tests/crashes/148511.rs | 42 +++++++++++++++++++++++++++++++++++++++++ tests/crashes/148629.rs | 13 +++++++++++++ tests/crashes/148630.rs | 13 +++++++++++++ tests/crashes/148632.rs | 10 ++++++++++ tests/crashes/148890.rs | 6 ++++++ tests/crashes/148891.rs | 14 ++++++++++++++ tests/crashes/149162.rs | 30 +++++++++++++++++++++++++++++ tests/crashes/149703.rs | 12 ++++++++++++ tests/crashes/152205.rs | 20 ++++++++++++++++++++ tests/crashes/152295.rs | 11 +++++++++++ tests/crashes/154556.rs | 25 ++++++++++++++++++++++++ tests/crashes/154964.rs | 9 +++++++++ tests/crashes/157568.rs | 10 ++++++++++ 14 files changed, 231 insertions(+) create mode 100644 tests/crashes/147719.rs create mode 100644 tests/crashes/148511.rs create mode 100644 tests/crashes/148629.rs create mode 100644 tests/crashes/148630.rs create mode 100644 tests/crashes/148632.rs create mode 100644 tests/crashes/148890.rs create mode 100644 tests/crashes/148891.rs create mode 100644 tests/crashes/149162.rs create mode 100644 tests/crashes/149703.rs create mode 100644 tests/crashes/152205.rs create mode 100644 tests/crashes/152295.rs create mode 100644 tests/crashes/154556.rs create mode 100644 tests/crashes/154964.rs create mode 100644 tests/crashes/157568.rs diff --git a/tests/crashes/147719.rs b/tests/crashes/147719.rs new file mode 100644 index 0000000000000..d9595b8ead565 --- /dev/null +++ b/tests/crashes/147719.rs @@ -0,0 +1,16 @@ +//@ known-bug: #147719 +//@ edition: 2024 +trait NodeImpl {} +struct Wrap(F, P); +impl Wrap { + fn new(_: F) -> Self { + loop {} + } +} +trait Arg {} +impl NodeImpl for Wrap where A: Arg {} +impl NodeImpl for Wrap where F: Fn(&(), A) -> Fut {} +fn trigger_ice() { + let _: &dyn NodeImpl = &Wrap::<_, (i128,)>::new(async |_: &(), i128| 0); +} +fn main() {} diff --git a/tests/crashes/148511.rs b/tests/crashes/148511.rs new file mode 100644 index 0000000000000..69203ebeb2d21 --- /dev/null +++ b/tests/crashes/148511.rs @@ -0,0 +1,42 @@ +//@ known-bug: #148511 +//@ edition: 2021 +use std::any::Any; + +fn main() { + use_service(make_service()); + let _future = async {}; +} + +fn make_service() -> impl FooService, Response: Body> {} + +fn use_service(_service: S) +where + S: FooService, + ::Data: Any, +{ +} + +trait Service { + type Response: Body; +} + +impl Service for T { + type Response = (); +} + +trait FooService: Service {} + +impl FooService for T +where + T: Service, + Resp: Body, +{ +} + +trait Body { + type Data; +} + +impl Body for T { + type Data = (); +} diff --git a/tests/crashes/148629.rs b/tests/crashes/148629.rs new file mode 100644 index 0000000000000..926b4cc75c952 --- /dev/null +++ b/tests/crashes/148629.rs @@ -0,0 +1,13 @@ +//@ known-bug: #148629 +#![feature(with_negative_coherence)] +trait Foo { + type AssociatedType; +} + +impl Foo for [(); N] {} + +pub struct Happy; + +impl Foo for Happy {} + +impl Foo for Happy where <[(); N] as Foo>::AssociatedType: Clone {} diff --git a/tests/crashes/148630.rs b/tests/crashes/148630.rs new file mode 100644 index 0000000000000..7b857bbb408a1 --- /dev/null +++ b/tests/crashes/148630.rs @@ -0,0 +1,13 @@ +//@ known-bug: #148630 +#![feature(unboxed_closures)] + +trait Tr {} +trait Foo { + fn foo() -> impl Sized + where + for<'a> <() as FnOnce<&'a i32>>::Output: Tr, + { + } +} + +fn main() {} diff --git a/tests/crashes/148632.rs b/tests/crashes/148632.rs new file mode 100644 index 0000000000000..c77ff5e8d7954 --- /dev/null +++ b/tests/crashes/148632.rs @@ -0,0 +1,10 @@ +//@ known-bug: #148632 +trait D {} + +trait Project { + const SELF: dyn D; +} + +fn main() { + let _: &dyn Project; +} diff --git a/tests/crashes/148890.rs b/tests/crashes/148890.rs new file mode 100644 index 0000000000000..6e78ca6717f15 --- /dev/null +++ b/tests/crashes/148890.rs @@ -0,0 +1,6 @@ +//@ known-bug: #148890 +impl std::ops::Neg for u128 {} + +fn foo(-128..=127: u128) {} + +fn main() {} diff --git a/tests/crashes/148891.rs b/tests/crashes/148891.rs new file mode 100644 index 0000000000000..75534440d4902 --- /dev/null +++ b/tests/crashes/148891.rs @@ -0,0 +1,14 @@ +//@ known-bug: #148891 +macro_rules! values { + ($inner:ty) => { + #[derive(Debug)] + pub enum TokenKind { + #[cfg(test)] + STRING ([u8; $inner]), + } + }; +} + +values!(String); + +pub fn main() {} diff --git a/tests/crashes/149162.rs b/tests/crashes/149162.rs new file mode 100644 index 0000000000000..33532fe365ba4 --- /dev/null +++ b/tests/crashes/149162.rs @@ -0,0 +1,30 @@ +//@ known-bug: #149162 +use std::marker::PhantomData; +pub trait ViewArgument { + type Params<'a>; +} + +impl ViewArgument for () { + type Params<'a> = (); +} + +pub trait View {} + +pub fn buttons() -> Option { + Some(()).map(|()| text_button(|()| {})) +} +pub fn text_button( + _: impl Fn(::Params<'_>), +) -> Button { + Button { + callback: || (), + phantom: PhantomData, + } +} +pub struct Button { + pub callback: F, + pub phantom: PhantomData, +} + +impl View for Button<(), F> {} +fn main() {} diff --git a/tests/crashes/149703.rs b/tests/crashes/149703.rs new file mode 100644 index 0000000000000..40d78484307a6 --- /dev/null +++ b/tests/crashes/149703.rs @@ -0,0 +1,12 @@ +//@ known-bug: #149703 +#![feature(const_trait_impl)] +trait Z { + type Assoc; +} +struct A; +impl Z for T { + type Assoc = (); +} +impl From<::Assoc> for T {} + +fn main() {} diff --git a/tests/crashes/152205.rs b/tests/crashes/152205.rs new file mode 100644 index 0000000000000..d6e5a92df5d7a --- /dev/null +++ b/tests/crashes/152205.rs @@ -0,0 +1,20 @@ +//@ known-bug: #152205 +#![deny(rust_2021_incompatible_closure_captures)] +struct Foo; +struct S; +impl Drop for S { + fn drop(&mut self) {} +} +struct U(::Assoc); +fn test_precise_analysis_long_path(u: U) { + let _ = || { + let _x = u.0.0; + }; +} +trait NewTrait { + type Assoc; +} +impl NewTrait for Foo { + type Assoc = (S,); +} +fn main(){} diff --git a/tests/crashes/152295.rs b/tests/crashes/152295.rs new file mode 100644 index 0000000000000..85e23f0f41037 --- /dev/null +++ b/tests/crashes/152295.rs @@ -0,0 +1,11 @@ +//@ known-bug: #152295 +#![feature(type_alias_impl_trait)] +type Tait = impl Sized; +trait Foo: Bar {} +trait Bar { + fn bar(&self); +} +fn test_correct2(x: &dyn Foo) { + x.bar(); +} +fn main() {} diff --git a/tests/crashes/154556.rs b/tests/crashes/154556.rs new file mode 100644 index 0000000000000..d858588c11ec2 --- /dev/null +++ b/tests/crashes/154556.rs @@ -0,0 +1,25 @@ +//@ known-bug: #154556 +#![feature(generic_const_exprs)] + +pub trait Foo { + // Has to take self or a reference to self to ICE. + fn eq(self); +} + +pub trait Bar { + const NUMBER: usize; +} + +impl Foo for T +where + [(); T::NUMBER]: Sized, // Bound required for ICE +{ + fn eq(self) {} +} + +// As long as the method called below has the same name as the one defined in Foo, the ICE happens. +fn baz() { + ().eq(&()); +} + +fn main() {} diff --git a/tests/crashes/154964.rs b/tests/crashes/154964.rs new file mode 100644 index 0000000000000..d44700f5a92b3 --- /dev/null +++ b/tests/crashes/154964.rs @@ -0,0 +1,9 @@ +//@ known-bug: #154964 +//@ edition: 2021 +//@ compile-flags: -Zprint-type-sizes +fn main() { + |foo: Foo<_>| async { foo.bar }; +} +struct Foo { + bar: (), +} diff --git a/tests/crashes/157568.rs b/tests/crashes/157568.rs new file mode 100644 index 0000000000000..627bfbadf6bc4 --- /dev/null +++ b/tests/crashes/157568.rs @@ -0,0 +1,10 @@ +//@ known-bug: #157568 +//@ compile-flags: -Zpolonius +#![warn(rust_2024_compatibility)] +pub struct F; +impl std::fmt::Debug for F { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("F").finish_non_exhaustive() + } +} +fn main() {} From c3864bee809f41cca3d1eace7e7c7a93f0c1a091 Mon Sep 17 00:00:00 2001 From: Rain Date: Fri, 26 Jun 2026 20:12:27 -0700 Subject: [PATCH 238/278] rustdoc: show impl Trait> for Foreign, etc on Local's docs This is a generalization of PR 92940: that PR handled cases like `impl Foreign for Box`, but was missing handling for a few other closely related cases. My particular interest was with showing `impl From> for Box` in camino's documentation. But I ended up handling a bunch of related cases along the way. I'm new to rustdoc so please let me know if I got anything wrong :) took a bit to fully understand how this worked. --- src/librustdoc/formats/cache.rs | 45 ++++++++------ .../impl/impl-fundamental-nesting.rs | 58 +++++++++++++++++++ .../rustdoc-json/impls/fundamental_nesting.rs | 57 ++++++++++++++++++ 3 files changed, 143 insertions(+), 17 deletions(-) create mode 100644 tests/rustdoc-html/impl/impl-fundamental-nesting.rs create mode 100644 tests/rustdoc-json/impls/fundamental_nesting.rs diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index fee783133133f..ccee062584e01 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -233,6 +233,30 @@ impl Cache { } } +impl CacheBuilder<'_, '_> { + /// Extends `dids` with ones that an impl should be associated with for a type appearing in its + /// `Self` type or trait generic arguments, accounting for references and `#[fundamental]` + /// wrappers. + /// + /// This ensures that impls like `impl Trait> for Foreign`, `impl Trait for + /// Box`, and other variations of these, are documented on `Local`'s page. + fn extend_with_fundamental_dids(&self, ty: &clean::Type, dids: &mut FxIndexSet) { + dids.extend(ty.def_id(self.cache)); + // without_borrowed_ref allows cases like `impl Trait<&Box> for Foreign` to be + // handled by this function. (This is rare in practice, but easy to handle here.) + if let clean::Type::Path { path } = ty.without_borrowed_ref() + && let Some(generics) = path.generics() + && let ty::Adt(adt, _) = + self.tcx.type_of(path.def_id()).instantiate_identity().skip_norm_wip().kind() + && adt.is_fundamental() + { + for inner in generics { + self.extend_with_fundamental_dids(inner, dids); + } + } + } +} + impl DocFolder for CacheBuilder<'_, '_> { fn fold_item(&mut self, item: clean::Item) -> Option { if item.item_id.is_local() { @@ -418,22 +442,9 @@ impl DocFolder for CacheBuilder<'_, '_> { // Note: matching twice to restrict the lifetime of the `i` borrow. let mut dids = FxIndexSet::default(); match i.for_ { - clean::Type::Path { ref path } - | clean::BorrowedRef { type_: clean::Type::Path { ref path }, .. } => { - dids.insert(path.def_id()); - if let Some(generics) = path.generics() - && let ty::Adt(adt, _) = self - .tcx - .type_of(path.def_id()) - .instantiate_identity() - .skip_norm_wip() - .kind() - && adt.is_fundamental() - { - for ty in generics { - dids.extend(ty.def_id(self.cache)); - } - } + clean::Type::Path { .. } + | clean::BorrowedRef { type_: clean::Type::Path { .. }, .. } => { + self.extend_with_fundamental_dids(&i.for_, &mut dids); } clean::DynTrait(ref bounds, _) | clean::BorrowedRef { type_: clean::DynTrait(ref bounds, _), .. } => { @@ -452,7 +463,7 @@ impl DocFolder for CacheBuilder<'_, '_> { && let Some(generics) = trait_.generics() { for bound in generics { - dids.extend(bound.def_id(self.cache)); + self.extend_with_fundamental_dids(bound, &mut dids); } } let impl_item = Impl { impl_item: item }; diff --git a/tests/rustdoc-html/impl/impl-fundamental-nesting.rs b/tests/rustdoc-html/impl/impl-fundamental-nesting.rs new file mode 100644 index 0000000000000..25aed33e53514 --- /dev/null +++ b/tests/rustdoc-html/impl/impl-fundamental-nesting.rs @@ -0,0 +1,58 @@ +// Followup to https://github.com/rust-lang/rust/issues/92940 and impl-box.rs. +// +// Show traits implemented on fundamental types that wrap local ones: nested edition. + +#![crate_name = "foo"] + +use std::pin::Pin; + +pub struct Local; + +//@ has 'foo/struct.Local.html' + +// Nested fundamental + foreign Self. +//@ has '-' '//*[@id="impl-From%3CBox%3CLocal%3E%3E-for-String"]' 'impl From> for String' +impl From> for String { + fn from(_: Box) -> String { + String::new() + } +} + +// Also test with Pin. +//@ has '-' '//*[@id="impl-From%3CPin%3CLocal%3E%3E-for-u8"]' 'impl From> for u8' +impl From> for u8 { + fn from(_: Pin) -> u8 { + 0 + } +} + +// Reference to a fundamental wrapper. +//@ has '-' '//*[@id="impl-From%3C%26Box%3CLocal%3E%3E-for-u16"]' "impl<'a> From<&'a Box> for u16" +impl<'a> From<&'a Box> for u16 { + fn from(_: &'a Box) -> u16 { + 0 + } +} + +// Nested two levels deep in Self. +//@ has '-' '//*[@id="impl-From%3Cu32%3E-for-Box%3CBox%3CLocal%3E%3E"]' 'impl From for Box>' +impl From for Box> { + fn from(_: u32) -> Box> { + Box::new(Box::new(Local)) + } +} + +// Mixed fundamental wrappers in Self. +//@ has '-' '//*[@id="impl-From%3Cu64%3E-for-Pin%3CBox%3CLocal%3E%3E"]' 'impl From for Pin>' +impl From for Pin> { + fn from(_: u64) -> Pin> { + Pin::new(Box::new(Local)) + } +} + +// A non-fundamental wrapper must not show up on Local's page, but it should still be listed on the +// trait's own page. +pub trait Marker {} +//@ has 'foo/trait.Marker.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec' +//@ !has 'foo/struct.Local.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec' +impl Marker for Vec {} diff --git a/tests/rustdoc-json/impls/fundamental_nesting.rs b/tests/rustdoc-json/impls/fundamental_nesting.rs new file mode 100644 index 0000000000000..a6aefb94f961f --- /dev/null +++ b/tests/rustdoc-json/impls/fundamental_nesting.rs @@ -0,0 +1,57 @@ +// Companion to tests/rustdoc-html/impl/impl-fundamental-nesting.rs. +// +// Show traits implemented on fundamental types that wrap local ones: nested edition. + +use std::pin::Pin; + +pub struct Local; + +// Nested fundamental + foreign Self. +/// from box local +impl From> for String { + fn from(_: Box) -> String { + String::new() + } +} +//@ set from_box_local = "$.index[?(@.docs=='from box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_box_local + +// Reference to a fundamental wrapper. +/// from ref box local +impl<'a> From<&'a Box> for u16 { + fn from(_: &'a Box) -> u16 { + 0 + } +} +//@ set from_ref_box_local = "$.index[?(@.docs=='from ref box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_ref_box_local + +// Nested two levels deep in Self. +/// u32 for box box local +impl From for Box> { + fn from(_: u32) -> Box> { + Box::new(Box::new(Local)) + } +} +//@ set u32_for_box_box_local = "$.index[?(@.docs=='u32 for box box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u32_for_box_box_local + +// Mixed fundamental wrappers in Self. +/// u64 for pin box local +impl From for Pin> { + fn from(_: u64) -> Pin> { + Pin::new(Box::new(Local)) + } +} +//@ set u64_for_pin_box_local = "$.index[?(@.docs=='u64 for pin box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u64_for_pin_box_local + +// A non-fundamental wrapper must not associate the impl with Local, but the impl must still be +// listed on the trait itself. +pub trait Marker {} + +/// marker for vec local +impl Marker for Vec {} +//@ set marker_for_vec_local = "$.index[?(@.docs=='marker for vec local')].id" +//@ !has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $marker_for_vec_local +//@ has "$.index[?(@.name=='Marker')].inner.trait.implementations[*]" $marker_for_vec_local From dde5f49de98295da08aa694823fb9eaceda1e4cb Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Sat, 27 Jun 2026 16:43:23 +0600 Subject: [PATCH 239/278] fix: add regression test for invalid `Trait` bound suggestion --- compiler/rustc_middle/src/ty/diagnostics.rs | 27 +++++-------------- ...restrict-bound-already-has-generic-args.rs | 2 ++ 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index ec9b73d415f93..fadf1ee2ee384 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -398,28 +398,15 @@ pub fn suggest_constraining_type_params<'a>( } }; let constraint = constraint.join(" + "); - let mut suggest_restrict = |span: Span, bound_list_non_empty, open_paren_sp| { - let (span, suggestion) = if span_to_replace.is_some() { - (span, constraint.clone()) - } else if let Some(rest) = constraint.strip_prefix('<') { - // `constraint` adds generic args; check whether the bound already has some. - // Naively inserting `` after `Foo` yields invalid `Foo`. - if span.lo() > BytePos(0) - && tcx - .sess - .source_map() - .span_to_snippet(span.with_lo(span.lo() - BytePos(1))) - .as_deref() - == Ok(">") - { - (span.with_lo(span.lo() - BytePos(1)), format!(", {rest}")) - } else { - (span, constraint.clone()) - } + let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| { + let suggestion = if span_to_replace.is_some() { + constraint.clone() + } else if constraint.starts_with('<') { + constraint.clone() } else if bound_list_non_empty { - (span, format!(" + {constraint}")) + format!(" + {constraint}") } else { - (span, format!(" {constraint}")) + format!(" {constraint}") }; if let Some(open_paren_sp) = open_paren_sp { diff --git a/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs b/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs index 912d92fdc762f..0350ef50705d9 100644 --- a/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs +++ b/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs @@ -18,6 +18,8 @@ impl Pair for (A, B) { fn frob(pair: impl Pair) -> impl Pair { //~^ ERROR type mismatch + //~| HELP consider further restricting this bound + //~| SUGGESTION , Right = B let (left, right) = pair.split(); (left, right) } From cf443cb75973ddcfd604f271210ce0c851307313 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 27 Jun 2026 12:21:44 +0200 Subject: [PATCH 240/278] add smoketest for std::net::hostname --- library/std/src/net/ip_addr/tests.rs | 2 +- library/std/src/net/mod.rs | 2 +- library/std/src/net/socket_addr/tests.rs | 2 +- library/std/src/net/tcp/tests.rs | 2 +- library/std/src/net/{test.rs => tests.rs} | 10 ++++++++++ library/std/src/net/udp/tests.rs | 2 +- library/std/src/os/net/linux_ext/tests.rs | 4 ++-- 7 files changed, 17 insertions(+), 7 deletions(-) rename library/std/src/net/{test.rs => tests.rs} (83%) diff --git a/library/std/src/net/ip_addr/tests.rs b/library/std/src/net/ip_addr/tests.rs index 7bed6f8a0f523..666146319d161 100644 --- a/library/std/src/net/ip_addr/tests.rs +++ b/library/std/src/net/ip_addr/tests.rs @@ -1,5 +1,5 @@ use crate::net::Ipv4Addr; -use crate::net::test::{sa4, tsa}; +use crate::net::tests::{sa4, tsa}; #[test] fn to_socket_addr_socketaddr() { diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 3e4447eb33f2a..2a8b0f8ca9aad 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -43,7 +43,7 @@ mod ip_addr; mod socket_addr; mod tcp; #[cfg(test)] -pub(crate) mod test; +pub(crate) mod tests; mod udp; /// Possible values which can be passed to the [`TcpStream::shutdown`] method. diff --git a/library/std/src/net/socket_addr/tests.rs b/library/std/src/net/socket_addr/tests.rs index 6a065cfba2105..2f3e3add5c22d 100644 --- a/library/std/src/net/socket_addr/tests.rs +++ b/library/std/src/net/socket_addr/tests.rs @@ -1,4 +1,4 @@ -use crate::net::test::{sa4, sa6, tsa}; +use crate::net::tests::{sa4, sa6, tsa}; use crate::net::*; #[test] diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 8a3a7ee7c5cba..88f6af0e33df7 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -3,7 +3,7 @@ use rand::Rng; use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut}; use crate::mem::MaybeUninit; -use crate::net::test::{LOCALHOST_IP4, LOCALHOST_IP6}; +use crate::net::tests::{LOCALHOST_IP4, LOCALHOST_IP6}; use crate::net::*; use crate::sync::mpsc::channel; use crate::time::{Duration, Instant}; diff --git a/library/std/src/net/test.rs b/library/std/src/net/tests.rs similarity index 83% rename from library/std/src/net/test.rs rename to library/std/src/net/tests.rs index 5002bb5c8e083..3f5d22a910005 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/tests.rs @@ -36,3 +36,13 @@ pub fn compare_ignore_zoneid(a: &SocketAddr, b: &SocketAddr) -> bool { _ => a == b, } } + +#[test] +fn hostname_smoketest() { + // Just a smoke test to ensure it can be called. + let name = crate::net::hostname(); + if cfg!(windows) || cfg!(unix) { + // At least on Windows and Unix, this should succeed. + name.unwrap(); + } +} diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index ede0240fbda57..9e9d3bf77e50b 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -1,5 +1,5 @@ use crate::io::ErrorKind; -use crate::net::test::{LOCALHOST_IP4, LOCALHOST_IP6, compare_ignore_zoneid}; +use crate::net::tests::{LOCALHOST_IP4, LOCALHOST_IP6, compare_ignore_zoneid}; use crate::net::*; use crate::sync::mpsc::channel; use crate::thread; diff --git a/library/std/src/os/net/linux_ext/tests.rs b/library/std/src/os/net/linux_ext/tests.rs index 02839ee6a2aad..a63524e63b6d8 100644 --- a/library/std/src/os/net/linux_ext/tests.rs +++ b/library/std/src/os/net/linux_ext/tests.rs @@ -1,6 +1,6 @@ #[test] fn quickack() { - use crate::net::test::LOCALHOST_IP4; + use crate::net::tests::LOCALHOST_IP4; use crate::net::{TcpListener, TcpStream}; use crate::os::net::linux_ext::tcp::TcpStreamExt; @@ -29,7 +29,7 @@ fn quickack() { #[test] #[cfg(target_os = "linux")] fn deferaccept() { - use crate::net::test::LOCALHOST_IP4; + use crate::net::tests::LOCALHOST_IP4; use crate::net::{TcpListener, TcpStream}; use crate::os::net::linux_ext::tcp::TcpStreamExt; use crate::time::Duration; From 5eb878a65c0311c03285b573812e0fa660bd26e0 Mon Sep 17 00:00:00 2001 From: zedddie Date: Sat, 27 Jun 2026 14:00:46 +0200 Subject: [PATCH 241/278] move batch --- .../nested-fnonce-output-projection.rs} | 0 .../projection-as-type-alias.rs} | 0 .../issue-28600.rs => extern/extern-c-method-with-str-param.rs} | 0 .../issue-28936.rs => inference/closure-arg-lifetime-by-ref.rs} | 0 .../field-borrow-lifetime-inference.rs} | 0 .../rfc-1238-nonparametric-dropck/must-work-ex1.rs} | 0 .../rfc-1238-nonparametric-dropck/must-work-ex2.rs} | 0 .../rfc-1238-nonparametric-dropck/reject-ex1.rs} | 0 .../rfc-1238-nonparametric-dropck/reject-ex1.stderr} | 0 .../rfc-1238-nonparametric-dropck/ugeh-ex1.rs} | 0 tests/ui/{issues/issue-28472.rs => span/foreign-item-vis-span.rs} | 0 .../issue-28472.stderr => span/foreign-item-vis-span.stderr} | 0 .../issue-28776.rs => unsafe/unsafe-fn-called-through-ref.rs} | 0 .../unsafe-fn-called-through-ref.stderr} | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-28550.rs => associated-types/nested-fnonce-output-projection.rs} (100%) rename tests/ui/{issues/issue-28828.rs => associated-types/projection-as-type-alias.rs} (100%) rename tests/ui/{issues/issue-28600.rs => extern/extern-c-method-with-str-param.rs} (100%) rename tests/ui/{issues/issue-28936.rs => inference/closure-arg-lifetime-by-ref.rs} (100%) rename tests/ui/{issues/issue-28999.rs => lifetimes/field-borrow-lifetime-inference.rs} (100%) rename tests/ui/{issues/issue-28498-must-work-ex1.rs => rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs} (100%) rename tests/ui/{issues/issue-28498-must-work-ex2.rs => rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs} (100%) rename tests/ui/{span/issue28498-reject-ex1.rs => rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs} (100%) rename tests/ui/{span/issue28498-reject-ex1.stderr => rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr} (100%) rename tests/ui/{issues/issue-28498-ugeh-ex1.rs => rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs} (100%) rename tests/ui/{issues/issue-28472.rs => span/foreign-item-vis-span.rs} (100%) rename tests/ui/{issues/issue-28472.stderr => span/foreign-item-vis-span.stderr} (100%) rename tests/ui/{issues/issue-28776.rs => unsafe/unsafe-fn-called-through-ref.rs} (100%) rename tests/ui/{issues/issue-28776.stderr => unsafe/unsafe-fn-called-through-ref.stderr} (100%) diff --git a/tests/ui/issues/issue-28550.rs b/tests/ui/associated-types/nested-fnonce-output-projection.rs similarity index 100% rename from tests/ui/issues/issue-28550.rs rename to tests/ui/associated-types/nested-fnonce-output-projection.rs diff --git a/tests/ui/issues/issue-28828.rs b/tests/ui/associated-types/projection-as-type-alias.rs similarity index 100% rename from tests/ui/issues/issue-28828.rs rename to tests/ui/associated-types/projection-as-type-alias.rs diff --git a/tests/ui/issues/issue-28600.rs b/tests/ui/extern/extern-c-method-with-str-param.rs similarity index 100% rename from tests/ui/issues/issue-28600.rs rename to tests/ui/extern/extern-c-method-with-str-param.rs diff --git a/tests/ui/issues/issue-28936.rs b/tests/ui/inference/closure-arg-lifetime-by-ref.rs similarity index 100% rename from tests/ui/issues/issue-28936.rs rename to tests/ui/inference/closure-arg-lifetime-by-ref.rs diff --git a/tests/ui/issues/issue-28999.rs b/tests/ui/lifetimes/field-borrow-lifetime-inference.rs similarity index 100% rename from tests/ui/issues/issue-28999.rs rename to tests/ui/lifetimes/field-borrow-lifetime-inference.rs diff --git a/tests/ui/issues/issue-28498-must-work-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs similarity index 100% rename from tests/ui/issues/issue-28498-must-work-ex1.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs diff --git a/tests/ui/issues/issue-28498-must-work-ex2.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs similarity index 100% rename from tests/ui/issues/issue-28498-must-work-ex2.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs diff --git a/tests/ui/span/issue28498-reject-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs similarity index 100% rename from tests/ui/span/issue28498-reject-ex1.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs diff --git a/tests/ui/span/issue28498-reject-ex1.stderr b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr similarity index 100% rename from tests/ui/span/issue28498-reject-ex1.stderr rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr diff --git a/tests/ui/issues/issue-28498-ugeh-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs similarity index 100% rename from tests/ui/issues/issue-28498-ugeh-ex1.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs diff --git a/tests/ui/issues/issue-28472.rs b/tests/ui/span/foreign-item-vis-span.rs similarity index 100% rename from tests/ui/issues/issue-28472.rs rename to tests/ui/span/foreign-item-vis-span.rs diff --git a/tests/ui/issues/issue-28472.stderr b/tests/ui/span/foreign-item-vis-span.stderr similarity index 100% rename from tests/ui/issues/issue-28472.stderr rename to tests/ui/span/foreign-item-vis-span.stderr diff --git a/tests/ui/issues/issue-28776.rs b/tests/ui/unsafe/unsafe-fn-called-through-ref.rs similarity index 100% rename from tests/ui/issues/issue-28776.rs rename to tests/ui/unsafe/unsafe-fn-called-through-ref.rs diff --git a/tests/ui/issues/issue-28776.stderr b/tests/ui/unsafe/unsafe-fn-called-through-ref.stderr similarity index 100% rename from tests/ui/issues/issue-28776.stderr rename to tests/ui/unsafe/unsafe-fn-called-through-ref.stderr From 2e088054ddacaf5dad97624af7ca9c9f0764b326 Mon Sep 17 00:00:00 2001 From: WhySoBad <49595640+WhySoBad@users.noreply.github.com> Date: Sun, 14 Jun 2026 01:11:36 +0200 Subject: [PATCH 242/278] fix: keep readable readiness when socket read end is closed Removing the readable readiness after a short read even when the read end is closed would mean that applications which strictly rely on the readable readiness (e.g. tokio) would no longer read zero (indicating EOF) on the closed socket. This is problematic as those applications could block indefinitely waiting for a readable event on the socket. --- src/tools/miri/src/shims/unix/socket.rs | 36 +++- .../libc/libc-socket-no-blocking-epoll.rs | 197 +++++++++++++++++- 2 files changed, 214 insertions(+), 19 deletions(-) diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index ae882f8ff3a40..72b614f58d0a4 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -1487,9 +1487,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Only shutting down the write end doesn't cause an EPOLLHUP event // and thus we won't set the `write_closed` readiness for it here. readiness.write_closed |= is_read_write_shutdown; - // The Linux kernel also sets EPOLLIN when both ends of a socket are closed: + // The Linux kernel also sets EPOLLIN when the read end of a socket is closed: // - readiness.readable |= is_read_write_shutdown; + readiness.readable |= is_read_shutdown || is_read_write_shutdown; drop(readiness); @@ -1697,12 +1697,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Result> { let this = self.eval_context_mut(); - let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else { + let mut state = socket.state.borrow_mut(); + let SocketState::Connected(stream) = &mut *state else { panic!("try_non_block_send must only be called when the socket is connected") }; // This is a *non-blocking* write. let result = this.write_to_host(stream, length, buffer_ptr)?; + + drop(state); + match result { Err(IoError::HostError(e)) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) => @@ -1715,8 +1719,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK. interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into()))) } - Ok(bytes_written) if bytes_written < length => { - // We had a short write. On Unix hosts using the `epoll` and `kqueue` backends, a + Ok(bytes_written) + if bytes_written < length && !socket.io_readiness.borrow().write_closed => + { + // We had a short write. (Note that we don't want to clear the writable readiness for + // sockets whose write end has already been closed as those never block a write, i.e., + // they are always write-ready.) + // On Unix hosts using the `epoll` and `kqueue` backends, a // short write means that the write buffer is full. We update the readiness // accordingly, which means that next time we see "writable" we will report an epoll // edge. Some applications (e.g. tokio) rely on this behavior; see @@ -1820,7 +1829,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Result> { let this = self.eval_context_mut(); - let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else { + let mut state = socket.state.borrow_mut(); + let SocketState::Connected(stream) = &mut *state else { panic!("try_non_block_recv must only be called when the socket is connected") }; @@ -1832,6 +1842,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { length, buffer_ptr, )?; + + drop(state); + match result { Err(IoError::HostError(e)) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) => @@ -1844,9 +1857,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK. interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into()))) } - Ok(bytes_read) if !should_peek && bytes_read < length && bytes_read > 0 => { + Ok(bytes_read) + if !should_peek + && bytes_read < length + && bytes_read > 0 + && !socket.io_readiness.borrow().read_closed => + { // We had a short read (and were not peeking). (Note that reading 0 bytes is guaranteed - // to indicate EOF, and can never happen spuriously, so we have to exclude that case.) + // to indicate EOF, and can never happen spuriously, so we have to exclude that case. + // We also don't want to clear the readable readiness for sockets whose read end has + // already been closed as those never block a read, i.e., they are always read-ready.) // On Unix hosts using the `epoll` and `kqueue` backends, a short read means that the // read buffer is empty. We update the readiness accordingly, which means that next time // we see "readable" we will report an epoll edge. Some applications (e.g. tokio) rely on diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs index 9ed0b9c735979..fd657db582acb 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs @@ -1,5 +1,8 @@ //@only-target: linux android illumos //@compile-flags: -Zmiri-disable-isolation +//@revisions: windows_host unix_host +//@[unix_host] ignore-host: windows +//@[windows_host] only-host: windows //@run-native #![feature(io_error_inprogress)] @@ -28,6 +31,8 @@ fn main() { test_readiness_after_short_read(); test_readiness_after_short_peek(); test_readiness_after_short_write(); + test_readable_after_read_shutdown_and_short_read(); + test_writable_after_write_shutdown_with_full_buffer(); } /// Test that connecting to a server socket works when the client @@ -562,24 +567,14 @@ fn test_readiness_after_short_write() { unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); - // Spawn the server thread. - let server_thread = thread::spawn(move || { - let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); - // Return the peer socket file descriptor such that we can use - // it after joining the server thread. - peerfd - }); - net::connect_ipv4(client_sockfd, addr).unwrap(); + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); unsafe { // Change client socket to be non-blocking. errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); } - // The peer socket is a blocking socket. - let peerfd = server_thread.join().unwrap(); - // Add client socket with writable interest to epoll. epoll_ctl_add(epfd, client_sockfd, EPOLLET | EPOLLOUT).unwrap(); @@ -636,3 +631,183 @@ fn test_readiness_after_short_write() { // We should again be able to write into the socket. libc_utils::write_all(client_sockfd, &buffer).unwrap(); } + +/// Test that Miri correctly keeps the readable readiness when the read end of the client +/// socket has been closed -- even after a short read. +fn test_readable_after_read_shutdown_and_short_read() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + + // Spawn the server thread. + let server_thread = thread::spawn(move || { + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + // Write `TEST_BYTES` into the stream. + libc_utils::write_all(peerfd, TEST_BYTES).unwrap(); + }); + + net::connect_ipv4(client_sockfd, addr).unwrap(); + + unsafe { + // Change client socket to be non-blocking. + errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); + } + + server_thread.join().unwrap(); + + // Close the read end of the client socket. + unsafe { + errno_check(libc::shutdown(client_sockfd, libc::SHUT_RD)); + } + + // Add client socket with "read closed" and "readable" interest to epoll. + epoll_ctl_add(epfd, client_sockfd, EPOLLET | EPOLLIN | EPOLLRDHUP).unwrap(); + + let events = if cfg!(windows_host) { + // On Windows hosts, the TCP connection is reset when the read-end of the + // socket is closed whilst there still being some unread/incoming data. + // We thus also expect the EPOLLHUP readiness (we don't need to register it, + // as `epoll_wait` registers it implicitly). + // See : + // "For TCP sockets, if there is still data queued on the socket waiting to + // be received, or data arrives subsequently, the connection is reset, since + // the data cannot be delivered to the user" + EPOLLIN | EPOLLRDHUP | EPOLLHUP + } else { + EPOLLIN | EPOLLRDHUP + }; + + // Ensure that the socket is readable and that its read end is closed. + check_epoll_wait(epfd, &[Ev { events, data: client_sockfd }], -1); + + let mut buffer = [0u8; 1024]; + + if cfg!(windows_host) { + // Because the TCP connection has been reset on Windows hosts, + // we cannot read anything from the client socket anymore. + // We thus only test that the connection has indeed been reset + // and then we return from the test. + let err = unsafe { + errno_result(libc::read( + client_sockfd, + buffer.as_mut_ptr().cast(), + // Attempt to read a chunk of 16 bytes. + 16, + )) + .unwrap_err() + }; + assert_eq!(err.kind(), ErrorKind::ConnectionAborted); + return; + } + + // We're not on a Windows host. + + // We want to read in chunks of 16 bytes. To ensure we get a short read, `TEST_BYTES.len()` + // must not be dividable by 16. + assert!(TEST_BYTES.len() % 16 != 0); + + let mut total_bytes_read = 0; + // Read everything from the socket until we get a short read. + // We don't want to provide `TEST_BYTES.len()` as `count` because then we won't trigger + // a short read. + loop { + let bytes_read = unsafe { + errno_result(libc::read( + client_sockfd, + buffer.as_mut_ptr().byte_add(total_bytes_read).cast(), + // Read a chunk of 16 bytes. + 16, + )) + .unwrap() + }; + + total_bytes_read += bytes_read as usize; + if bytes_read < 16 { + // We had a short read; we thus assume the read buffer is empty. + break; + } + } + assert_eq!(total_bytes_read, TEST_BYTES.len()); + + // We had a short read because `buffer` is bigger than `TEST_BYTES`. + // Because the read end of the socket is closed, we should still be able to + // read to detect EOFs. + + // Ensure that the "readable" and "read closed" readiness flags are still set. + assert_eq!( + current_epoll_readiness::<8>(client_sockfd, EPOLLIN | EPOLLET | EPOLLRDHUP), + EPOLLIN | EPOLLRDHUP + ); + + let mut buffer = [1u8; 16]; + let bytes_read = unsafe { + errno_result(libc::read(client_sockfd, buffer.as_mut_ptr().cast(), buffer.len())).unwrap() + }; + // The read should not block and return 0, indicating EOF. + assert_eq!(bytes_read, 0); +} + +/// Test that the writable readiness gets set when the write end of a socket +/// is closed -- even when the socket write buffer is full. +fn test_writable_after_write_shutdown_with_full_buffer() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + + net::connect_ipv4(client_sockfd, addr).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); + + unsafe { + // Change client socket to be non-blocking. + errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); + } + + // Add client socket with level-triggered "writable" and "write closed" interest to epoll. + epoll_ctl_add(epfd, client_sockfd, EPOLLOUT | EPOLLHUP).unwrap(); + + // Wait until the socket becomes writable. + check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: client_sockfd }], -1); + + // We now want to fill the write buffer of the socket by repeatedly writing + // `buffer` into it. The last write should then be a short write. + // We assume/hope that the write buffer length is not divisible by 1039. + let buffer = [123u8; 1039]; + + loop { + let result = unsafe { + errno_result(libc::write(client_sockfd, buffer.as_ptr().cast(), buffer.len())) + }; + + match result { + Ok(bytes_written) => { + if (bytes_written as usize) < buffer.len() { + // We had a short write; we thus assume the write buffer is full. + break; + } + } + Err(err) if err.kind() == ErrorKind::WouldBlock => { + // Windows and Apple hosts behave weirdly when attempting to fill up the write buffer. + // Instead of doing a short write to completely fill the buffer, they can return an + // EWOULDBLOCK when the next write wouldn't fit into the buffer. + // When we get such an error, we also assume the write buffer is full. + break; + } + Err(err) => panic!("unexpected error whilst filling up buffer: {err}"), + } + } + + // The write buffer is full; because this is a level-triggered interest, + // a readiness of 0 means that the socket would now block on writes. + check_epoll_wait(epfd, &[], 0); + + // Close the socket write end. + unsafe { + errno_check(libc::shutdown(client_sockfd, libc::SHUT_WR)); + } + + // The socket should no longer block on writes after its write end is closed. + check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: client_sockfd }], -1); +} From 80f8d1c182cf93e04f3a1610920124eb376f0714 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 27 Jun 2026 13:27:30 +0200 Subject: [PATCH 243/278] avoid Windows quirk, and make write test consistent with read test --- .../libc/libc-socket-no-blocking-epoll.rs | 63 +++++-------------- 1 file changed, 15 insertions(+), 48 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs index fd657db582acb..68c69cd5668e6 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs @@ -1,8 +1,5 @@ //@only-target: linux android illumos //@compile-flags: -Zmiri-disable-isolation -//@revisions: windows_host unix_host -//@[unix_host] ignore-host: windows -//@[windows_host] only-host: windows //@run-native #![feature(io_error_inprogress)] @@ -646,64 +643,29 @@ fn test_readable_after_read_shutdown_and_short_read() { // Write `TEST_BYTES` into the stream. libc_utils::write_all(peerfd, TEST_BYTES).unwrap(); + + // Close the write end, so that the reader will get an EOF. + // (We could alternatively test this by closing the read end of the client socket, + // but Windows has some special behavior when closing a read end while there's still + // data coming in, so we avoid that.) + unsafe { errno_check(libc::shutdown(peerfd, libc::SHUT_WR)) }; }); net::connect_ipv4(client_sockfd, addr).unwrap(); - unsafe { - // Change client socket to be non-blocking. - errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); - } + // Change client socket to be non-blocking. + unsafe { errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)) }; server_thread.join().unwrap(); - // Close the read end of the client socket. - unsafe { - errno_check(libc::shutdown(client_sockfd, libc::SHUT_RD)); - } - // Add client socket with "read closed" and "readable" interest to epoll. epoll_ctl_add(epfd, client_sockfd, EPOLLET | EPOLLIN | EPOLLRDHUP).unwrap(); - let events = if cfg!(windows_host) { - // On Windows hosts, the TCP connection is reset when the read-end of the - // socket is closed whilst there still being some unread/incoming data. - // We thus also expect the EPOLLHUP readiness (we don't need to register it, - // as `epoll_wait` registers it implicitly). - // See : - // "For TCP sockets, if there is still data queued on the socket waiting to - // be received, or data arrives subsequently, the connection is reset, since - // the data cannot be delivered to the user" - EPOLLIN | EPOLLRDHUP | EPOLLHUP - } else { - EPOLLIN | EPOLLRDHUP - }; - // Ensure that the socket is readable and that its read end is closed. - check_epoll_wait(epfd, &[Ev { events, data: client_sockfd }], -1); + check_epoll_wait(epfd, &[Ev { events: EPOLLIN | EPOLLRDHUP, data: client_sockfd }], -1); let mut buffer = [0u8; 1024]; - if cfg!(windows_host) { - // Because the TCP connection has been reset on Windows hosts, - // we cannot read anything from the client socket anymore. - // We thus only test that the connection has indeed been reset - // and then we return from the test. - let err = unsafe { - errno_result(libc::read( - client_sockfd, - buffer.as_mut_ptr().cast(), - // Attempt to read a chunk of 16 bytes. - 16, - )) - .unwrap_err() - }; - assert_eq!(err.kind(), ErrorKind::ConnectionAborted); - return; - } - - // We're not on a Windows host. - // We want to read in chunks of 16 bytes. To ensure we get a short read, `TEST_BYTES.len()` // must not be dividable by 16. assert!(TEST_BYTES.len() % 16 != 0); @@ -741,11 +703,11 @@ fn test_readable_after_read_shutdown_and_short_read() { EPOLLIN | EPOLLRDHUP ); + // A read should not block and return 0, indicating EOF. let mut buffer = [1u8; 16]; let bytes_read = unsafe { errno_result(libc::read(client_sockfd, buffer.as_mut_ptr().cast(), buffer.len())).unwrap() }; - // The read should not block and return 0, indicating EOF. assert_eq!(bytes_read, 0); } @@ -810,4 +772,9 @@ fn test_writable_after_write_shutdown_with_full_buffer() { // The socket should no longer block on writes after its write end is closed. check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: client_sockfd }], -1); + + // A write should not block and return an error. + let result = + unsafe { errno_result(libc::write(client_sockfd, buffer.as_ptr().cast(), buffer.len())) }; + assert_eq!(result.unwrap_err().kind(), ErrorKind::BrokenPipe); } From eb9bdc40dc474cdfdf9ee3fbfe06c6ccecad2940 Mon Sep 17 00:00:00 2001 From: zedddie Date: Sat, 27 Jun 2026 14:08:25 +0200 Subject: [PATCH 244/278] bless batch --- src/tools/tidy/src/issues.txt | 1 - .../nested-fnonce-output-projection.rs | 2 ++ tests/ui/associated-types/projection-as-type-alias.rs | 3 +++ tests/ui/extern/extern-c-method-with-str-param.rs | 3 ++- tests/ui/inference/closure-arg-lifetime-by-ref.rs | 2 ++ tests/ui/lifetimes/field-borrow-lifetime-inference.rs | 3 +++ .../rfc-1238-nonparametric-dropck/must-work-ex1.rs | 7 +++---- .../rfc-1238-nonparametric-dropck/must-work-ex2.rs | 7 +++---- .../rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs | 10 ++++------ .../rfc-1238-nonparametric-dropck/reject-ex1.stderr | 2 +- .../ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs | 8 +++----- tests/ui/span/foreign-item-vis-span.rs | 3 ++- tests/ui/span/foreign-item-vis-span.stderr | 4 ++-- tests/ui/unsafe/unsafe-fn-called-through-ref.rs | 3 +++ tests/ui/unsafe/unsafe-fn-called-through-ref.stderr | 2 +- 15 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index b63f4d46139c7..db6008da59b34 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -2442,7 +2442,6 @@ ui/span/issue-42234-unknown-receiver-type.rs ui/span/issue-43927-non-ADT-derive.rs ui/span/issue-71363.rs ui/span/issue-81800.rs -ui/span/issue28498-reject-ex1.rs ui/span/issue28498-reject-lifetime-param.rs ui/span/issue28498-reject-passed-to-fn.rs ui/span/issue28498-reject-trait-bound.rs diff --git a/tests/ui/associated-types/nested-fnonce-output-projection.rs b/tests/ui/associated-types/nested-fnonce-output-projection.rs index 31c7057d06f8c..aa03ca10430ac 100644 --- a/tests/ui/associated-types/nested-fnonce-output-projection.rs +++ b/tests/ui/associated-types/nested-fnonce-output-projection.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ run-pass + struct AT,T>(F::Output); struct BT,T>(A); diff --git a/tests/ui/associated-types/projection-as-type-alias.rs b/tests/ui/associated-types/projection-as-type-alias.rs index b5d7385cf28f0..b3e01be818e1f 100644 --- a/tests/ui/associated-types/projection-as-type-alias.rs +++ b/tests/ui/associated-types/projection-as-type-alias.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! This failed to compile as associated types aliases were not normalized. //@ run-pass + pub trait Foo { type Out; } diff --git a/tests/ui/extern/extern-c-method-with-str-param.rs b/tests/ui/extern/extern-c-method-with-str-param.rs index a5427b94a57c5..d0fe55bd1d0f1 100644 --- a/tests/ui/extern/extern-c-method-with-str-param.rs +++ b/tests/ui/extern/extern-c-method-with-str-param.rs @@ -1,5 +1,6 @@ +//! Regression test for . +//! pub extern fn with parameter type &str inside struct impl caused ICE. //@ build-pass -// #28600 ICE: pub extern fn with parameter type &str inside struct impl struct Test; diff --git a/tests/ui/inference/closure-arg-lifetime-by-ref.rs b/tests/ui/inference/closure-arg-lifetime-by-ref.rs index 96503f0711d27..f00a38ea061c8 100644 --- a/tests/ui/inference/closure-arg-lifetime-by-ref.rs +++ b/tests/ui/inference/closure-arg-lifetime-by-ref.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ check-pass + pub type Session = i32; pub struct StreamParser<'a, T> { _tokens: T, diff --git a/tests/ui/lifetimes/field-borrow-lifetime-inference.rs b/tests/ui/lifetimes/field-borrow-lifetime-inference.rs index 572a0beff61f6..a8582dbd29b62 100644 --- a/tests/ui/lifetimes/field-borrow-lifetime-inference.rs +++ b/tests/ui/lifetimes/field-borrow-lifetime-inference.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! `this.v` was not constrained and inferred `'a`. //@ check-pass + pub struct Xyz<'a, V> { pub v: (V, &'a u32), } diff --git a/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs index 813cd8645f923..9afd7ddbd88a9 100644 --- a/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs @@ -1,8 +1,7 @@ +//! Test for . +//! Example taken from RFC 1238 text +//! . //@ run-pass -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #examples-of-code-that-must-continue-to-work use std::cell::Cell; diff --git a/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs index 98514b1163675..4e8d403672641 100644 --- a/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs @@ -1,8 +1,7 @@ +//! Test for . +//! Example taken from RFC 1238 text +//! . //@ run-pass -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #examples-of-code-that-must-continue-to-work use std::cell::Cell; diff --git a/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs index 4d1b4125503b6..3209a17041f67 100644 --- a/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs @@ -1,9 +1,7 @@ -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #examples-of-code-that-will-start-to-be-rejected - -// Compare against test/run-pass/issue28498-must-work-ex2.rs +//! Test for . +//! Example taken from RFC 1238 text +//! . +//! Compare against tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs. use std::cell::Cell; diff --git a/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr index 7b8af23a6cd7b..6e20f06d7fe4c 100644 --- a/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr @@ -1,5 +1,5 @@ error[E0713]: borrow may still be in use when destructor runs - --> $DIR/issue28498-reject-ex1.rs:34:29 + --> $DIR/reject-ex1.rs:32:29 | LL | foo.data[0].1.set(Some(&foo.data[1])); | ^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs index f606d2489484a..722523c094329 100644 --- a/tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs @@ -1,10 +1,8 @@ +//! Test for . +//! Example taken from RFC 1238 text +//! . //@ run-pass -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #example-of-the-unguarded-escape-hatch - #![feature(dropck_eyepatch)] use std::cell::Cell; diff --git a/tests/ui/span/foreign-item-vis-span.rs b/tests/ui/span/foreign-item-vis-span.rs index 6db1f9a5bddf9..839e07ea1b961 100644 --- a/tests/ui/span/foreign-item-vis-span.rs +++ b/tests/ui/span/foreign-item-vis-span.rs @@ -1,4 +1,5 @@ -// Check that the visibility modifier is included in the span of foreign items. +//! Regression test for . +//! Check that the visibility modifier is included in the span of foreign items. extern "C" { fn foo(); diff --git a/tests/ui/span/foreign-item-vis-span.stderr b/tests/ui/span/foreign-item-vis-span.stderr index 051ed25b6c9d9..912a338e4616a 100644 --- a/tests/ui/span/foreign-item-vis-span.stderr +++ b/tests/ui/span/foreign-item-vis-span.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `foo` is defined multiple times - --> $DIR/issue-28472.rs:6:3 + --> $DIR/foreign-item-vis-span.rs:7:3 | LL | fn foo(); | --------- previous definition of the value `foo` here @@ -11,7 +11,7 @@ LL | | fn foo(); = note: `foo` must be defined only once in the value namespace of this module error[E0428]: the name `foo` is defined multiple times - --> $DIR/issue-28472.rs:9:3 + --> $DIR/foreign-item-vis-span.rs:10:3 | LL | fn foo(); | --------- previous definition of the value `foo` here diff --git a/tests/ui/unsafe/unsafe-fn-called-through-ref.rs b/tests/ui/unsafe/unsafe-fn-called-through-ref.rs index e564ebcd110cb..d5ac06fe32956 100644 --- a/tests/ui/unsafe/unsafe-fn-called-through-ref.rs +++ b/tests/ui/unsafe/unsafe-fn-called-through-ref.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! Unsafe fn could be called outside of unsafe block through autoderef. + use std::ptr; fn main() { diff --git a/tests/ui/unsafe/unsafe-fn-called-through-ref.stderr b/tests/ui/unsafe/unsafe-fn-called-through-ref.stderr index 3db94ee181017..6978839ed1c8b 100644 --- a/tests/ui/unsafe/unsafe-fn-called-through-ref.stderr +++ b/tests/ui/unsafe/unsafe-fn-called-through-ref.stderr @@ -1,5 +1,5 @@ error[E0133]: call to unsafe function `std::ptr::write` is unsafe and requires unsafe function or block - --> $DIR/issue-28776.rs:4:5 + --> $DIR/unsafe-fn-called-through-ref.rs:7:5 | LL | (&ptr::write)(1 as *mut _, 42); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function From 22c9087a6f1fb32aadfff3fa54c0548a7293539c Mon Sep 17 00:00:00 2001 From: Valentyn Kit Date: Sat, 27 Jun 2026 15:56:23 +0300 Subject: [PATCH 245/278] std: treat ENFILE as transient in the pidfd support probe The probe handles EMFILE (per-process fd limit) as a transient error and re-probes later, but lets ENFILE (system-wide fd limit) fall through to the catch-all arm meant for an old kernel without pidfd support, which permanently caches the pidfd path as unsupported. ENFILE is the same transient condition, so handle both. --- library/std/src/sys/process/unix/unix.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 529cdaf72cc6c..1dd684975c2dd 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -510,7 +510,7 @@ impl Command { support = SPAWN; } } - Err(e) if e.raw_os_error() == Some(libc::EMFILE) => { + Err(e) if matches!(e.raw_os_error(), Some(libc::EMFILE | libc::ENFILE)) => { // We're temporarily(?) out of file descriptors. In this case pidfd_spawnp would also fail // Don't update the support flag so we can probe again later. return Err(e) From 9ab513578e4301ed0837dcee582a914fca8496d6 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sat, 27 Jun 2026 10:07:44 -0400 Subject: [PATCH 246/278] Upgrade `rustdoc-json-types` to 2024 edition. --- src/rustdoc-json-types/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml index 9e18691a9605a..ba6dc2d69e1ff 100644 --- a/src/rustdoc-json-types/Cargo.toml +++ b/src/rustdoc-json-types/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustdoc-json-types" version = "0.1.0" -edition = "2021" +edition = "2024" [lib] path = "lib.rs" From f31f522f45c4abb91125d8537919c83df8a5d03b Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Sat, 27 Jun 2026 17:49:30 +0200 Subject: [PATCH 247/278] Support DefKind::InlineConst in UnevaluatedConst --- compiler/rustc_ast_lowering/src/lib.rs | 2 +- .../rustc_borrowck/src/universal_regions.rs | 2 +- compiler/rustc_hir/src/def.rs | 37 -------- compiler/rustc_hir_analysis/src/collect.rs | 6 +- .../src/hir_ty_lowering/mod.rs | 12 +-- compiler/rustc_hir_analysis/src/lib.rs | 8 +- compiler/rustc_hir_typeck/src/loops.rs | 5 ++ compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_middle/src/hir/map.rs | 8 +- compiler/rustc_middle/src/ty/context.rs | 9 +- .../src/ty/context/impl_interner.rs | 2 +- compiler/rustc_middle/src/ty/util.rs | 33 +++++++- .../rustc_mir_transform/src/trivial_const.rs | 9 +- compiler/rustc_resolve/src/def_collector.rs | 84 +++++-------------- compiler/rustc_resolve/src/lib.rs | 9 -- compiler/rustc_ty_utils/src/opaque_types.rs | 17 ++-- compiler/rustc_ty_utils/src/sig_types.rs | 38 +++++---- .../mgca/double-inline-const.rs | 11 +++ .../mgca/double-inline-const.stderr | 15 ++++ 19 files changed, 151 insertions(+), 158 deletions(-) create mode 100644 tests/ui/const-generics/mgca/double-inline-const.rs create mode 100644 tests/ui/const-generics/mgca/double-inline-const.stderr diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 06e83a7486100..ceee45db20544 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2791,7 +2791,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::ConstBlock(anon_const) => { let def_id = self.local_def_id(anon_const.id); - assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id)); + assert_eq!(DefKind::InlineConst, self.tcx.def_kind(def_id)); self.lower_anon_const_to_const_arg(anon_const, span) } _ => overly_complex_const(self), diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 804d2757cf998..b0adbcd6db4ef 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -615,7 +615,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { BodyOwnerKind::Const { .. } | BodyOwnerKind::Static(..) => { match tcx.def_kind(self.mir_def) { - DefKind::InlineConst => { + DefKind::InlineConst if !tcx.is_type_system_inline_const(self.mir_def) => { // This is required for `AscribeUserType` canonical query, which will call // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes // into borrowck, which is ICE #78174. diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index e473db48b04c2..d0300d63d1213 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -448,43 +448,6 @@ impl DefKind { | DefKind::ExternCrate => false, } } - - /// Returns `true` if `self` is a kind of definition that does not have its own - /// type-checking context, i.e. closure, coroutine or inline const. - #[inline] - pub fn is_typeck_child(self) -> bool { - match self { - DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => true, - DefKind::Mod - | DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Variant - | DefKind::Trait - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::TraitAlias - | DefKind::AssocTy - | DefKind::TyParam - | DefKind::Fn - | DefKind::Const { .. } - | DefKind::ConstParam - | DefKind::Static { .. } - | DefKind::Ctor(_, _) - | DefKind::AssocFn - | DefKind::AssocConst { .. } - | DefKind::Macro(_) - | DefKind::ExternCrate - | DefKind::Use - | DefKind::ForeignMod - | DefKind::AnonConst - | DefKind::OpaqueTy - | DefKind::Field - | DefKind::LifetimeParam - | DefKind::GlobalAsm - | DefKind::Impl { .. } => false, - } - } } /// The resolution of a path or export. diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 275fed00a4f8a..05648bf7c2ece 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -15,7 +15,7 @@ //! crate as a kind of pass. This should eventually be factored away. use std::cell::Cell; -use std::{assert_matches, iter}; +use std::{assert_matches, debug_assert_matches, iter}; use rustc_abi::{ExternAbi, Size}; use rustc_ast::Recovered; @@ -1635,10 +1635,12 @@ fn const_param_default<'tcx>( } fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKind { + debug_assert_matches!(tcx.def_kind(def), DefKind::AnonConst | DefKind::InlineConst); let hir_id = tcx.local_def_id_to_hir_id(def); let const_arg_id = tcx.parent_hir_id(hir_id); match tcx.hir_node(const_arg_id) { - hir::Node::ConstArg(_) => { + hir::Node::ConstArg(const_arg) => { + debug_assert_matches!(const_arg.kind, hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) if *def_id == def); let parent_hir_node = tcx.hir_node(tcx.parent_hir_id(const_arg_id)); if tcx.features().generic_const_exprs() { ty::AnonConstKind::GCE diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 95a91f1444404..eb9ccb719a84a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -482,7 +482,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let parent_def_id = self.item_def_id(); if let Res::Def(DefKind::ConstParam, _) = res - && tcx.def_kind(parent_def_id) == DefKind::AnonConst + && matches!(tcx.def_kind(parent_def_id), DefKind::AnonConst | DefKind::InlineConst) && let ty::AnonConstKind::MCG = tcx.anon_const_kind(parent_def_id) { let folder = ForbidParamUsesFolder { @@ -511,15 +511,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Inline consts and closures can be nested inside anon consts that forbid generic // params (e.g. an enum discriminant). Walk up the def parent chain to find the // nearest enclosing AnonConst and use that to determine the context. + let parent_def_id = tcx.typeck_root_def_id(parent_def_id.into()); + let anon_const_def_id = match tcx.def_kind(parent_def_id) { DefKind::AnonConst => parent_def_id, - DefKind::InlineConst | DefKind::Closure => { - let root = tcx.typeck_root_def_id(parent_def_id.into()); - match tcx.def_kind(root) { - DefKind::AnonConst => root.expect_local(), - _ => return None, - } - } + DefKind::InlineConst if tcx.is_type_system_inline_const(parent_def_id) => parent_def_id, _ => return None, }; diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index c5a6ce75c0e41..ff00e18783706 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -186,9 +186,13 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } _ => (), } - // Skip `AnonConst`s because we feed their `type_of`. + // Skip `AnonConst`s and type system `InlineConst`s because we feed their `type_of` in + // `feed_anon_const_type`. // Also skip items for which typeck forwards to parent typeck. - if !(matches!(def_kind, DefKind::AnonConst) || def_kind.is_typeck_child()) { + if !(def_kind == DefKind::AnonConst + || def_kind == DefKind::InlineConst && tcx.is_type_system_inline_const(item_def_id) + || tcx.is_typeck_child(item_def_id.to_def_id())) + { tcx.ensure_ok().typeck(item_def_id); } // Ensure we generate the new `DefId` before finishing `check_crate`. diff --git a/compiler/rustc_hir_typeck/src/loops.rs b/compiler/rustc_hir_typeck/src/loops.rs index 21aad64f58d38..ebd8842bd753f 100644 --- a/compiler/rustc_hir_typeck/src/loops.rs +++ b/compiler/rustc_hir_typeck/src/loops.rs @@ -84,6 +84,11 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir CheckLoopVisitor { tcx, cx_stack: vec![Normal], block_breaks: Default::default() }; let cx = match tcx.def_kind(def_id) { DefKind::AnonConst => AnonConst, + DefKind::InlineConst => { + // only type system inline consts are typeck roots + debug_assert!(tcx.is_type_system_inline_const(def_id)); + ConstBlock + } _ => Fn, }; check.with_context(cx, |v| v.visit_body(body)); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c232f595d4229..0f63c8469953d 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1624,7 +1624,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); } } - if let DefKind::AnonConst = def_kind { + if let DefKind::AnonConst | DefKind::InlineConst = def_kind { record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); } if should_encode_const_of_item(self.tcx, def_id, def_kind) { diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 3cab936c45c1f..a37a9d7b4a4e2 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -22,7 +22,7 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, with_metavar_spans}; use crate::hir::{ModuleItems, ProjectedMaybeOwner, nested_filter}; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::{IntoQueryKey, LocalCrate}; -use crate::ty::TyCtxt; +use crate::ty::{self, TyCtxt}; /// An iterator that walks up the ancestor tree of a given `HirId`. /// Constructed using `tcx.hir_parent_iter(hir_id)`. @@ -1115,6 +1115,12 @@ impl<'tcx> TyCtxt<'tcx> { } } + pub fn is_type_system_inline_const(self, def_id: impl IntoQueryKey) -> bool { + let def_id = def_id.into_query_key(); + debug_assert_eq!(self.def_kind(def_id), DefKind::InlineConst); + self.anon_const_kind(def_id) != ty::AnonConstKind::NonTypeSystem + } + pub fn hir_maybe_get_struct_pattern_shorthand_field(self, expr: &Expr<'_>) -> Option { let local = match expr { Expr { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 569a1d5786095..3d7aef0b273f1 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -601,7 +601,14 @@ impl<'tcx> TyCtxt<'tcx> { /// effect. However, we do not want this as a general capability, so this interface restricts /// to the only allowed case. pub fn feed_anon_const_type(self, key: LocalDefId, value: ty::EarlyBinder<'tcx, Ty<'tcx>>) { - debug_assert_eq!(self.def_kind(key), DefKind::AnonConst); + if cfg!(debug_assertions) { + match self.def_kind(key) { + DefKind::AnonConst => (), + DefKind::InlineConst => assert!(self.is_type_system_inline_const(key)), + def_kind => bug!("unexpected DefKind in feed_anon_const_type: {def_kind:?}"), + } + } + TyCtxtFeed { tcx: self, key }.type_of(value) } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 60c2acdb4c4f9..86a6c752d14fe 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -247,7 +247,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy { def_id }, DefKind::TyAlias => ty::AliasTermKind::FreeTy { def_id }, DefKind::Const { .. } => ty::AliasTermKind::FreeConst { def_id }, - DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => { + DefKind::AnonConst | DefKind::InlineConst | DefKind::Ctor(_, CtorKind::Const) => { ty::AliasTermKind::AnonConst { def_id } } kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index ca3b3f0bbec5c..2c91555e3835f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -597,7 +597,38 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns `true` if `def_id` refers to a definition that does not have its own /// type-checking context, i.e. closure, coroutine or inline const. pub fn is_typeck_child(self, def_id: DefId) -> bool { - self.def_kind(def_id).is_typeck_child() + match self.def_kind(def_id) { + DefKind::InlineConst => !self.is_type_system_inline_const(def_id), + DefKind::Closure | DefKind::SyntheticCoroutineBody => true, + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::Fn + | DefKind::Const { .. } + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(_, _) + | DefKind::AssocFn + | DefKind::AssocConst { .. } + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Impl { .. } => false, + } } /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). diff --git a/compiler/rustc_mir_transform/src/trivial_const.rs b/compiler/rustc_mir_transform/src/trivial_const.rs index 25d15c12a7b3b..4b66c757729d3 100644 --- a/compiler/rustc_mir_transform/src/trivial_const.rs +++ b/compiler/rustc_mir_transform/src/trivial_const.rs @@ -52,11 +52,10 @@ where F: FnOnce() -> B, B: Deref>, { - if !matches!( - tcx.def_kind(def), - DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst - ) { - return None; + match tcx.def_kind(def) { + DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst => (), + DefKind::InlineConst if tcx.is_type_system_inline_const(def) => (), + _ => return None, } // If there are impossible predicates then MIR passes will replace the body with diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index a5049339382f7..de3f8c380fc44 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -17,8 +17,7 @@ use tracing::{debug, instrument}; use crate::macros::MacroRulesScopeRef; use crate::{ - ConstArgContext, ImplTraitContext, InvocationParent, ParentScope, Resolver, with_owner, - with_owner_tables, + ImplTraitContext, InvocationParent, ParentScope, Resolver, with_owner, with_owner_tables, }; pub(crate) fn collect_definitions<'ra>( @@ -115,12 +114,6 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { self.invocation_parent.impl_trait_context = orig_itc; } - fn with_const_arg(&mut self, ctxt: ConstArgContext, f: F) { - let orig = mem::replace(&mut self.invocation_parent.const_arg_context, ctxt); - f(self); - self.invocation_parent.const_arg_context = orig; - } - fn collect_field(&mut self, field: &'a FieldDef, index: Option) { let index = |this: &Self| { index.unwrap_or_else(|| { @@ -430,6 +423,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } fn visit_anon_const(&mut self, constant: &'a AnonConst) { + // Note that `visit_anon_const` is skipped for AnonConst nodes wrapped in an + // ExprKind::ConstBlock - these are handled in visit_expr, and are DefKind::InlineConst. + // `MgcaDisambiguation::Direct` is set even when MGCA is disabled, so // to avoid affecting stable we have to feature gate the not creating // anon consts @@ -441,16 +437,12 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } match constant.mgca_disambiguation { - MgcaDisambiguation::Direct => self.with_const_arg(ConstArgContext::Direct, |this| { - visit::walk_anon_const(this, constant); - }), + MgcaDisambiguation::Direct => visit::walk_anon_const(self, constant), MgcaDisambiguation::AnonConst => { - self.with_const_arg(ConstArgContext::NonDirect, |this| { - let parent = this - .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) - .def_id(); - this.with_parent(parent, |this| visit::walk_anon_const(this, constant)); - }) + let parent = self + .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) + .def_id(); + self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); } }; } @@ -459,60 +451,28 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_expr(&mut self, expr: &'a Expr) { debug!(?self.invocation_parent); - let parent_def = match &expr.kind { + match &expr.kind { ExprKind::MacCall(..) => { self.visit_macro_invoc(expr.id); self.visit_invoc(expr.id); - return; } ExprKind::Closure(..) | ExprKind::Gen(..) => { - self.create_def(expr.id, None, DefKind::Closure, expr.span).def_id() + let def = self.create_def(expr.id, None, DefKind::Closure, expr.span).def_id(); + self.with_parent(def, |this| visit::walk_expr(this, expr)); } ExprKind::ConstBlock(constant) => { - // Under `min_generic_const_args` a `const { }` block sometimes - // corresponds to an anon const rather than an inline const. - let def_kind = match self.invocation_parent.const_arg_context { - ConstArgContext::Direct => DefKind::AnonConst, - ConstArgContext::NonDirect => DefKind::InlineConst, - }; - - return self.with_const_arg(ConstArgContext::NonDirect, |this| { - for attr in &expr.attrs { - visit::walk_attribute(this, attr); - } - - let def = - this.create_def(constant.id, None, def_kind, constant.value.span).def_id(); - this.with_parent(def, |this| visit::walk_anon_const(this, constant)); - }); - } - - // Avoid overwriting `const_arg_context` as we may want to treat const blocks - // as being anon consts if we are inside a const argument. - ExprKind::Struct(_) | ExprKind::Call(..) | ExprKind::Tup(..) | ExprKind::Array(..) => { - return visit::walk_expr(self, expr); - } - // FIXME(mgca): we may want to handle block labels in some manner - ExprKind::Block(block, _) if let [stmt] = block.stmts.as_slice() => match stmt.kind { - // FIXME(mgca): this probably means that mac calls that expand - // to semi'd const blocks are handled differently to just writing - // out a semi'd const block. - StmtKind::Expr(..) | StmtKind::MacCall(..) => return visit::walk_expr(self, expr), - - // Fallback to normal behaviour - StmtKind::Let(..) | StmtKind::Item(..) | StmtKind::Semi(..) | StmtKind::Empty => { - self.invocation_parent.parent_def + for attr in &expr.attrs { + visit::walk_attribute(self, attr); } - }, - _ => self.invocation_parent.parent_def, - }; - - self.with_const_arg(ConstArgContext::NonDirect, |this| { - // Note in some cases the `parent_def` here may be the existing parent - // and this is actually a no-op `with_parent` call. - this.with_parent(parent_def, |this| visit::walk_expr(this, expr)) - }) + let def = self + .create_def(constant.id, None, DefKind::InlineConst, constant.value.span) + .def_id(); + // use specifically walk_anon_const, not walk_expr, to skip self.visit_anon_const + self.with_parent(def, |this| visit::walk_anon_const(this, constant)); + } + _ => visit::walk_expr(self, expr), + } } fn visit_ty(&mut self, ty: &'a Ty) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index a20a760f229ae..e7fdfcde0d572 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -186,7 +186,6 @@ struct InvocationParent { parent_def: LocalDefId, impl_trait_context: ImplTraitContext, in_attr: bool, - const_arg_context: ConstArgContext, owner: NodeId, } @@ -195,7 +194,6 @@ impl InvocationParent { parent_def: CRATE_DEF_ID, impl_trait_context: ImplTraitContext::Existential, in_attr: false, - const_arg_context: ConstArgContext::NonDirect, owner: CRATE_NODE_ID, }; } @@ -207,13 +205,6 @@ enum ImplTraitContext { InBinding, } -#[derive(Copy, Clone, Debug)] -enum ConstArgContext { - Direct, - /// Either inside of an `AnonConst` or not inside a const argument at all. - NonDirect, -} - /// Used for tracking import use types which will be used for redundant import checking. /// /// ### Used::Scope Example diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 5920a1e900d19..bac5d47e98676 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -317,6 +317,10 @@ fn opaque_types_defined_by<'tcx>( tcx: TyCtxt<'tcx>, item: LocalDefId, ) -> &'tcx ty::List { + // Closures and coroutines are type checked with their parent + // Note that we also support `SyntheticCoroutineBody` since we create + // a MIR body for the def kind, and some MIR passes (like promotion) + // may require doing analysis using its typing env. if tcx.is_typeck_child(item.to_def_id()) { return tcx.opaque_types_defined_by(tcx.local_parent(item)); } @@ -332,7 +336,11 @@ fn opaque_types_defined_by<'tcx>( | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } - | DefKind::AnonConst => { + | DefKind::AnonConst + | DefKind::InlineConst => { + // Non-type-system inline consts should be caught by `if tcx.is_typeck_child` above + debug_assert!(kind != DefKind::InlineConst || tcx.is_type_system_inline_const(item)); + collector.collect_taits_declared_in_body(); } DefKind::AssocTy | DefKind::TyAlias | DefKind::GlobalAsm => {} @@ -345,15 +353,8 @@ fn opaque_types_defined_by<'tcx>( | DefKind::Trait | DefKind::ForeignTy | DefKind::TraitAlias - - // Closures and coroutines are type checked with their parent - // Note that we also support `SyntheticCoroutineBody` since we create - // a MIR body for the def kind, and some MIR passes (like promotion) - // may require doing analysis using its typing env. | DefKind::Closure - | DefKind::InlineConst | DefKind::SyntheticCoroutineBody - | DefKind::TyParam | DefKind::ConstParam | DefKind::Ctor(_, _) diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index ebec1888e6a95..f60a03fa71b26 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -21,6 +21,24 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( ) -> V::Result { let kind = tcx.def_kind(item); trace!(?kind); + let mut visit_alias = || { + if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { + // If the type of the item uses `_`, we're gonna error out anyway, but + // typeck (which type_of invokes below), will call back into opaque_types_defined_by + // causing a cycle. So we just bail out in this case. + if ty.is_suggestable_infer_ty() { + return V::Result::output(); + } + // Associated types in traits don't necessarily have a type that we can visit + try_visit!( + visitor.visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) + ); + } + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { + try_visit!(visitor.visit(span, pred.skip_norm_wip())); + } + V::Result::output() + }; match kind { // Walk over the signature of the function DefKind::AssocFn | DefKind::Fn => { @@ -47,24 +65,8 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } - | DefKind::AnonConst => { - if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { - // If the type of the item uses `_`, we're gonna error out anyway, but - // typeck (which type_of invokes below), will call back into opaque_types_defined_by - // causing a cycle. So we just bail out in this case. - if ty.is_suggestable_infer_ty() { - return V::Result::output(); - } - // Associated types in traits don't necessarily have a type that we can visit - try_visit!( - visitor - .visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) - ); - } - for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { - try_visit!(visitor.visit(span, pred.skip_norm_wip())); - } - } + | DefKind::AnonConst => return visit_alias(), + DefKind::InlineConst if tcx.is_type_system_inline_const(item) => return visit_alias(), DefKind::OpaqueTy => { for (pred, span) in tcx .explicit_item_bounds(item) diff --git a/tests/ui/const-generics/mgca/double-inline-const.rs b/tests/ui/const-generics/mgca/double-inline-const.rs new file mode 100644 index 0000000000000..9bfbd1819c695 --- /dev/null +++ b/tests/ui/const-generics/mgca/double-inline-const.rs @@ -0,0 +1,11 @@ +#![feature(min_generic_const_args)] + +struct S; + +impl S { + const Q: usize = 2; + fn foo(_: S<{ const { const { Self::Q } } }>) {} + //~^ ERROR generic `Self` types are currently not permitted in anonymous constants +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/double-inline-const.stderr b/tests/ui/const-generics/mgca/double-inline-const.stderr new file mode 100644 index 0000000000000..bc73e3d93575e --- /dev/null +++ b/tests/ui/const-generics/mgca/double-inline-const.stderr @@ -0,0 +1,15 @@ +error: generic `Self` types are currently not permitted in anonymous constants + --> $DIR/double-inline-const.rs:7:35 + | +LL | fn foo(_: S<{ const { const { Self::Q } } }>) {} + | ^^^^ + | +note: not a concrete type + --> $DIR/double-inline-const.rs:5:22 + | +LL | impl S { + | ^^^^ + = help: add `#![feature(generic_const_args)]` to allow generic expressions as the RHS of const items + +error: aborting due to 1 previous error + From aac07469e653d3840c1a5ca2fb8e6c61cf33cc8c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 27 Jun 2026 15:33:09 +0200 Subject: [PATCH 248/278] run x86 intrinsic tests natively --- .../tests/pass/shims/x86/intrinsics-sha.rs | 1 + .../pass/shims/x86/intrinsics-x86-adx.rs | 1 + .../pass/shims/x86/intrinsics-x86-aes-vaes.rs | 10 +++++++-- .../pass/shims/x86/intrinsics-x86-avx.rs | 1 + .../pass/shims/x86/intrinsics-x86-avx2.rs | 1 + .../pass/shims/x86/intrinsics-x86-avx512.rs | 8 +++++++ .../pass/shims/x86/intrinsics-x86-bmi.rs | 1 + .../pass/shims/x86/intrinsics-x86-gfni.rs | 22 ++++++++++++++----- .../shims/x86/intrinsics-x86-pclmulqdq.rs | 1 + .../pass/shims/x86/intrinsics-x86-sse.rs | 1 + .../pass/shims/x86/intrinsics-x86-sse2.rs | 1 + .../shims/x86/intrinsics-x86-sse3-ssse3.rs | 1 + .../pass/shims/x86/intrinsics-x86-sse41.rs | 1 + .../pass/shims/x86/intrinsics-x86-sse42.rs | 1 + .../shims/x86/intrinsics-x86-vpclmulqdq.rs | 1 + .../tests/pass/shims/x86/intrinsics-x86.rs | 5 +++-- 16 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs index ae5731bc8a6a3..1e36867c4a0b6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sha,+sse2,+ssse3,+sse4.1 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs index baa984e68d83b..87b2f5d3d80b3 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+adx +//@run-native #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod x86 { diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs index 8936ae8e91268..ba51dcea35cc6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+aes,+vaes,+avx512f +//@run-native use core::mem::transmute; #[cfg(target_arch = "x86")] @@ -11,7 +12,6 @@ use std::arch::x86_64::*; fn main() { assert!(is_x86_feature_detected!("aes")); assert!(is_x86_feature_detected!("vaes")); - assert!(is_x86_feature_detected!("avx512f")); unsafe { test_aes(); @@ -86,7 +86,7 @@ unsafe fn test_aes() { // be interpreted as integers; signedness does not make sense for them, but // __m128i happens to be defined in terms of signed integers. #[allow(overflowing_literals)] -#[target_feature(enable = "vaes,avx512f")] +#[target_feature(enable = "vaes")] unsafe fn test_vaes() { #[target_feature(enable = "avx")] unsafe fn get_a256() -> __m256i { @@ -177,6 +177,12 @@ unsafe fn test_vaes() { } test_mm256_aesenclast_epi128(); + // The tests below require avx512. + if !is_x86_feature_detected!("avx512f") { + println!("warning: skipping avx512 tests"); + return; + } + #[target_feature(enable = "avx512f")] unsafe fn get_a512() -> __m512i { // Constants are random diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs index 9f7c12c4393b5..8709a17e8aeb4 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs index de1abc818420c..0c88dbef43f87 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs index 31f47b57fd285..0541e9a6cc6e0 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512bitalg,+avx512vpopcntdq,+avx512vnni,+avx512vbmi +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; @@ -9,6 +10,13 @@ use std::arch::x86_64::*; use std::mem::transmute; fn main() { + if !is_x86_feature_detected!("avx512f") { + // GH runners don't have this, but we still want to run this natively if + // the machine happens to have gfni. So we bail out dynamically. + println!("warning: skipping AVX512 tests"); + return; + } + assert!(is_x86_feature_detected!("avx512f")); assert!(is_x86_feature_detected!("avx512vl")); assert!(is_x86_feature_detected!("avx512bw")); diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs index 030258f21fa51..d6c05b601f4eb 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+bmi1,+bmi2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs index 48958ef581096..ef0fd62da5f3f 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+gfni,+avx512f +//@run-native // The constants in the tests below are just bit patterns. They should not // be interpreted as integers; signedness does not make sense for them, but @@ -20,8 +21,14 @@ const CONSTANT_BYTE: i32 = 0x63; fn main() { // Mostly copied from library/stdarch/crates/core_arch/src/x86/gfni.rs - assert!(is_x86_feature_detected!("avx512f")); - assert!(is_x86_feature_detected!("gfni")); + assert!(is_x86_feature_detected!("avx")); + + if !is_x86_feature_detected!("gfni") { + // GH runners don't have this, but we still want to run this natively if + // the machine happens to have gfni. So we bail out dynamically. + println!("warning: skipping gfni tests"); + return; + } unsafe { let byte_mul_test_data = generate_byte_mul_test_data(); @@ -29,15 +36,20 @@ fn main() { let affine_mul_test_data_constant = generate_affine_mul_test_data(CONSTANT_BYTE as u8); let inv_tests_data = generate_inv_tests_data(); - test_mm512_gf2p8mul_epi8(&byte_mul_test_data); test_mm256_gf2p8mul_epi8(&byte_mul_test_data); test_mm_gf2p8mul_epi8(&byte_mul_test_data); - test_mm512_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); test_mm256_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); test_mm_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); - test_mm512_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); test_mm256_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); test_mm_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); + + if is_x86_feature_detected!("avx512f") { + test_mm512_gf2p8mul_epi8(&byte_mul_test_data); + test_mm512_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); + test_mm512_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); + } else { + println!("warning: skipping avx512 tests"); + } } } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs index 6051987f8d4c1..50ce9928ab7ed 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+pclmulqdq +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs index 9136b5eda3870..f42c6db676839 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs @@ -1,5 +1,6 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 +//@run-native #![allow(unnecessary_transmutes)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs index 570da30f0b622..8e4b284b1d150 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs @@ -1,5 +1,6 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 +//@run-native #![allow(unnecessary_transmutes)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs index 10842160abdc6..e226f3512ac6e 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs @@ -2,6 +2,7 @@ //@only-target: x86_64 i686 // SSSE3 implicitly enables SSE3 //@compile-flags: -C target-feature=+ssse3 +//@run-native use core::mem::transmute; #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs index 7331c6ed0db33..48510b46ff327 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sse4.1 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs index 30908baa6c15e..c998fe800df45 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sse4.2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs index e2a045bf81ff1..5ceaf405f4040 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs @@ -3,6 +3,7 @@ //@only-target: x86_64 i686 //@[avx512]compile-flags: -C target-feature=+vpclmulqdq,+avx512f //@[avx]compile-flags: -C target-feature=+vpclmulqdq,+avx2 +//@run-native // The constants in the tests below are just bit patterns. They should not // be interpreted as integers; signedness does not make sense for them, but diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs index a18b6d01524e8..9745a437eccb6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs @@ -1,4 +1,6 @@ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +//@only-target: x86_64 i686 +//@run-native + mod x86 { #[cfg(target_arch = "x86")] use core::arch::x86 as arch; @@ -84,7 +86,6 @@ mod x86_64 { } fn main() { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] x86::main(); #[cfg(target_arch = "x86_64")] x86_64::main(); From a67266628172a3459d87f6261ef54633c8bd9340 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 27 Jun 2026 15:39:29 +0200 Subject: [PATCH 249/278] run aarch64 intrinsic tests natively --- .../miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs | 1 + .../miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs | 1 + .../miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs index 1345924beecf0..c640dde5afaf5 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs @@ -1,6 +1,7 @@ // We're testing aarch64 AES target specific features. //@only-target: aarch64 //@compile-flags: -C target-feature=+neon,+aes +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs index 849f99ee36cce..8d50d152f2ebe 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs @@ -1,6 +1,7 @@ // We're testing aarch64 CRC32 target specific features //@only-target: aarch64 //@compile-flags: -C target-feature=+crc +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs index 884f8eff41bdb..f2fd4b0c7f713 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs @@ -1,6 +1,7 @@ // We're testing aarch64 target specific features //@only-target: aarch64 //@compile-flags: -C target-feature=+neon +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; From 21dfe67a80cbf9ff42bd2b62af42653dba9c8335 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 27 Jun 2026 16:54:44 +0200 Subject: [PATCH 250/278] fix bug in `mpsadbw` the 256-bit implementation actually takes a 6-bit IMM and uses the low 3 bits for the first 128-bit chunk, the next 3 bits for the second 128-bit chunk. Previously we only used the low 3 bits for both chunks. --- src/tools/miri/src/shims/x86/mod.rs | 22 ++++++++++++------- .../pass/shims/x86/intrinsics-x86-avx2.rs | 18 ++++++++++----- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 64b5b786c6c08..b3a8291b9361b 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -879,33 +879,39 @@ fn mpsadbw<'tcx>( assert_eq!(left.layout.size, dest.layout.size); let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?; + assert!(num_chunks <= 2); + let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?; let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?; assert_eq!(op_items_per_chunk, dest_items_per_chunk.strict_mul(2)); let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?; - // Bit 2 of `imm` specifies the offset for indices of `left`. - // The offset is 0 when the bit is 0 or 4 when the bit is 1. - let left_offset = u64::try_from((imm >> 2) & 1).unwrap().strict_mul(4); - // Bits 0..=1 of `imm` specify the offset for indices of - // `right` in blocks of 4 elements. - let right_offset = u64::try_from(imm & 0b11).unwrap().strict_mul(4); for i in 0..num_chunks { let left = ecx.project_index(&left, i)?; let right = ecx.project_index(&right, i)?; let dest = ecx.project_index(&dest, i)?; + // The first 128-bit chunk uses the low 3 bits of IMM, the second chunk uses bits 3..6. + let lane_imm = imm.strict_shr(i.strict_mul(3).try_into().unwrap()); + + // Bit 2 of `lane_imm` specifies the offset for indices of `left`. + // The offset is 0 when the bit is 0 or 4 when the bit is 1. + let left_base = u64::try_from((lane_imm >> 2) & 1).unwrap().strict_mul(4); + // Bits 0..=1 of `lane_imm` specify the offset for indices of + // `right` in blocks of 4 elements. + let right_base = u64::try_from(lane_imm & 0b11).unwrap().strict_mul(4); + for j in 0..dest_items_per_chunk { - let left_offset = left_offset.strict_add(j); + let left_offset = left_base.strict_add(j); let mut res: u16 = 0; for k in 0..4 { let left = ecx .read_scalar(&ecx.project_index(&left, left_offset.strict_add(k))?)? .to_u8()?; let right = ecx - .read_scalar(&ecx.project_index(&right, right_offset.strict_add(k))?)? + .read_scalar(&ecx.project_index(&right, right_base.strict_add(k))?)? .to_u8()?; res = res.strict_add(left.abs_diff(right).into()); } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs index 0c88dbef43f87..7fe75254c2dd8 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs @@ -1069,23 +1069,31 @@ unsafe fn test_avx2() { 18, 20, 22, 24, 26, 28, 30, ); - let r = _mm256_mpsadbw_epu8::<0b000>(a, a); + let r = _mm256_mpsadbw_epu8::<0b00000>(a, a); let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 0, 8, 16, 24, 32, 40, 48, 56); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b001>(a, a); + let r = _mm256_mpsadbw_epu8::<0b001001>(a, a); let e = _mm256_setr_epi16(16, 12, 8, 4, 0, 4, 8, 12, 32, 24, 16, 8, 0, 8, 16, 24); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b100>(a, a); + let r = _mm256_mpsadbw_epu8::<0b000001>(a, a); + let e = _mm256_setr_epi16(16, 12, 8, 4, 0, 4, 8, 12, 0, 8, 16, 24, 32, 40, 48, 56); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b001000>(a, a); + let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 32, 24, 16, 8, 0, 8, 16, 24); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b100100>(a, a); let e = _mm256_setr_epi16(16, 20, 24, 28, 32, 36, 40, 44, 32, 40, 48, 56, 64, 72, 80, 88); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b101>(a, a); + let r = _mm256_mpsadbw_epu8::<0b101101>(a, a); let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 0, 8, 16, 24, 32, 40, 48, 56); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b111>(a, a); + let r = _mm256_mpsadbw_epu8::<0b111111>(a, a); let e = _mm256_setr_epi16(32, 28, 24, 20, 16, 12, 8, 4, 64, 56, 48, 40, 32, 24, 16, 8); assert_eq_m256i(r, e); } From 18c10cd1aa1d624824bf109e06eb408f24548e0e Mon Sep 17 00:00:00 2001 From: mehdiakiki Date: Sat, 20 Jun 2026 18:27:17 -0400 Subject: [PATCH 251/278] Adds RmetaLinkCache a per-link cache that uses path as the key of decoded lib.rmeta-link archive members, and routes add_archive read through it so each rlib link metadata is decoded at most once per link. This is a demand from a previous discussion and we split it out as its own PR. It gives that PR a decode once path tp read instead of reparsing each rlib per crate once native_lib_filenames moves to a link time read. --- .../rustc_codegen_ssa/src/back/archive.rs | 26 +++++++++++++------ compiler/rustc_codegen_ssa/src/back/link.rs | 16 ++++++++++-- .../rustc_codegen_ssa/src/back/rmeta_link.rs | 18 ++++++++++++- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index bdf346d8e69d2..4fc516b244857 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -22,7 +22,7 @@ use rustc_target::spec::Arch; use tracing::trace; use super::metadata::{create_compressed_metadata_file, search_for_section}; -use super::rmeta_link; +use super::rmeta_link::{self, RmetaLinkCache}; use super::symbol_edit::{apply_edits, collect_internal_names}; use crate::common; // Public for ArchiveBuilderBuilder::extract_bundled_libs @@ -311,7 +311,7 @@ fn find_binutils_dlltool(sess: &Session) -> OsString { } pub enum AddArchiveKind<'a> { - Rlib(/*skip*/ &'a dyn Fn(&str, ArchiveEntryKind) -> bool), + Rlib(&'a mut RmetaLinkCache, /*skip*/ &'a dyn Fn(&str, ArchiveEntryKind) -> bool), Other, } @@ -466,7 +466,11 @@ pub fn try_extract_macho_fat_archive( } impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { - fn add_archive(&mut self, archive_path: &Path, ar_kind: AddArchiveKind<'_>) -> io::Result<()> { + fn add_archive( + &mut self, + archive_path: &Path, + mut ar_kind: AddArchiveKind<'_>, + ) -> io::Result<()> { let mut archive_path = archive_path.to_path_buf(); if self.sess.target.llvm_target.contains("-apple-macosx") && let Some(new_archive_path) = try_extract_macho_fat_archive(self.sess, &archive_path)? @@ -481,8 +485,14 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? }; let archive = ArchiveFile::parse(&*archive_map) .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; - let metadata_link = match ar_kind { - AddArchiveKind::Rlib(..) => rmeta_link::read(&archive, &archive_map, &archive_path), + let skip = match &ar_kind { + AddArchiveKind::Rlib(_, skip) => Some(*skip), + AddArchiveKind::Other => None, + }; + let metadata_link = match &mut ar_kind { + AddArchiveKind::Rlib(cache, _) => cache.get_or_insert_with(&archive_path, || { + rmeta_link::read(&archive, &archive_map, &archive_path) + }), AddArchiveKind::Other => None, }; let archive_index = self.src_archives.len(); @@ -512,9 +522,9 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { } else { ArchiveEntryKind::Other }; - let drop = match ar_kind { - AddArchiveKind::Rlib(skip) => skip(&file_name, kind), - AddArchiveKind::Other => false, + let drop = match skip { + Some(skip) => skip(&file_name, kind), + None => false, }; if !drop { let source = if entry.is_thin() { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 2c3ee1bae09f8..0f709247d2333 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -59,6 +59,7 @@ use super::archive::{ use super::command::Command; use super::linker::{self, Linker}; use super::metadata::{MetadataPosition, create_wrapper_file}; +use super::rmeta_link::RmetaLinkCache; use super::rpath::{self, RPathConfig}; use super::{apple, rmeta_link, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; @@ -86,6 +87,7 @@ pub fn link_binary( let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); let mut tempfiles_for_stdout_output: Vec = Vec::new(); + let mut rmeta_link_cache = RmetaLinkCache::default(); for &crate_type in &crate_info.crate_types { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) @@ -139,6 +141,7 @@ pub fn link_binary( link_staticlib( sess, archive_builder_builder, + &mut rmeta_link_cache, &compiled_modules, &crate_info, &metadata, @@ -150,6 +153,7 @@ pub fn link_binary( link_natively( sess, archive_builder_builder, + &mut rmeta_link_cache, crate_type, &out_filename, &compiled_modules, @@ -502,6 +506,7 @@ fn link_rlib<'a>( fn link_staticlib( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, compiled_modules: &CompiledModules, crate_info: &CrateInfo, metadata: &EncodedMetadata, @@ -531,7 +536,7 @@ fn link_staticlib( let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect(); ab.add_archive( path, - AddArchiveKind::Rlib(&|fname: &str, entry_kind| { + AddArchiveKind::Rlib(rmeta_link_cache, &|fname: &str, entry_kind| { // Ignore metadata and rmeta-link files. if fname == METADATA_FILENAME || fname == rmeta_link::FILENAME { return true; @@ -939,6 +944,7 @@ fn report_linker_output( fn link_natively( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_type: CrateType, out_filename: &Path, compiled_modules: &CompiledModules, @@ -965,6 +971,7 @@ fn link_natively( flavor, sess, archive_builder_builder, + rmeta_link_cache, crate_type, tmpdir, temp_filename, @@ -2491,6 +2498,7 @@ fn linker_with_args( flavor: LinkerFlavor, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_type: CrateType, tmpdir: &Path, out_filename: &Path, @@ -2619,6 +2627,7 @@ fn linker_with_args( cmd, sess, archive_builder_builder, + rmeta_link_cache, crate_info, crate_type, tmpdir, @@ -3055,6 +3064,7 @@ fn add_upstream_rust_crates( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_info: &CrateInfo, crate_type: CrateType, tmpdir: &Path, @@ -3107,6 +3117,7 @@ fn add_upstream_rust_crates( cmd, sess, archive_builder_builder, + rmeta_link_cache, crate_info, tmpdir, cnum, @@ -3238,6 +3249,7 @@ fn add_static_crate( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_info: &CrateInfo, tmpdir: &Path, cnum: CrateNum, @@ -3268,7 +3280,7 @@ fn add_static_crate( let mut archive = archive_builder_builder.new_archive_builder(sess); if let Err(error) = archive.add_archive( cratepath, - AddArchiveKind::Rlib(&|f, entry_kind| { + AddArchiveKind::Rlib(rmeta_link_cache, &|f, entry_kind| { if f == METADATA_FILENAME || f == rmeta_link::FILENAME { return true; } diff --git a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs index 68b23ca9ac5cb..58da783277dfe 100644 --- a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs +++ b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs @@ -2,9 +2,10 @@ //! and potentially other data collected and used when building or linking a rlib. //! See . -use std::path::Path; +use std::path::{Path, PathBuf}; use object::read::archive::ArchiveFile; +use rustc_data_structures::fx::FxHashMap; use rustc_serialize::opaque::mem_encoder::MemEncoder; use rustc_serialize::opaque::{MAGIC_END_BYTES, MemDecoder}; use rustc_serialize::{Decodable, Encodable}; @@ -54,3 +55,18 @@ pub fn read_from_data(archive_data: &[u8], rlib_path: &Path) -> Option>, +} + +impl RmetaLinkCache { + pub fn get_or_insert_with( + &mut self, + rlib_path: &Path, + load: impl FnOnce() -> Option, + ) -> Option<&RmetaLink> { + self.cache.entry(rlib_path.to_path_buf()).or_insert_with(load).as_ref() + } +} From 94888f003a2e436f788965cdedfc6a5efc55bed5 Mon Sep 17 00:00:00 2001 From: Emmanuel Ugwu Date: Sat, 27 Jun 2026 17:09:13 +0100 Subject: [PATCH 252/278] update bless output Signed-off-by: Emmanuel Ugwu --- tests/ui/lint/lint-stability.stderr | 95 ++++++++++++++++------------- 1 file changed, 52 insertions(+), 43 deletions(-) diff --git a/tests/ui/lint/lint-stability.stderr b/tests/ui/lint/lint-stability.stderr index fd57908a77b53..67f6804d7c076 100644 --- a/tests/ui/lint/lint-stability.stderr +++ b/tests/ui/lint/lint-stability.stderr @@ -8,7 +8,16 @@ LL | extern crate stability_cfg2; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:45:9 + --> $DIR/lint-stability.rs:21:34 + | +LL | fn test_foreign_type(_: &mut UnstableForeignType) { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `unstable_test_feature` + --> $DIR/lint-stability.rs:48:9 | LL | deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^ @@ -17,7 +26,7 @@ LL | deprecated_unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:47:9 + --> $DIR/lint-stability.rs:50:9 | LL | Trait::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +35,7 @@ LL | Trait::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:49:9 + --> $DIR/lint-stability.rs:52:9 | LL | ::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -35,7 +44,7 @@ LL | ::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:52:9 + --> $DIR/lint-stability.rs:55:9 | LL | deprecated_unstable_text(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +53,7 @@ LL | deprecated_unstable_text(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:54:9 + --> $DIR/lint-stability.rs:57:9 | LL | Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +62,7 @@ LL | Trait::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:56:9 + --> $DIR/lint-stability.rs:59:9 | LL | ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +71,7 @@ LL | ::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:59:9 + --> $DIR/lint-stability.rs:62:9 | LL | unstable(); | ^^^^^^^^ @@ -71,7 +80,7 @@ LL | unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:60:9 + --> $DIR/lint-stability.rs:63:9 | LL | Trait::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +89,7 @@ LL | Trait::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:61:9 + --> $DIR/lint-stability.rs:64:9 | LL | ::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +98,7 @@ LL | ::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:63:9 + --> $DIR/lint-stability.rs:66:9 | LL | unstable_text(); | ^^^^^^^^^^^^^ @@ -98,7 +107,7 @@ LL | unstable_text(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:65:9 + --> $DIR/lint-stability.rs:68:9 | LL | Trait::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +116,7 @@ LL | Trait::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:67:9 + --> $DIR/lint-stability.rs:70:9 | LL | ::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -116,7 +125,7 @@ LL | ::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:99:17 + --> $DIR/lint-stability.rs:102:17 | LL | let _ = DeprecatedUnstableStruct { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -125,7 +134,7 @@ LL | let _ = DeprecatedUnstableStruct { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:103:17 + --> $DIR/lint-stability.rs:106:17 | LL | let _ = UnstableStruct { i: 0 }; | ^^^^^^^^^^^^^^ @@ -134,7 +143,7 @@ LL | let _ = UnstableStruct { i: 0 }; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:107:17 + --> $DIR/lint-stability.rs:110:17 | LL | let _ = DeprecatedUnstableUnitStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -143,7 +152,7 @@ LL | let _ = DeprecatedUnstableUnitStruct; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:109:17 + --> $DIR/lint-stability.rs:112:17 | LL | let _ = UnstableUnitStruct; | ^^^^^^^^^^^^^^^^^^ @@ -152,7 +161,7 @@ LL | let _ = UnstableUnitStruct; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:113:17 + --> $DIR/lint-stability.rs:116:17 | LL | let _ = Enum::DeprecatedUnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -161,7 +170,7 @@ LL | let _ = Enum::DeprecatedUnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:115:17 + --> $DIR/lint-stability.rs:118:17 | LL | let _ = Enum::UnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^ @@ -170,7 +179,7 @@ LL | let _ = Enum::UnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:119:17 + --> $DIR/lint-stability.rs:122:17 | LL | let _ = DeprecatedUnstableTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -179,7 +188,7 @@ LL | let _ = DeprecatedUnstableTupleStruct (1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:121:17 + --> $DIR/lint-stability.rs:124:17 | LL | let _ = UnstableTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^ @@ -188,7 +197,7 @@ LL | let _ = UnstableTupleStruct (1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:130:25 + --> $DIR/lint-stability.rs:133:25 | LL | macro_test_arg!(deprecated_unstable_text()); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +206,7 @@ LL | macro_test_arg!(deprecated_unstable_text()); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:144:9 + --> $DIR/lint-stability.rs:147:9 | LL | Trait::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,7 +215,7 @@ LL | Trait::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:146:9 + --> $DIR/lint-stability.rs:149:9 | LL | ::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -215,7 +224,7 @@ LL | ::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:148:9 + --> $DIR/lint-stability.rs:151:9 | LL | Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -224,7 +233,7 @@ LL | Trait::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:150:9 + --> $DIR/lint-stability.rs:153:9 | LL | ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +242,7 @@ LL | ::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:152:9 + --> $DIR/lint-stability.rs:155:9 | LL | Trait::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^ @@ -242,7 +251,7 @@ LL | Trait::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:153:9 + --> $DIR/lint-stability.rs:156:9 | LL | ::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -251,7 +260,7 @@ LL | ::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:154:9 + --> $DIR/lint-stability.rs:157:9 | LL | Trait::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -260,7 +269,7 @@ LL | Trait::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:156:9 + --> $DIR/lint-stability.rs:159:9 | LL | ::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -269,7 +278,7 @@ LL | ::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:172:10 + --> $DIR/lint-stability.rs:175:10 | LL | impl UnstableTrait for S { } | ^^^^^^^^^^^^^ @@ -278,7 +287,7 @@ LL | impl UnstableTrait for S { } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:174:24 + --> $DIR/lint-stability.rs:177:24 | LL | trait LocalTrait : UnstableTrait { } | ^^^^^^^^^^^^^ @@ -287,7 +296,7 @@ LL | trait LocalTrait : UnstableTrait { } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:179:9 + --> $DIR/lint-stability.rs:182:9 | LL | fn trait_unstable(&self) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -296,7 +305,7 @@ LL | fn trait_unstable(&self) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:184:5 + --> $DIR/lint-stability.rs:187:5 | LL | extern crate inherited_stability; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -305,7 +314,7 @@ LL | extern crate inherited_stability; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:185:9 + --> $DIR/lint-stability.rs:188:9 | LL | use self::inherited_stability::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -314,7 +323,7 @@ LL | use self::inherited_stability::*; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:188:9 + --> $DIR/lint-stability.rs:191:9 | LL | unstable(); | ^^^^^^^^ @@ -323,7 +332,7 @@ LL | unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:191:9 + --> $DIR/lint-stability.rs:194:9 | LL | stable_mod::unstable(); | ^^^^^^^^^^^^^^^^^^^^ @@ -332,7 +341,7 @@ LL | stable_mod::unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:194:9 + --> $DIR/lint-stability.rs:197:9 | LL | unstable_mod::deprecated(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -341,7 +350,7 @@ LL | unstable_mod::deprecated(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:195:9 + --> $DIR/lint-stability.rs:198:9 | LL | unstable_mod::unstable(); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -350,7 +359,7 @@ LL | unstable_mod::unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:197:17 + --> $DIR/lint-stability.rs:200:17 | LL | let _ = Unstable::UnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -359,7 +368,7 @@ LL | let _ = Unstable::UnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:198:17 + --> $DIR/lint-stability.rs:201:17 | LL | let _ = Unstable::StableVariant; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -368,7 +377,7 @@ LL | let _ = Unstable::StableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:88:48 + --> $DIR/lint-stability.rs:91:48 | LL | struct S1(T::TypeUnstable); | ^^^^^^^^^^^^^^^ @@ -377,7 +386,7 @@ LL | struct S1(T::TypeUnstable); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:92:13 + --> $DIR/lint-stability.rs:95:13 | LL | TypeUnstable = u8, | ^^^^^^^^^^^^^^^^^ @@ -385,6 +394,6 @@ LL | TypeUnstable = u8, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 43 previous errors +error: aborting due to 44 previous errors For more information about this error, try `rustc --explain E0658`. From 075f549a564287065b631a8d8a769dd0fc2864f1 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 22 Jun 2026 22:32:58 +0200 Subject: [PATCH 253/278] add `vqdmulh*` aarch64 intrinsics --- src/tools/miri/src/shims/aarch64.rs | 39 +++++++++++++++ .../shims/aarch64/intrinsics-aarch64-neon.rs | 49 +++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/src/tools/miri/src/shims/aarch64.rs b/src/tools/miri/src/shims/aarch64.rs index deab856b3e24f..3d8946fb36720 100644 --- a/src/tools/miri/src/shims/aarch64.rs +++ b/src/tools/miri/src/shims/aarch64.rs @@ -146,6 +146,45 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } + // Signed saturating doubling multiply returning the high half. + // + // Used by the `vqdmulh*` functions. + // + // This LLVM intrinsic multiplies the values of corresponding elements of the two source + // vector registers (which are signed integers), doubles the results, places the most significant half of the + // final results (using a saturating cast to fit the element type) into a vector, and writes the vector to the destination register. + // + // https://developer.arm.com/architectures/instruction-sets/intrinsics#f:@navigationhierarchiessimdisa=[Neon]&q=vqdmulh + name if name.starts_with("neon.sqdmulh.") => { + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + let (left, left_len) = this.project_to_simd(left)?; + let (right, right_len) = this.project_to_simd(right)?; + let (dest, dest_len) = this.project_to_simd(dest)?; + assert_eq!(left_len, right_len); + assert_eq!(left_len, dest_len); + + let elem_size = dest.layout.field(this, 0).size; + let bits = elem_size.bits(); + let min = elem_size.signed_int_min(); + let max = elem_size.signed_int_max(); + + for i in 0..dest_len { + let a = this.read_scalar(&this.project_index(&left, i)?)?.to_int(elem_size)?; + let b = this.read_scalar(&this.project_index(&right, i)?)?.to_int(elem_size)?; + + // Uses i128 arithmetic, which cannot overflow because the intrinsic takes at most i32. + let doubled = a.strict_mul(b).strict_mul(2); + let res = (doubled >> bits).clamp(min, max); + + this.write_scalar( + Scalar::from_int(res, elem_size), + &this.project_index(&dest, i)?, + )?; + } + } + // Vector table lookup: each index selects a byte from the 16-byte table, out-of-range -> 0. // Used to implement vtbl1_u8 function. // LLVM does not have a portable shuffle that takes non-const indices diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs index 884f8eff41bdb..9a400b61e2e59 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs @@ -14,6 +14,7 @@ fn main() { test_tbl1_v16i8_basic(); test_vpadd(); test_vpaddl(); + test_vqdmulh(); } } @@ -157,3 +158,51 @@ unsafe fn test_vpaddl() { vst1q_u64(r.as_mut_ptr(), vpaddlq_u32(a)); assert_eq!(r, e); } + +#[target_feature(enable = "neon")] +unsafe fn test_vqdmulh() { + let a = vld1_s32([i32::MIN, i32::MAX].as_ptr()); + let r: [i32; 2] = transmute(vqdmulh_n_s32(a, i32::MIN)); + assert_eq!(r, [i32::MAX, -i32::MAX]); + + // This is the actual calculation that happens. + let product = i32::MIN as i128 * i32::MIN as i128 * 2; + assert_eq!(i32::MAX, (product >> 32).clamp(i32::MIN as i128, i32::MAX as i128) as i32); + + let product = i32::MAX as i128 * i32::MIN as i128 * 2; + assert_eq!(-i32::MAX, (product >> 32).clamp(i32::MIN as i128, i32::MAX as i128) as i32); + + let b = vld1_s32([123, i32::MIN].as_ptr()); + let r: [i32; 2] = transmute(vqdmulh_s32(a, b)); + assert_eq!(r, [-123, -i32::MAX]); + + // Wider 32-bit versions. + let a = vld1q_s32([0x4000_0000, -0x4000_0000, i32::MIN, i32::MAX].as_ptr()); + + let b = vld1q_s32([123, 456, 0x4000_0000, 789].as_ptr()); + let r: [i32; 4] = transmute(vqdmulhq_s32(a, b)); + assert_eq!(r, [61, -228, -1073741824, 788]); + + let r: [i32; 4] = transmute(vqdmulhq_n_s32(a, 0x4000_0000)); + assert_eq!(r, [536870912, -536870912, -1073741824, 1073741823]); + + // 16-bit versions. + + let a = vld1_s16([i16::MIN, i16::MAX, 0, 16384].as_ptr()); + let r: [i16; 4] = transmute(vqdmulh_n_s16(a, i16::MIN)); + assert_eq!(r, [i16::MAX, -i16::MAX, 0, -16384]); + + let b = vld1_s16([123, i16::MIN, 456, 789].as_ptr()); + let r: [i16; 4] = transmute(vqdmulh_s16(a, b)); + assert_eq!(r, [-123, -i16::MAX, 0, 394]); + + // Wider 16-bit versions. + + let a = vld1q_s16([i16::MIN, i16::MAX, 0, 16384, -16384, 8192, 1, -1].as_ptr()); + let b = vld1q_s16([123, 456, 789, i16::MIN, 1, 2, 3, 4].as_ptr()); + let r: [i16; 8] = transmute(vqdmulhq_s16(a, b)); + assert_eq!(r, [-123, 455, 0, -16384, -1, 0, 0, -1]); + + let r: [i16; 8] = transmute(vqdmulhq_n_s16(a, i16::MIN)); + assert_eq!(r, [32767, -32767, 0, -16384, 16384, -8192, -1, 1]); +} From 9aaea562307a3b1866ba53d895fc13ab5073ea66 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sat, 27 Jun 2026 16:59:46 -0300 Subject: [PATCH 254/278] Recover deferred closure calls after errors --- compiler/rustc_hir_typeck/src/callee.rs | 17 +++++--- .../src/error_reporting/traits/suggestions.rs | 10 +++++ ...rred-closure-call-recovery-issue-157951.rs | 10 +++++ ...-closure-call-recovery-issue-157951.stderr | 39 +++++++++++++++++++ 4 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs create mode 100644 tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 57ab29ac752ad..57734d9be582d 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -9,11 +9,11 @@ use rustc_hir::{self as hir, HirId, LangItem, find_attr}; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; +use rustc_middle::bug; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::{self, FnSig, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; -use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; use rustc_span::{Ident, Span, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; @@ -1161,11 +1161,16 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> { ); } None => { - span_bug!( - self.call_expr.span, - "Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`", - self.closure_ty - ) + let guar = fcx.tainted_by_errors().unwrap_or_else(|| { + fcx.dcx().span_delayed_bug( + self.call_expr.span, + format!( + "Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`", + self.closure_ty + ), + ) + }); + fcx.write_resolution(self.call_expr.hir_id, Err(guar)); } } } 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 c3f4a09b2d431..754fbe24a8464 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2555,6 +2555,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.children.clear(); let mut span = obligation.cause.span; + let mut is_async_fn_return = false; if let DefKind::Closure = self.tcx.def_kind(obligation.cause.body_id) && let parent = self.tcx.local_parent(obligation.cause.body_id) && let DefKind::Fn | DefKind::AssocFn = self.tcx.def_kind(parent) @@ -2570,10 +2571,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // and // async fn foo() -> dyn Display Box span = fn_sig.decl.output.span(); + is_async_fn_return = true; err.span(span); } let body = self.tcx.hir_body_owned_by(obligation.cause.body_id); + if !is_async_fn_return + && let Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = + self.tcx.hir_node_by_def_id(obligation.cause.body_id) + && matches!(closure.fn_decl.output, hir::FnRetTy::DefaultReturn(_)) + { + return true; + } + let mut visitor = ReturnsVisitor::default(); visitor.visit_body(&body); diff --git a/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs new file mode 100644 index 0000000000000..d1e2a25b29521 --- /dev/null +++ b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs @@ -0,0 +1,10 @@ +//@ compile-flags: -Znext-solver=globally +//@ check-fail + +fn main() { + let f = |f: dyn Fn()| f; + //~^ ERROR the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time + //~| ERROR return type cannot be a trait object without pointer indirection + f(); + //~^ ERROR this function takes 1 argument but 0 arguments were supplied +} diff --git a/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr new file mode 100644 index 0000000000000..a20e5f331d31a --- /dev/null +++ b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr @@ -0,0 +1,39 @@ +error[E0277]: the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:17 + | +LL | let f = |f: dyn Fn()| f; + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Fn() + 'static)` + = help: unsized fn params are gated as an unstable feature +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | let f = |f: &dyn Fn()| f; + | + + +error[E0746]: return type cannot be a trait object without pointer indirection + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:27 + | +LL | let f = |f: dyn Fn()| f; + | ^ doesn't have a size known at compile-time + +error[E0057]: this function takes 1 argument but 0 arguments were supplied + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:8:5 + | +LL | f(); + | ^-- argument #1 of type `(dyn Fn() + 'static)` is missing + | +note: closure defined here + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:13 + | +LL | let f = |f: dyn Fn()| f; + | ^^^^^^^^^^^^^ +help: provide the argument + | +LL | f(/* (dyn Fn() + 'static) */); + | ++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0057, E0277, E0746. +For more information about an error, try `rustc --explain E0057`. From 3515461784dd940137379fe8d86e07d6783c5817 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 27 Jun 2026 23:14:21 +0200 Subject: [PATCH 255/278] use `"llvm.prefetch.p0"` instead of `"llvm.prefetch"` LLVM updated the name, the old one still works but stdarch is now using the new one --- compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs | 2 +- compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs | 2 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 +- src/tools/miri/src/shims/foreign_items.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index 2dee9176936fd..9ec84d135dc8f 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -19,7 +19,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( } match intrinsic { - "llvm.prefetch" => { + "llvm.prefetch.p0" => { // Nothing to do. This is merely a perf hint. } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs index d58697f1bf270..41efe3e8209bf 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs @@ -1044,7 +1044,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function #[cfg(feature = "master")] pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function<'gcc> { let gcc_name = match name { - "llvm.prefetch" => { + "llvm.prefetch.p0" => { let gcc_name = "__builtin_prefetch"; let func = cx.context.get_builtin_function(gcc_name); cx.functions.borrow_mut().insert(gcc_name.to_string(), func); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 0b03d6862ca84..4ec398a6ec27d 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -402,7 +402,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let ptr = args[0].immediate(); let locality = fn_args.const_at(1).to_leaf().to_i32(); self.call_intrinsic( - "llvm.prefetch", + "llvm.prefetch.p0", &[self.val_ty(ptr)], &[ ptr, diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index e4daab7a2005d..5fd86769e96b5 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -789,7 +789,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } // LLVM intrinsics - "llvm.prefetch" => { + "llvm.prefetch.p0" => { let [p, rw, loc, ty] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; From 96bf9d674be7b8867999840cc1eb7a17bd0ceb4c Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 27 Jun 2026 17:29:27 -0400 Subject: [PATCH 256/278] Revert "LLVM 23: Adapt codegen test to moved assume" This reverts commit 46918905875c454418f8b41bbbc982e54f8ab668. --- .../issues/issue-107681-unwrap_unchecked.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs index c594e187a0eb5..5834255f3d313 100644 --- a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs +++ b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs @@ -1,6 +1,4 @@ //@ compile-flags: -Copt-level=3 -//@ filecheck-flags: --implicit-check-not 'br {{.*}}' --implicit-check-not 'select' -//@ min-llvm-version: 22 // Test for #107681. // Make sure we don't create `br` or `select` instructions. @@ -13,8 +11,10 @@ use std::slice::Iter; #[no_mangle] pub unsafe fn foo(x: &mut Copied>) -> u32 { // CHECK-LABEL: @foo( - // CHECK: [[INNER:%.*]] = load ptr, ptr %x - // CHECK: [[RET:%.*]] = load i32, ptr [[INNER]] - // CHECK: ret i32 [[RET]] + // CHECK-NOT: br {{.*}} + // CHECK-NOT: select + // CHECK: [[RET:%.*]] = load i32, ptr + // CHECK-NEXT: assume + // CHECK-NEXT: ret i32 [[RET]] x.next().unwrap_unchecked() } From 77e396d08db606c7d4529391f0b029027db0c093 Mon Sep 17 00:00:00 2001 From: Valentyn Kit Date: Sun, 28 Jun 2026 11:10:47 +0300 Subject: [PATCH 257/278] std: treat ENOMEM as transient in the pidfd support probe --- library/std/src/sys/process/unix/unix.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 1dd684975c2dd..b6ccc6b698fda 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -510,8 +510,8 @@ impl Command { support = SPAWN; } } - Err(e) if matches!(e.raw_os_error(), Some(libc::EMFILE | libc::ENFILE)) => { - // We're temporarily(?) out of file descriptors. In this case pidfd_spawnp would also fail + Err(e) if matches!(e.raw_os_error(), Some(libc::EMFILE | libc::ENFILE | libc::ENOMEM)) => { + // We're temporarily(?) out of file descriptors or memory. In this case pidfd_spawnp would also fail // Don't update the support flag so we can probe again later. return Err(e) } From 3d31306f1dc081eeaf46e7890e84274963869305 Mon Sep 17 00:00:00 2001 From: Hanna Kruppe Date: Sat, 6 Jun 2026 18:06:07 +0200 Subject: [PATCH 258/278] Merge and reframe strict_provenance_lints --- .../rustc_lint/src/fuzzy_provenance_casts.rs | 79 ------------ .../src/implicit_provenance_casts.rs | 122 ++++++++++++++++++ compiler/rustc_lint/src/lib.rs | 11 +- compiler/rustc_lint/src/lints.rs | 28 ++-- .../rustc_lint/src/lossy_provenance_casts.rs | 98 -------------- library/alloc/src/lib.rs | 3 +- library/alloctests/benches/lib.rs | 3 +- library/alloctests/tests/lib.rs | 3 +- library/core/src/lib.rs | 3 +- library/core/src/ptr/const_ptr.rs | 2 +- library/core/src/ptr/mod.rs | 4 +- library/core/src/ptr/mut_ptr.rs | 2 +- library/coretests/tests/lib.rs | 3 +- library/std/src/lib.rs | 11 +- library/std/src/os/unix/thread.rs | 4 +- library/std/src/os/windows/io/socket.rs | 2 +- library/std/src/sys/args/sgx.rs | 3 +- library/std/src/sys/env/sgx.rs | 3 +- library/std/src/sys/pal/sgx/mod.rs | 3 +- .../strict-provenance-lints.md | 11 +- .../feature-gate-strict_provenance_lints.rs | 10 +- ...eature-gate-strict_provenance_lints.stderr | 39 +++++- .../lint-strict-provenance-fuzzy-casts.rs | 4 +- .../lint-strict-provenance-fuzzy-casts.stderr | 11 +- .../lint-strict-provenance-lossy-casts.rs | 10 +- .../lint-strict-provenance-lossy-casts.stderr | 24 ++-- .../lint-strict-provenance-macro-casts.rs | 11 +- .../lint-strict-provenance-macro-casts.stderr | 37 +++--- 28 files changed, 253 insertions(+), 291 deletions(-) delete mode 100644 compiler/rustc_lint/src/fuzzy_provenance_casts.rs create mode 100644 compiler/rustc_lint/src/implicit_provenance_casts.rs delete mode 100644 compiler/rustc_lint/src/lossy_provenance_casts.rs diff --git a/compiler/rustc_lint/src/fuzzy_provenance_casts.rs b/compiler/rustc_lint/src/fuzzy_provenance_casts.rs deleted file mode 100644 index 699004a3b37e0..0000000000000 --- a/compiler/rustc_lint/src/fuzzy_provenance_casts.rs +++ /dev/null @@ -1,79 +0,0 @@ -use rustc_hir as hir; -use rustc_session::{declare_lint, declare_lint_pass}; - -use crate::lints::{LossyProvenanceInt2Ptr, LossyProvenanceInt2PtrSuggestion}; -use crate::{LateContext, LateLintPass}; - -declare_lint! { - /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer - /// and a pointer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(fuzzy_provenance_casts)] - /// - /// fn main() { - /// let _dangling = 16_usize as *const u8; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting an integer to a pointer is considered bad style, as a pointer - /// contains, besides the *address* also a *provenance*, indicating what - /// memory the pointer is allowed to read/write. Casting an integer, which - /// doesn't have provenance, to a pointer requires the compiler to assign - /// (guess) provenance. The compiler assigns "all exposed valid" (see the - /// docs of [`ptr::with_exposed_provenance`] for more information about this - /// "exposing"). This penalizes the optimiser and is not well suited for - /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI - /// platforms). - /// - /// It is much better to use [`ptr::with_addr`] instead to specify the - /// provenance you want. If using this function is not possible because the - /// code relies on exposed provenance then there is as an escape hatch - /// [`ptr::with_exposed_provenance`]. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr - /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html - pub FUZZY_PROVENANCE_CASTS, - Allow, - "a fuzzy integer to pointer cast is used", - @feature_gate = strict_provenance_lints; -} - -declare_lint_pass!( - /// Lint for `as` casts between an integer and a pointer. - FuzzyProvenanceCasts => [FUZZY_PROVENANCE_CASTS] -); - -impl<'tcx> LateLintPass<'tcx> for FuzzyProvenanceCasts { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; - - let typeck_results = cx.typeck_results(); - // Only lint casts from integer to pointer - let cast_from_ty = typeck_results.expr_ty(cast_from_expr); - if !cast_from_ty.is_integral() { - return; - } - let cast_to_ty = typeck_results.expr_ty(expr); - if !cast_to_ty.is_raw_ptr() { - return; - } - - let sugg = - expr.span.can_be_used_for_suggestions().then(|| LossyProvenanceInt2PtrSuggestion { - lo: cast_from_expr.span.shrink_to_lo(), - hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span), - }); - let lint = LossyProvenanceInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg }; - cx.tcx.emit_node_span_lint(FUZZY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint) - } -} diff --git a/compiler/rustc_lint/src/implicit_provenance_casts.rs b/compiler/rustc_lint/src/implicit_provenance_casts.rs new file mode 100644 index 0000000000000..c77ecbe273387 --- /dev/null +++ b/compiler/rustc_lint/src/implicit_provenance_casts.rs @@ -0,0 +1,122 @@ +use rustc_ast::util::parser::ExprPrecedence; +use rustc_hir as hir; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{ + ImplicitProvenanceCastsInt2Ptr, ImplicitProvenanceCastsPtr2Int, Int2PtrSuggestion, + Ptr2IntSuggestion, +}; +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `implicit_provenance_casts` lint detects integer-to-pointer and pointer-to-integer + /// casts. + /// + /// ### Example + /// + /// ```rust + /// #![feature(strict_provenance_lints)] + /// #![warn(implicit_provenance_casts)] + /// + /// fn main() { + /// let x: u8 = 37; + /// let addr: usize = &x as *const u8 as usize; + /// let _ptr = addr as *const u8; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint exists to help migrate code to [*Strict Provenance* APIs][strict-provenance] where + /// possible, and make remaining uses of [*Exposed Provenance*][exposed-provenance] explicit. + /// For more information on pointer provenance, see the [`std::ptr` documentation][provenance]. + /// + /// Earlier versions of Rust did not have a clear answer how integer-to-pointer and + /// pointer-to-integer casts interact with provenance. Such casts are now defined to use the + /// exposed provenance model, but in many cases the code can be updated to strict provenance + /// APIs, which is preferable as it enables more precise reasoning about unsafe code, both by + /// humans and by tools like [Miri]. + /// + /// However, there are situations where exposed provenance is required or following the strict + /// provenance model requires major refactorings. In those cases, it's still useful to replace + /// the `as` casts with equivalent explicit use of exposed provenance APIs and a comment + /// explaining why they are needed. + /// + /// [provenance]: https://doc.rust-lang.org/core/ptr/index.html#provenance + /// [strict-provenance]: https://doc.rust-lang.org/core/ptr/index.html#strict-provenance + /// [exposed-provenance]: https://doc.rust-lang.org/core/ptr/index.html#exposed-provenance + /// [Miri]: https://github.com/rust-lang/miri + pub IMPLICIT_PROVENANCE_CASTS, + Allow, + "an `as` cast relying on exposed provenance is used", + @feature_gate = strict_provenance_lints; +} + +declare_lint_pass!( + /// Lint for int2ptr and ptr2int `as` casts. + ImplicitProvenanceCasts => [IMPLICIT_PROVENANCE_CASTS] +); + +impl<'tcx> LateLintPass<'tcx> for ImplicitProvenanceCasts { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; + + let typeck_results = cx.typeck_results(); + let cast_from_ty = typeck_results.expr_ty(cast_from_expr); + if cast_from_ty.is_raw_ptr() { + let cast_to_ty = typeck_results.expr_ty(expr); + if cast_to_ty.is_integral() { + lint_ptr2int(cx, expr, cast_from_expr, cast_from_ty, cast_to_hir, cast_to_ty) + } + } else if cast_from_ty.is_integral() { + let cast_to_ty = typeck_results.expr_ty(expr); + if cast_to_ty.is_raw_ptr() { + lint_int2ptr(cx, expr, cast_from_expr, cast_from_ty, cast_to_hir, cast_to_ty) + } + } + } +} + +fn lint_ptr2int<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + cast_from_expr: &'tcx hir::Expr<'tcx>, + cast_from_ty: Ty<'tcx>, + cast_to_hir: &'tcx hir::Ty<'tcx>, + cast_to_ty: Ty<'tcx>, +) { + let sugg = expr.span.can_be_used_for_suggestions().then(|| { + let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous; + let needs_cast = !cast_to_ty.is_usize(); + let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span); + let expr_span = cast_from_expr.span.shrink_to_lo(); + match (needs_parens, needs_cast) { + (true, true) => Ptr2IntSuggestion::NeedsParensCast { expr_span, cast_span, cast_to_ty }, + (true, false) => Ptr2IntSuggestion::NeedsParens { expr_span, cast_span }, + (false, true) => Ptr2IntSuggestion::NeedsCast { cast_span, cast_to_ty }, + (false, false) => Ptr2IntSuggestion::Other { cast_span }, + } + }); + + let lint = ImplicitProvenanceCastsPtr2Int { cast_from_ty, cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(IMPLICIT_PROVENANCE_CASTS, expr.hir_id, expr.span, lint); +} + +fn lint_int2ptr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + cast_from_expr: &'tcx hir::Expr<'tcx>, + cast_from_ty: Ty<'tcx>, + cast_to_hir: &'tcx hir::Ty<'tcx>, + cast_to_ty: Ty<'tcx>, +) { + let sugg = expr.span.can_be_used_for_suggestions().then(|| Int2PtrSuggestion { + lo: cast_from_expr.span.shrink_to_lo(), + hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span), + }); + let lint = ImplicitProvenanceCastsInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(IMPLICIT_PROVENANCE_CASTS, expr.hir_id, expr.span, lint) +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 72c9ba0e5afbf..74bee7d705e3a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -45,10 +45,10 @@ mod expect; mod for_loops_over_fallibles; mod foreign_modules; mod function_cast_as_integer; -mod fuzzy_provenance_casts; mod gpukernel_abi; mod if_let_rescope; mod impl_trait_overcaptures; +mod implicit_provenance_casts; mod interior_mutable_consts; mod internal; mod invalid_from_utf8; @@ -57,7 +57,6 @@ mod let_underscore; mod levels; pub mod lifetime_syntax; mod lints; -mod lossy_provenance_casts; mod macro_expr_fragment_specifier_2024_migration; mod map_unit_fn; mod multiple_supertrait_upcastable; @@ -95,16 +94,15 @@ use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; use function_cast_as_integer::*; -use fuzzy_provenance_casts::FuzzyProvenanceCasts; use gpukernel_abi::*; use if_let_rescope::IfLetRescope; use impl_trait_overcaptures::ImplTraitOvercaptures; +use implicit_provenance_casts::ImplicitProvenanceCasts; use interior_mutable_consts::*; use internal::*; use invalid_from_utf8::*; use let_underscore::*; use lifetime_syntax::*; -use lossy_provenance_casts::LossyProvenanceCasts; use macro_expr_fragment_specifier_2024_migration::*; use map_unit_fn::*; use multiple_supertrait_upcastable::*; @@ -270,8 +268,7 @@ late_lint_methods!( CheckTransmutes: CheckTransmutes, LifetimeSyntax: LifetimeSyntax, InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, - FuzzyProvenanceCasts: FuzzyProvenanceCasts, - LossyProvenanceCasts: LossyProvenanceCasts, + ImplicitProvenanceCasts: ImplicitProvenanceCasts, ] ] ); @@ -410,6 +407,8 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("static_mut_ref", "static_mut_refs"); store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"); store.register_renamed("elided_named_lifetimes", "mismatched_lifetime_syntaxes"); + store.register_renamed("fuzzy_provenance_casts", "implicit_provenance_casts"); + store.register_renamed("lossy_provenance_casts", "implicit_provenance_casts"); // These were moved to tool lints, but rustc still sees them when compiling normally, before // tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 43c0ab4804c84..83827c57aef00 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2934,23 +2934,24 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { pub(crate) struct EqInternalMethodImplemented; #[derive(Diagnostic)] -#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")] +#[diag("cast from `{$expr_ty}` to `{$cast_ty}` implicitly relies on exposed provenance")] #[help( - "if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead" + "if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()`" )] -pub(crate) struct LossyProvenanceInt2Ptr<'tcx> { +#[note("for more information, visit ")] +pub(crate) struct ImplicitProvenanceCastsInt2Ptr<'tcx> { pub expr_ty: Ty<'tcx>, pub cast_ty: Ty<'tcx>, #[subdiagnostic] - pub sugg: Option, + pub sugg: Option, } #[derive(Subdiagnostic)] #[multipart_suggestion( - "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address", + "use `.with_addr()` to adjust the address of a valid pointer in the same allocation", applicability = "has-placeholders" )] -pub(crate) struct LossyProvenanceInt2PtrSuggestion { +pub(crate) struct Int2PtrSuggestion { #[suggestion_part(code = "(...).with_addr(")] pub lo: Span, #[suggestion_part(code = ")")] @@ -2958,21 +2959,18 @@ pub(crate) struct LossyProvenanceInt2PtrSuggestion { } #[derive(Diagnostic)] -#[diag( - "under strict provenance it is considered bad style to cast pointer `{$cast_from_ty}` to integer `{$cast_to_ty}`" -)] -#[help( - "if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead" -)] -pub(crate) struct LossyProvenancePtr2Int<'tcx> { +#[diag("cast from `{$cast_from_ty}` to `{$cast_to_ty}` implicitly exposes pointer provenance")] +#[help("if conforming to strict provenance is not possible, use `.expose_provenance()`")] +#[note("for more information, visit ")] +pub(crate) struct ImplicitProvenanceCastsPtr2Int<'tcx> { pub cast_from_ty: Ty<'tcx>, pub cast_to_ty: Ty<'tcx>, #[subdiagnostic] - pub sugg: Option>, + pub sugg: Option>, } #[derive(Subdiagnostic)] -pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> { +pub(crate) enum Ptr2IntSuggestion<'tcx> { #[multipart_suggestion( "use `.addr()` to obtain the address of a pointer", applicability = "maybe-incorrect" diff --git a/compiler/rustc_lint/src/lossy_provenance_casts.rs b/compiler/rustc_lint/src/lossy_provenance_casts.rs deleted file mode 100644 index bdb5bdf6bb0e3..0000000000000 --- a/compiler/rustc_lint/src/lossy_provenance_casts.rs +++ /dev/null @@ -1,98 +0,0 @@ -use rustc_ast::util::parser::ExprPrecedence; -use rustc_hir as hir; -use rustc_session::{declare_lint, declare_lint_pass}; - -use crate::lints::{LossyProvenancePtr2Int, LossyProvenancePtr2IntSuggestion}; -use crate::{LateContext, LateLintPass}; - -declare_lint! { - /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer - /// and an integer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(lossy_provenance_casts)] - /// - /// fn main() { - /// let x: u8 = 37; - /// let _addr: usize = &x as *const u8 as usize; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting a pointer to an integer is a lossy operation, because beyond - /// just an *address* a pointer may be associated with a particular - /// *provenance*. This information is used by the optimiser and for dynamic - /// analysis/dynamic program verification (e.g. Miri or CHERI platforms). - /// - /// Since this cast is lossy, it is considered good style to use the - /// [`ptr::addr`] method instead, which has a similar effect, but doesn't - /// "expose" the pointer provenance. This improves optimisation potential. - /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information - /// about exposing pointer provenance. - /// - /// If your code can't comply with strict provenance and needs to expose - /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch, - /// which preserves the behaviour of `as usize` casts while being explicit - /// about the semantics. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr - /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance - pub LOSSY_PROVENANCE_CASTS, - Allow, - "a lossy pointer to integer cast is used", - @feature_gate = strict_provenance_lints; -} - -declare_lint_pass!( - /// Lint for `as` casts between a pointer and an integer. - LossyProvenanceCasts => [LOSSY_PROVENANCE_CASTS] -); - -impl<'tcx> LateLintPass<'tcx> for LossyProvenanceCasts { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; - - let typeck_results = cx.typeck_results(); - // Only lint casts from pointer to integer - let cast_from_ty = typeck_results.expr_ty(cast_from_expr); - if !cast_from_ty.is_raw_ptr() { - return; - } - let cast_to_ty = typeck_results.expr_ty(expr); - if !cast_to_ty.is_integral() { - return; - } - - let sugg = expr.span.can_be_used_for_suggestions().then(|| { - let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous; - let needs_cast = !cast_to_ty.is_usize(); - let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span); - let expr_span = cast_from_expr.span.shrink_to_lo(); - match (needs_parens, needs_cast) { - (true, true) => LossyProvenancePtr2IntSuggestion::NeedsParensCast { - expr_span, - cast_span, - cast_to_ty, - }, - (true, false) => { - LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } - } - (false, true) => { - LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_to_ty } - } - (false, false) => LossyProvenancePtr2IntSuggestion::Other { cast_span }, - } - }); - - let lint = LossyProvenancePtr2Int { cast_from_ty, cast_to_ty, sugg }; - cx.tcx.emit_node_span_lint(LOSSY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint); - } -} diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index f8cab52633fe1..1c6a74943a2f8 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -75,8 +75,7 @@ #![needs_allocator] // Lints: #![deny(unsafe_op_in_unsafe_fn)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![warn(deprecated_in_future)] #![warn(missing_debug_implementations)] #![warn(missing_docs)] diff --git a/library/alloctests/benches/lib.rs b/library/alloctests/benches/lib.rs index 2be7a24d2de7b..4b7139d943593 100644 --- a/library/alloctests/benches/lib.rs +++ b/library/alloctests/benches/lib.rs @@ -6,8 +6,7 @@ #![feature(slice_partition_dedup)] #![feature(strict_provenance_lints)] #![feature(test)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] extern crate test; diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index f7feb3bbbca85..f89b974cd290c 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -43,8 +43,7 @@ #![feature(vec_try_remove)] #![feature(ptr_cast_slice)] #![allow(internal_features)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] extern crate alloc; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 61b6741219f7e..50e584f1dc539 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -68,8 +68,7 @@ // Lints: #![deny(rust_2021_incompatible_or_patterns)] #![deny(unsafe_op_in_unsafe_fn)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![warn(deprecated_in_future)] #![warn(missing_debug_implementations)] #![warn(missing_docs)] diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index b7fcd6a5cd3de..db9e60d379f84 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -183,7 +183,7 @@ impl *const T { /// [`with_exposed_provenance`]: with_exposed_provenance #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] - #[expect(lossy_provenance_casts, reason = "this *is* the replacement")] + #[expect(implicit_provenance_casts, reason = "this *is* the replacement")] pub fn expose_provenance(self) -> usize { self.cast::<()>() as usize } diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 593011edecf27..e41ede7498b06 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1000,7 +1000,7 @@ pub const fn dangling_mut() -> *mut T { #[stable(feature = "exposed_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "const_exposed_provenance", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead +#[allow(implicit_provenance_casts)] // this *is* the explicit provenance API one should use instead pub const fn with_exposed_provenance(addr: usize) -> *const T { addr as *const T } @@ -1041,7 +1041,7 @@ pub const fn with_exposed_provenance(addr: usize) -> *const T { #[stable(feature = "exposed_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "const_exposed_provenance", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead +#[allow(implicit_provenance_casts)] // this *is* the explicit provenance API one should use instead pub const fn with_exposed_provenance_mut(addr: usize) -> *mut T { addr as *mut T } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index fd8dafa18f56b..b333f8217b2c0 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -174,7 +174,7 @@ impl *mut T { /// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] - #[expect(lossy_provenance_casts, reason = "this *is* the replacement")] + #[expect(implicit_provenance_casts, reason = "this *is* the replacement")] pub fn expose_provenance(self) -> usize { self.cast::<()>() as usize } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index ec2201aa7b9da..92fccea38bac9 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -128,8 +128,7 @@ #![feature(unwrap_infallible)] // tidy-alphabetical-end #![allow(internal_features)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] /// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality. diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 1b27be8e2dde3..c7fb97a0dc50e 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -245,8 +245,7 @@ #![allow(explicit_outlives_requirements)] #![allow(unused_lifetimes)] #![allow(internal_features)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] @@ -726,13 +725,7 @@ pub mod alloc; mod panicking; #[path = "../../backtrace/src/lib.rs"] -#[allow( - dead_code, - unused_attributes, - fuzzy_provenance_casts, - lossy_provenance_casts, - unsafe_op_in_unsafe_fn -)] +#[allow(dead_code, unused_attributes, implicit_provenance_casts, unsafe_op_in_unsafe_fn)] mod backtrace_rs; #[stable(feature = "cfg_select", since = "1.95.0")] diff --git a/library/std/src/os/unix/thread.rs b/library/std/src/os/unix/thread.rs index efb349b18d6b5..c19ccaf742da9 100644 --- a/library/std/src/os/unix/thread.rs +++ b/library/std/src/os/unix/thread.rs @@ -34,12 +34,12 @@ impl JoinHandleExt for JoinHandle { // This is an int2ptr cast on some platforms (e.g., *-musl) where RawPthread // is an integer but libc::pthread_t is a pointer. Exposed provenance is the // safe choice here, but `as` also works when it's int2int or ptr2ptr. - #[allow(lossy_provenance_casts)] + #[allow(implicit_provenance_casts)] fn as_pthread_t(&self) -> RawPthread { self.as_inner().id() as RawPthread } - #[allow(lossy_provenance_casts)] // see above for why + #[allow(implicit_provenance_casts)] // see above for why fn into_pthread_t(self) -> RawPthread { self.into_inner().into_id() as RawPthread } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 28e972925e667..ae1b7eaee8d12 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -75,7 +75,7 @@ impl OwnedSocket { } // FIXME(strict_provenance_magic): we defined RawSocket to be a u64 ;-; - #[allow(fuzzy_provenance_casts)] + #[allow(implicit_provenance_casts)] #[cfg(not(target_vendor = "uwp"))] pub(crate) fn set_no_inherit(&self) -> io::Result<()> { cvt(unsafe { diff --git a/library/std/src/sys/args/sgx.rs b/library/std/src/sys/args/sgx.rs index 9403059e7c607..8c1e07a787716 100644 --- a/library/std/src/sys/args/sgx.rs +++ b/library/std/src/sys/args/sgx.rs @@ -1,5 +1,4 @@ -// FIXME: this module systematically confuses pointers and integers -#![allow(fuzzy_provenance_casts, lossy_provenance_casts)] +#![allow(implicit_provenance_casts)] // FIXME: this module systematically confuses pointers and integers use crate::ffi::OsString; use crate::num::NonZero; diff --git a/library/std/src/sys/env/sgx.rs b/library/std/src/sys/env/sgx.rs index 0c19fcc848f4d..094a7dfa90581 100644 --- a/library/std/src/sys/env/sgx.rs +++ b/library/std/src/sys/env/sgx.rs @@ -1,5 +1,4 @@ -// FIXME: this module systematically confuses pointers and integers -#![allow(fuzzy_provenance_casts, lossy_provenance_casts)] +#![allow(implicit_provenance_casts)] // FIXME: this module systematically confuses pointers and integers pub use super::common::Env; use crate::collections::HashMap; diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 7b2c8e5a8024a..6ed6e39c61897 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -3,8 +3,7 @@ //! This module contains the facade (aka platform-specific) implementations of //! OS level functionality for Fortanix SGX. #![deny(unsafe_op_in_unsafe_fn)] -// FIXME: this entire module systematically confuses pointers and integers -#![allow(fuzzy_provenance_casts, lossy_provenance_casts)] +#![allow(implicit_provenance_casts)] // FIXME: this entire module systematically confuses pointers and integers use crate::io; use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; diff --git a/src/doc/unstable-book/src/language-features/strict-provenance-lints.md b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md index fec908fe3212e..3c7f29dc69f46 100644 --- a/src/doc/unstable-book/src/language-features/strict-provenance-lints.md +++ b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md @@ -5,17 +5,18 @@ The tracking issue for this feature is: [#130351] [#130351]: https://github.com/rust-lang/rust/issues/130351 ----- -The `strict_provenance_lints` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints. -These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model. +The `strict_provenance_lints` feature allows to enable the `implicit_provenance_casts` lint. ## Example ```rust #![feature(strict_provenance_lints)] -#![warn(fuzzy_provenance_casts)] +#![warn(implicit_provenance_casts)] fn main() { - let _dangling = 16_usize as *const u8; - //~^ WARNING: strict provenance disallows casting integer `usize` to pointer `*const u8` + let dangling = 16_usize as *const u8; + //~^ WARNING: cast from `usize` to `*const u8` implicitly relies on exposed provenance + let _addr = dangling as usize; + //~^ WARNING: cast from `*const u8` to `usize` implicitly exposes pointer provenance } ``` diff --git a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs index 738c8daa1687f..a6cbbcc9347ce 100644 --- a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs +++ b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs @@ -1,9 +1,15 @@ //@ check-pass +#![deny(implicit_provenance_casts)] +//~^ WARNING unknown lint: `implicit_provenance_casts` + +// feature-gating also applies when the old names are helpfully replaced with the new one: #![deny(fuzzy_provenance_casts)] -//~^ WARNING unknown lint: `fuzzy_provenance_casts` +//~^ WARNING lint `fuzzy_provenance_casts` has been renamed to `implicit_provenance_casts` +//~| WARNING unknown lint: `implicit_provenance_casts` #![deny(lossy_provenance_casts)] -//~^ WARNING unknown lint: `lossy_provenance_casts` +//~^ WARNING lint `lossy_provenance_casts` has been renamed to `implicit_provenance_casts` +//~| WARNING unknown lint: `implicit_provenance_casts` fn main() { // no warnings emitted since the lints are not activated diff --git a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr index 3f3b49bc60671..4e7c835b57912 100644 --- a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr +++ b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr @@ -1,25 +1,50 @@ -warning: unknown lint: `fuzzy_provenance_casts` +warning: unknown lint: `implicit_provenance_casts` --> $DIR/feature-gate-strict_provenance_lints.rs:3:9 | +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `implicit_provenance_casts` lint is unstable + = note: see issue #130351 for more information + = help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(unknown_lints)]` on by default + +warning: lint `fuzzy_provenance_casts` has been renamed to `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:7:9 + | +LL | #![deny(fuzzy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `implicit_provenance_casts` + | + = note: `#[warn(renamed_and_removed_lints)]` on by default + +warning: unknown lint: `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:7:9 + | LL | #![deny(fuzzy_provenance_casts)] | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: the `fuzzy_provenance_casts` lint is unstable + = note: the `implicit_provenance_casts` lint is unstable = note: see issue #130351 for more information = help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `#[warn(unknown_lints)]` on by default -warning: unknown lint: `lossy_provenance_casts` - --> $DIR/feature-gate-strict_provenance_lints.rs:5:9 +warning: lint `lossy_provenance_casts` has been renamed to `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:10:9 + | +LL | #![deny(lossy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `implicit_provenance_casts` + +warning: unknown lint: `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:10:9 | LL | #![deny(lossy_provenance_casts)] | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: the `lossy_provenance_casts` lint is unstable + = note: the `implicit_provenance_casts` lint is unstable = note: see issue #130351 for more information = help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: 2 warnings emitted +warning: 5 warnings emitted diff --git a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs index 187209d4e207e..c9f494200b30a 100644 --- a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs +++ b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs @@ -1,7 +1,7 @@ #![feature(strict_provenance_lints)] -#![deny(fuzzy_provenance_casts)] +#![deny(implicit_provenance_casts)] fn main() { let dangling = 16_usize as *const u8; - //~^ ERROR strict provenance disallows casting integer `usize` to pointer `*const u8` + //~^ ERROR cast from `usize` to `*const u8` implicitly relies on exposed provenance } diff --git a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr index f5eec6fc65667..b88e3d4a9c738 100644 --- a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr @@ -1,16 +1,17 @@ -error: strict provenance disallows casting integer `usize` to pointer `*const u8` +error: cast from `usize` to `*const u8` implicitly relies on exposed provenance --> $DIR/lint-strict-provenance-fuzzy-casts.rs:5:20 | LL | let dangling = 16_usize as *const u8; | ^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead + = help: if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()` + = note: for more information, visit note: the lint level is defined here --> $DIR/lint-strict-provenance-fuzzy-casts.rs:2:9 | -LL | #![deny(fuzzy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ -help: use `.with_addr()` to adjust a valid pointer in the same allocation, to this address +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use `.with_addr()` to adjust the address of a valid pointer in the same allocation | LL - let dangling = 16_usize as *const u8; LL + let dangling = (...).with_addr(16_usize); diff --git a/tests/ui/lint/lint-strict-provenance-lossy-casts.rs b/tests/ui/lint/lint-strict-provenance-lossy-casts.rs index 395dc75f82555..8f27d170ddec4 100644 --- a/tests/ui/lint/lint-strict-provenance-lossy-casts.rs +++ b/tests/ui/lint/lint-strict-provenance-lossy-casts.rs @@ -1,18 +1,18 @@ #![feature(strict_provenance_lints)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] fn main() { let x: u8 = 37; let addr: usize = &x as *const u8 as usize; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + //~^ ERROR cast from `*const u8` to `usize` implicitly exposes pointer provenance let addr_32bit = &x as *const u8 as u32; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` + //~^ ERROR cast from `*const u8` to `u32` implicitly exposes pointer provenance // don't add unnecessary parens in the suggestion let ptr = &x as *const u8; let ptr_addr = ptr as usize; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + //~^ ERROR cast from `*const u8` to `usize` implicitly exposes pointer provenance let ptr_addr_32bit = ptr as u32; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` + //~^ ERROR cast from `*const u8` to `u32` implicitly exposes pointer provenance } diff --git a/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr b/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr index bcef0ae424e68..22c45d7f6e52a 100644 --- a/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr @@ -1,34 +1,36 @@ -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:6:23 | LL | let addr: usize = &x as *const u8 as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit note: the lint level is defined here --> $DIR/lint-strict-provenance-lossy-casts.rs:2:9 | -LL | #![deny(lossy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.addr()` to obtain the address of a pointer | LL - let addr: usize = &x as *const u8 as usize; LL + let addr: usize = (&x as *const u8).addr(); | -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` +error: cast from `*const u8` to `u32` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:9:22 | LL | let addr_32bit = &x as *const u8 as u32; | ^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit help: use `.addr()` to obtain the address of a pointer | LL | let addr_32bit = (&x as *const u8).addr() as u32; | + ++++++++ -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:14:20 | LL | let ptr_addr = ptr as usize; @@ -36,9 +38,10 @@ LL | let ptr_addr = ptr as usize; | | | help: use `.addr()` to obtain the address of a pointer: `.addr()` | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` +error: cast from `*const u8` to `u32` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:16:26 | LL | let ptr_addr_32bit = ptr as u32; @@ -46,7 +49,8 @@ LL | let ptr_addr_32bit = ptr as u32; | | | help: use `.addr()` to obtain the address of a pointer: `.addr() as u32` | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit error: aborting due to 4 previous errors diff --git a/tests/ui/lint/lint-strict-provenance-macro-casts.rs b/tests/ui/lint/lint-strict-provenance-macro-casts.rs index 6d1c02c91fdbd..59a844b0fa134 100644 --- a/tests/ui/lint/lint-strict-provenance-macro-casts.rs +++ b/tests/ui/lint/lint-strict-provenance-macro-casts.rs @@ -1,23 +1,22 @@ #![feature(strict_provenance_lints)] -#![deny(lossy_provenance_casts)] -#![deny(fuzzy_provenance_casts)] +#![deny(implicit_provenance_casts)] macro_rules! cast { ($e:expr, $t:ty) => { $e as $t - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` - //~| ERROR strict provenance disallows casting integer `usize` to pointer `*const u8` + //~^ ERROR cast from `*const u8` to `usize` + //~| ERROR cast from `usize` to `*const u8` }; } macro_rules! p2i { ($e:expr) => { $e as usize }; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + //~^ ERROR cast from `*const u8` to `usize` } macro_rules! i2p { ($e:expr) => { $e as *const () }; - //~^ ERROR strict provenance disallows casting integer `usize` to pointer `*const ()` + //~^ ERROR cast from `usize` to `*const ()` } fn main() { diff --git a/tests/ui/lint/lint-strict-provenance-macro-casts.stderr b/tests/ui/lint/lint-strict-provenance-macro-casts.stderr index a7ec3d56965f3..08a1707490623 100644 --- a/tests/ui/lint/lint-strict-provenance-macro-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-macro-casts.stderr @@ -1,5 +1,5 @@ -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` - --> $DIR/lint-strict-provenance-macro-casts.rs:7:9 +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:6:9 | LL | $e as $t | ^^^^^^^^ @@ -7,16 +7,17 @@ LL | $e as $t LL | let _addr = cast!(ptr, usize); | ----------------- in this macro invocation | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit note: the lint level is defined here --> $DIR/lint-strict-provenance-macro-casts.rs:2:9 | -LL | #![deny(lossy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `cast` (in Nightly builds, run with -Z macro-backtrace for more info) -error: strict provenance disallows casting integer `usize` to pointer `*const u8` - --> $DIR/lint-strict-provenance-macro-casts.rs:7:9 +error: cast from `usize` to `*const u8` implicitly relies on exposed provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:6:9 | LL | $e as $t | ^^^^^^^^ @@ -24,16 +25,12 @@ LL | $e as $t LL | let _ptr = cast!(0usize, *const u8); | ------------------------ in this macro invocation | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead -note: the lint level is defined here - --> $DIR/lint-strict-provenance-macro-casts.rs:3:9 - | -LL | #![deny(fuzzy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ + = help: if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()` + = note: for more information, visit = note: this error originates in the macro `cast` (in Nightly builds, run with -Z macro-backtrace for more info) -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` - --> $DIR/lint-strict-provenance-macro-casts.rs:14:20 +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:13:20 | LL | ($e:expr) => { $e as usize }; | ^^^^^^^^^^^ @@ -41,11 +38,12 @@ LL | ($e:expr) => { $e as usize }; LL | p2i!(&raw const x); | ------------------ in this macro invocation | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit = note: this error originates in the macro `p2i` (in Nightly builds, run with -Z macro-backtrace for more info) -error: strict provenance disallows casting integer `usize` to pointer `*const ()` - --> $DIR/lint-strict-provenance-macro-casts.rs:19:20 +error: cast from `usize` to `*const ()` implicitly relies on exposed provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:18:20 | LL | ($e:expr) => { $e as *const () }; | ^^^^^^^^^^^^^^^ @@ -53,7 +51,8 @@ LL | ($e:expr) => { $e as *const () }; LL | i2p!(0x42); | ---------- in this macro invocation | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead + = help: if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()` + = note: for more information, visit = note: this error originates in the macro `i2p` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors From f96764a5e1fb1845b687f3c5360a811517e8cf52 Mon Sep 17 00:00:00 2001 From: Tam Pham Date: Sun, 28 Jun 2026 03:44:00 -0500 Subject: [PATCH 259/278] Fix a / an typo in E0277 --- library/core/src/ops/function.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index 25664a51a1d7b..15b54243c1eed 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -67,7 +67,7 @@ use crate::marker::Tuple; // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{This:resolved}` closure, found `{Self}`", + message = "expected an `{This:resolved}` closure, found `{Self}`", label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` @@ -154,7 +154,7 @@ pub const trait Fn: [const] FnMut { // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{This:resolved}` closure, found `{Self}`", + message = "expected an `{This:resolved}` closure, found `{Self}`", label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` @@ -233,7 +233,7 @@ pub const trait FnMut: FnOnce { // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{This:resolved}` closure, found `{Self}`", + message = "expected an `{This:resolved}` closure, found `{Self}`", label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` From b140addbd540939febad4a94cebc171822ce3043 Mon Sep 17 00:00:00 2001 From: Tam Pham Date: Sun, 28 Jun 2026 03:48:49 -0500 Subject: [PATCH 260/278] Bless ui tests --- tests/ui/abi/bad-custom.stderr | 2 +- .../normalization-ice-issue-149746.stderr | 8 ++++---- tests/ui/async-await/async-fn/impl-header.stderr | 2 +- tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr | 2 +- tests/ui/closures/closure-expected.stderr | 2 +- tests/ui/closures/coerce-unsafe-to-closure.stderr | 2 +- tests/ui/contracts/empty-ensures.stderr | 2 +- .../block_instead_of_closure_in_arg.stderr | 2 +- .../ruby_style_closure_successful_parse.stderr | 2 +- tests/ui/extern/extern-wrong-value-type.stderr | 2 +- ...ature-gate-unboxed-closures-manual-impls.stderr | 4 ++-- tests/ui/fn/fn-trait-formatting.stderr | 2 +- tests/ui/fn/issue-39259.stderr | 2 +- .../issue-68642-broken-llvm-ir.stderr | 2 +- .../issue-68643-broken-mir.stderr | 2 +- .../issue-68644-codegen-selection.stderr | 2 +- .../issue-68645-codegen-fulfillment.stderr | 2 +- .../normalize-under-binder/issue-62529-3.stderr | 2 +- tests/ui/implied-bounds/issue-100690.stderr | 2 +- tests/ui/intrinsics/const-eval-select-bad.stderr | 4 ++-- .../ui/iterators/fold-iterator-error-23966.stderr | 2 +- .../ui/lifetimes/issue-76168-hr-outlives-3.stderr | 6 +++--- tests/ui/lifetimes/issue-95023.stderr | 2 +- ...n-tokenstream-for-contracts-issue-140683.stderr | 2 +- tests/ui/methods/filter-relevant-fn-bounds.stderr | 2 +- ...gestion-trait-with-extra-generics-no-ice.stderr | 2 +- ...osure-arg-type-mismatch-issue-45727.next.stderr | 4 ++-- .../suggest-option-asderef-unfixable.stderr | 4 ++-- .../rfc-2396-target_feature-11/fn-traits.stderr | 14 +++++++------- tests/ui/trait-bounds/mismatch-fn-trait.stderr | 10 +++++----- .../check-trait-object-bounds-2.stderr | 2 +- tests/ui/traits/issue-87558.stderr | 2 +- .../const-host-effect-hrtb-no-ice.stderr | 2 +- tests/ui/traits/next-solver/fn-trait.stderr | 8 ++++---- tests/ui/type-alias-impl-trait/issue-63279.stderr | 4 ++-- .../lazy_subtyping_of_opaques.stderr | 4 ++-- .../unboxed-closures-fnmut-as-fn.stderr | 2 +- .../unboxed-closures-unsafe-extern-fn.stderr | 6 +++--- .../unboxed-closures-wrong-abi.stderr | 6 +++--- ...nboxed-closures-wrong-arg-type-extern-fn.stderr | 6 +++--- 40 files changed, 70 insertions(+), 70 deletions(-) diff --git a/tests/ui/abi/bad-custom.stderr b/tests/ui/abi/bad-custom.stderr index d7c9cae35c7b3..46da91d80c993 100644 --- a/tests/ui/abi/bad-custom.stderr +++ b/tests/ui/abi/bad-custom.stderr @@ -184,7 +184,7 @@ LL + #[unsafe(naked)] LL | async unsafe extern "custom" fn no_async_fn() { | -error[E0277]: expected a `Fn()` closure, found `unsafe extern "custom" fn()` +error[E0277]: expected an `Fn()` closure, found `unsafe extern "custom" fn()` --> $DIR/bad-custom.rs:102:64 | LL | fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn() { diff --git a/tests/ui/associated-types/normalization-ice-issue-149746.stderr b/tests/ui/associated-types/normalization-ice-issue-149746.stderr index 7f983d6e14ae2..ae6f6bbbb52c9 100644 --- a/tests/ui/associated-types/normalization-ice-issue-149746.stderr +++ b/tests/ui/associated-types/normalization-ice-issue-149746.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:6:24 | LL | _significant_drop: <() as Owner>::Ty, @@ -35,7 +35,7 @@ help: add a dummy let to cause `w` to be fully captured LL | _ = || { let _ = &w; w.field } | +++++++++++++ + -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:12:9 | LL | _ = || w.field @@ -48,7 +48,7 @@ note: required by a bound in `Owner::Ty` LL | trait Owner { type Ty; } | ^^^^^^^ required by this bound in `Owner::Ty` -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:10:16 | LL | pub fn test(w: Warns) { @@ -61,7 +61,7 @@ note: required by a bound in `Owner::Ty` LL | trait Owner { type Ty; } | ^^^^^^^ required by this bound in `Owner::Ty` -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:12:9 | LL | _ = || w.field diff --git a/tests/ui/async-await/async-fn/impl-header.stderr b/tests/ui/async-await/async-fn/impl-header.stderr index d0cf0d822f254..d1e3f884d02b3 100644 --- a/tests/ui/async-await/async-fn/impl-header.stderr +++ b/tests/ui/async-await/async-fn/impl-header.stderr @@ -30,7 +30,7 @@ LL | impl async Fn<()> for F {} | = help: implement the missing item: `fn call(&self, _: ()) -> >::Output { todo!() }` -error[E0277]: expected a `FnMut()` closure, found `F` +error[E0277]: expected an `FnMut()` closure, found `F` --> $DIR/impl-header.rs:5:23 | LL | impl async Fn<()> for F {} diff --git a/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr index d0f0bcb201425..a830ddc4468ff 100644 --- a/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr +++ b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `()` +error[E0277]: expected an `Fn()` closure, found `()` --> $DIR/raw-ptr-to-dyn-fn-raw-ptr.rs:6:16 | LL | &mut *(ptr as *mut dyn Fn()) diff --git a/tests/ui/closures/closure-expected.stderr b/tests/ui/closures/closure-expected.stderr index 53a93e1e84dd0..490988f247cdd 100644 --- a/tests/ui/closures/closure-expected.stderr +++ b/tests/ui/closures/closure-expected.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce()` closure, found `{integer}` +error[E0277]: expected an `FnOnce()` closure, found `{integer}` --> $DIR/closure-expected.rs:3:23 | LL | let y = x.or_else(4); diff --git a/tests/ui/closures/coerce-unsafe-to-closure.stderr b/tests/ui/closures/coerce-unsafe-to-closure.stderr index 013b9009da406..2bf2739be52d6 100644 --- a/tests/ui/closures/coerce-unsafe-to-closure.stderr +++ b/tests/ui/closures/coerce-unsafe-to-closure.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&str)` closure, found `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` +error[E0277]: expected an `FnOnce(&str)` closure, found `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` --> $DIR/coerce-unsafe-to-closure.rs:2:44 | LL | let x: Option<&[u8]> = Some("foo").map(std::mem::transmute); diff --git a/tests/ui/contracts/empty-ensures.stderr b/tests/ui/contracts/empty-ensures.stderr index 6a19d234b52c2..b87f709eeb7a4 100644 --- a/tests/ui/contracts/empty-ensures.stderr +++ b/tests/ui/contracts/empty-ensures.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&_)` closure, found `()` +error[E0277]: expected an `Fn(&_)` closure, found `()` --> $DIR/empty-ensures.rs:8:1 | LL | #[ensures()] diff --git a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr index c4c1c830afa6e..b8b4a0a17bcf6 100644 --- a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr +++ b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&bool)` closure, found `bool` +error[E0277]: expected an `FnOnce(&bool)` closure, found `bool` --> $DIR/block_instead_of_closure_in_arg.rs:3:23 | LL | Some(true).filter({ diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr index 54d3c6727f77f..ea41e77952ff5 100644 --- a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce({integer})` closure, found `Option` +error[E0277]: expected an `FnOnce({integer})` closure, found `Option` --> $DIR/ruby_style_closure_successful_parse.rs:3:31 | LL | let p = Some(45).and_then({|x| diff --git a/tests/ui/extern/extern-wrong-value-type.stderr b/tests/ui/extern/extern-wrong-value-type.stderr index 692a660117112..e1686f8a3ab9b 100644 --- a/tests/ui/extern/extern-wrong-value-type.stderr +++ b/tests/ui/extern/extern-wrong-value-type.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `extern "C" fn() {f}` +error[E0277]: expected an `Fn()` closure, found `extern "C" fn() {f}` --> $DIR/extern-wrong-value-type.rs:9:11 | LL | is_fn(f); diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr index b2a2eba9ad1da..56cfd6d0c52b9 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr @@ -87,7 +87,7 @@ LL | impl FnMut<()> for Bar { | = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable -error[E0277]: expected a `FnMut()` closure, found `Foo` +error[E0277]: expected an `FnMut()` closure, found `Foo` --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:9:17 | LL | impl Fn<()> for Foo { @@ -161,7 +161,7 @@ help: change the self-receiver type to match the trait LL | extern "rust-call" fn call_mut(&mut self, args: ()) -> () {} | +++ -error[E0277]: expected a `FnOnce()` closure, found `Bar` +error[E0277]: expected an `FnOnce()` closure, found `Bar` --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:26:20 | LL | impl FnMut<()> for Bar { diff --git a/tests/ui/fn/fn-trait-formatting.stderr b/tests/ui/fn/fn-trait-formatting.stderr index bfd86106bf923..0b86634a52e56 100644 --- a/tests/ui/fn/fn-trait-formatting.stderr +++ b/tests/ui/fn/fn-trait-formatting.stderr @@ -39,7 +39,7 @@ LL | let _: () = Box::new(|| -> isize { unimplemented!() }) as Box isize>` -error[E0277]: expected a `Fn(isize)` closure, found `{integer}` +error[E0277]: expected an `Fn(isize)` closure, found `{integer}` --> $DIR/fn-trait-formatting.rs:19:14 | LL | needs_fn(1); diff --git a/tests/ui/fn/issue-39259.stderr b/tests/ui/fn/issue-39259.stderr index b932731b5275e..7f2f4ecdd8d9b 100644 --- a/tests/ui/fn/issue-39259.stderr +++ b/tests/ui/fn/issue-39259.stderr @@ -22,7 +22,7 @@ help: add the missing parameter from the trait LL | fn call(&self, args: Args) -> u32 { | ++++++++++++ -error[E0277]: expected a `FnMut(u32)` closure, found `S` +error[E0277]: expected an `FnMut(u32)` closure, found `S` --> $DIR/issue-39259.rs:6:25 | LL | impl Fn(u32) -> u32 for S { diff --git a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr index d98071efe8311..7821560d96090 100644 --- a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr +++ b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68642-broken-llvm-ir.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr b/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr index cd4c06a8660a8..99b56dd061933 100644 --- a/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr +++ b/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68643-broken-mir.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr b/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr index 12f9949a0d304..4537091a60915 100644 --- a/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr +++ b/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68644-codegen-selection.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr index 8b23f60953061..cc324f91d75eb 100644 --- a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr +++ b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68645-codegen-fulfillment.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr index 36264b0d997b2..96cef0a6a5c9e 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(<_ as ATC<'a>>::Type)` closure, found `F` +error[E0277]: expected an `Fn(<_ as ATC<'a>>::Type)` closure, found `F` --> $DIR/issue-62529-3.rs:25:14 | LL | call(f, ()); diff --git a/tests/ui/implied-bounds/issue-100690.stderr b/tests/ui/implied-bounds/issue-100690.stderr index d00895d8fc9cc..b5465e2f3ce08 100644 --- a/tests/ui/implied-bounds/issue-100690.stderr +++ b/tests/ui/implied-bounds/issue-100690.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&mut UIView<'_, T>)` closure, found `F` +error[E0277]: expected an `FnOnce(&mut UIView<'_, T>)` closure, found `F` --> $DIR/issue-100690.rs:34:23 | LL | real_dispatch(f) diff --git a/tests/ui/intrinsics/const-eval-select-bad.stderr b/tests/ui/intrinsics/const-eval-select-bad.stderr index d701f5ea90973..7880dfa36a8f4 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.stderr +++ b/tests/ui/intrinsics/const-eval-select-bad.stderr @@ -9,7 +9,7 @@ LL | const_eval_select((), || {}, || {}); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL -error[E0277]: expected a `FnOnce()` closure, found `{integer}` +error[E0277]: expected an `FnOnce()` closure, found `{integer}` --> $DIR/const-eval-select-bad.rs:9:27 | LL | const_eval_select((), 42, 0xDEADBEEF); @@ -22,7 +22,7 @@ LL | const_eval_select((), 42, 0xDEADBEEF); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL -error[E0277]: expected a `FnOnce()` closure, found `{integer}` +error[E0277]: expected an `FnOnce()` closure, found `{integer}` --> $DIR/const-eval-select-bad.rs:9:31 | LL | const_eval_select((), 42, 0xDEADBEEF); diff --git a/tests/ui/iterators/fold-iterator-error-23966.stderr b/tests/ui/iterators/fold-iterator-error-23966.stderr index 15249a935972a..2e27108e6a483 100644 --- a/tests/ui/iterators/fold-iterator-error-23966.stderr +++ b/tests/ui/iterators/fold-iterator-error-23966.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut(_, char)` closure, found `()` +error[E0277]: expected an `FnMut(_, char)` closure, found `()` --> $DIR/fold-iterator-error-23966.rs:3:32 | LL | "".chars().fold(|_, _| (), ()); diff --git a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr index bf70826165341..7a825b93461c0 100644 --- a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr +++ b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:1 | LL | / async fn wrapper(f: F) @@ -9,7 +9,7 @@ LL | | for<'a> >::Output: Future | = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:26 | LL | async fn wrapper(f: F) @@ -17,7 +17,7 @@ LL | async fn wrapper(f: F) | = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:26 | LL | async fn wrapper(f: F) diff --git a/tests/ui/lifetimes/issue-95023.stderr b/tests/ui/lifetimes/issue-95023.stderr index 013bc51ff78a4..afb627de1c1bb 100644 --- a/tests/ui/lifetimes/issue-95023.stderr +++ b/tests/ui/lifetimes/issue-95023.stderr @@ -40,7 +40,7 @@ LL | impl Fn(&isize) for Error { | = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` -error[E0277]: expected a `FnMut(&isize)` closure, found `Error` +error[E0277]: expected an `FnMut(&isize)` closure, found `Error` --> $DIR/issue-95023.rs:3:21 | LL | impl Fn(&isize) for Error { diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr index f1ffda2a9bee6..56dbdae14189b 100644 --- a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr @@ -16,7 +16,7 @@ LL | fn b() {(loop)} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: expected a `Fn(&_)` closure, found `()` +error[E0277]: expected an `Fn(&_)` closure, found `()` --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:7:5 | LL | #[core::contracts::ensures] diff --git a/tests/ui/methods/filter-relevant-fn-bounds.stderr b/tests/ui/methods/filter-relevant-fn-bounds.stderr index 4efe40ae0903b..9ad0319440e9a 100644 --- a/tests/ui/methods/filter-relevant-fn-bounds.stderr +++ b/tests/ui/methods/filter-relevant-fn-bounds.stderr @@ -12,7 +12,7 @@ help: consider further restricting type parameter `F` with trait `Output` LL | F: for<'a> FnOnce(>::Type) + for<'a> Output<'a>, | ++++++++++++++++++++ -error[E0277]: expected a `FnOnce(<{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41} as Output<'a>>::Type)` closure, found `{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41}` +error[E0277]: expected an `FnOnce(<{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41} as Output<'a>>::Type)` closure, found `{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41}` --> $DIR/filter-relevant-fn-bounds.rs:18:34 | LL | wrapper.do_something_wrapper(|value| ()); diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr index aa46a41fa3378..73f5f441b3fef 100644 --- a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr @@ -22,7 +22,7 @@ help: there is a method `borrow_mut` with a similar name LL | foo.borrow_mut(); | ++++ -error[E0277]: expected a `Fn(bool)` closure, found `Bar` +error[E0277]: expected an `Fn(bool)` closure, found `Bar` --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:18:43 | LL | let foo: Box usize> = Box::new(Bar); diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr index 412fd53baeb37..eab9bf1ae34a8 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}` +error[E0277]: expected an `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}` --> $DIR/closure-arg-type-mismatch-issue-45727.rs:6:29 | LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); @@ -23,7 +23,7 @@ LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error[E0277]: expected a `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:10:29: 10:40}` +error[E0277]: expected an `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:10:29: 10:40}` --> $DIR/closure-arg-type-mismatch-issue-45727.rs:10:29 | LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr index a657a65c426d4..14c821b260299 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -18,7 +18,7 @@ help: consider wrapping the function in a closure LL | let _ = produces_string().and_then(|arg0: String| takes_str_but_too_many_refs(/* &&str */)); | ++++++++++++++ +++++++++++++ -error[E0277]: expected a `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` +error[E0277]: expected an `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` --> $DIR/suggest-option-asderef-unfixable.rs:26:40 | LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); @@ -30,7 +30,7 @@ LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL -error[E0277]: expected a `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` +error[E0277]: expected an `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` --> $DIR/suggest-option-asderef-unfixable.rs:28:40 | LL | let _ = produces_string().and_then(takes_str_but_unsafe); diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr index 52543caad37db..5333307a59280 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `#[target_features] fn() {foo}` +error[E0277]: expected an `Fn()` closure, found `#[target_features] fn() {foo}` --> $DIR/fn-traits.rs:29:10 | LL | call(foo); @@ -16,7 +16,7 @@ note: required by a bound in `call` LL | fn call(f: impl Fn()) { | ^^^^ required by this bound in `call` -error[E0277]: expected a `FnMut()` closure, found `#[target_features] fn() {foo}` +error[E0277]: expected an `FnMut()` closure, found `#[target_features] fn() {foo}` --> $DIR/fn-traits.rs:30:14 | LL | call_mut(foo); @@ -34,7 +34,7 @@ note: required by a bound in `call_mut` LL | fn call_mut(mut f: impl FnMut()) { | ^^^^^^^ required by this bound in `call_mut` -error[E0277]: expected a `FnOnce()` closure, found `#[target_features] fn() {foo}` +error[E0277]: expected an `FnOnce()` closure, found `#[target_features] fn() {foo}` --> $DIR/fn-traits.rs:31:15 | LL | call_once(foo); @@ -52,7 +52,7 @@ note: required by a bound in `call_once` LL | fn call_once(f: impl FnOnce()) { | ^^^^^^^^ required by this bound in `call_once` -error[E0277]: expected a `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` +error[E0277]: expected an `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` --> $DIR/fn-traits.rs:32:19 | LL | call_once_i32(bar); @@ -69,7 +69,7 @@ note: required by a bound in `call_once_i32` LL | fn call_once_i32(f: impl FnOnce(i32)) { | ^^^^^^^^^^^ required by this bound in `call_once_i32` -error[E0277]: expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected an `Fn()` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:34:10 | LL | call(foo_unsafe); @@ -88,7 +88,7 @@ note: required by a bound in `call` LL | fn call(f: impl Fn()) { | ^^^^ required by this bound in `call` -error[E0277]: expected a `FnMut()` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected an `FnMut()` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:36:14 | LL | call_mut(foo_unsafe); @@ -107,7 +107,7 @@ note: required by a bound in `call_mut` LL | fn call_mut(mut f: impl FnMut()) { | ^^^^^^^ required by this bound in `call_mut` -error[E0277]: expected a `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected an `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:38:15 | LL | call_once(foo_unsafe); diff --git a/tests/ui/trait-bounds/mismatch-fn-trait.stderr b/tests/ui/trait-bounds/mismatch-fn-trait.stderr index 051e660c2d106..a7805e25f651e 100644 --- a/tests/ui/trait-bounds/mismatch-fn-trait.stderr +++ b/tests/ui/trait-bounds/mismatch-fn-trait.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnMut(u32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnMut(u32)` --> $DIR/mismatch-fn-trait.rs:4:10 | LL | take(f) @@ -14,7 +14,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnMut(i32, i32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnMut(i32, i32)` --> $DIR/mismatch-fn-trait.rs:9:10 | LL | take(f) @@ -29,7 +29,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnMut()` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnMut()` --> $DIR/mismatch-fn-trait.rs:14:10 | LL | take(f) @@ -44,7 +44,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnOnce(i32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnOnce(i32)` --> $DIR/mismatch-fn-trait.rs:19:10 | LL | take(f) @@ -59,7 +59,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnOnce(u32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnOnce(u32)` --> $DIR/mismatch-fn-trait.rs:24:10 | LL | take(f) diff --git a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr index ebd45b9dd9635..c4bc50746ebba 100644 --- a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr +++ b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&i32)` closure, found `i32` --> $DIR/check-trait-object-bounds-2.rs:13:9 | LL | f:: X<'x, F = i32>>(); diff --git a/tests/ui/traits/issue-87558.stderr b/tests/ui/traits/issue-87558.stderr index ca908a3062d33..b8b9ea57612b4 100644 --- a/tests/ui/traits/issue-87558.stderr +++ b/tests/ui/traits/issue-87558.stderr @@ -32,7 +32,7 @@ LL | impl Fn(&isize) for Error { | = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` -error[E0277]: expected a `FnMut(&isize)` closure, found `Error` +error[E0277]: expected an `FnMut(&isize)` closure, found `Error` --> $DIR/issue-87558.rs:3:21 | LL | impl Fn(&isize) for Error { diff --git a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr index 8ec772a3d7fa8..7b98ba618a727 100644 --- a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr +++ b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&'a ())` closure, found `()` +error[E0277]: expected an `Fn(&'a ())` closure, found `()` --> $DIR/const-host-effect-hrtb-no-ice.rs:9:21 | LL | with_positive::<()>(); diff --git a/tests/ui/traits/next-solver/fn-trait.stderr b/tests/ui/traits/next-solver/fn-trait.stderr index 77c834f377852..6218928b309f7 100644 --- a/tests/ui/traits/next-solver/fn-trait.stderr +++ b/tests/ui/traits/next-solver/fn-trait.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `unsafe fn() -> i32` +error[E0277]: expected an `Fn()` closure, found `unsafe fn() -> i32` --> $DIR/fn-trait.rs:20:16 | LL | require_fn(f as unsafe fn() -> i32); @@ -15,7 +15,7 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0277]: expected a `Fn()` closure, found `extern "C" fn() -> i32 {g}` +error[E0277]: expected an `Fn()` closure, found `extern "C" fn() -> i32 {g}` --> $DIR/fn-trait.rs:22:16 | LL | require_fn(g); @@ -31,7 +31,7 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0277]: expected a `Fn()` closure, found `extern "C" fn() -> i32` +error[E0277]: expected an `Fn()` closure, found `extern "C" fn() -> i32` --> $DIR/fn-trait.rs:24:16 | LL | require_fn(g as extern "C" fn() -> i32); @@ -47,7 +47,7 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0277]: expected a `Fn()` closure, found `unsafe fn() -> i32 {h}` +error[E0277]: expected an `Fn()` closure, found `unsafe fn() -> i32 {h}` --> $DIR/fn-trait.rs:26:16 | LL | require_fn(h); diff --git a/tests/ui/type-alias-impl-trait/issue-63279.stderr b/tests/ui/type-alias-impl-trait/issue-63279.stderr index 8f0e9c5fde04d..165c1c25280bd 100644 --- a/tests/ui/type-alias-impl-trait/issue-63279.stderr +++ b/tests/ui/type-alias-impl-trait/issue-63279.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/issue-63279.rs:6:11 | LL | fn c() -> Closure { @@ -7,7 +7,7 @@ LL | fn c() -> Closure { = help: the trait `FnOnce()` is not implemented for `()` = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/issue-63279.rs:8:11 | LL | || -> Closure { || () } diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr index 0bee0dfa9c7a3..22d16e39fc404 100644 --- a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr @@ -10,7 +10,7 @@ LL | Thunk::new(|cont| cont) = note: expected struct `Thunk<_>` found unit type `()` -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/lazy_subtyping_of_opaques.rs:12:23 | LL | Thunk::new(|cont| cont) @@ -19,7 +19,7 @@ LL | Thunk::new(|cont| cont) = help: the trait `FnOnce()` is not implemented for `()` = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/lazy_subtyping_of_opaques.rs:10:23 | LL | fn reify_as_tait() -> Thunk { diff --git a/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr index 0dd6dcfe6a3f2..d579b5bd75002 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(isize)` closure, found `S` +error[E0277]: expected an `Fn(isize)` closure, found `S` --> $DIR/unboxed-closures-fnmut-as-fn.rs:27:21 | LL | let x = call_it(&S, 22); diff --git a/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr index dfdb3ea19c3f2..a637b180e31e7 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` +error[E0277]: expected an `Fn(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:20:21 | LL | let x = call_it(&square, 22); @@ -14,7 +14,7 @@ note: required by a bound in `call_it` LL | fn call_it isize>(_: &F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it` -error[E0277]: expected a `FnMut(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnMut(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:25:25 | LL | let y = call_it_mut(&mut square, 22); @@ -30,7 +30,7 @@ note: required by a bound in `call_it_mut` LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut` -error[E0277]: expected a `FnOnce(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnOnce(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:30:26 | LL | let z = call_it_once(square, 22); diff --git a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr b/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr index b4521cc6890d8..25cf793ca00e8 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` +error[E0277]: expected an `Fn(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:20:21 | LL | let x = call_it(&square, 22); @@ -13,7 +13,7 @@ note: required by a bound in `call_it` LL | fn call_it isize>(_: &F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it` -error[E0277]: expected a `FnMut(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnMut(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:25:25 | LL | let y = call_it_mut(&mut square, 22); @@ -28,7 +28,7 @@ note: required by a bound in `call_it_mut` LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut` -error[E0277]: expected a `FnOnce(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnOnce(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:30:26 | LL | let z = call_it_once(square, 22); diff --git a/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr index 5519bb33ebc27..e09eda3b99d29 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&isize)` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected an `Fn(&isize)` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:21:21 | LL | let x = call_it(&square, 22); @@ -14,7 +14,7 @@ note: required by a bound in `call_it` LL | fn call_it isize>(_: &F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it` -error[E0277]: expected a `FnMut(&isize)` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected an `FnMut(&isize)` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:26:25 | LL | let y = call_it_mut(&mut square, 22); @@ -30,7 +30,7 @@ note: required by a bound in `call_it_mut` LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut` -error[E0277]: expected a `FnOnce(&isize)` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected an `FnOnce(&isize)` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:31:26 | LL | let z = call_it_once(square, 22); From fd6762120b31fb040e8988858d873bfc4b1f932f Mon Sep 17 00:00:00 2001 From: Tam Pham Date: Sun, 28 Jun 2026 03:56:28 -0500 Subject: [PATCH 261/278] Bless ui test runners --- tests/ui/abi/bad-custom.rs | 2 +- .../normalization-ice-issue-149746.rs | 8 ++++---- tests/ui/async-await/async-fn/impl-header.rs | 2 +- tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs | 2 +- tests/ui/closures/closure-expected.rs | 2 +- tests/ui/contracts/empty-ensures.rs | 2 +- .../block_instead_of_closure_in_arg.rs | 2 +- .../ruby_style_closure_successful_parse.rs | 2 +- tests/ui/extern/extern-wrong-value-type.rs | 2 +- .../feature-gate-unboxed-closures-manual-impls.rs | 4 ++-- tests/ui/fn/fn-trait-formatting.rs | 2 +- tests/ui/fn/issue-39259.rs | 2 +- .../issue-68642-broken-llvm-ir.rs | 2 +- .../issue-68643-broken-mir.rs | 2 +- .../issue-68644-codegen-selection.rs | 2 +- .../issue-68645-codegen-fulfillment.rs | 2 +- tests/ui/implied-bounds/issue-100690.rs | 2 +- tests/ui/intrinsics/const-eval-select-bad.rs | 4 ++-- tests/ui/lifetimes/issue-76168-hr-outlives-3.rs | 6 +++--- tests/ui/lifetimes/issue-95023.rs | 2 +- ...ce-in-tokenstream-for-contracts-issue-140683.rs | 2 +- tests/ui/methods/filter-relevant-fn-bounds.rs | 2 +- ...-suggestion-trait-with-extra-generics-no-ice.rs | 2 +- ...ure-arg-type-mismatch-issue-45727.current.fixed | 4 ++-- .../closure-arg-type-mismatch-issue-45727.rs | 4 ++-- .../suggest-option-asderef-unfixable.rs | 4 ++-- .../rfcs/rfc-2396-target_feature-11/fn-traits.rs | 14 +++++++------- .../check-trait-object-bounds-2.rs | 2 +- .../diagnostics/const-host-effect-hrtb-no-ice.rs | 2 +- tests/ui/traits/next-solver/fn-trait.rs | 8 ++++---- tests/ui/type-alias-impl-trait/issue-63279.rs | 4 ++-- .../lazy_subtyping_of_opaques.rs | 4 ++-- 32 files changed, 53 insertions(+), 53 deletions(-) diff --git a/tests/ui/abi/bad-custom.rs b/tests/ui/abi/bad-custom.rs index 7c881134ccb4c..10e34fa19648a 100644 --- a/tests/ui/abi/bad-custom.rs +++ b/tests/ui/abi/bad-custom.rs @@ -100,7 +100,7 @@ async unsafe extern "custom" fn no_async_fn() { } fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn() { - //~^ ERROR expected a `Fn()` closure, found `unsafe extern "custom" fn()` + //~^ ERROR expected an `Fn()` closure, found `unsafe extern "custom" fn()` f } diff --git a/tests/ui/associated-types/normalization-ice-issue-149746.rs b/tests/ui/associated-types/normalization-ice-issue-149746.rs index 8932d321b7e5b..29661092b5751 100644 --- a/tests/ui/associated-types/normalization-ice-issue-149746.rs +++ b/tests/ui/associated-types/normalization-ice-issue-149746.rs @@ -4,14 +4,14 @@ trait Owner { type Ty; } impl Owner for () { type Ty = T; } pub struct Warns { _significant_drop: <() as Owner>::Ty, - //~^ ERROR expected a `FnMut()` closure, found `T` + //~^ ERROR expected an `FnMut()` closure, found `T` field: String, } pub fn test(w: Warns) { - //~^ ERROR expected a `FnMut()` closure, found `T` + //~^ ERROR expected an `FnMut()` closure, found `T` _ = || w.field - //~^ ERROR expected a `FnMut()` closure, found `T` - //~| ERROR expected a `FnMut()` closure, found `T` + //~^ ERROR expected an `FnMut()` closure, found `T` + //~| ERROR expected an `FnMut()` closure, found `T` //~| WARN: changes to closure capture in Rust 2021 will affect drop order } fn main() {} diff --git a/tests/ui/async-await/async-fn/impl-header.rs b/tests/ui/async-await/async-fn/impl-header.rs index 9af5f1f42a957..349a875d4a6c4 100644 --- a/tests/ui/async-await/async-fn/impl-header.rs +++ b/tests/ui/async-await/async-fn/impl-header.rs @@ -6,7 +6,7 @@ impl async Fn<()> for F {} //~^ ERROR `async` trait implementations are unsupported //~| ERROR the precise format of `Fn`-family traits' type parameters is subject to change //~| ERROR manual implementations of `Fn` are experimental -//~| ERROR expected a `FnMut()` closure, found `F` +//~| ERROR expected an `FnMut()` closure, found `F` //~| ERROR not all trait items implemented, missing: `call` fn main() {} diff --git a/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs index 6572140786d2e..d966781f90d9f 100644 --- a/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs +++ b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs @@ -4,6 +4,6 @@ fn main() { let ptr: *mut () = core::ptr::null_mut(); let _: &mut dyn Fn() = unsafe { &mut *(ptr as *mut dyn Fn()) - //~^ ERROR expected a `Fn()` closure, found `()` + //~^ ERROR expected an `Fn()` closure, found `()` }; } diff --git a/tests/ui/closures/closure-expected.rs b/tests/ui/closures/closure-expected.rs index d730bcd1faf51..4fc1b3a1fd006 100644 --- a/tests/ui/closures/closure-expected.rs +++ b/tests/ui/closures/closure-expected.rs @@ -1,5 +1,5 @@ fn main() { let x = Some(1); let y = x.or_else(4); - //~^ ERROR expected a `FnOnce()` closure, found `{integer}` + //~^ ERROR expected an `FnOnce()` closure, found `{integer}` } diff --git a/tests/ui/contracts/empty-ensures.rs b/tests/ui/contracts/empty-ensures.rs index 0a52391117861..79e57df6eb984 100644 --- a/tests/ui/contracts/empty-ensures.rs +++ b/tests/ui/contracts/empty-ensures.rs @@ -6,7 +6,7 @@ extern crate core; use core::contracts::ensures; #[ensures()] -//~^ ERROR expected a `Fn(&_)` closure, found `()` [E0277] +//~^ ERROR expected an `Fn(&_)` closure, found `()` [E0277] fn foo(x: u32) -> u32 { x * 2 } diff --git a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs index 5e64fd5d2af4a..94c6c85d28d27 100644 --- a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs +++ b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs @@ -1,6 +1,6 @@ fn main() { let number = 2; - Some(true).filter({ //~ ERROR expected a `FnOnce(&bool)` closure, found `bool` + Some(true).filter({ //~ ERROR expected an `FnOnce(&bool)` closure, found `bool` if number % 2 == 0 { number == 0 } else { diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs index bb2e9c0a63c75..faad88764bf98 100644 --- a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs @@ -1,6 +1,6 @@ const x: usize =42; fn main() { - let p = Some(45).and_then({|x| //~ ERROR expected a `FnOnce({integer})` closure, found `Option` + let p = Some(45).and_then({|x| //~ ERROR expected an `FnOnce({integer})` closure, found `Option` 1 + 1; Some(x * 2) }); diff --git a/tests/ui/extern/extern-wrong-value-type.rs b/tests/ui/extern/extern-wrong-value-type.rs index 56c6cf1dfaba7..ae07a721e83ee 100644 --- a/tests/ui/extern/extern-wrong-value-type.rs +++ b/tests/ui/extern/extern-wrong-value-type.rs @@ -7,5 +7,5 @@ fn main() { // extern functions are extern "C" fn let _x: extern "C" fn() = f; // OK is_fn(f); - //~^ ERROR expected a `Fn()` closure, found `extern "C" fn() {f}` + //~^ ERROR expected an `Fn()` closure, found `extern "C" fn() {f}` } diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs index 7b40c8760e368..d33625f06872e 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs @@ -9,7 +9,7 @@ struct Foo; impl Fn<()> for Foo { //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change //~| ERROR manual implementations of `Fn` are experimental - //~| ERROR expected a `FnMut()` closure, found `Foo` + //~| ERROR expected an `FnMut()` closure, found `Foo` extern "rust-call" fn call(self, args: ()) -> () {} //~^ ERROR "rust-call" ABI is experimental and subject to change //~| ERROR `call` has an incompatible type for trait @@ -26,7 +26,7 @@ struct Bar; impl FnMut<()> for Bar { //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change //~| ERROR manual implementations of `FnMut` are experimental - //~| ERROR expected a `FnOnce()` closure, found `Bar` + //~| ERROR expected an `FnOnce()` closure, found `Bar` extern "rust-call" fn call_mut(&self, args: ()) -> () {} //~^ ERROR "rust-call" ABI is experimental and subject to change //~| ERROR incompatible type for trait diff --git a/tests/ui/fn/fn-trait-formatting.rs b/tests/ui/fn/fn-trait-formatting.rs index be74d1ca9be3c..a7b00491ade01 100644 --- a/tests/ui/fn/fn-trait-formatting.rs +++ b/tests/ui/fn/fn-trait-formatting.rs @@ -17,5 +17,5 @@ fn main() { //~| NOTE found struct `Box isize>` needs_fn(1); - //~^ ERROR expected a `Fn(isize)` closure, found `{integer}` + //~^ ERROR expected an `Fn(isize)` closure, found `{integer}` } diff --git a/tests/ui/fn/issue-39259.rs b/tests/ui/fn/issue-39259.rs index d4569a9094b2f..d2b545e000b97 100644 --- a/tests/ui/fn/issue-39259.rs +++ b/tests/ui/fn/issue-39259.rs @@ -5,7 +5,7 @@ struct S; impl Fn(u32) -> u32 for S { //~^ ERROR associated item constraints are not allowed here [E0229] - //~| ERROR expected a `FnMut(u32)` closure, found `S` + //~| ERROR expected an `FnMut(u32)` closure, found `S` fn call(&self) -> u32 { //~^ ERROR method `call` has 1 parameter but the declaration in trait `call` has 2 5 diff --git a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs index c444ee9e1cdb4..edd9ad11ae778 100644 --- a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs +++ b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } fn main() { diff --git a/tests/ui/generic-associated-types/issue-68643-broken-mir.rs b/tests/ui/generic-associated-types/issue-68643-broken-mir.rs index 39db51c0e2a21..23586f6af98bd 100644 --- a/tests/ui/generic-associated-types/issue-68643-broken-mir.rs +++ b/tests/ui/generic-associated-types/issue-68643-broken-mir.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } pub fn main() { diff --git a/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs b/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs index e379bce07d313..85b932a063c14 100644 --- a/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs +++ b/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } fn main() { diff --git a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs index e69a08b0a3991..b8133148b1029 100644 --- a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs +++ b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } fn main() { diff --git a/tests/ui/implied-bounds/issue-100690.rs b/tests/ui/implied-bounds/issue-100690.rs index b0dbf749c4670..4f8a2df9834f5 100644 --- a/tests/ui/implied-bounds/issue-100690.rs +++ b/tests/ui/implied-bounds/issue-100690.rs @@ -32,7 +32,7 @@ impl<'a, T: 'a> Handle<'a, T, UIView<'a, T>, Result<(), io::Error>> for TUIHandl F: FnOnce(&mut UIView<'a, T>) -> Result<(), io::Error> + Send + 'static, { real_dispatch(f) - //~^ ERROR expected a `FnOnce(&mut UIView<'_, T>)` closure, found `F` + //~^ ERROR expected an `FnOnce(&mut UIView<'_, T>)` closure, found `F` } } diff --git a/tests/ui/intrinsics/const-eval-select-bad.rs b/tests/ui/intrinsics/const-eval-select-bad.rs index f407125129921..23a7a4be7ff10 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.rs +++ b/tests/ui/intrinsics/const-eval-select-bad.rs @@ -7,8 +7,8 @@ const fn not_fn_items() { const_eval_select((), || {}, || {}); //~^ ERROR const FnOnce()` is not satisfied const_eval_select((), 42, 0xDEADBEEF); - //~^ ERROR expected a `FnOnce()` closure - //~| ERROR expected a `FnOnce()` closure + //~^ ERROR expected an `FnOnce()` closure + //~| ERROR expected an `FnOnce()` closure } const fn foo(n: i32) -> i32 { diff --git a/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs b/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs index 77a1f0b4f25d6..ab3bf619f83b2 100644 --- a/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs +++ b/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs @@ -4,9 +4,9 @@ use std::future::Future; async fn wrapper(f: F) -//~^ ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` -//~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` -//~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` +//~^ ERROR: expected an `FnOnce(&'a mut i32)` closure, found `i32` +//~| ERROR: expected an `FnOnce(&'a mut i32)` closure, found `i32` +//~| ERROR: expected an `FnOnce(&'a mut i32)` closure, found `i32` where F:, for<'a> >::Output: Future + 'a, diff --git a/tests/ui/lifetimes/issue-95023.rs b/tests/ui/lifetimes/issue-95023.rs index 1faae50d9f158..d26317d2b9058 100644 --- a/tests/ui/lifetimes/issue-95023.rs +++ b/tests/ui/lifetimes/issue-95023.rs @@ -4,7 +4,7 @@ impl Fn(&isize) for Error { //~^ ERROR manual implementations of `Fn` are experimental [E0183] //~| ERROR associated item constraints are not allowed here [E0229] //~| ERROR not all trait items implemented - //~| ERROR expected a `FnMut(&isize)` closure, found `Error` + //~| ERROR expected an `FnMut(&isize)` closure, found `Error` fn foo(&self) -> Self::B<{ N }>; //~^ ERROR associated function in `impl` without body //~| ERROR method `foo` is not a member of trait `Fn` [E0407] diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs index 68346a00ae1a7..2b1bacf7e0c31 100644 --- a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs @@ -4,7 +4,7 @@ struct T; impl T { - #[core::contracts::ensures] //~ ERROR expected a `Fn(&_)` closure, found `()` + #[core::contracts::ensures] //~ ERROR expected an `Fn(&_)` closure, found `()` fn b() {(loop)} //~^ ERROR expected `{`, found `)` //~| ERROR expected `{`, found `)` diff --git a/tests/ui/methods/filter-relevant-fn-bounds.rs b/tests/ui/methods/filter-relevant-fn-bounds.rs index 7945eaa0daa27..a23f4600d6d1b 100644 --- a/tests/ui/methods/filter-relevant-fn-bounds.rs +++ b/tests/ui/methods/filter-relevant-fn-bounds.rs @@ -16,5 +16,5 @@ impl Wrapper { fn main() { let mut wrapper = Wrapper; wrapper.do_something_wrapper(|value| ()); - //~^ ERROR expected a `FnOnce + //~^ ERROR expected an `FnOnce } diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs index 0a98cdef5c923..f204e55f5a578 100644 --- a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs @@ -16,7 +16,7 @@ struct Bar; fn main() { let foo: Box usize> = Box::new(Bar); - //~^ ERROR expected a `Fn(bool)` closure, found `Bar` + //~^ ERROR expected an `Fn(bool)` closure, found `Bar` foo.borrow(); //~^ ERROR no method named `borrow` found foo.take() diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed index c489b6e445dda..b1158e1cbffa7 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed @@ -5,10 +5,10 @@ fn main() { let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:6:29} as FnOnce<(&{integer},)>>::Output == bool` let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:10:29} as FnOnce<(&{integer},)>>::Output == bool` } diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs index 434b5d3d23110..09f7af7c0253e 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs @@ -5,10 +5,10 @@ fn main() { let _ = (-10..=10).find(|x: i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:6:29} as FnOnce<(&{integer},)>>::Output == bool` let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:10:29} as FnOnce<(&{integer},)>>::Output == bool` } diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs index 42adff43a58f2..445a98cd49630 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs @@ -24,9 +24,9 @@ fn main() { let _ = produces_string().and_then(takes_str_but_too_many_refs); //~^ ERROR type mismatch in function arguments let _ = produces_string().and_then(takes_str_but_wrong_abi); - //~^ ERROR expected a `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` + //~^ ERROR expected an `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` let _ = produces_string().and_then(takes_str_but_unsafe); - //~^ ERROR expected a `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + //~^ ERROR expected an `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` let _ = produces_string().and_then(no_args); //~^ ERROR function is expected to take 1 argument, but it takes 0 arguments let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs index 82053a12b133f..b7eaabdf6ad3f 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs @@ -26,15 +26,15 @@ fn call_once_i32(f: impl FnOnce(i32)) { } fn main() { - call(foo); //~ ERROR expected a `Fn()` closure, found `#[target_features] fn() {foo}` - call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `#[target_features] fn() {foo}` - call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `#[target_features] fn() {foo}` - call_once_i32(bar); //~ ERROR expected a `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` + call(foo); //~ ERROR expected an `Fn()` closure, found `#[target_features] fn() {foo}` + call_mut(foo); //~ ERROR expected an `FnMut()` closure, found `#[target_features] fn() {foo}` + call_once(foo); //~ ERROR expected an `FnOnce()` closure, found `#[target_features] fn() {foo}` + call_once_i32(bar); //~ ERROR expected an `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` call(foo_unsafe); - //~^ ERROR expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected an `Fn()` closure, found `unsafe fn() {foo_unsafe}` call_mut(foo_unsafe); - //~^ ERROR expected a `FnMut()` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected an `FnMut()` closure, found `unsafe fn() {foo_unsafe}` call_once(foo_unsafe); - //~^ ERROR expected a `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected an `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` } diff --git a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs index 891cc32e7b758..f19c9da7c5eb5 100644 --- a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs +++ b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs @@ -11,5 +11,5 @@ fn f X<'r> + ?Sized>() { fn main() { f:: X<'x, F = i32>>(); - //~^ ERROR expected a `FnOnce(&i32)` closure, found `i32` + //~^ ERROR expected an `FnOnce(&i32)` closure, found `i32` } diff --git a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs index 8463cb3f6ea1b..a0d1db3ff02b3 100644 --- a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs +++ b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs @@ -7,7 +7,7 @@ const fn with_positive [const] Fn(&'a ())>() {} const _: () = { with_positive::<()>(); - //~^ ERROR expected a `Fn(&'a ())` closure, found `()` + //~^ ERROR expected an `Fn(&'a ())` closure, found `()` //~| ERROR type mismatch resolving `<() as FnOnce<(&(),)>>::Output == ()` }; diff --git a/tests/ui/traits/next-solver/fn-trait.rs b/tests/ui/traits/next-solver/fn-trait.rs index 7994dd3052c40..e31d4d2b580a5 100644 --- a/tests/ui/traits/next-solver/fn-trait.rs +++ b/tests/ui/traits/next-solver/fn-trait.rs @@ -18,11 +18,11 @@ fn main() { require_fn(f); require_fn(f as fn() -> i32); require_fn(f as unsafe fn() -> i32); - //~^ ERROR: expected a `Fn()` closure, found `unsafe fn() -> i32` + //~^ ERROR: expected an `Fn()` closure, found `unsafe fn() -> i32` require_fn(g); - //~^ ERROR: expected a `Fn()` closure, found `extern "C" fn() -> i32 {g}` + //~^ ERROR: expected an `Fn()` closure, found `extern "C" fn() -> i32 {g}` require_fn(g as extern "C" fn() -> i32); - //~^ ERROR: expected a `Fn()` closure, found `extern "C" fn() -> i32` + //~^ ERROR: expected an `Fn()` closure, found `extern "C" fn() -> i32` require_fn(h); - //~^ ERROR: expected a `Fn()` closure, found `unsafe fn() -> i32 {h}` + //~^ ERROR: expected an `Fn()` closure, found `unsafe fn() -> i32 {h}` } diff --git a/tests/ui/type-alias-impl-trait/issue-63279.rs b/tests/ui/type-alias-impl-trait/issue-63279.rs index 10da40d2f90d2..50d4a42e1f355 100644 --- a/tests/ui/type-alias-impl-trait/issue-63279.rs +++ b/tests/ui/type-alias-impl-trait/issue-63279.rs @@ -4,11 +4,11 @@ type Closure = impl FnOnce(); #[define_opaque(Closure)] fn c() -> Closure { - //~^ ERROR: expected a `FnOnce()` closure, found `()` + //~^ ERROR: expected an `FnOnce()` closure, found `()` || -> Closure { || () } //~^ ERROR: mismatched types //~| ERROR: mismatched types - //~| ERROR: expected a `FnOnce()` closure, found `()` + //~| ERROR: expected an `FnOnce()` closure, found `()` } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs index 075b0bd75fa53..aec311ede864f 100644 --- a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs @@ -8,10 +8,10 @@ type Tait = impl FnOnce() -> (); #[define_opaque(Tait)] fn reify_as_tait() -> Thunk { - //~^ ERROR: expected a `FnOnce()` closure, found `()` + //~^ ERROR: expected an `FnOnce()` closure, found `()` Thunk::new(|cont| cont) //~^ ERROR: mismatched types - //~| ERROR: expected a `FnOnce()` closure, found `()` + //~| ERROR: expected an `FnOnce()` closure, found `()` } struct Thunk(F); From e87b27518f3b51c95bf4e0a61b7f7b87468a3070 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sun, 28 Jun 2026 09:39:21 +0000 Subject: [PATCH 262/278] add crashtests --- tests/crashes/149748.rs | 13 +++++++++++++ tests/crashes/150040.rs | 7 +++++++ tests/crashes/150049.rs | 12 ++++++++++++ tests/crashes/150128.rs | 8 ++++++++ tests/crashes/150296.rs | 14 ++++++++++++++ tests/crashes/150387.rs | 13 +++++++++++++ tests/crashes/150403.rs | 26 ++++++++++++++++++++++++++ tests/crashes/150517.rs | 15 +++++++++++++++ tests/crashes/150545.rs | 8 ++++++++ tests/crashes/150749.rs | 12 ++++++++++++ tests/crashes/150969.rs | 7 +++++++ tests/crashes/151069.rs | 16 ++++++++++++++++ 12 files changed, 151 insertions(+) create mode 100644 tests/crashes/149748.rs create mode 100644 tests/crashes/150040.rs create mode 100644 tests/crashes/150049.rs create mode 100644 tests/crashes/150128.rs create mode 100644 tests/crashes/150296.rs create mode 100644 tests/crashes/150387.rs create mode 100644 tests/crashes/150403.rs create mode 100644 tests/crashes/150517.rs create mode 100644 tests/crashes/150545.rs create mode 100644 tests/crashes/150749.rs create mode 100644 tests/crashes/150969.rs create mode 100644 tests/crashes/151069.rs diff --git a/tests/crashes/149748.rs b/tests/crashes/149748.rs new file mode 100644 index 0000000000000..99e4a6ae15b80 --- /dev/null +++ b/tests/crashes/149748.rs @@ -0,0 +1,13 @@ +//@ known-bug: #149748 +//@ edition: 2024 +//@ compile-flags: -Zmir-enable-passes=+Inline -Zmir-enable-passes=+ReferencePropagation -Zlint-mir + +#![feature(gen_blocks)] +gen fn foo(z: i32) -> i32 { + yield z; + z; +} +pub fn main() { + let mut iter = foo(3); + assert_eq!(iter.next(), Some(3)) +} diff --git a/tests/crashes/150040.rs b/tests/crashes/150040.rs new file mode 100644 index 0000000000000..bf5b7bbf9b536 --- /dev/null +++ b/tests/crashes/150040.rs @@ -0,0 +1,7 @@ +//@ known-bug: #150040 + +fn main() { + let [(ref a, b), x]; + a = ""; + b = 5; +} diff --git a/tests/crashes/150049.rs b/tests/crashes/150049.rs new file mode 100644 index 0000000000000..fa39785779c0f --- /dev/null +++ b/tests/crashes/150049.rs @@ -0,0 +1,12 @@ +//@ known-bug: #150049 +#![feature(min_generic_const_args)] +#![feature(inherent_associated_types)] +struct Foo<'a> { + x: &'a (), +} + +impl<'a> Foo<'a> { + fn foo(_: [u8; Foo::X]) { std::mem::transmute([4]) } +} + +fn main() {} diff --git a/tests/crashes/150128.rs b/tests/crashes/150128.rs new file mode 100644 index 0000000000000..28372ee7935f7 --- /dev/null +++ b/tests/crashes/150128.rs @@ -0,0 +1,8 @@ +//@ known-bug: #150128 +fn main() { + match 0 { + _ => || (), + _ => || (), + _ => (|| ()) as unsafe fn, + }; +} diff --git a/tests/crashes/150296.rs b/tests/crashes/150296.rs new file mode 100644 index 0000000000000..f7d5a54ca9b65 --- /dev/null +++ b/tests/crashes/150296.rs @@ -0,0 +1,14 @@ +//@ known-bug: #150296 +#[derive(PartialEq)] +pub struct Thing; + +impl Thing { + const A: Self = Thing; +} + +fn broken(x: Thing) { + match x { + >::A => {} + _ => {} + } +} diff --git a/tests/crashes/150387.rs b/tests/crashes/150387.rs new file mode 100644 index 0000000000000..b29ce143fb4ea --- /dev/null +++ b/tests/crashes/150387.rs @@ -0,0 +1,13 @@ +//@ known-bug: #150387 +#![feature(min_specialization)] +#![allow(dead_code)] + +struct Thing(T) where [T]: Sized; + +impl Drop for Thing where [T]: Sized { + default fn drop(&mut self) {} +} +impl Drop for Thing where [T]: Sized { + fn drop(&mut self) {} +} +fn main() {} diff --git a/tests/crashes/150403.rs b/tests/crashes/150403.rs new file mode 100644 index 0000000000000..bc00fc32135fc --- /dev/null +++ b/tests/crashes/150403.rs @@ -0,0 +1,26 @@ +//@ known-bug: #150403 +#![feature(non_lifetime_binders)] + +trait A { + type GAT: A; + fn foo(self, t: T) -> Self::GAT + where + Self: Sized; +} + +trait B: A where + for Self::GAT: B, +{ + fn bar(self) -> Self::GAT + where + Self: Sized; + + fn baz(self, t: T) -> Self::GAT + where + Self: Sized, + { + self.foo(t).bar() + } +} + +fn main() {} diff --git a/tests/crashes/150517.rs b/tests/crashes/150517.rs new file mode 100644 index 0000000000000..de14628547d28 --- /dev/null +++ b/tests/crashes/150517.rs @@ -0,0 +1,15 @@ +//@ known-bug: #150517 +trait Stream { + type Item; + fn next(self) -> (); +} +impl Stream for &'a () {} +impl<'a, A> Stream for <&A as Stream>::Item {} +trait StreamExt { + fn f(self) -> () + where + for<'b> &'b A: Stream, + { + self.next() + } +} diff --git a/tests/crashes/150545.rs b/tests/crashes/150545.rs new file mode 100644 index 0000000000000..04e084919d4f2 --- /dev/null +++ b/tests/crashes/150545.rs @@ -0,0 +1,8 @@ +//@ known-bug: #150545 +#![feature(non_lifetime_binders)] +trait Foo: for Bar { + type Item; + fn next(self) -> Self::Item; +} +trait Bar {} +fn main() {} diff --git a/tests/crashes/150749.rs b/tests/crashes/150749.rs new file mode 100644 index 0000000000000..572dc67c27511 --- /dev/null +++ b/tests/crashes/150749.rs @@ -0,0 +1,12 @@ +//@ known-bug: #150749 +#![feature(min_generic_const_args)] + +trait CollectArray { + fn inner_array(); +} +impl CollectArray for () { + fn inner_array() { + let temp_ptr: [(); Self]; + } +} +fn main() {} diff --git a/tests/crashes/150969.rs b/tests/crashes/150969.rs new file mode 100644 index 0000000000000..daab977bce193 --- /dev/null +++ b/tests/crashes/150969.rs @@ -0,0 +1,7 @@ +//@ known-bug: #150969 +#![feature(generic_const_exprs)] +#![feature(min_generic_const_args)] + +fn pass_enum { + pass_enum::<{None}> +} diff --git a/tests/crashes/151069.rs b/tests/crashes/151069.rs new file mode 100644 index 0000000000000..ae6f4da5c6c70 --- /dev/null +++ b/tests/crashes/151069.rs @@ -0,0 +1,16 @@ +//@ known-bug: #151069 +trait Trait { + type Assoc2; +} +struct Bar; +impl Trait for Bar +where + ::Assoc2: Trait, +{ + type Assoc2 = (); +} +struct Foo { + field: ::Assoc2, +} +static FOO2: &Foo = 0; +fn main() {} From bd938c5bfb48fe83ef8f2f54f562c2394c17d89f Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sat, 27 Jun 2026 01:13:26 -0400 Subject: [PATCH 263/278] Include default-stability info in rustdoc JSON. --- src/librustdoc/json/conversions.rs | 69 ++++++-- src/rustdoc-json-types/lib.rs | 39 ++++- src/tools/jsondoclint/src/validator.rs | 43 ++++- src/tools/jsondoclint/src/validator/tests.rs | 154 +++++++++++++++++- .../attrs/stability/default_body.rs | 76 +++++++++ 5 files changed, 364 insertions(+), 17 deletions(-) create mode 100644 tests/rustdoc-json/attrs/stability/default_body.rs diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 882220a0cae03..7083d730de58c 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -270,6 +270,18 @@ impl FromClean for Stability { } } +impl FromClean for Box { + fn from_clean(stab: &hir::DefaultBodyStability, _renderer: &JsonRenderer<'_>) -> Self { + let hir::StabilityLevel::Unstable { .. } = stab.level else { + bug!( + "unexpected stable default-body stability, \ + there's no stable equivalent of `#[rustc_default_body_unstable]`" + ) + }; + Box::new(ProvidedDefaultUnstable { feature: stab.feature.to_string() }) + } +} + impl FromClean for Option> { fn from_clean(generic_args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericArgs::*; @@ -353,18 +365,23 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)), VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)), FunctionItem(f) => { - ItemEnum::Function(from_clean_function(f, true, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(f, true, None, header.unwrap(), renderer)) } ForeignFunctionItem(f, _) => { - ItemEnum::Function(from_clean_function(f, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(f, false, None, header.unwrap(), renderer)) } TraitItem(t) => ItemEnum::Trait(t.into_json(renderer)), TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)), - MethodItem(m, _) => { - ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer)) - } + MethodItem(m, _) => ItemEnum::Function(from_clean_function( + m, + true, + default_body_stability_for_def_id(renderer.tcx, item.item_id.expect_def_id()) + .map(|stab| stab.into_json(renderer)), + header.unwrap(), + renderer, + )), RequiredMethodItem(m, _) => { - ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(m, false, None, header.unwrap(), renderer)) } ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)), StaticItem(s) => ItemEnum::Static(from_clean_static(s, rustc_hir::Safety::Safe, renderer)), @@ -385,23 +402,41 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum }) } // FIXME(generic_const_items): Add support for generic associated consts. - RequiredAssocConstItem(_generics, ty) => { - ItemEnum::AssocConst { type_: ty.into_json(renderer), value: None } - } + RequiredAssocConstItem(_generics, ty) => ItemEnum::AssocConst { + type_: ty.into_json(renderer), + value: None, + default_unstable: None, + }, // FIXME(generic_const_items): Add support for generic associated consts. - ProvidedAssocConstItem(ci) | ImplAssocConstItem(ci) => ItemEnum::AssocConst { + ProvidedAssocConstItem(ci) => ItemEnum::AssocConst { type_: ci.type_.into_json(renderer), value: Some(ci.kind.expr(renderer.tcx)), + default_unstable: default_body_stability_for_def_id( + renderer.tcx, + item.item_id.expect_def_id(), + ) + .map(|stab| stab.into_json(renderer)), + }, + ImplAssocConstItem(ci) => ItemEnum::AssocConst { + type_: ci.type_.into_json(renderer), + value: Some(ci.kind.expr(renderer.tcx)), + default_unstable: None, }, RequiredAssocTypeItem(g, b) => ItemEnum::AssocType { generics: g.into_json(renderer), bounds: b.into_json(renderer), type_: None, + default_unstable: None, }, AssocTypeItem(t, b) => ItemEnum::AssocType { generics: t.generics.into_json(renderer), bounds: b.into_json(renderer), type_: Some(t.item_type.as_ref().unwrap_or(&t.type_).into_json(renderer)), + default_unstable: default_body_stability_for_def_id( + renderer.tcx, + item.item_id.expect_def_id(), + ) + .map(|stab| stab.into_json(renderer)), }, // `convert_item` early returns `None` for stripped items, keywords, attributes and // "special" macro rules. @@ -815,6 +850,7 @@ impl FromClean for Impl { pub(crate) fn from_clean_function( clean::Function { decl, generics }: &clean::Function, has_body: bool, + default_unstable: Option>, header: rustc_hir::FnHeader, renderer: &JsonRenderer<'_>, ) -> Function { @@ -823,6 +859,7 @@ pub(crate) fn from_clean_function( generics: generics.into_json(renderer), header: header.into_json(renderer), has_body, + default_unstable, } } @@ -972,6 +1009,17 @@ impl FromClean for ItemKind { } } +fn default_body_stability_for_def_id( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> Option { + let stability = tcx.lookup_default_body_stability(def_id)?; + match stability.level { + hir::StabilityLevel::Unstable { .. } => Some(stability), + hir::StabilityLevel::Stable { .. } => None, + } +} + fn const_stability_for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { if !tcx.is_conditionally_const(def_id) { // The item cannot be conditionally-const. No const stability here. @@ -1040,6 +1088,7 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) AK::Deprecated { .. } => return Vec::new(), // Handled separately into Item::deprecation. AK::Stability { .. } => return Vec::new(), // Handled separately into Item::stability AK::RustcConstStability { .. } => return Vec::new(), // Handled separately into Item::const_stability. + AK::RustcBodyStability { .. } => return Vec::new(), // Handled separately by `default_unstable`. AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"), diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index e6af2fa04bb10..be4921d888278 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -114,8 +114,8 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line // are deliberately not in a doc comment, because they need not be in public docs.) // -// Latest feature: Add `Item::const_stability`. -pub const FORMAT_VERSION: u32 = 59; +// Latest feature: Add default-body stability metadata. +pub const FORMAT_VERSION: u32 = 60; /// The root of the emitted JSON blob. /// @@ -288,6 +288,9 @@ pub struct Item { /// - `#[stable]` and `#[unstable]` attributes: see the [`Self::stability`] field instead. /// - `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes: /// see the [`Self::const_stability`] field instead. + /// - `#[rustc_default_body_unstable]` attributes: instead see `default_unstable` fields on + /// item kinds that can have unstable default values, such as [`Function::default_unstable`], + /// [`ItemEnum::AssocConst::default_unstable`], and [`ItemEnum::AssocType::default_unstable`]. /// /// Attributes appear in pretty-printed Rust form, regardless of their formatting /// in the original source code. For example: @@ -367,6 +370,19 @@ pub enum StabilityLevel { Unstable, } +/// Information about an unstable default provided by a trait item. +/// +/// Example unstable defaults include: +/// - a stable trait function or method whose body is not stable +/// - a stable trait associated type or const whose default value is not stable +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] +pub struct ProvidedDefaultUnstable { + /// The feature that must be enabled to use the provided default. + pub feature: String, +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] #[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] @@ -379,6 +395,9 @@ pub enum StabilityLevel { /// - `#[stable]` and `#[unstable]`. These are in [`Item::stability`] instead. /// - `#[rustc_const_stable]` and `#[rustc_const_unstable]`. These are in /// [`Item::const_stability`] instead. +/// - `#[rustc_default_body_unstable]`. These are in the `default_unstable` field on the appropriate +/// item kinds: [`Function::default_unstable`], [`ItemEnum::AssocConst::default_unstable`], +/// and [`ItemEnum::AssocType::default_unstable`]. pub enum Attribute { /// `#[non_exhaustive]` NonExhaustive, @@ -875,6 +894,11 @@ pub enum ItemEnum { /// // ^^^^^^^^^^ /// ``` value: Option, + /// Metadata about an unstable default value provided for the associated constant, if any. + /// + /// Empty if the associated constant has no default (see [`ItemEnum::AssocConst::value`]), + /// or if the default value is stable. + default_unstable: Option>, }, /// An associated type of a trait or a type. AssocType { @@ -899,6 +923,11 @@ pub enum ItemEnum { /// ``` #[serde(rename = "type")] type_: Option, + /// Metadata about an unstable default value provided for the associated type, if any. + /// + /// Empty if the associated type has no default (see [`ItemEnum::AssocType::type_`]), + /// or if the default value is stable. + default_unstable: Option>, }, } @@ -1188,6 +1217,12 @@ pub struct Function { pub header: FunctionHeader, /// Whether the function has a body, i.e. an implementation. pub has_body: bool, + /// Metadata about a possible unstable provided default implementation for trait methods. + /// + /// Only populated for function items inside traits. Empty if the trait method + /// does not have a default implementation (see [`Function::has_body`]), + /// or if its default implementation is stable. + pub default_unstable: Option>, } /// Generic parameters accepted by an item and `where` clauses imposed on it and the parameters. diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 01deafe20b354..76a47c4f4fecf 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -97,7 +97,7 @@ impl<'a> Validator<'a> { ItemEnum::StructField(x) => self.check_struct_field(x), ItemEnum::Enum(x) => self.check_enum(x), ItemEnum::Variant(x) => self.check_variant(x, id), - ItemEnum::Function(x) => self.check_function(x), + ItemEnum::Function(x) => self.check_function(x, id), ItemEnum::Trait(x) => self.check_trait(x, id), ItemEnum::TraitAlias(x) => self.check_trait_alias(x), ItemEnum::Impl(x) => self.check_impl(x, id), @@ -114,12 +114,35 @@ impl<'a> Validator<'a> { ItemEnum::Module(x) => self.check_module(x, id), // FIXME: Why don't these have their own structs? ItemEnum::ExternCrate { .. } => {} - ItemEnum::AssocConst { type_, value: _ } => self.check_type(type_), - ItemEnum::AssocType { generics, bounds, type_ } => { + ItemEnum::AssocConst { type_, value, default_unstable } => { + self.check_type(type_); + if value.is_none() + && let Some(default_unstable) = default_unstable + { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `value` is `None`, but \ + assoc const id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); + } + } + ItemEnum::AssocType { generics, bounds, type_, default_unstable } => { self.check_generics(generics); bounds.iter().for_each(|b| self.check_generic_bound(b)); if let Some(ty) = type_ { self.check_type(ty); + } else if let Some(default_unstable) = default_unstable { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `type_` is `None`, but \ + assoc type id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); } } } @@ -194,9 +217,21 @@ impl<'a> Validator<'a> { } } - fn check_function(&mut self, x: &'a Function) { + fn check_function(&mut self, x: &'a Function, id: &Id) { self.check_generics(&x.generics); self.check_function_signature(&x.sig); + if !x.has_body + && let Some(default_unstable) = &x.default_unstable + { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `has_body == false`, but \ + function item id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); + } } fn check_trait(&mut self, x: &'a Trait, id: &Id) { diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs index ff2fae157f04c..f9b54a2cc0936 100644 --- a/src/tools/jsondoclint/src/validator/tests.rs +++ b/src/tools/jsondoclint/src/validator/tests.rs @@ -1,5 +1,7 @@ use rustc_hash::FxHashMap; -use rustdoc_json_types::{Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, Visibility}; +use rustdoc_json_types::{ + Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, ProvidedDefaultUnstable, Visibility, +}; use super::*; use crate::json_find::SelectorPart; @@ -221,6 +223,7 @@ fn errors_on_missing_path() { abi: Abi::Rust, }, has_body: true, + default_unstable: None, }), }, ), @@ -246,6 +249,155 @@ fn errors_on_missing_path() { ); } +fn krate_with_trait_item(inner: ItemEnum) -> Crate { + let item_id = Id(2); + Crate { + root: Id(0), + crate_version: None, + includes_private: false, + index: FxHashMap::from_iter([ + ( + Id(0), + Item { + id: Id(0), + crate_id: 0, + name: Some("root".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner: ItemEnum::Module(Module { + is_crate: true, + items: vec![Id(1)], + is_stripped: false, + }), + }, + ), + ( + Id(1), + Item { + id: Id(1), + crate_id: 0, + name: Some("Trait".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner: ItemEnum::Trait(Trait { + is_auto: false, + is_unsafe: false, + is_dyn_compatible: true, + items: vec![item_id], + generics: Generics { params: vec![], where_predicates: vec![] }, + bounds: vec![], + implementations: vec![], + }), + }, + ), + ( + item_id, + Item { + id: item_id, + crate_id: 0, + name: Some("TraitItem".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner, + }, + ), + ]), + paths: FxHashMap::default(), + external_crates: FxHashMap::default(), + target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] }, + format_version: FORMAT_VERSION, + } +} + +#[test] +fn errors_on_default_unstable_without_function_body() { + let krate = krate_with_trait_item(ItemEnum::Function(Function { + sig: FunctionSignature { inputs: vec![], output: None, is_c_variadic: false }, + generics: Generics { params: vec![], where_predicates: vec![] }, + header: FunctionHeader { + is_const: false, + is_unsafe: false, + is_async: false, + abi: Abi::Rust, + }, + has_body: false, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + })); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `has_body == false`, but \ + function item id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + +#[test] +fn errors_on_default_unstable_without_assoc_const_value() { + let krate = krate_with_trait_item(ItemEnum::AssocConst { + type_: Type::Primitive("usize".to_owned()), + value: None, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + }); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `value` is `None`, but \ + assoc const id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + +#[test] +fn errors_on_default_unstable_without_assoc_type_default() { + let krate = krate_with_trait_item(ItemEnum::AssocType { + generics: Generics { params: vec![], where_predicates: vec![] }, + bounds: vec![], + type_: None, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + }); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `type_` is `None`, but \ + assoc type id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + #[test] #[should_panic = "LOCAL_CRATE_ID is wrong"] fn checks_local_crate_id_is_correct() { diff --git a/tests/rustdoc-json/attrs/stability/default_body.rs b/tests/rustdoc-json/attrs/stability/default_body.rs new file mode 100644 index 0000000000000..5a70e47d6c505 --- /dev/null +++ b/tests/rustdoc-json/attrs/stability/default_body.rs @@ -0,0 +1,76 @@ +#![feature(staged_api, rustc_attrs, associated_type_defaults)] + +#[stable(feature = "default_body_trait_feature", since = "1.0.0")] +pub trait TraitWithDefaults { + //@ is "$.index[?(@.docs=='method with unstable default body')].inner.function.has_body" true + //@ is "$.index[?(@.docs=='method with unstable default body')].inner.function.default_unstable.feature" '"method_default_body_feature"' + //@ is "$.index[?(@.docs=='method with unstable default body')].attrs" [] + /// method with unstable default body + #[stable(feature = "default_body_method_feature", since = "1.1.0")] + #[rustc_default_body_unstable(feature = "method_default_body_feature", issue = "none")] + fn method_with_unstable_default() {} + + //@ is "$.index[?(@.docs=='required method without default body')].inner.function.has_body" false + //@ is "$.index[?(@.docs=='required method without default body')].inner.function.default_unstable" null + /// required method without default body + #[stable(feature = "required_method_feature", since = "1.2.0")] + fn required_method(); + + //@ is "$.index[?(@.docs=='method with stable default body')].inner.function.has_body" true + //@ is "$.index[?(@.docs=='method with stable default body')].inner.function.default_unstable" null + /// method with stable default body + #[stable(feature = "stable_default_method_feature", since = "1.3.0")] + fn method_with_stable_default() {} + + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].inner.assoc_const.value" '"0"' + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].inner.assoc_const.default_unstable.feature" '"assoc_const_default_value_feature"' + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].attrs" [] + /// associated constant with unstable default value + #[stable(feature = "assoc_const_with_unstable_default_feature", since = "1.4.0")] + #[rustc_default_body_unstable(feature = "assoc_const_default_value_feature", issue = "none")] + const UNSTABLE_DEFAULT_CONST: usize = 0; + + //@ is "$.index[?(@.docs=='required associated constant')].inner.assoc_const.value" null + //@ is "$.index[?(@.docs=='required associated constant')].inner.assoc_const.default_unstable" null + /// required associated constant + #[stable(feature = "required_assoc_const_feature", since = "1.5.0")] + const REQUIRED_CONST: usize; + + //@ is "$.index[?(@.docs=='associated type with unstable default type')].inner.assoc_type.default_unstable.feature" '"assoc_type_default_type_feature"' + //@ is "$.index[?(@.docs=='associated type with unstable default type')].attrs" [] + /// associated type with unstable default type + #[stable(feature = "assoc_type_with_unstable_default_feature", since = "1.6.0")] + #[rustc_default_body_unstable(feature = "assoc_type_default_type_feature", issue = "none")] + type UnstableDefaultType = usize; + + //@ is "$.index[?(@.docs=='required associated type')].inner.assoc_type.type" null + //@ is "$.index[?(@.docs=='required associated type')].inner.assoc_type.default_unstable" null + /// required associated type + #[stable(feature = "required_assoc_type_feature", since = "1.7.0")] + type RequiredType; +} + +#[stable(feature = "default_body_impl_target_feature", since = "2.0.0")] +pub struct ImplTarget; + +// Impl items provide their own definitions, so they do not use the trait's unstable defaults. +#[stable(feature = "default_body_impl_feature", since = "2.1.0")] +impl TraitWithDefaults for ImplTarget { + //@ is "$.index[?(@.docs=='impl override for unstable default body')].inner.function.default_unstable" null + /// impl override for unstable default body + fn method_with_unstable_default() {} + + fn required_method() {} + + //@ is "$.index[?(@.docs=='impl override for unstable default value')].inner.assoc_const.default_unstable" null + /// impl override for unstable default value + const UNSTABLE_DEFAULT_CONST: usize = 1; + + const REQUIRED_CONST: usize = 2; + + //@ is "$.index[?(@.docs=='impl override for unstable default type')].inner.assoc_type.default_unstable" null + /// impl override for unstable default type + type UnstableDefaultType = u8; + + type RequiredType = (); +} From e101c8d3f303631d167164a3bc3c65f5ba0ddab3 Mon Sep 17 00:00:00 2001 From: Vastargazing Date: Wed, 6 May 2026 12:50:17 +0300 Subject: [PATCH 264/278] tests: check wasm compiler_builtins object architecture Add a run-make test that checks the `.o` members in the prebuilt `libcompiler_builtins` archive for `wasm32-wasip1` are wasm objects. This guards against building wasm compiler-rt fallbacks with the host C toolchain. --- src/tools/run-make-support/Cargo.toml | 2 +- .../rmake.rs | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 918f5ef0d5069..c1bff4f94272c 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -13,7 +13,7 @@ edition = "2024" bstr = "1.12" gimli = "0.32" libc = "0.2" -object = "0.37" +object = { version = "0.37", features = ["wasm"] } regex = "1.11" serde_json = "1.0" similar = "2.7" diff --git a/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs b/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs new file mode 100644 index 0000000000000..0294308d199f2 --- /dev/null +++ b/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs @@ -0,0 +1,54 @@ +//! Regression test for . +//! +//! The prebuilt `libcompiler_builtins` rlib bundled in the wasm sysroot must +//! contain wasm object files — never host ELF/Mach-O/COFF. Bootstrap could +//! previously pick the host C toolchain for compiler-rt fallbacks on wasm +//! targets and silently embed host objects into the wasm sysroot +//! (fixed in rust-lang/rust#137457). + +//@ only-wasm32 + +use run_make_support::object::read::Object; +use run_make_support::object::read::archive::ArchiveFile; +use run_make_support::object::{self, Architecture}; +use run_make_support::{has_extension, has_prefix, rfs, rustc, shallow_find_files}; + +fn main() { + let libdir = rustc().print("target-libdir").run().stdout_utf8(); + let libdir = libdir.trim(); + + let rlibs = shallow_find_files(libdir, |path| { + has_prefix(path, "libcompiler_builtins") && has_extension(path, "rlib") + }); + assert!(!rlibs.is_empty(), "no libcompiler_builtins rlib found in {libdir}"); + + let data = rfs::read(&rlibs[0]); + let archive = ArchiveFile::parse(&*data).unwrap(); + + let mut checked = 0usize; + for member in archive.members() { + let member = member.unwrap(); + let name = std::str::from_utf8(member.name()).unwrap_or(""); + if name.ends_with(".rmeta") || name.ends_with(".rmeta-link") { + continue; + } + let obj_data = member.data(&*data).unwrap(); + let obj = object::File::parse(obj_data).unwrap_or_else(|e| { + panic!("failed to parse member `{name}` in compiler_builtins rlib: {e}") + }); + let arch = obj.architecture(); + assert!( + matches!(arch, Architecture::Wasm32 | Architecture::Wasm64), + "object `{name}` in compiler_builtins rlib has architecture {arch:?}, \ + expected wasm — see rust-lang/rust#132802", + ); + checked += 1; + } + + assert!( + checked > 0, + "no object members found in compiler_builtins rlib at {} — \ + archive should always contain object files", + rlibs[0].display(), + ); +} From 3fa872ca7932853d12ef66a37c39988cd7bec92b Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 29 Jun 2026 09:26:46 +1000 Subject: [PATCH 265/278] Increase `wasm-panic-small` byte limit by 5,000 bytes Previous limit of 40,000 is now broken by 2,281 bytes. --- tests/run-make/wasm-panic-small/rmake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-make/wasm-panic-small/rmake.rs b/tests/run-make/wasm-panic-small/rmake.rs index ea0b6faf037c5..f6504830a5263 100644 --- a/tests/run-make/wasm-panic-small/rmake.rs +++ b/tests/run-make/wasm-panic-small/rmake.rs @@ -24,5 +24,5 @@ fn test(cfg: &str) { let bytes = rfs::read("foo.wasm"); println!("{}", bytes.len()); - assert!(bytes.len() < 40_000, "bytes len was: {}", bytes.len()); + assert!(bytes.len() < 45_000, "bytes len was: {}", bytes.len()); } From 5128fcb1991d35c0e23d2622504d76d33b480825 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 29 Jun 2026 14:58:44 +1000 Subject: [PATCH 266/278] Note where and why documentation hard-links are used Also link back to a relevant issue to track possible changes in functionality. --- library/alloc/src/io/error.rs | 3 +++ library/core/src/io/cursor.rs | 2 ++ library/core/src/io/error.rs | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs index 8053e73917c7f..eb02664ee7e46 100644 --- a/library/alloc/src/io/error.rs +++ b/library/alloc/src/io/error.rs @@ -55,6 +55,7 @@ impl Error { /// originate from the OS itself. It is a shortcut for [`Error::new`][new] /// with [`ErrorKind::Other`]. /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method /// [new]: struct.Error.html#method.new /// /// # Examples @@ -84,6 +85,7 @@ impl Error { /// then this function will return [`Some`], /// otherwise it will return [`None`]. /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method /// [new]: struct.Error.html#method.new /// [other]: struct.Error.html#method.other /// @@ -141,6 +143,7 @@ impl Error { /// `Box::downcast` on the custom boxed error, returned by /// [`Error::into_inner`][into_inner]. /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method /// [into_inner]: struct.Error.html#method.into_inner /// /// # Examples diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index ebbec6c5e8210..24d3bfc67b988 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -16,6 +16,7 @@ /// code, but use an in-memory buffer in our tests. We can do this with /// `Cursor`: /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [bytes]: crate::slice "slice" /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html @@ -80,6 +81,7 @@ impl Cursor { /// is not empty. So writing to cursor starts with overwriting [`Vec`] /// content, not with appending to it. /// + // FIXME(#74481): Hard-links required to link from `core` to `alloc` /// [`Vec`]: ../../alloc/vec/struct.Vec.html /// /// # Examples diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index f9e45313be53d..2d15307daf153 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -45,6 +45,7 @@ use crate::{error, fmt, result}; /// will generally use `io::Result` instead of shadowing the [prelude]'s import /// of [`core::result::Result`][`Result`]. /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [`std::io`]: ../../std/io/index.html /// [`io::Error`]: Error /// [`Result`]: crate::result::Result @@ -76,6 +77,7 @@ pub type Result = result::Result; /// `Error` can be created with crafted error messages and a particular value of /// [`ErrorKind`]. /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [Read]: ../../std/io/trait.Read.html /// [Write]: ../../std/io/trait.Write.html /// [Seek]: ../../std/io/trait.Seek.html @@ -171,6 +173,7 @@ pub struct SimpleMessage { /// Contrary to [`Error::new`][new], this macro does not allocate and can be used in /// `const` contexts. /// +// FIXME(#74481): Hard-links required to link from `core` to `alloc` for incoherent method /// [new]: ../../alloc/io/struct.Error.html#method.new /// /// # Example @@ -286,6 +289,7 @@ impl Error { /// [`from_raw_os_error`][from_raw_os_error], then this function will return [`Some`], otherwise /// it will return [`None`]. /// + // FIXME(#74481): Hard-links required to link from `core` to `std` for incoherent method /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error /// [from_raw_os_error]: ../../std/io/struct.Error.html#method.from_raw_os_error /// @@ -366,6 +370,7 @@ impl Error { /// If this [`Error`] was constructed via [`new`][new] then this function will /// return [`Some`], otherwise it will return [`None`]. /// + // FIXME(#74481): Hard-links required to link from `core` to `std` /// [new]: ../../alloc/io/struct.Error.html#method.new /// /// # Examples @@ -441,6 +446,7 @@ impl Error { /// it will be a value inferred from the system's error encoding. /// See [`last_os_error`][last_os_error] for more details. /// + // FIXME(#74481): Hard-links required to link from `core` to `std` /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error /// /// # Examples From 91a762bd474be0c3605f9a8b5969f376c1400652 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 29 Jun 2026 16:53:58 +0300 Subject: [PATCH 267/278] Inline information of whether this segment is delegation's child segment into `PathSegment` --- compiler/rustc_ast_lowering/src/delegation.rs | 7 ++++++- .../rustc_ast_lowering/src/delegation/generics.rs | 12 ++++++++++-- compiler/rustc_ast_lowering/src/lib.rs | 1 + compiler/rustc_ast_lowering/src/path.rs | 1 + compiler/rustc_hir/src/hir.rs | 14 +++++++++++++- compiler/rustc_hir/src/intravisit.rs | 3 ++- .../src/hir_ty_lowering/bounds.rs | 1 + .../src/hir_ty_lowering/errors.rs | 1 + .../src/hir_ty_lowering/generics.rs | 2 +- .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 2 +- compiler/rustc_middle/src/hir/map.rs | 7 ------- 11 files changed, 37 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 078ebd6987ae3..7021ac8cfedaf 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -55,7 +55,9 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::kw; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; -use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults}; +use crate::delegation::generics::{ + GenericsGenerationResult, GenericsGenerationResults, GenericsPosition, +}; use crate::diagnostics::{ CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion, DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee, @@ -505,6 +507,7 @@ impl<'hir> LoweringContext<'_, 'hir> { res: Res::Local(param_id), args: None, infer_args: false, + delegation_child_segment: false, })); let path = self.arena.alloc(hir::Path { span, res: Res::Local(param_id), segments }); @@ -714,6 +717,8 @@ impl<'hir> LoweringContext<'_, 'hir> { result.args_segment_id = segment.hir_id; result.use_for_sig_inheritance = !result.generics.is_trait_impl(); + segment.delegation_child_segment = result.generics.pos() == GenericsPosition::Child; + segment } diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index 79a3961c22a53..2a751ffae3eb7 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -12,7 +12,7 @@ use rustc_span::{Ident, Span, sym}; use crate::LoweringContext; use crate::diagnostics::DelegationInfersMismatch; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub(super) enum GenericsPosition { Parent, Child, @@ -155,7 +155,7 @@ impl<'hir> DelegationGenericArgsIterator<'hir> { impl<'hir> HirOrTyGenerics<'hir> { pub(super) fn into_hir_generics(&mut self, ctx: &mut LoweringContext<'_, 'hir>, span: Span) { if let HirOrTyGenerics::Ty(ty) = self { - let rename_self = matches!(ty.pos, GenericsPosition::Child); + let rename_self = ty.pos == GenericsPosition::Child; let params = ctx.uplift_delegation_generic_params(span, &ty.data, rename_self); *self = HirOrTyGenerics::Hir(DelegationGenerics { @@ -218,6 +218,13 @@ impl<'hir> HirOrTyGenerics<'hir> { .expect("`Self` generic param is not found while expected"), } } + + pub(crate) fn pos(&self) -> GenericsPosition { + match self { + HirOrTyGenerics::Ty(ty) => ty.pos, + HirOrTyGenerics::Hir(hir) => hir.pos, + } + } } impl<'hir> GenericsGenerationResult<'hir> { @@ -590,6 +597,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ident: p.name.ident(), infer_args: false, res, + delegation_child_segment: false, }]), res, span: p.span, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 06e83a7486100..1bf798c357bd2 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1014,6 +1014,7 @@ impl<'hir> LoweringContext<'_, 'hir> { res, args, infer_args: args.is_none(), + delegation_child_segment: false, }]), }) } diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 42c4af5add12d..18e037527836a 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -412,6 +412,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { Some(generic_args.into_generic_args(self)) }, + delegation_child_segment: false, } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6d2e68d20bf30..254d5fdd5f80c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -387,12 +387,24 @@ pub struct PathSegment<'hir> { /// out of those only the segments with no type parameters /// to begin with, e.g., `Vec::new` is `>::new::<..>`. pub infer_args: bool, + + /// Whether this segment is a delegation's child segment: + /// `reuse Trait::foo`, in this case `foo` is a delegation's child segment. + /// Used for faster check during generic args lowering. + pub delegation_child_segment: bool, } impl<'hir> PathSegment<'hir> { /// Converts an identifier to the corresponding segment. pub fn new(ident: Ident, hir_id: HirId, res: Res) -> PathSegment<'hir> { - PathSegment { ident, hir_id, res, infer_args: true, args: None } + PathSegment { + ident, + hir_id, + res, + infer_args: true, + args: None, + delegation_child_segment: false, + } } pub fn invalid() -> Self { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9e0eaef596420..f1f542e9ce1e9 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1468,7 +1468,8 @@ pub fn walk_path_segment<'v, V: Visitor<'v>>( visitor: &mut V, segment: &'v PathSegment<'v>, ) -> V::Result { - let PathSegment { ident, hir_id, res: _, args, infer_args: _ } = segment; + let PathSegment { ident, hir_id, res: _, args, infer_args: _, delegation_child_segment: _ } = + segment; try_visit!(visitor.visit_ident(*ident)); try_visit!(visitor.visit_id(*hir_id)); visit_opt!(visitor, visit_generic_args, *args); 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 810e14a7d6d0b..d730683b132d4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -467,6 +467,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { res: Res::Err, args: Some(constraint.gen_args), infer_args: false, + delegation_child_segment: false, }; let alias_args = self.lower_generic_args_of_assoc_item( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 32678e8a5a12f..9d22d3060e1ff 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -471,6 +471,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { res: Res::Err, args: Some(constraint.gen_args), infer_args: false, + delegation_child_segment: false, }; let alias_args = self.lower_generic_args_of_assoc_item( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index a36bb7bce9bab..45c2ed205c74d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -434,7 +434,7 @@ pub(crate) fn check_generic_arg_count( // Suppress this warning for delegations as it is compiler generated and lifetimes are // propagated while late-bound lifetimes may be present. - let explicit_late_bound = match tcx.hir_is_delegation_child_segment(seg) { + let explicit_late_bound = match seg.delegation_child_segment { true => ExplicitLateBound::No, false => prohibit_explicit_late_bound_lifetimes(cx, gen_params, gen_args, gen_pos), }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index e8a7156b41fe4..7edcb40be616e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -870,7 +870,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, generic_args: segment.args(), infer_args: segment.infer_args, - create_synth_args: tcx.hir_is_delegation_child_segment(segment), + create_synth_args: segment.delegation_child_segment, incorrect_args: &arg_count.correct, }; diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 3cab936c45c1f..444433dcb6c54 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -879,13 +879,6 @@ impl<'tcx> TyCtxt<'tcx> { self.hir_opt_delegation_info(delegation_id).expect("processing delegation") } - pub fn hir_is_delegation_child_segment(self, segment: &PathSegment<'_>) -> bool { - let parent_def = self.hir_get_parent_item(segment.hir_id).def_id; - - self.hir_opt_delegation_info(parent_def) - .is_some_and(|info| info.child_seg_id == segment.hir_id) - } - #[inline] fn hir_opt_ident(self, id: HirId) -> Option { match self.hir_node(id) { From cd55f64904d8dfe73145a71b82b86d34dd222ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Mon, 29 Jun 2026 15:55:27 +0200 Subject: [PATCH 268/278] fix typo in `-Z min-recursion-limit` unstable doc --- .../{min-recursive-limit.md => min-recursion-limit.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/doc/unstable-book/src/compiler-flags/{min-recursive-limit.md => min-recursion-limit.md} (100%) diff --git a/src/doc/unstable-book/src/compiler-flags/min-recursive-limit.md b/src/doc/unstable-book/src/compiler-flags/min-recursion-limit.md similarity index 100% rename from src/doc/unstable-book/src/compiler-flags/min-recursive-limit.md rename to src/doc/unstable-book/src/compiler-flags/min-recursion-limit.md From 3056b8e4ec9fe9498be410e35308f3ba673bba6d Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Fri, 19 Jun 2026 16:45:26 +0200 Subject: [PATCH 269/278] Use an explicit set to detect resolution cycles instead of `try_borrow_mut` on `RefCell`. Use a TLS for this set ahead of parallel import resolution. --- compiler/rustc_resolve/src/ident.rs | 30 +++++++------- compiler/rustc_resolve/src/imports.rs | 56 ++++++++++++++++++++++++++- compiler/rustc_resolve/src/lib.rs | 27 ++++++------- 3 files changed, 82 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 9d08e8b98cb31..f5b38e750a544 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -16,7 +16,7 @@ use tracing::{debug, instrument}; use crate::diagnostics::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; use crate::hygiene::Macros20NormalizedSyntaxContext; -use crate::imports::{Import, NameResolution}; +use crate::imports::{Import, NameResolution, cycle_detection}; use crate::late::{ ConstantHasGenerics, DiagMetadata, NoConstantGenericsReason, PathSource, Rib, RibKind, }; @@ -1148,13 +1148,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import: Option>, ) -> Result, ControlFlow> { let key = BindingKey::new(ident, ns); - // `try_borrow_mut` is required to ensure exclusive access, even if the resulting binding - // doesn't need to be mutable. It will fail when there is a cycle of imports, and without - // the exclusive access infinite recursion will crash the compiler with stack overflow. - let resolution = &*self - .resolution_or_default(module.to_module(), key, orig_ident_span) - .try_borrow_mut_unchecked() - .map_err(|_| ControlFlow::Continue(Determined))?; + let resolution_ref = self.resolution_or_default(module.to_module(), key, orig_ident_span); + let resolution = resolution_ref.borrow(); let binding = resolution.non_glob_decl.filter(|b| Some(*b) != ignore_decl); @@ -1175,6 +1170,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) }; } + // We need to detect resolution cycles to avoid infinite recursion. The guard ensures + // the resolution is removed when this resolve call ends. + let _cycle_guard = cycle_detection::enter_cycle_detector(resolution_ref) + .map_err(|_| ControlFlow::Continue(Determined))?; + // Check if one of single imports can still define the name, block if it can. if self.reborrow().single_import_can_define_name( &resolution, @@ -1210,13 +1210,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import: Option>, ) -> Result, ControlFlow> { let key = BindingKey::new(ident, ns); - // `try_borrow_mut` is required to ensure exclusive access, even if the resulting binding - // doesn't need to be mutable. It will fail when there is a cycle of imports, and without - // the exclusive access infinite recursion will crash the compiler with stack overflow. - let resolution = &*self - .resolution_or_default(module.to_module(), key, orig_ident_span) - .try_borrow_mut_unchecked() - .map_err(|_| ControlFlow::Continue(Determined))?; + let resolution_ref = self.resolution_or_default(module.to_module(), key, orig_ident_span); + let resolution = resolution_ref.borrow(); let binding = resolution.glob_decl.filter(|b| Some(*b) != ignore_decl); @@ -1231,6 +1226,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ); } + // We need to detect resolution cycles to avoid infinite recursion. The guard ensures + // the resolution is removed when this resolve call ends. + let _cycle_guard = cycle_detection::enter_cycle_detector(resolution_ref) + .map_err(|_| ControlFlow::Continue(Determined))?; + // Check if one of single imports can still define the name, // if it can then our result is not determined and can be invalidated. if self.reborrow().single_import_can_define_name( diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index a3cb526e60a87..08807616d5097 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -32,7 +32,7 @@ use crate::diagnostics::{ ConsiderMarkingAsPubCrate, }; use crate::error_helper::{OnUnknownData, Suggestion}; -use crate::ref_mut::CmCell; +use crate::ref_mut::{CmCell, CmRefCell}; use crate::{ AmbiguityError, BindingKey, CmResolver, Decl, DeclData, DeclKind, Determinacy, Finalize, IdentKey, ImportSuggestion, ImportSummary, LocalModule, ModuleOrUniformRoot, ParentScope, @@ -292,6 +292,8 @@ pub(crate) struct NameResolution<'ra> { pub orig_ident_span: Span, } +pub(crate) type NameResolutionRef<'ra> = Interned<'ra, CmRefCell>>; + impl<'ra> NameResolution<'ra> { pub(crate) fn new(orig_ident_span: Span) -> Self { NameResolution { single_imports: FxIndexSet::default(), orig_ident_span, .. } @@ -320,6 +322,57 @@ impl<'ra> NameResolution<'ra> { } } +// module to keep the TLS private and only accessible through the function `enter_cycle_detector`. +pub(crate) mod cycle_detection { + use std::ptr; + + use crate::CacheRefCell; + use crate::imports::NameResolutionRef; + + thread_local!( + /// During import resolution, recursive imports can form cycles. + /// This set stores the active resolution stack for the current thread. + /// So it's essentially a recursion stack. + /// + /// The key is the interned address of a `RefCell>` allocated + /// in the `Resolver Arenas` (lifetime `'ra`), it is thus stable and allows casting + /// to a `*const ()` for comparison. This is done because we can't use lifetimes + /// other than `'static` in thread local storage. + static ACTIVE_RESOLUTIONS: CacheRefCell> = Default::default(); + ); + + pub(crate) struct ActiveResolutionGuard { + key: *const (), + } + + impl Drop for ActiveResolutionGuard { + fn drop(&mut self) { + ACTIVE_RESOLUTIONS.with_borrow_mut(|ar| { + // Only this guard is allowed to remove this key. + assert!( + Some(self.key) == ar.pop(), + "This guard should be the only one removing this key" + ); + }); + } + } + + /// Returns `Err(())` if a cycle is detected, otherwise this returns a + /// guard that will remove the resolution when dropped. + pub(crate) fn enter_cycle_detector<'ra>( + resolution: NameResolutionRef<'ra>, + ) -> Result { + let key = ptr::from_ref(resolution.0).cast::<()>(); + ACTIVE_RESOLUTIONS.with_borrow_mut(|ar| { + if ar.contains(&key) { + return Err(()); + } + ar.push(key); + Ok(ActiveResolutionGuard { key }) + }) + } +} + /// An error that may be transformed into a diagnostic later. Used to combine multiple unresolved /// import errors within the same use tree into a single diagnostic. #[derive(Debug, Clone)] @@ -640,6 +693,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let (binding, t) = { let resolution = &mut *self .resolution_or_default(module.to_module(), key, orig_ident_span) + .0 .borrow_mut(self); let old_decl = resolution.determined_decl(); let old_vis = old_decl.map(|d| d.vis()); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 1f1884ea0cbe2..be76951572427 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -77,6 +77,7 @@ use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; use crate::error_helper::OnUnknownData; +use crate::imports::NameResolutionRef; use crate::ref_mut::{CmCell, CmRefCell}; mod build_reduced_graph; @@ -639,7 +640,7 @@ impl BindingKey { } } -type Resolutions<'ra> = CmRefCell>>>; +type Resolutions<'ra> = CmRefCell>>; /// One node in the tree of modules. /// @@ -1597,11 +1598,10 @@ impl<'ra> ResolverArenas<'ra> { fn alloc_import(&'ra self, import: ImportData<'ra>) -> Import<'ra> { Interned::new_unchecked(self.imports.alloc(import)) } - fn alloc_name_resolution( - &'ra self, - orig_ident_span: Span, - ) -> &'ra CmRefCell> { - self.name_resolutions.alloc(CmRefCell::new(NameResolution::new(orig_ident_span))) + fn alloc_name_resolution(&'ra self, orig_ident_span: Span) -> NameResolutionRef<'ra> { + Interned::new_unchecked( + self.name_resolutions.alloc(CmRefCell::new(NameResolution::new(orig_ident_span))), + ) } fn alloc_macro_rules_scope(&'ra self, scope: MacroRulesScope<'ra>) -> MacroRulesScopeRef<'ra> { self.dropless.alloc(CacheCell::new(scope)) @@ -2212,7 +2212,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module: Module<'ra>, key: BindingKey, ) -> Option>> { - self.resolutions(module).borrow().get(&key).map(|resolution| resolution.borrow()) + self.resolutions(module).borrow().get(&key).map(|resolution| resolution.0.borrow()) } fn resolution_or_default( @@ -2220,8 +2220,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module: Module<'ra>, key: BindingKey, orig_ident_span: Span, - ) -> &'ra CmRefCell> { - self.resolutions(module) + ) -> NameResolutionRef<'ra> { + *self + .resolutions(module) .borrow_mut_unchecked() .entry(key) .or_insert_with(|| self.arenas.alloc_name_resolution(orig_ident_span)) @@ -2832,8 +2833,6 @@ type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>; // parallel name resolution. use std::cell::{Cell as CacheCell, RefCell as CacheRefCell}; -// FIXME: `*_unchecked` methods in the module below should be eliminated in the process -// of migration to parallel name resolution. mod ref_mut { use std::cell::{BorrowMutError, Cell, Ref, RefCell, RefMut}; use std::fmt; @@ -2941,6 +2940,8 @@ mod ref_mut { } #[track_caller] + // FIXME: this should be eliminated in the process of migration + // to parallel name resolution. pub(crate) fn borrow_mut_unchecked(&self) -> RefMut<'_, T> { self.0.borrow_mut() } @@ -2953,10 +2954,6 @@ mod ref_mut { self.0.borrow_mut() } - pub(crate) fn try_borrow_mut_unchecked(&self) -> Result, BorrowMutError> { - self.0.try_borrow_mut() - } - #[track_caller] pub(crate) fn try_borrow_mut<'ra, 'tcx>( &self, From a60be48dff6b696a9b55c2878d0731fea26230a2 Mon Sep 17 00:00:00 2001 From: Liza Burakova Date: Mon, 29 Jun 2026 15:09:50 +0000 Subject: [PATCH 270/278] llvm-wrapper: use accessors for private fields in LLVM 23+ In LLVM, FeatureKV/SubtargetKV pointers are now private: https://github.com/llvm/llvm-project/pull/206237 This change fixes compiler errors when building rustc with ToT LLVM by using the key() and desc() accessors. --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 5dcaa5f6f84b8..45229e5399dd5 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -288,7 +288,11 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, // Just print a bare list of target CPU names, and let Rust-side code handle // the full formatting of `--print=target-cpus`. for (auto &CPU : CPUTable) { +#if LLVM_VERSION_GE(23, 0) + OS << CPU.key() << "\n"; +#else OS << CPU.Key << "\n"; +#endif } } @@ -315,9 +319,15 @@ extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index, #endif const ArrayRef FeatTable = MCInfo.getAllProcessorFeatures(); +#if LLVM_VERSION_GE(23, 0) + const SubtargetFeatureKV &Feat = FeatTable[Index]; + *Feature = Feat.key(); + *Desc = Feat.desc(); +#else const SubtargetFeatureKV Feat = FeatTable[Index]; *Feature = Feat.Key; *Desc = Feat.Desc; +#endif } extern "C" const char *LLVMRustGetHostCPUName(size_t *OutLen) { From 7bfdb60f23f254d2f38699295c46feafe3b87ef8 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Mon, 29 Jun 2026 12:28:25 -0700 Subject: [PATCH 271/278] Comment on needed RAM in huge-stacks.rs --- tests/ui/codegen/huge-stacks.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ui/codegen/huge-stacks.rs b/tests/ui/codegen/huge-stacks.rs index e0f2cd2cb05d6..9aee3937c7534 100644 --- a/tests/ui/codegen/huge-stacks.rs +++ b/tests/ui/codegen/huge-stacks.rs @@ -5,6 +5,7 @@ //@ min-llvm-version: 22 // Regression test for https://github.com/rust-lang/rust/issues/83060 +// Verifies a program is not miscompiled if it includes a 4GB array on the stack fn func() { const CAP: usize = std::u32::MAX as usize; @@ -17,7 +18,7 @@ fn main() { std::thread::Builder::new() .stack_size(5 * 1024 * 1024 * 1024) .spawn(func) - .unwrap() + .expect("huge-stacks.rs requires 5GB RAM to run") .join() .unwrap(); } From abbfd2ce05dc13e28db659b2b9e8e6403ae827e1 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 8 May 2026 00:52:44 +0300 Subject: [PATCH 272/278] Pass the whole `GenericArgs` to `Interner::for_each_relevant_impl()` And not just the self type. rustc does not make use of this, but rust-analyzer needs it to support impls in the same block as args, see https://rust-lang.zulipchat.com/#narrow/channel/144729-t-types/topic/non.20local.20impls.20for.20generic.20args/with/593629693. I'm not entirely sure this covers all cases (e.g. an unnormalized alias), and wants feedback from a types team member. --- .../src/ty/context/impl_interner.rs | 5 ++- .../src/solve/assembly/mod.rs | 38 +++++++++---------- .../src/solve/trait_goals.rs | 10 ++--- compiler/rustc_type_ir/src/interner.rs | 5 +-- 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 5819470765e69..cf6385522bdad 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -541,8 +541,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { // only want to consider types that *actually* unify with float/int vars. fn for_each_relevant_impl( self, - trait_def_id: DefId, - self_ty: Ty<'tcx>, + trait_ref: ty::TraitRef<'tcx>, mut f: impl FnMut(DefId) -> R, ) -> R { macro_rules! ret { @@ -554,6 +553,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { }; } + let trait_def_id = trait_ref.def_id; + let self_ty = trait_ref.self_ty(); let tcx = self; let trait_impls = tcx.trait_impls_of(trait_def_id); let mut consider_impls_for_simplified_type = |simp| { diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 0421bd7aaedc0..db1a8228c43cb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -539,28 +539,24 @@ where candidates: &mut Vec>, ) -> Result<(), RerunNonErased> { let cx = self.cx(); - cx.for_each_relevant_impl( - goal.predicate.trait_def_id(cx), - goal.predicate.self_ty(), - |impl_def_id| -> Result<_, _> { - // For every `default impl`, there's always a non-default `impl` - // that will *also* apply. There's no reason to register a candidate - // for this impl, since it is *not* proof that the trait goal holds. - if cx.impl_is_default(impl_def_id) { - return Ok(()); - } - match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { - ecx.evaluate_added_goals_and_make_canonical_response(certainty) - }) - .map_err_to_rerun()? - { - Ok(candidate) => candidates.push(candidate), - Err(NoSolution) => {} - } + cx.for_each_relevant_impl(goal.predicate.trait_ref(cx), |impl_def_id| -> Result<_, _> { + // For every `default impl`, there's always a non-default `impl` + // that will *also* apply. There's no reason to register a candidate + // for this impl, since it is *not* proof that the trait goal holds. + if cx.impl_is_default(impl_def_id) { + return Ok(()); + } + match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + }) + .map_err_to_rerun()? + { + Ok(candidate) => candidates.push(candidate), + Err(NoSolution) => {} + } - Ok(()) - }, - ) + Ok(()) + }) } #[instrument(level = "trace", skip_all)] diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 5c002d09a75cc..09d96e3b3a143 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1246,13 +1246,9 @@ where let self_ty = goal.predicate.self_ty(); let check_impls = || { let mut disqualifying_impl = None; - self.cx().for_each_relevant_impl( - goal.predicate.def_id(), - goal.predicate.self_ty(), - |impl_def_id| { - disqualifying_impl = Some(impl_def_id); - }, - ); + self.cx().for_each_relevant_impl(goal.predicate.trait_ref, |impl_def_id| { + disqualifying_impl = Some(impl_def_id); + }); if let Some(def_id) = disqualifying_impl { trace!(?def_id, ?goal, "disqualified auto-trait implementation"); // No need to actually consider the candidate here, diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 6fd19f9362a9b..815890c989d93 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -16,7 +16,7 @@ use crate::solve::{ AccessedOpaques, CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect, }; use crate::visit::{Flags, TypeVisitable}; -use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph}; +use crate::{self as ty, CanonicalParamEnvCacheEntry, TraitRef, search_graph}; #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_interner")] pub trait Interner: @@ -398,8 +398,7 @@ pub trait Interner: fn for_each_relevant_impl( self, - trait_def_id: Self::TraitId, - self_ty: Self::Ty, + trait_ref: TraitRef, f: impl FnMut(Self::ImplId) -> R, ) -> R; fn for_each_blanket_impl( From f89e171060fe5ecfec88eb1d070c18132fc0404a Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 4 May 2026 08:13:09 +0000 Subject: [PATCH 273/278] Add library support for `aarch64-unknown-linux-pauthtest` --- .../std/src/sys/pal/unix/stack_overflow.rs | 20 +++++++++--- library/std/src/sys/personality/gcc.rs | 31 ++++++++++++++++++- library/std/tests/pipe_subprocess.rs | 5 +++ library/std/tests/process_spawning.rs | 12 +++++-- .../std_detect/src/detect/os/linux/auxvec.rs | 4 +-- library/unwind/src/lib.rs | 5 +++ 6 files changed, 68 insertions(+), 9 deletions(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 3b951899dfec9..0b67f4f8b5e37 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -409,9 +409,15 @@ mod imp { unsafe { // this way someone on any unix-y OS can check that all these compile - if cfg!(all(target_os = "linux", not(target_env = "musl"))) { + if cfg!(all( + target_os = "linux", + not(any(target_env = "musl", target_env = "pauthtest")) + )) { install_main_guard_linux(page_size) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { + } else if cfg!(all( + target_os = "linux", + any(target_env = "musl", target_env = "pauthtest") + )) { install_main_guard_linux_musl(page_size) } else if cfg!(target_os = "freebsd") { #[cfg(not(target_os = "freebsd"))] @@ -588,7 +594,10 @@ mod imp { let mut guardsize = 0; assert_eq!(libc::pthread_attr_getguardsize(attr.as_ptr(), &mut guardsize), 0); if guardsize == 0 { - if cfg!(all(target_os = "linux", target_env = "musl")) { + if cfg!(all( + target_os = "linux", + any(target_env = "musl", target_env = "pauthtest") + )) { // musl versions before 1.1.19 always reported guard // size obtained from pthread_attr_get_np as zero. // Use page size as a fallback. @@ -604,7 +613,10 @@ mod imp { let stackaddr = stackptr.addr(); ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { + } else if cfg!(all( + target_os = "linux", + any(target_env = "musl", target_env = "pauthtest") + )) { Some(stackaddr - guardsize..stackaddr) } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) { diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 019d5629d6d6e..ea1a6be30354d 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -89,6 +89,34 @@ const UNWIND_DATA_REG: (i32, i32) = (10, 11); // x10, x11 #[cfg(any(target_arch = "loongarch32", target_arch = "loongarch64"))] const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 +unsafe fn sign_lpad(context: *mut uw::_Unwind_Context, lpad: *const u8) -> *const u8 { + cfg_select! { + all(target_env = "pauthtest", target_arch = "aarch64") => { + // DWARF register number for SP on AArch64. + const SP_REG: i32 = 31; + + unsafe { + let sp = uw::_Unwind_GetGR(context, SP_REG) as u64; + let mut addr = lpad as usize; + + // `pacib` corresponds to `ptrauth_key_process_dependent_code` in . + core::arch::asm!( + "pacib {addr}, {sp}", + addr = inout(reg) addr, + sp = in(reg) sp, + options(nostack, preserves_flags) + ); + + lpad.with_addr(addr) + } + } + _ => { + let _ = context; + lpad + } + } +} + // The following code is based on GCC's C and C++ personality routines. For reference, see: // https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c @@ -239,7 +267,8 @@ cfg_select! { exception_object.cast(), ); uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); - uw::_Unwind_SetIP(context, lpad); + let maybe_signed_lpad = sign_lpad(context, lpad); + uw::_Unwind_SetIP(context, maybe_signed_lpad); uw::_URC_INSTALL_CONTEXT } EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index dad1ea6c57377..6cc9303f5eea1 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -13,10 +13,15 @@ fn main() { fn parent() { let me = env::current_exe().unwrap(); + // If `runner` is set up for current target, we'll be executing `./runner ./test`, not + // just `./test`. For such a case, use the same arguments for child to avoid executing + // `runner` without actual executable. + let args = env::args(); let (rx, tx) = pipe().unwrap(); assert!( process::Command::new(me) + .args(args) .env("I_AM_THE_CHILD", "1") .stdout(tx) .status() diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 93f73ccad3ea4..b1973b54d7670 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -26,8 +26,16 @@ fn issue_15149() { env::join_paths(paths).unwrap() }; - let child_output = - process::Command::new("mytest").env("PATH", &path).arg("child").output().unwrap(); + // If `runner` is set up for current target, we'll be executing `./runner ./test`, not + // just `./test`. For such a case, use the same arguments for child to avoid executing + // `runner` without actual executable. + let args = env::args(); + let child_output = process::Command::new("mytest") + .args(args) + .env("PATH", &path) + .arg("child") + .output() + .unwrap(); assert!( child_output.status.success(), diff --git a/library/std_detect/src/detect/os/linux/auxvec.rs b/library/std_detect/src/detect/os/linux/auxvec.rs index c0bbc7d4efa88..dec33ffeedd34 100644 --- a/library/std_detect/src/detect/os/linux/auxvec.rs +++ b/library/std_detect/src/detect/os/linux/auxvec.rs @@ -51,7 +51,7 @@ pub(crate) struct AuxVec { /// Note that run-time feature detection is not invoked for features that can /// be detected at compile-time. /// -/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos}*` and +/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos,pauthtest}*` and /// `*-android*` targets rather than `dlsym` it because we can safely assume /// `getauxval` is linked to the binary. /// - `*-linux-gnu*` targets ([since Rust 1.64](https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements.html)) @@ -125,7 +125,7 @@ fn getauxval(key: usize) -> Result { any( all( target_os = "linux", - any(target_env = "gnu", target_env = "musl", target_env = "ohos"), + any(target_env = "gnu", target_env = "musl", target_env = "ohos", target_env = "pauthtest"), ), target_os = "android", ) => { diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 4e380d8894781..11ea306c4923c 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -94,6 +94,11 @@ cfg_select! { } } +// For pauthtest the only supported unwinding mechanism is provided by libunwind. +#[cfg(target_env = "pauthtest")] +#[link(name = "unwind")] +unsafe extern "C" {} + // This is the same as musl except that we default to using the system libunwind // instead of libgcc. #[cfg(target_env = "ohos")] From 109801455cffcaad64e926d01a0757bcf4462743 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 11 May 2026 12:46:32 +0000 Subject: [PATCH 274/278] Use target_env = "musl" & target_abi = "pauthtest" instead of env --- .../std/src/sys/pal/unix/stack_overflow.rs | 20 ++++--------------- library/std/src/sys/personality/gcc.rs | 2 +- .../std_detect/src/detect/os/linux/auxvec.rs | 4 ++-- library/unwind/src/lib.rs | 12 +++++------ 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 0b67f4f8b5e37..3b951899dfec9 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -409,15 +409,9 @@ mod imp { unsafe { // this way someone on any unix-y OS can check that all these compile - if cfg!(all( - target_os = "linux", - not(any(target_env = "musl", target_env = "pauthtest")) - )) { + if cfg!(all(target_os = "linux", not(target_env = "musl"))) { install_main_guard_linux(page_size) - } else if cfg!(all( - target_os = "linux", - any(target_env = "musl", target_env = "pauthtest") - )) { + } else if cfg!(all(target_os = "linux", target_env = "musl")) { install_main_guard_linux_musl(page_size) } else if cfg!(target_os = "freebsd") { #[cfg(not(target_os = "freebsd"))] @@ -594,10 +588,7 @@ mod imp { let mut guardsize = 0; assert_eq!(libc::pthread_attr_getguardsize(attr.as_ptr(), &mut guardsize), 0); if guardsize == 0 { - if cfg!(all( - target_os = "linux", - any(target_env = "musl", target_env = "pauthtest") - )) { + if cfg!(all(target_os = "linux", target_env = "musl")) { // musl versions before 1.1.19 always reported guard // size obtained from pthread_attr_get_np as zero. // Use page size as a fallback. @@ -613,10 +604,7 @@ mod imp { let stackaddr = stackptr.addr(); ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all( - target_os = "linux", - any(target_env = "musl", target_env = "pauthtest") - )) { + } else if cfg!(all(target_os = "linux", target_env = "musl")) { Some(stackaddr - guardsize..stackaddr) } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) { diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index ea1a6be30354d..4a55613ca3f6b 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -91,7 +91,7 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 unsafe fn sign_lpad(context: *mut uw::_Unwind_Context, lpad: *const u8) -> *const u8 { cfg_select! { - all(target_env = "pauthtest", target_arch = "aarch64") => { + all(target_abi = "pauthtest", target_arch = "aarch64") => { // DWARF register number for SP on AArch64. const SP_REG: i32 = 31; diff --git a/library/std_detect/src/detect/os/linux/auxvec.rs b/library/std_detect/src/detect/os/linux/auxvec.rs index dec33ffeedd34..c0bbc7d4efa88 100644 --- a/library/std_detect/src/detect/os/linux/auxvec.rs +++ b/library/std_detect/src/detect/os/linux/auxvec.rs @@ -51,7 +51,7 @@ pub(crate) struct AuxVec { /// Note that run-time feature detection is not invoked for features that can /// be detected at compile-time. /// -/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos,pauthtest}*` and +/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos}*` and /// `*-android*` targets rather than `dlsym` it because we can safely assume /// `getauxval` is linked to the binary. /// - `*-linux-gnu*` targets ([since Rust 1.64](https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements.html)) @@ -125,7 +125,7 @@ fn getauxval(key: usize) -> Result { any( all( target_os = "linux", - any(target_env = "gnu", target_env = "musl", target_env = "ohos", target_env = "pauthtest"), + any(target_env = "gnu", target_env = "musl", target_env = "ohos"), ), target_os = "android", ) => { diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 11ea306c4923c..1bc8d1ab81fb5 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -56,7 +56,12 @@ cfg_select! { } } -#[cfg(target_env = "musl")] +// For pauthtest the only supported unwinding mechanism is provided by libunwind. +#[cfg(target_abi = "pauthtest")] +#[link(name = "unwind")] +unsafe extern "C" {} + +#[cfg(all(target_env = "musl", not(target_abi = "pauthtest")))] cfg_select! { all(feature = "llvm-libunwind", feature = "system-llvm-libunwind") => { compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); @@ -94,11 +99,6 @@ cfg_select! { } } -// For pauthtest the only supported unwinding mechanism is provided by libunwind. -#[cfg(target_env = "pauthtest")] -#[link(name = "unwind")] -unsafe extern "C" {} - // This is the same as musl except that we default to using the system libunwind // instead of libgcc. #[cfg(target_env = "ohos")] From f971addb61b9b0a1343e0d25e4de61d07c8d1008 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Wed, 3 Jun 2026 13:45:14 +0000 Subject: [PATCH 275/278] Update landing pad to use addr() --- library/std/src/sys/personality/gcc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 4a55613ca3f6b..549d3ec1313c6 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -96,8 +96,8 @@ unsafe fn sign_lpad(context: *mut uw::_Unwind_Context, lpad: *const u8) -> *cons const SP_REG: i32 = 31; unsafe { - let sp = uw::_Unwind_GetGR(context, SP_REG) as u64; - let mut addr = lpad as usize; + let sp = uw::_Unwind_GetGR(context, SP_REG).addr() as u64; + let mut addr = lpad.addr(); // `pacib` corresponds to `ptrauth_key_process_dependent_code` in . core::arch::asm!( From f16c778517acdb87945e21ee13456073e4a4011c Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Wed, 1 Jul 2026 09:15:42 +0000 Subject: [PATCH 276/278] PR feedback: Add library support --- library/std/tests/pipe_subprocess.rs | 7 ++++--- library/std/tests/process_spawning.rs | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index 6cc9303f5eea1..9643c3b7bdad8 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -13,9 +13,10 @@ fn main() { fn parent() { let me = env::current_exe().unwrap(); - // If `runner` is set up for current target, we'll be executing `./runner ./test`, not - // just `./test`. For such a case, use the same arguments for child to avoid executing - // `runner` without actual executable. + // If a custom `runner` is set up for the current target, we'll be + // executing `./runner ./test`, not just `./test`. For such a case, + // use the same arguments for child to avoid executing `runner` + // without an actual executable. let args = env::args(); let (rx, tx) = pipe().unwrap(); diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index b1973b54d7670..80e712a2388a1 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -26,9 +26,9 @@ fn issue_15149() { env::join_paths(paths).unwrap() }; - // If `runner` is set up for current target, we'll be executing `./runner ./test`, not - // just `./test`. For such a case, use the same arguments for child to avoid executing - // `runner` without actual executable. + // If a custom `runner` is set up for the current target, we'll be executing `./runner ./test`, + // not just `./test`. For such a case, use the same arguments for child to avoid executing + // `runner` without an actual executable. let args = env::args(); let child_output = process::Command::new("mytest") .args(args) From 0f439207e2bdb34750e2c224040e700b15db2792 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Wed, 1 Jul 2026 09:15:58 +0000 Subject: [PATCH 277/278] PR feedback: Use target_env --- library/unwind/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 1bc8d1ab81fb5..b19694f25c646 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -56,7 +56,9 @@ cfg_select! { } } -// For pauthtest the only supported unwinding mechanism is provided by libunwind. +// `target_abi = "pauthtest"` is currently only used by the Linux/musl +// `aarch64-unknown-linux-pauthtest` target. That target only supports unwinding +// via libunwind. #[cfg(target_abi = "pauthtest")] #[link(name = "unwind")] unsafe extern "C" {} From 4c59d2cf86b55b006e7c870a92d4f705500dcf82 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 3 Jul 2026 10:35:46 +0000 Subject: [PATCH 278/278] Use rust_force_inline on sign_lpad function --- library/std/src/sys/personality/gcc.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 549d3ec1313c6..379b0f6a1187c 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -89,6 +89,7 @@ const UNWIND_DATA_REG: (i32, i32) = (10, 11); // x10, x11 #[cfg(any(target_arch = "loongarch32", target_arch = "loongarch64"))] const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 +#[rustc_force_inline] unsafe fn sign_lpad(context: *mut uw::_Unwind_Context, lpad: *const u8) -> *const u8 { cfg_select! { all(target_abi = "pauthtest", target_arch = "aarch64") => {