From 701b1948abd46425ab588108bb406b7519ec16fa Mon Sep 17 00:00:00 2001 From: tiif Date: Tue, 21 Jan 2025 08:08:58 +0000 Subject: [PATCH 001/245] impl Display for Conv --- .../rustc_const_eval/src/interpret/call.rs | 4 +-- compiler/rustc_target/src/callconv/mod.rs | 32 +++++++++++++++++++ src/tools/miri/src/helpers.rs | 3 +- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 29f819cca1fb..35e251877017 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -353,8 +353,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if caller_fn_abi.conv != callee_fn_abi.conv { throw_ub_custom!( fluent::const_eval_incompatible_calling_conventions, - callee_conv = format!("{:?}", callee_fn_abi.conv), - caller_conv = format!("{:?}", caller_fn_abi.conv), + callee_conv = format!("{}", callee_fn_abi.conv), + caller_conv = format!("{}", caller_fn_abi.conv), ) } diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 6d0ee3c7ee58..0ea0d2d0173a 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::str::FromStr; use std::{fmt, iter}; @@ -897,6 +898,37 @@ impl FromStr for Conv { } } +impl Display for Conv { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Conv::C => "C", + Conv::Rust => "Rust", + Conv::Cold => "Cold", + Conv::PreserveMost => "PreserveMost", + Conv::PreserveAll => "PreserveAll", + Conv::ArmAapcs => "ArmAapcs", + Conv::CCmseNonSecureCall => "CCmseNonSecureCall", + Conv::CCmseNonSecureEntry => "CCmseNonSecureEntry", + Conv::Msp430Intr => "Msp430Intr", + Conv::PtxKernel => "PtxKernel", + Conv::GpuKernel => "GpuKernel", + Conv::X86Fastcall => "X86Fastcall", + Conv::X86Intr => "X86Intr", + Conv::X86Stdcall => "X86Stdcall", + Conv::X86ThisCall => "X86ThisCall", + Conv::X86VectorCall => "X86VectorCall", + Conv::X86_64SysV => "X86_64SysV", + Conv::X86_64Win64 => "X86_64Win64", + Conv::AvrInterrupt => "AvrInterrupt", + Conv::AvrNonBlockingInterrupt => "AvrNonBlockingInterrupt", + Conv::RiscvInterrupt { kind: RiscvInterruptKind::Machine } => "RiscvInterrupt(machine)", + Conv::RiscvInterrupt { kind: RiscvInterruptKind::Supervisor } => { + "RiscvInterrupt(supervisor)" + } + }) + } +} + // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 12e7d0f1a62c..bf08f9639c0c 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -932,8 +932,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn check_abi<'a>(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, exp_abi: Conv) -> InterpResult<'a, ()> { if fn_abi.conv != exp_abi { throw_ub_format!( - "calling a function with ABI {:?} using caller ABI {:?}", - exp_abi, + "calling a function with ABI {exp_abi} using caller ABI {}", fn_abi.conv ); } From 17c6eae2910ffcb0e2a285625984f2eded45a285 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 3 Feb 2025 13:50:39 +0000 Subject: [PATCH 002/245] Add test and change ub message wording --- src/tools/miri/src/helpers.rs | 8 ++++---- .../miri/tests/fail/shims/callconv_mismatch.rs | 11 +++++++++++ .../tests/fail/shims/callconv_mismatch.stderr | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/tools/miri/tests/fail/shims/callconv_mismatch.rs create mode 100644 src/tools/miri/tests/fail/shims/callconv_mismatch.stderr diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index bf08f9639c0c..04829a9424c1 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -928,11 +928,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi) } - /// Check that the ABI is what we expect. - fn check_abi<'a>(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, exp_abi: Conv) -> InterpResult<'a, ()> { + /// Check that the calling convention is what we expect. + fn check_callconv<'a>(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, exp_abi: Conv) -> InterpResult<'a, ()> { if fn_abi.conv != exp_abi { throw_ub_format!( - "calling a function with ABI {exp_abi} using caller ABI {}", + "calling a function with calling convention {exp_abi} using caller calling convention {}", fn_abi.conv ); } @@ -968,7 +968,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { exp_abi: Conv, link_name: Symbol, ) -> InterpResult<'tcx, ()> { - self.check_abi(abi, exp_abi)?; + self.check_callconv(abi, exp_abi)?; if let Some((body, instance)) = self.eval_context_mut().lookup_exported_symbol(link_name)? { // If compiler-builtins is providing the symbol, then don't treat it as a clash. // We'll use our built-in implementation in `emulate_foreign_item_inner` for increased diff --git a/src/tools/miri/tests/fail/shims/callconv_mismatch.rs b/src/tools/miri/tests/fail/shims/callconv_mismatch.rs new file mode 100644 index 000000000000..2f9b89ae08d2 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/callconv_mismatch.rs @@ -0,0 +1,11 @@ +extern "Rust" { + fn pipe(fds: *mut std::ffi::c_int) -> std::ffi::c_int; +} + +// Test the error for calling convention mismatch. +fn main() { + let mut fds = [-1, -1]; + let res = unsafe { pipe(fds.as_mut_ptr()) }; + //~^ ERROR: calling a function with calling convention C using caller calling convention Rust + assert_eq!(res, 0); +} \ No newline at end of file diff --git a/src/tools/miri/tests/fail/shims/callconv_mismatch.stderr b/src/tools/miri/tests/fail/shims/callconv_mismatch.stderr new file mode 100644 index 000000000000..11b57de19539 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/callconv_mismatch.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: calling a function with calling convention C using caller calling convention Rust + --> tests/fail/shims/callconv_mismatch.rs:LL:CC + | +LL | let res = unsafe { pipe(fds.as_mut_ptr()) }; + | ^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention C using caller calling convention Rust + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/shims/callconv_mismatch.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 43c51e9ff33e0014c0e17712d01ae8dc44a57096 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 3 Feb 2025 13:51:44 +0000 Subject: [PATCH 003/245] Remove unnecessary assert --- src/tools/miri/tests/fail/shims/callconv_mismatch.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/miri/tests/fail/shims/callconv_mismatch.rs b/src/tools/miri/tests/fail/shims/callconv_mismatch.rs index 2f9b89ae08d2..adde34dc1fae 100644 --- a/src/tools/miri/tests/fail/shims/callconv_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/callconv_mismatch.rs @@ -7,5 +7,4 @@ fn main() { let mut fds = [-1, -1]; let res = unsafe { pipe(fds.as_mut_ptr()) }; //~^ ERROR: calling a function with calling convention C using caller calling convention Rust - assert_eq!(res, 0); } \ No newline at end of file From d463c5d07c4715f4ebd86b2093139784fa69bd2b Mon Sep 17 00:00:00 2001 From: tiif Date: Tue, 11 Mar 2025 16:46:26 +0000 Subject: [PATCH 004/245] Implement Display by mapping Conv to ExternAbi --- compiler/rustc_target/src/callconv/mod.rs | 53 ++++++++++--------- .../fail/function_calls/check_arg_abi.rs | 2 +- .../fail/function_calls/check_arg_abi.stderr | 4 +- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 0ea0d2d0173a..8d8270cd8cd9 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -898,34 +898,35 @@ impl FromStr for Conv { } } +fn conv_to_externabi(conv: &Conv) -> ExternAbi { + match conv { + Conv::Rust => ExternAbi::Rust, + Conv::PreserveMost => ExternAbi::RustCold, + Conv::X86Stdcall => ExternAbi::Stdcall{unwind: false}, + Conv::X86Fastcall => ExternAbi::Fastcall{unwind: false}, + Conv::X86VectorCall => ExternAbi::Vectorcall{unwind: false}, + Conv::X86ThisCall => ExternAbi::Thiscall{unwind: false}, + Conv::C => ExternAbi::C{unwind: false}, + Conv::X86_64Win64 => ExternAbi::Win64{unwind: false}, + Conv::X86_64SysV => ExternAbi::SysV64{unwind: false}, + Conv::ArmAapcs => ExternAbi::Aapcs{unwind: false}, + Conv::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall, + Conv::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry, + Conv::PtxKernel => ExternAbi::PtxKernel, + Conv::Msp430Intr => ExternAbi::Msp430Interrupt, + Conv::X86Intr => ExternAbi::X86Interrupt, + Conv::GpuKernel => ExternAbi::GpuKernel, + Conv::AvrInterrupt => ExternAbi::AvrInterrupt, + Conv::AvrNonBlockingInterrupt => ExternAbi::AvrNonBlockingInterrupt, + Conv::RiscvInterrupt { kind: RiscvInterruptKind::Machine } => ExternAbi::RiscvInterruptM, + Conv::RiscvInterrupt { kind: RiscvInterruptKind::Supervisor } => ExternAbi::RiscvInterruptS, + Conv::Cold | Conv::PreserveAll => panic!("This is deadcode"), + } +} + impl Display for Conv { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - Conv::C => "C", - Conv::Rust => "Rust", - Conv::Cold => "Cold", - Conv::PreserveMost => "PreserveMost", - Conv::PreserveAll => "PreserveAll", - Conv::ArmAapcs => "ArmAapcs", - Conv::CCmseNonSecureCall => "CCmseNonSecureCall", - Conv::CCmseNonSecureEntry => "CCmseNonSecureEntry", - Conv::Msp430Intr => "Msp430Intr", - Conv::PtxKernel => "PtxKernel", - Conv::GpuKernel => "GpuKernel", - Conv::X86Fastcall => "X86Fastcall", - Conv::X86Intr => "X86Intr", - Conv::X86Stdcall => "X86Stdcall", - Conv::X86ThisCall => "X86ThisCall", - Conv::X86VectorCall => "X86VectorCall", - Conv::X86_64SysV => "X86_64SysV", - Conv::X86_64Win64 => "X86_64Win64", - Conv::AvrInterrupt => "AvrInterrupt", - Conv::AvrNonBlockingInterrupt => "AvrNonBlockingInterrupt", - Conv::RiscvInterrupt { kind: RiscvInterruptKind::Machine } => "RiscvInterrupt(machine)", - Conv::RiscvInterrupt { kind: RiscvInterruptKind::Supervisor } => { - "RiscvInterrupt(supervisor)" - } - }) + write!(f, "{}", conv_to_externabi(self)) } } diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs b/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs index ffa0443ce507..0e9163641690 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs @@ -4,6 +4,6 @@ fn main() { } unsafe { - let _ = malloc(0); //~ ERROR: calling a function with ABI C using caller ABI Rust + let _ = malloc(0); //~ ERROR: calling a function with calling convention "C" using caller calling convention "Rust" }; } diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr b/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr index bf1fbb7721f1..787301829231 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_arg_abi.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function with ABI C using caller ABI Rust +error: Undefined Behavior: calling a function with calling convention "C" using caller calling convention "Rust" --> tests/fail/function_calls/check_arg_abi.rs:LL:CC | LL | let _ = malloc(0); - | ^^^^^^^^^ calling a function with ABI C using caller ABI Rust + | ^^^^^^^^^ calling a function with calling convention "C" using caller calling convention "Rust" | = 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 From 3079213020e518c29046b01b483ea37e949eb27d Mon Sep 17 00:00:00 2001 From: tiif Date: Tue, 11 Mar 2025 16:47:40 +0000 Subject: [PATCH 005/245] Remove test --- .../miri/tests/fail/shims/callconv_mismatch.rs | 10 ---------- .../tests/fail/shims/callconv_mismatch.stderr | 15 --------------- 2 files changed, 25 deletions(-) delete mode 100644 src/tools/miri/tests/fail/shims/callconv_mismatch.rs delete mode 100644 src/tools/miri/tests/fail/shims/callconv_mismatch.stderr diff --git a/src/tools/miri/tests/fail/shims/callconv_mismatch.rs b/src/tools/miri/tests/fail/shims/callconv_mismatch.rs deleted file mode 100644 index adde34dc1fae..000000000000 --- a/src/tools/miri/tests/fail/shims/callconv_mismatch.rs +++ /dev/null @@ -1,10 +0,0 @@ -extern "Rust" { - fn pipe(fds: *mut std::ffi::c_int) -> std::ffi::c_int; -} - -// Test the error for calling convention mismatch. -fn main() { - let mut fds = [-1, -1]; - let res = unsafe { pipe(fds.as_mut_ptr()) }; - //~^ ERROR: calling a function with calling convention C using caller calling convention Rust -} \ No newline at end of file diff --git a/src/tools/miri/tests/fail/shims/callconv_mismatch.stderr b/src/tools/miri/tests/fail/shims/callconv_mismatch.stderr deleted file mode 100644 index 11b57de19539..000000000000 --- a/src/tools/miri/tests/fail/shims/callconv_mismatch.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: calling a function with calling convention C using caller calling convention Rust - --> tests/fail/shims/callconv_mismatch.rs:LL:CC - | -LL | let res = unsafe { pipe(fds.as_mut_ptr()) }; - | ^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention C using caller calling convention Rust - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at tests/fail/shims/callconv_mismatch.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 c8197b77c9dcda42df92944d10e45382b490e504 Mon Sep 17 00:00:00 2001 From: tiif Date: Tue, 11 Mar 2025 16:48:20 +0000 Subject: [PATCH 006/245] fmt --- compiler/rustc_target/src/callconv/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 8d8270cd8cd9..e019dc55950e 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -902,14 +902,14 @@ fn conv_to_externabi(conv: &Conv) -> ExternAbi { match conv { Conv::Rust => ExternAbi::Rust, Conv::PreserveMost => ExternAbi::RustCold, - Conv::X86Stdcall => ExternAbi::Stdcall{unwind: false}, - Conv::X86Fastcall => ExternAbi::Fastcall{unwind: false}, - Conv::X86VectorCall => ExternAbi::Vectorcall{unwind: false}, - Conv::X86ThisCall => ExternAbi::Thiscall{unwind: false}, - Conv::C => ExternAbi::C{unwind: false}, - Conv::X86_64Win64 => ExternAbi::Win64{unwind: false}, - Conv::X86_64SysV => ExternAbi::SysV64{unwind: false}, - Conv::ArmAapcs => ExternAbi::Aapcs{unwind: false}, + Conv::X86Stdcall => ExternAbi::Stdcall { unwind: false }, + Conv::X86Fastcall => ExternAbi::Fastcall { unwind: false }, + Conv::X86VectorCall => ExternAbi::Vectorcall { unwind: false }, + Conv::X86ThisCall => ExternAbi::Thiscall { unwind: false }, + Conv::C => ExternAbi::C { unwind: false }, + Conv::X86_64Win64 => ExternAbi::Win64 { unwind: false }, + Conv::X86_64SysV => ExternAbi::SysV64 { unwind: false }, + Conv::ArmAapcs => ExternAbi::Aapcs { unwind: false }, Conv::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall, Conv::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry, Conv::PtxKernel => ExternAbi::PtxKernel, From 71c4b2492de8a34a81f362ef9769b1608b8310e4 Mon Sep 17 00:00:00 2001 From: tiif Date: Tue, 11 Mar 2025 17:23:12 +0000 Subject: [PATCH 007/245] Remove invalid Conv --- compiler/rustc_target/src/callconv/mod.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index e019dc55950e..6aae8bc75560 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -900,22 +900,21 @@ impl FromStr for Conv { fn conv_to_externabi(conv: &Conv) -> ExternAbi { match conv { + Conv::C => ExternAbi::C { unwind: false }, Conv::Rust => ExternAbi::Rust, Conv::PreserveMost => ExternAbi::RustCold, - Conv::X86Stdcall => ExternAbi::Stdcall { unwind: false }, - Conv::X86Fastcall => ExternAbi::Fastcall { unwind: false }, - Conv::X86VectorCall => ExternAbi::Vectorcall { unwind: false }, - Conv::X86ThisCall => ExternAbi::Thiscall { unwind: false }, - Conv::C => ExternAbi::C { unwind: false }, - Conv::X86_64Win64 => ExternAbi::Win64 { unwind: false }, - Conv::X86_64SysV => ExternAbi::SysV64 { unwind: false }, Conv::ArmAapcs => ExternAbi::Aapcs { unwind: false }, Conv::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall, Conv::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry, - Conv::PtxKernel => ExternAbi::PtxKernel, Conv::Msp430Intr => ExternAbi::Msp430Interrupt, - Conv::X86Intr => ExternAbi::X86Interrupt, Conv::GpuKernel => ExternAbi::GpuKernel, + Conv::X86Fastcall => ExternAbi::Fastcall { unwind: false }, + Conv::X86Intr => ExternAbi::X86Interrupt, + Conv::X86Stdcall => ExternAbi::Stdcall { unwind: false }, + Conv::X86ThisCall => ExternAbi::Thiscall { unwind: false }, + Conv::X86VectorCall => ExternAbi::Vectorcall { unwind: false }, + Conv::X86_64SysV => ExternAbi::SysV64 { unwind: false }, + Conv::X86_64Win64 => ExternAbi::Win64 { unwind: false }, Conv::AvrInterrupt => ExternAbi::AvrInterrupt, Conv::AvrNonBlockingInterrupt => ExternAbi::AvrNonBlockingInterrupt, Conv::RiscvInterrupt { kind: RiscvInterruptKind::Machine } => ExternAbi::RiscvInterruptM, From 650c8a2fb592a7519c37549734b664301b4b13be Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 12 Mar 2025 16:59:25 +0000 Subject: [PATCH 008/245] bless test --- .../miri/tests/fail/function_calls/check_callback_abi.rs | 2 +- .../tests/fail/function_calls/check_callback_abi.stderr | 4 ++-- .../exported_symbol_abi_mismatch.cache.stderr | 4 ++-- .../exported_symbol_abi_mismatch.fn_ptr.stderr | 4 ++-- .../exported_symbol_abi_mismatch.no_cache.stderr | 4 ++-- .../fail/function_calls/exported_symbol_abi_mismatch.rs | 6 +++--- src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs | 2 +- src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr | 4 ++-- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs b/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs index 6a7a26710d16..177e38105e6f 100644 --- a/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs +++ b/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs @@ -9,7 +9,7 @@ fn main() { // Make sure we check the ABI when Miri itself invokes a function // as part of a shim implementation. std::intrinsics::catch_unwind( - //~^ ERROR: calling a function with calling convention C using calling convention Rust + //~^ ERROR: calling a function with calling convention "C" using calling convention "Rust" std::mem::transmute::(try_fn), std::ptr::null_mut(), |_, _| unreachable!(), diff --git a/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr b/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr index 6b0692e1c6e8..20182ac92367 100644 --- a/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with calling convention C using calling convention Rust +error: Undefined Behavior: calling a function with calling convention "C" using calling convention "Rust" --> tests/fail/function_calls/check_callback_abi.rs:LL:CC | LL | / std::intrinsics::catch_unwind( @@ -7,7 +7,7 @@ LL | | std::mem::transmute::(try_fn), LL | | std::ptr::null_mut(), LL | | |_, _| unreachable!(), LL | | ); - | |_________^ calling a function with calling convention C using calling convention Rust + | |_________^ calling a function with calling convention "C" using calling convention "Rust" | = 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 diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.cache.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.cache.stderr index e4302ad1d3a5..46a32d1487ea 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.cache.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.cache.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function with calling convention Rust using calling convention C +error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C" --> tests/fail/function_calls/exported_symbol_abi_mismatch.rs:LL:CC | LL | foo(); - | ^^^^^ calling a function with calling convention Rust using calling convention C + | ^^^^^ calling a function with calling convention "Rust" using calling convention "C" | = 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 diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.fn_ptr.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.fn_ptr.stderr index 9f40c48b338e..387252899190 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.fn_ptr.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.fn_ptr.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function with calling convention Rust using calling convention C +error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C" --> tests/fail/function_calls/exported_symbol_abi_mismatch.rs:LL:CC | LL | std::mem::transmute::(foo)(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention Rust using calling convention C + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention "Rust" using calling convention "C" | = 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 diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.no_cache.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.no_cache.stderr index e4302ad1d3a5..46a32d1487ea 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.no_cache.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.no_cache.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function with calling convention Rust using calling convention C +error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C" --> tests/fail/function_calls/exported_symbol_abi_mismatch.rs:LL:CC | LL | foo(); - | ^^^^^ calling a function with calling convention Rust using calling convention C + | ^^^^^ calling a function with calling convention "Rust" using calling convention "C" | = 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 diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.rs index 50a0e8e6edef..1950e162c070 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.rs +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_abi_mismatch.rs @@ -12,7 +12,7 @@ fn main() { #[cfg(fn_ptr)] unsafe { std::mem::transmute::(foo)(); - //~[fn_ptr]^ ERROR: calling a function with calling convention Rust using calling convention C + //~[fn_ptr]^ ERROR: calling a function with calling convention "Rust" using calling convention "C" } // `Instance` caching should not suppress ABI check. @@ -28,8 +28,8 @@ fn main() { } unsafe { foo(); - //~[no_cache]^ ERROR: calling a function with calling convention Rust using calling convention C - //~[cache]| ERROR: calling a function with calling convention Rust using calling convention C + //~[no_cache]^ ERROR: calling a function with calling convention "Rust" using calling convention "C" + //~[cache]| ERROR: calling a function with calling convention "Rust" using calling convention "C" } } } diff --git a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs index 5f00dbf25736..952f9697fc79 100644 --- a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs +++ b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs @@ -1,4 +1,4 @@ -//@error-in-other-file: Undefined Behavior: calling a function with calling convention C using calling convention Rust +//@error-in-other-file: Undefined Behavior: calling a function with calling convention "C" using calling convention "Rust" #![feature(explicit_tail_calls)] #![allow(incomplete_features)] diff --git a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr index 5061c9e8dc3f..61ddea644720 100644 --- a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr +++ b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function with calling convention C using calling convention Rust +error: Undefined Behavior: calling a function with calling convention "C" using calling convention "Rust" --> RUSTLIB/core/src/ops/function.rs:LL:CC | LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention C using calling convention Rust + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention "C" using calling convention "Rust" | = 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 From e74fe7a6ad02391ff873916a4e0243144e5e9355 Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 12 Mar 2025 17:13:47 +0000 Subject: [PATCH 009/245] Bless more test --- tests/ui/consts/miri_unleashed/abi-mismatch.rs | 2 +- tests/ui/consts/miri_unleashed/abi-mismatch.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/consts/miri_unleashed/abi-mismatch.rs b/tests/ui/consts/miri_unleashed/abi-mismatch.rs index ea640ae78d55..727cfecfa416 100644 --- a/tests/ui/consts/miri_unleashed/abi-mismatch.rs +++ b/tests/ui/consts/miri_unleashed/abi-mismatch.rs @@ -10,6 +10,6 @@ const fn call_rust_fn(my_fn: extern "Rust" fn()) { static VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); //~^ ERROR could not evaluate static initializer -//~| NOTE calling a function with calling convention C using calling convention Rust +//~| NOTE calling a function with calling convention "C" using calling convention "Rust" fn main() {} diff --git a/tests/ui/consts/miri_unleashed/abi-mismatch.stderr b/tests/ui/consts/miri_unleashed/abi-mismatch.stderr index 88623b134b0b..7d1fdcce5261 100644 --- a/tests/ui/consts/miri_unleashed/abi-mismatch.stderr +++ b/tests/ui/consts/miri_unleashed/abi-mismatch.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/abi-mismatch.rs:11:18 | LL | static VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention C using calling convention Rust + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention "C" using calling convention "Rust" | note: inside `call_rust_fn` --> $DIR/abi-mismatch.rs:7:5 From 25f17dd8254588300f7bc431d586f3b7c4ee2df1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 29 Jan 2025 22:22:56 +0100 Subject: [PATCH 010/245] Optimize `ToString` implementation for integers --- library/alloc/src/string.rs | 48 +++++++++++++++++++++++++++++++++++++ library/core/src/fmt/num.rs | 28 +++++++++++++++------- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 9a161d057db0..1a0b3b43eb69 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2824,7 +2824,54 @@ impl SpecToString for bool { } } +macro_rules! impl_to_string { + ($($signed:ident, $unsigned:ident,)*) => { + $( + #[cfg(not(no_global_oom_handling))] + #[cfg(not(feature = "optimize_for_size"))] + impl SpecToString for $signed { + #[inline] + fn spec_to_string(&self) -> String { + const SIZE: usize = $signed::MAX.ilog(10) as usize + 1; + let mut buf = [core::mem::MaybeUninit::::uninit(); SIZE]; + // Only difference between signed and unsigned are these 8 lines. + let mut out; + if *self < 0 { + out = String::with_capacity(SIZE + 1); + out.push('-'); + } else { + out = String::with_capacity(SIZE); + } + + out.push_str(self.unsigned_abs()._fmt(&mut buf)); + out + } + } + #[cfg(not(no_global_oom_handling))] + #[cfg(not(feature = "optimize_for_size"))] + impl SpecToString for $unsigned { + #[inline] + fn spec_to_string(&self) -> String { + const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1; + let mut buf = [core::mem::MaybeUninit::::uninit(); SIZE]; + + self._fmt(&mut buf).to_string() + } + } + )* + } +} + +impl_to_string! { + i8, u8, + i16, u16, + i32, u32, + i64, u64, + isize, usize, +} + #[cfg(not(no_global_oom_handling))] +#[cfg(feature = "optimize_for_size")] impl SpecToString for u8 { #[inline] fn spec_to_string(&self) -> String { @@ -2844,6 +2891,7 @@ impl SpecToString for u8 { } #[cfg(not(no_global_oom_handling))] +#[cfg(feature = "optimize_for_size")] impl SpecToString for i8 { #[inline] fn spec_to_string(&self) -> String { diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 4467b37bd451..ba30518d70bc 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -208,7 +208,11 @@ macro_rules! impl_Display { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "optimize_for_size"))] { - self._fmt(true, f) + const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; + // Buffer decimals for $unsigned with right alignment. + let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + + f.pad_integral(true, "", self._fmt(&mut buf)) } #[cfg(feature = "optimize_for_size")] { @@ -222,7 +226,11 @@ macro_rules! impl_Display { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "optimize_for_size"))] { - return self.unsigned_abs()._fmt(*self >= 0, f); + const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; + // Buffer decimals for $unsigned with right alignment. + let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + + f.pad_integral(*self >= 0, "", self.unsigned_abs()._fmt(&mut buf)) } #[cfg(feature = "optimize_for_size")] { @@ -233,10 +241,13 @@ macro_rules! impl_Display { #[cfg(not(feature = "optimize_for_size"))] impl $unsigned { - fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; - // Buffer decimals for $unsigned with right alignment. - let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + #[doc(hidden)] + #[unstable( + feature = "fmt_internals", + reason = "specialized method meant to only be used by `SpecToString` implementation", + issue = "none" + )] + pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit::]) -> &'a str { // Count the number of bytes in buf that are not initialized. let mut offset = buf.len(); // Consume the least-significant decimals from a working copy. @@ -301,13 +312,12 @@ macro_rules! impl_Display { // SAFETY: All buf content since offset is set. let written = unsafe { buf.get_unchecked(offset..) }; // SAFETY: Writes use ASCII from the lookup table exclusively. - let as_str = unsafe { + unsafe { str::from_utf8_unchecked(slice::from_raw_parts( MaybeUninit::slice_as_ptr(written), written.len(), )) - }; - f.pad_integral(is_nonnegative, "", as_str) + } } })* From 1ef7585c9ef976211b897e22fad6ae9aa2c2f415 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 30 Jan 2025 11:05:34 +0100 Subject: [PATCH 011/245] Update weirdly failing ui tests --- tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs | 2 +- tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs | 2 +- .../equal-pointers-unequal/exposed-provenance/inline2.rs | 2 +- .../codegen/equal-pointers-unequal/exposed-provenance/zero.rs | 2 +- .../codegen/equal-pointers-unequal/strict-provenance/inline2.rs | 2 +- .../ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs b/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs index 9a1ace86e4db..5ec3c7cbdf55 100644 --- a/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs +++ b/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs @@ -23,7 +23,7 @@ fn main() { let v = 0; &v as *const _ as usize }; - assert_eq!(a.to_string(), b.to_string()); + assert_eq!(format!("{a}"), format!("{b}")); assert_eq!(format!("{}", a == b), "true"); assert_eq!(format!("{}", cmp_in(a, b)), "true"); assert_eq!(format!("{}", cmp(a, b)), "true"); diff --git a/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs b/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs index d1aa95a9a569..731c5b67882b 100644 --- a/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs +++ b/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs @@ -21,7 +21,7 @@ fn main() { // It's not zero, which means `a` and `b` are not equal. assert_ne!(i, 0); // But it looks like zero... - assert_eq!(i.to_string(), "0"); + assert_eq!(format!("{i}"), "0"); // ...and now it *is* zero? assert_eq!(i, 0); // So `a` and `b` are equal after all? diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs index f128e1bb0841..94739708ab8b 100644 --- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs +++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs @@ -25,7 +25,7 @@ fn main() { let v = 0; ptr::from_ref(&v).expose_provenance() }; - assert_eq!(a.to_string(), b.to_string()); + assert_eq!(format!("{a}"), format!("{b}")); assert_eq!(format!("{}", a == b), "true"); assert_eq!(format!("{}", cmp_in(a, b)), "true"); assert_eq!(format!("{}", cmp(a, b)), "true"); diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs index 7ccff8d0848e..b7824f53d77f 100644 --- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs +++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs @@ -23,7 +23,7 @@ fn main() { // It's not zero, which means `a` and `b` are not equal. assert_ne!(i, 0); // But it looks like zero... - assert_eq!(i.to_string(), "0"); + assert_eq!(format!("{i}"), "0"); // ...and now it *is* zero? assert_eq!(i, 0); // So `a` and `b` are equal after all? diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs index 0414879804a5..0f838af1fb19 100644 --- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs +++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs @@ -25,7 +25,7 @@ fn main() { let v = 0; ptr::from_ref(&v).addr() }; - assert_eq!(a.to_string(), b.to_string()); + assert_eq!(format!("{a}"), format!("{b}")); assert_eq!(format!("{}", a == b), "true"); assert_eq!(format!("{}", cmp_in(a, b)), "true"); assert_eq!(format!("{}", cmp(a, b)), "true"); diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs index d963e45e4cdf..20ed991ed3d6 100644 --- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs +++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs @@ -23,7 +23,7 @@ fn main() { // It's not zero, which means `a` and `b` are not equal. assert_ne!(i, 0); // But it looks like zero... - assert_eq!(i.to_string(), "0"); + assert_eq!(format!("{i}"), "0"); // ...and now it *is* zero? assert_eq!(i, 0); // So `a` and `b` are equal after all? From 14662fabeb69fe5ab6c6e68051bf9f80d4aaaa35 Mon Sep 17 00:00:00 2001 From: Ruan Comelli Date: Sun, 13 Apr 2025 09:15:16 -0300 Subject: [PATCH 012/245] docs(library/core/src/pin): fix typo "necessarily" -> "necessary" --- library/core/src/pin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 2ef1bbfd1fa7..504801f3e4e5 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -12,7 +12,7 @@ //! "pinned," in that it has been permanently (until the end of its lifespan) attached to its //! location in memory, as though pinned to a pinboard. Pinning a value is an incredibly useful //! building block for [`unsafe`] code to be able to reason about whether a raw pointer to the -//! pinned value is still valid. [As we'll see later][drop-guarantee], this is necessarily from the +//! pinned value is still valid. [As we'll see later][drop-guarantee], this is necessary from the //! time the value is first pinned until the end of its lifespan. This concept of "pinning" is //! necessary to implement safe interfaces on top of things like self-referential types and //! intrusive data structures which cannot currently be modeled in fully safe Rust using only From 6f23f167af57af26543fa93f87a12f637e66c5e8 Mon Sep 17 00:00:00 2001 From: Ruan Comelli Date: Sun, 13 Apr 2025 16:11:55 -0300 Subject: [PATCH 013/245] docs(library/core/src/pin): rewrite for clarity --- library/core/src/pin.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 504801f3e4e5..9ee5369139ff 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -12,11 +12,11 @@ //! "pinned," in that it has been permanently (until the end of its lifespan) attached to its //! location in memory, as though pinned to a pinboard. Pinning a value is an incredibly useful //! building block for [`unsafe`] code to be able to reason about whether a raw pointer to the -//! pinned value is still valid. [As we'll see later][drop-guarantee], this is necessary from the -//! time the value is first pinned until the end of its lifespan. This concept of "pinning" is -//! necessary to implement safe interfaces on top of things like self-referential types and -//! intrusive data structures which cannot currently be modeled in fully safe Rust using only -//! borrow-checked [references][reference]. +//! pinned value is still valid. [As we'll see later][drop-guarantee], once a value is pinned, +//! it is necessarily valid at its memory location until the end of its lifespan. This concept +//! of "pinning" is necessary to implement safe interfaces on top of things like self-referential +//! types and intrusive data structures which cannot currently be modeled in fully safe Rust using +//! only borrow-checked [references][reference]. //! //! "Pinning" allows us to put a *value* which exists at some location in memory into a state where //! safe code cannot *move* that value to a different location in memory or otherwise invalidate it From 8582bc4e37a60957f78f39a64b6e0eeda8007dae Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 12 Dec 2024 19:52:38 +0000 Subject: [PATCH 014/245] Pass MonoItemData to MonoItem::define --- src/base.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base.rs b/src/base.rs index 9b495174a3fa..292f0514070b 100644 --- a/src/base.rs +++ b/src/base.rs @@ -223,8 +223,8 @@ pub fn compile_codegen_unit( } // ... and now that we have everything pre-defined, fill out those definitions. - for &(mono_item, _) in &mono_items { - mono_item.define::>(&cx); + for &(mono_item, item_data) in &mono_items { + mono_item.define::>(&cx, item_data); } // If this codegen unit contains the main function, also create the From 25df5d021de5f109a71ad6a3a41f24aa9303d98d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:16:58 +0000 Subject: [PATCH 015/245] Pass &mut self to codegen_global_asm --- src/asm.rs | 2 +- src/base.rs | 4 ++-- src/builder.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/asm.rs b/src/asm.rs index 415f8affab90..235814c948f7 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -794,7 +794,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn codegen_global_asm( - &self, + &mut self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, diff --git a/src/base.rs b/src/base.rs index 292f0514070b..a9d7808c833b 100644 --- a/src/base.rs +++ b/src/base.rs @@ -206,7 +206,7 @@ pub fn compile_codegen_unit( let f128_type_supported = target_info.supports_target_dependent_type(CType::Float128); let u128_type_supported = target_info.supports_target_dependent_type(CType::UInt128t); // TODO: improve this to avoid passing that many arguments. - let cx = CodegenCx::new( + let mut cx = CodegenCx::new( &context, cgu, tcx, @@ -224,7 +224,7 @@ pub fn compile_codegen_unit( // ... and now that we have everything pre-defined, fill out those definitions. for &(mono_item, item_data) in &mono_items { - mono_item.define::>(&cx, item_data); + mono_item.define::>(&mut cx, item_data); } // If this codegen unit contains the main function, also create the diff --git a/src/builder.rs b/src/builder.rs index 6573b5b165e6..ecb4620e4261 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -45,7 +45,7 @@ enum ExtremumOperation { Min, } -pub struct Builder<'a: 'gcc, 'gcc, 'tcx> { +pub struct Builder<'a, 'gcc, 'tcx> { pub cx: &'a CodegenCx<'gcc, 'tcx>, pub block: Block<'gcc>, pub location: Option>, From 7bfb15cd11d21c0edf0d60fb8419f0736e10937f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 17 Apr 2025 16:31:48 +0200 Subject: [PATCH 016/245] Fix `new_lint` clippy command --- clippy_dev/src/new_lint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 96e12706c9e2..771f48b3f8b1 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -448,7 +448,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> let mut file_contents = fs::read_to_string(path)?; assert!( - !file_contents.contains(&lint_name_upper), + !file_contents.contains(&format!("pub {lint_name_upper},")), "Lint `{}` already defined in `{}`", lint.name, path.display() From 0b27121212078b55296e5daf9332300266043d18 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sat, 19 Apr 2025 16:42:20 +0200 Subject: [PATCH 017/245] Fix clippy::version of elidable_lifetime_names to 1.87 The lint becomes part of stable in 1.87, not in 1.84 --- clippy_lints/src/lifetimes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index fd6208f6b5ef..de35a458afb5 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -88,7 +88,7 @@ declare_clippy_lint! { /// x.chars() /// } /// ``` - #[clippy::version = "1.84.0"] + #[clippy::version = "1.87.0"] pub ELIDABLE_LIFETIME_NAMES, pedantic, "lifetime name that can be replaced with the anonymous lifetime" From 257f68777f469bdd26d4763dc0cbd99c99b74e1d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Mar 2025 17:53:43 +0000 Subject: [PATCH 018/245] Use the new solver in the impossible_predicates --- .../rustc_trait_selection/src/traits/mod.rs | 14 +++---- tests/ui/traits/vtable/impossible-method.rs | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 tests/ui/traits/vtable/impossible-method.rs diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 0987c5b42d88..7a744dfc2ad8 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -690,8 +690,11 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( /// used during analysis. pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec>) -> bool { debug!("impossible_predicates(predicates={:?})", predicates); - let (infcx, param_env) = - tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized()); + let (infcx, param_env) = tcx + .infer_ctxt() + .with_next_trait_solver(true) + .build_with_typing_env(ty::TypingEnv::fully_monomorphized()); + let ocx = ObligationCtxt::new(&infcx); let predicates = ocx.normalize(&ObligationCause::dummy(), param_env, predicates); for predicate in predicates { @@ -704,13 +707,6 @@ pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec; +} +impl Id for T { + type This<'a> = T; +} + +trait Trait {} +impl Trait fn(T::This<'a>)> for T {} + +trait Method { + fn call_me(&self) + where + T: Trait fn(T::This<'a>)>; +} + +impl Method for T { + fn call_me(&self) { + println!("method was reachable"); + } +} + +fn generic(x: &dyn Method) { + // Proving `T: Trait fn(T::This<'a>)>` holds. + x.call_me(); +} + +fn main() { + // Proving `u32: Trait` fails due to incompleteness. + // We don't add the method to the vtable of `dyn Method`, so + // calling it causes UB. + generic::(&()); +} From 61d1c13b3e90cd8a021a9dc9be3fc4311be7088c Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Fri, 25 Apr 2025 10:44:19 -0400 Subject: [PATCH 019/245] Merge commit '4f83a4258deb99f3288a7122c0d5a78200931c61' into subtree-update_cg_gcc_2025-04-25 --- rust-toolchain | 2 +- src/asm.rs | 8 ++++---- src/common.rs | 11 +++++------ src/consts.rs | 8 ++++---- src/debuginfo.rs | 17 +++++++++-------- src/gcc_util.rs | 14 ++++++-------- src/lib.rs | 2 +- src/type_of.rs | 14 +++++++------- tests/lang_tests_common.rs | 2 ++ tests/run/ptr_cast.rs | 7 +++++++ 10 files changed, 46 insertions(+), 39 deletions(-) diff --git a/rust-toolchain b/rust-toolchain index fd898c59707b..452d3f22dc51 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-04-17" +channel = "nightly-2025-04-25" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/src/asm.rs b/src/asm.rs index dbdf37ee6c9e..396c6d579501 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -165,10 +165,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { let mut input_registers = vec![]; for op in rust_operands { - if let InlineAsmOperandRef::In { reg, .. } = *op { - if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) { - input_registers.push(reg_name); - } + if let InlineAsmOperandRef::In { reg, .. } = *op + && let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) + { + input_registers.push(reg_name); } } diff --git a/src/common.rs b/src/common.rs index a63da6b6e27d..918195364ffe 100644 --- a/src/common.rs +++ b/src/common.rs @@ -33,12 +33,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> { - if value.get_type() == self.bool_type.make_pointer() { - if let Some(pointee) = typ.get_pointee() { - if pointee.dyncast_vector().is_some() { - panic!() - } - } + if value.get_type() == self.bool_type.make_pointer() + && let Some(pointee) = typ.get_pointee() + && pointee.dyncast_vector().is_some() + { + panic!() } // NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some // SIMD builtins require a constant value. diff --git a/src/consts.rs b/src/consts.rs index acb393746285..0a67bd7bc71a 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -242,10 +242,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let fn_attrs = self.tcx.codegen_fn_attrs(def_id); let global = if def_id.is_local() && !self.tcx.is_foreign_item(def_id) { - if let Some(global) = self.get_declared_value(sym) { - if self.val_ty(global) != self.type_ptr_to(gcc_type) { - span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); - } + if let Some(global) = self.get_declared_value(sym) + && self.val_ty(global) != self.type_ptr_to(gcc_type) + { + span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); } let is_tls = fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); diff --git a/src/debuginfo.rs b/src/debuginfo.rs index 55e01687400a..f3ced8643952 100644 --- a/src/debuginfo.rs +++ b/src/debuginfo.rs @@ -126,14 +126,15 @@ fn make_mir_scope<'gcc, 'tcx>( return; }; - if let Some(ref vars) = *variables { - if !vars.contains(scope) && scope_data.inlined.is_none() { - // Do not create a DIScope if there are no variables defined in this - // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. - debug_context.scopes[scope] = parent_scope; - instantiated.insert(scope); - return; - } + if let Some(ref vars) = *variables + && !vars.contains(scope) + && scope_data.inlined.is_none() + { + // Do not create a DIScope if there are no variables defined in this + // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. + debug_context.scopes[scope] = parent_scope; + instantiated.insert(scope); + return; } let loc = cx.lookup_debug_loc(scope_data.span.lo()); diff --git a/src/gcc_util.rs b/src/gcc_util.rs index 202764d56491..955f90202357 100644 --- a/src/gcc_util.rs +++ b/src/gcc_util.rs @@ -136,14 +136,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec( let mut name = with_no_trimmed_paths!(layout.ty.to_string()); if let (&ty::Adt(def, _), &Variants::Single { index }) = (layout.ty.kind(), &layout.variants) + && def.is_enum() + && !def.variants().is_empty() { - if def.is_enum() && !def.variants().is_empty() { - write!(&mut name, "::{}", def.variant(index).name).unwrap(); - } + write!(&mut name, "::{}", def.variant(index).name).unwrap(); } if let (&ty::Coroutine(_, _), &Variants::Single { index }) = (layout.ty.kind(), &layout.variants) @@ -264,10 +264,10 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { } fn immediate_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { - if let BackendRepr::Scalar(ref scalar) = self.backend_repr { - if scalar.is_bool() { - return cx.type_i1(); - } + if let BackendRepr::Scalar(ref scalar) = self.backend_repr + && scalar.is_bool() + { + return cx.type_i1(); } self.gcc_type(cx) } diff --git a/tests/lang_tests_common.rs b/tests/lang_tests_common.rs index 64c932a26581..d5a0d71c4b29 100644 --- a/tests/lang_tests_common.rs +++ b/tests/lang_tests_common.rs @@ -1,5 +1,7 @@ //! The common code for `tests/lang_tests_*.rs` +#![allow(clippy::uninlined_format_args)] + use std::env::{self, current_dir}; use std::path::{Path, PathBuf}; use std::process::Command; diff --git a/tests/run/ptr_cast.rs b/tests/run/ptr_cast.rs index c1254c51ce91..e627886a9d57 100644 --- a/tests/run/ptr_cast.rs +++ b/tests/run/ptr_cast.rs @@ -5,6 +5,7 @@ // stdout: 10 // 10 // 42 +// 1 #![feature(no_core)] #![no_std] @@ -21,6 +22,8 @@ fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u3 ) } +static mut ONE: usize = 1; + #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let (a, b, c, d, e, f, g, h, i, j) = int_cast(10, 42); @@ -28,6 +31,10 @@ extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { libc::printf(b"%d\n\0" as *const u8 as *const i8, c); libc::printf(b"%ld\n\0" as *const u8 as *const i8, d); libc::printf(b"%ld\n\0" as *const u8 as *const i8, j); + + let ptr = ONE as *mut usize; + let value = ptr as usize; + libc::printf(b"%ld\n\0" as *const u8 as *const i8, value); } 0 } From 84c5fd784e3005ef150aa4c0f8313c8c74b5d60f Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 25 Apr 2025 22:35:58 +0200 Subject: [PATCH 020/245] Enable `[no-mentions]` and `[issue-links]` in `rustbot` --- triagebot.toml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 triagebot.toml diff --git a/triagebot.toml b/triagebot.toml new file mode 100644 index 000000000000..13da0a87def3 --- /dev/null +++ b/triagebot.toml @@ -0,0 +1,7 @@ +# Documentation at https://forge.rust-lang.org/triagebot/index.html + +# Prevents un-canonicalized issue links (to avoid wrong issues being linked in r-l/rust) +[issue-links] + +# Prevents mentions in commits to avoid users being spammed +[no-mentions] From 3893560e694c28b100b75355622f23b63531d800 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 26 Apr 2025 16:02:17 -0400 Subject: [PATCH 021/245] Update to nightly-2025-04-26 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 452d3f22dc51..fbaa22190052 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-04-25" +channel = "nightly-2025-04-26" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From 8858b35c66dc7c07cf7abf0e74ea4370ff0210ef Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sun, 27 Apr 2025 19:11:35 +0200 Subject: [PATCH 022/245] Fix a few more `clippy::version`s --- clippy_lints/src/attrs/mod.rs | 2 +- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/implicit_saturating_sub.rs | 2 +- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/manual_abs_diff.rs | 2 +- clippy_lints/src/mem_replace.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/single_option_map.rs | 2 +- clippy_lints/src/types/mod.rs | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index f7f168cb2679..9a1242980418 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -468,7 +468,7 @@ declare_clippy_lint! { /// #[ignore = "Some good reason"] /// fn test() {} /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.88.0"] pub IGNORE_WITHOUT_REASON, pedantic, "ignored tests without messages" diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 7e4b43575672..51982c4951c9 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -780,7 +780,7 @@ declare_clippy_lint! { /// let aligned = std::ptr::dangling::(); /// let mut_ptr: *mut i64 = std::ptr::dangling_mut(); /// ``` - #[clippy::version = "1.87.0"] + #[clippy::version = "1.88.0"] pub MANUAL_DANGLING_PTR, style, "casting small constant literals to pointers to create dangling pointers" diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index ab77edf1147c..87da380e9540 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -93,7 +93,7 @@ declare_clippy_lint! { /// ```no_run /// //! [first](x)second /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub DOC_LINK_CODE, nursery, "link with code back-to-back with other code" diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 514e72a48682..0823ef53ef98 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -69,7 +69,7 @@ declare_clippy_lint! { /// /// let result = a.saturating_sub(b); /// ``` - #[clippy::version = "1.44.0"] + #[clippy::version = "1.83.0"] pub INVERTED_SATURATING_SUB, correctness, "Check if a variable is smaller than another one and still subtract from it even if smaller" diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 2b66827e82ee..56d2bef2305a 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -778,7 +778,7 @@ declare_clippy_lint! { /// let _ = s[idx..]; /// } /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.88.0"] pub CHAR_INDICES_AS_BYTE_INDICES, correctness, "using the character position yielded by `.chars().enumerate()` in a context where a byte index is expected" diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index c515e41f242f..7d92208056ee 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { /// a.abs_diff(b) /// # ; /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub MANUAL_ABS_DIFF, complexity, "using an if-else pattern instead of `abs_diff`" diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index a54d835b538c..28efd2038b38 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -62,7 +62,7 @@ declare_clippy_lint! { /// let mut an_option = Some(0); /// let taken = an_option.replace(1); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub MEM_REPLACE_OPTION_WITH_SOME, style, "replacing an `Option` with `Some` instead of `replace()`" diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ad374dee516c..06aa5b6f1d2d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4454,7 +4454,7 @@ declare_clippy_lint! { /// values.contains(&10) /// } /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub MANUAL_CONTAINS, perf, "unnecessary `iter().any()` on slices that can be replaced with `contains()`" diff --git a/clippy_lints/src/single_option_map.rs b/clippy_lints/src/single_option_map.rs index 1fb54950612a..cc497c97a472 100644 --- a/clippy_lints/src/single_option_map.rs +++ b/clippy_lints/src/single_option_map.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// param * 2 /// } /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub SINGLE_OPTION_MAP, nursery, "Checks for functions with method calls to `.map(_)` on an arg of type `Option` as the outermost expression." diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 3147058b4cda..c1c7cc516565 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -385,7 +385,7 @@ declare_clippy_lint! { /// ```no_run /// let right: std::borrow::Cow<'_, [u8]>; /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.87.0"] pub OWNED_COW, style, "needlessly owned Cow type" From c7d3d6d4683ee5d440a51a53e2601ac7bd13ed9f Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Sun, 27 Apr 2025 19:27:21 +0200 Subject: [PATCH 023/245] Update clippy_lints/src/manual_abs_diff.rs --- clippy_lints/src/manual_abs_diff.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index 7d92208056ee..bac4b3d32f2a 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { /// a.abs_diff(b) /// # ; /// ``` - #[clippy::version = "1.87.0"] + #[clippy::version = "1.88.0"] pub MANUAL_ABS_DIFF, complexity, "using an if-else pattern instead of `abs_diff`" From d62fe1e043dae471e6f29648eaddcac876b4e962 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 24 Apr 2025 22:11:23 +0000 Subject: [PATCH 024/245] Implement the internal feature `cfg_target_has_reliable_f16_f128` Support for `f16` and `f128` is varied across targets, backends, and backend versions. Eventually we would like to reach a point where all backends support these approximately equally, but until then we have to work around some of these nuances of support being observable. Introduce the `cfg_target_has_reliable_f16_f128` internal feature, which provides the following new configuration gates: * `cfg(target_has_reliable_f16)` * `cfg(target_has_reliable_f16_math)` * `cfg(target_has_reliable_f128)` * `cfg(target_has_reliable_f128_math)` `reliable_f16` and `reliable_f128` indicate that basic arithmetic for the type works correctly. The `_math` versions indicate that anything relying on `libm` works correctly, since sometimes this hits a separate class of codegen bugs. These options match configuration set by the build script at [1]. The logic for LLVM support is duplicated as-is from the same script. There are a few possible updates that will come as a follow up. The config introduced here is not planned to ever become stable, it is only intended to replace the build scripts for `std` tests and `compiler-builtins` that don't have any way to configure based on the codegen backend. MCP: https://github.com/rust-lang/compiler-team/issues/866 Closes: https://github.com/rust-lang/compiler-team/issues/866 [1]: https://github.com/rust-lang/rust/blob/555e1d0386f024a8359645c3217f4b3eae9be042/library/std/build.rs#L84-L186 --- src/gcc_util.rs | 2 +- src/lib.rs | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/gcc_util.rs b/src/gcc_util.rs index 955f90202357..2b053abdd190 100644 --- a/src/gcc_util.rs +++ b/src/gcc_util.rs @@ -55,7 +55,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec. all_rust_features.push((false, feature)); } else if !feature.is_empty() && diagnostics { diff --git a/src/lib.rs b/src/lib.rs index 555f164e53fd..2c5a78716838 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,7 +102,7 @@ use rustc_codegen_ssa::back::write::{ }; use rustc_codegen_ssa::base::codegen_crate; use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods}; -use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen}; +use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::IntoDynSyncSend; use rustc_errors::DiagCtxtHandle; @@ -260,8 +260,8 @@ impl CodegenBackend for GccCodegenBackend { .join(sess) } - fn target_features_cfg(&self, sess: &Session) -> (Vec, Vec) { - target_features_cfg(sess, &self.target_info) + fn target_config(&self, sess: &Session) -> TargetConfig { + target_config(sess, &self.target_info) } } @@ -485,10 +485,7 @@ fn to_gcc_opt_level(optlevel: Option) -> OptimizationLevel { } /// Returns the features that should be set in `cfg(target_feature)`. -fn target_features_cfg( - sess: &Session, - target_info: &LockedTargetInfo, -) -> (Vec, Vec) { +fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig { // TODO(antoyo): use global_gcc_features. let f = |allow_unstable| { sess.target @@ -523,5 +520,14 @@ fn target_features_cfg( let target_features = f(false); let unstable_target_features = f(true); - (target_features, unstable_target_features) + + TargetConfig { + target_features, + unstable_target_features, + // There are no known bugs with GCC support for f16 or f128 + has_reliable_f16: true, + has_reliable_f16_math: true, + has_reliable_f128: true, + has_reliable_f128_math: true, + } } From f918b89f61d9aac3df4e57b05892f4614ddfc678 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 28 Apr 2025 16:36:25 +0000 Subject: [PATCH 025/245] Wf is not coinductive --- .../src/traits/select/mod.rs | 5 ++++- tests/ui/associated-types/issue-64855.stderr | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index df02a67c2c9b..de310f9d3716 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1242,7 +1242,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { self.infcx.tcx.trait_is_coinductive(data.def_id()) } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => true, + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => { + // TODO: GCE is going away + self.infcx.tcx.features().generic_const_exprs() + } _ => false, }) } diff --git a/tests/ui/associated-types/issue-64855.stderr b/tests/ui/associated-types/issue-64855.stderr index 7c09abdb3b6f..1562a10b4534 100644 --- a/tests/ui/associated-types/issue-64855.stderr +++ b/tests/ui/associated-types/issue-64855.stderr @@ -10,6 +10,19 @@ help: this trait has no implementations, consider adding one LL | pub trait Foo { | ^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error[E0275]: overflow evaluating the requirement `Bar well-formed` + --> $DIR/issue-64855.rs:5:46 + | +LL | pub struct Bar(::Type) where Self: ; + | ^^^^ + | +note: required by a bound in `Bar` + --> $DIR/issue-64855.rs:5:46 + | +LL | pub struct Bar(::Type) where Self: ; + | ^^^^ required by this bound in `Bar` -For more information about this error, try `rustc --explain E0277`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0275, E0277. +For more information about an error, try `rustc --explain E0275`. From 38c05a68ee94a01ac9ef91ab0b7d0d3fc6a33bda Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 29 Apr 2025 03:05:26 +0000 Subject: [PATCH 026/245] Adjust tests --- .../src/traits/select/mod.rs | 4 +++- tests/crashes/123456.rs | 16 ---------------- tests/ui/associated-types/issue-64855-2.rs | 5 ++++- tests/ui/associated-types/issue-64855-2.stderr | 15 +++++++++++++++ tests/ui/associated-types/issue-64855.rs | 5 +++++ tests/ui/associated-types/issue-64855.stderr | 8 ++++---- .../higher-ranked/trait-bounds/issue-95230.rs | 9 ++++----- .../trait-bounds/issue-95230.stderr | 18 ++++++++++++++++++ 8 files changed, 53 insertions(+), 27 deletions(-) delete mode 100644 tests/crashes/123456.rs create mode 100644 tests/ui/associated-types/issue-64855-2.stderr create mode 100644 tests/ui/higher-ranked/trait-bounds/issue-95230.stderr diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index de310f9d3716..1a5a1c214bd3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1243,7 +1243,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx.tcx.trait_is_coinductive(data.def_id()) } ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => { - // TODO: GCE is going away + // FIXME(generic_const_exprs): GCE needs well-formedness predicates to be + // coinductive, but GCE is on the way out anyways, so this should eventually + // be replaced with `false`. self.infcx.tcx.features().generic_const_exprs() } _ => false, diff --git a/tests/crashes/123456.rs b/tests/crashes/123456.rs deleted file mode 100644 index ed7cbada3f85..000000000000 --- a/tests/crashes/123456.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ known-bug: #123456 - -trait Project { - const SELF: Self; -} - -fn take1( - _: Project< - SELF = { - j2.join().unwrap(); - }, - >, -) { -} - -pub fn main() {} diff --git a/tests/ui/associated-types/issue-64855-2.rs b/tests/ui/associated-types/issue-64855-2.rs index 30cb37b5198e..20b8ff17e9e5 100644 --- a/tests/ui/associated-types/issue-64855-2.rs +++ b/tests/ui/associated-types/issue-64855-2.rs @@ -1,5 +1,8 @@ -//@ check-pass +// This was originally a test for a `ReEmpty` ICE, but became an unintentional test of +// the coinductiveness of WF predicates. That behavior was removed, and thus this is +// also inadvertently a test for the (non-)co-inductiveness of WF predicates. pub struct Bar<'a>(&'a Self) where Self: ; +//~^ ERROR overflow evaluating the requirement `Bar<'a> well-formed` fn main() {} diff --git a/tests/ui/associated-types/issue-64855-2.stderr b/tests/ui/associated-types/issue-64855-2.stderr new file mode 100644 index 000000000000..22292a8721a1 --- /dev/null +++ b/tests/ui/associated-types/issue-64855-2.stderr @@ -0,0 +1,15 @@ +error[E0275]: overflow evaluating the requirement `Bar<'a> well-formed` + --> $DIR/issue-64855-2.rs:5:36 + | +LL | pub struct Bar<'a>(&'a Self) where Self: ; + | ^^^^ + | +note: required by a bound in `Bar` + --> $DIR/issue-64855-2.rs:5:36 + | +LL | pub struct Bar<'a>(&'a Self) where Self: ; + | ^^^^ required by this bound in `Bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/associated-types/issue-64855.rs b/tests/ui/associated-types/issue-64855.rs index 81cf3ae6e83b..5d325b981a2c 100644 --- a/tests/ui/associated-types/issue-64855.rs +++ b/tests/ui/associated-types/issue-64855.rs @@ -1,8 +1,13 @@ +// This was originally a test for a `ReEmpty` ICE, but became an unintentional test of +// the coinductiveness of WF predicates. That behavior was removed, and thus this is +// also inadvertently a test for the (non-)co-inductiveness of WF predicates. + pub trait Foo { type Type; } pub struct Bar(::Type) where Self: ; //~^ ERROR the trait bound `Bar: Foo` is not satisfied +//~| ERROR overflow evaluating the requirement `Bar well-formed` fn main() {} diff --git a/tests/ui/associated-types/issue-64855.stderr b/tests/ui/associated-types/issue-64855.stderr index 1562a10b4534..d8ba1a9d07ee 100644 --- a/tests/ui/associated-types/issue-64855.stderr +++ b/tests/ui/associated-types/issue-64855.stderr @@ -1,23 +1,23 @@ error[E0277]: the trait bound `Bar: Foo` is not satisfied - --> $DIR/issue-64855.rs:5:19 + --> $DIR/issue-64855.rs:9:19 | LL | pub struct Bar(::Type) where Self: ; | ^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bar` | help: this trait has no implementations, consider adding one - --> $DIR/issue-64855.rs:1:1 + --> $DIR/issue-64855.rs:5:1 | LL | pub trait Foo { | ^^^^^^^^^^^^^ error[E0275]: overflow evaluating the requirement `Bar well-formed` - --> $DIR/issue-64855.rs:5:46 + --> $DIR/issue-64855.rs:9:46 | LL | pub struct Bar(::Type) where Self: ; | ^^^^ | note: required by a bound in `Bar` - --> $DIR/issue-64855.rs:5:46 + --> $DIR/issue-64855.rs:9:46 | LL | pub struct Bar(::Type) where Self: ; | ^^^^ required by this bound in `Bar` diff --git a/tests/ui/higher-ranked/trait-bounds/issue-95230.rs b/tests/ui/higher-ranked/trait-bounds/issue-95230.rs index d1ca6834551e..821a04ff0655 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-95230.rs +++ b/tests/ui/higher-ranked/trait-bounds/issue-95230.rs @@ -1,11 +1,10 @@ -//@ revisions: old next -//@[next] compile-flags: -Znext-solver -//@[old] check-pass -//@[next] known-bug: #109764 - +// This used to be a test for overflow handling + higher-ranked outlives +// in the new solver, but this test isn't expected to pass since WF preds +// are not coinductive anymore. pub struct Bar where for<'a> &'a mut Self:; +//~^ ERROR overflow evaluating the requirement `for<'a> &'a mut Bar well-formed` fn main() {} diff --git a/tests/ui/higher-ranked/trait-bounds/issue-95230.stderr b/tests/ui/higher-ranked/trait-bounds/issue-95230.stderr new file mode 100644 index 000000000000..7070af75d290 --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/issue-95230.stderr @@ -0,0 +1,18 @@ +error[E0275]: overflow evaluating the requirement `for<'a> &'a mut Bar well-formed` + --> $DIR/issue-95230.rs:7:13 + | +LL | for<'a> &'a mut Self:; + | ^^^^^^^^^^^^ + | +note: required by a bound in `Bar` + --> $DIR/issue-95230.rs:7:13 + | +LL | pub struct Bar + | --- required by a bound in this struct +LL | where +LL | for<'a> &'a mut Self:; + | ^^^^^^^^^^^^ required by this bound in `Bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. From ce5a198239ff3f656e0e5bb0035b6fac306a461a Mon Sep 17 00:00:00 2001 From: g4titanx Date: Fri, 3 Jan 2025 07:58:23 +0100 Subject: [PATCH 027/245] add contibuting.md --- CONTRIBUTING.md | 113 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000000..3bddb6ac63b5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,113 @@ +# Contributing to rust_codegen_gcc + +Welcome to the rust_codegen_gcc project! This guide will help you get started as a contributor. The project aims to provide a GCC codegen backend for rustc, allowing Rust compilation on platforms unsupported by LLVM and potentially improving runtime performance through GCC's optimizations. + +## Getting Started + +### Setting Up Your Development Environment + +1. Install the required dependencies: + - rustup (follow instructions on the [official website](https://rustup.rs)) + - DejaGnu (for running libgccjit test suite) + - Additional packages: `flex`, `libmpfr-dev`, `libgmp-dev`, `libmpc3`, `libmpc-dev` + +2. Clone and configure the repository: + ```bash + git clone https://github.com/rust-lang/rust_codegen_gcc + cd rust_codegen_gcc + cp config.example.toml config.toml + ``` + +3. Build the project: + ```bash + ./y.sh prepare # downloads and patches sysroot + ./y.sh build --sysroot --release + ``` + +### Running Tests + +To verify your setup: +```bash +# Run the full test suite +./y.sh test --release + +# Test with a simple program +./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml +``` + +## Communication Channels + +- Matrix: Join our [Matrix channel](https://matrix.to/#/#rustc_codegen_gcc:matrix.org) +- IRC: Join us on [IRC](https://web.libera.chat/#rustc_codegen_gcc) +- GitHub Issues: For bug reports and feature discussions + +We encourage new contributors to join our communication channels and introduce themselves. Feel free to ask questions about where to start or discuss potential contributions. + +## Understanding Core Concepts + +### Project Structure + +The project consists of several key components: +- The GCC backend integration through libgccjit +- Rust compiler interface +- Test infrastructure + +### Common Development Tasks + +#### Running Specific Tests +To run a specific test: +1. Individual test: `./y.sh test --test ` +2. libgccjit tests: + ```bash + cd gcc-build/gcc + make check-jit + # For a specific test: + make check-jit RUNTESTFLAGS="-v -v -v jit.exp=jit.dg/test-asm.cc" + ``` + +#### Debugging Tools +The project provides several environment variables for debugging: +- `CG_GCCJIT_DUMP_MODULE`: Dumps a specific module +- `CG_GCCJIT_DUMP_TO_FILE`: Creates C-like representation +- `CG_GCCJIT_DUMP_RTL`: Shows Register Transfer Language output + +Full list of debugging options can be found in the README. + +## Making Contributions + +### Finding Issues to Work On +1. Look for issues labeled with `good-first-issue` or `help-wanted` +2. Check the project roadmap for larger initiatives +3. Consider improving documentation or tests + +### Pull Request Process +1. Fork the repository and create a new branch +2. Make your changes with clear commit messages +3. Add tests for new functionality +4. Update documentation as needed +5. Submit a PR with a description of your changes + +### Code Style Guidelines +- Follow Rust standard coding conventions +- Ensure your code passes `rustfmt` and `clippy` +- Add comments explaining complex logic, especially in GCC interface code + +## Additional Resources + +- [Rustc Dev Guide](https://rustc-dev-guide.rust-lang.org/) +- [GCC Internals Documentation](https://gcc.gnu.org/onlinedocs/gccint/) +- Project-specific documentation in the `doc/` directory: + - Common errors + - Debugging GCC LTO + - Git subtree sync + - Sending patches to GCC + +## Getting Help + +If you're stuck or unsure about anything: +1. Check the existing documentation in the `doc/` directory +2. Ask in the IRC or Matrix channels +3. Open a GitHub issue for technical problems +4. Comment on the issue you're working on if you need guidance + +Remember that all contributions, including documentation improvements, bug reports, and feature requests, are valuable to the project. \ No newline at end of file From a5b947ba1cba06bbd08f4d5c83aac1dba1dd37ca Mon Sep 17 00:00:00 2001 From: g4titanx Date: Fri, 7 Feb 2025 11:22:21 +0100 Subject: [PATCH 028/245] modify docs --- CONTRIBUTING.md | 81 ++++++++++++++++++------------------------------- Readme.md | 43 +++++++++++++++++--------- 2 files changed, 58 insertions(+), 66 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3bddb6ac63b5..85ddb6c8f46c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,39 +1,19 @@ # Contributing to rust_codegen_gcc -Welcome to the rust_codegen_gcc project! This guide will help you get started as a contributor. The project aims to provide a GCC codegen backend for rustc, allowing Rust compilation on platforms unsupported by LLVM and potentially improving runtime performance through GCC's optimizations. +Welcome to the `rust_codegen_gcc` project! This guide will help you get started as a contributor. The project aims to provide a GCC codegen backend for rustc, allowing Rust compilation on platforms unsupported by LLVM and potentially improving runtime performance through GCC's optimizations. ## Getting Started ### Setting Up Your Development Environment -1. Install the required dependencies: - - rustup (follow instructions on the [official website](https://rustup.rs)) - - DejaGnu (for running libgccjit test suite) - - Additional packages: `flex`, `libmpfr-dev`, `libgmp-dev`, `libmpc3`, `libmpc-dev` +For detailed setup instructions including dependencies, build steps, and initial testing, please refer to our [README](https://github.com/rust-lang/rustc_codegen_gcc/blob/master/Readme.md). The README contains the most up-to-date information on: -2. Clone and configure the repository: - ```bash - git clone https://github.com/rust-lang/rust_codegen_gcc - cd rust_codegen_gcc - cp config.example.toml config.toml - ``` +- Required dependencies and system packages +- Repository setup and configuration +- Build process +- Basic test verification -3. Build the project: - ```bash - ./y.sh prepare # downloads and patches sysroot - ./y.sh build --sysroot --release - ``` - -### Running Tests - -To verify your setup: -```bash -# Run the full test suite -./y.sh test --release - -# Test with a simple program -./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml -``` +Once you've completed the setup process outlined in the README, you can proceed with the contributor-specific information below. ## Communication Channels @@ -45,31 +25,28 @@ We encourage new contributors to join our communication channels and introduce t ## Understanding Core Concepts -### Project Structure - -The project consists of several key components: -- The GCC backend integration through libgccjit -- Rust compiler interface -- Test infrastructure - ### Common Development Tasks #### Running Specific Tests -To run a specific test: -1. Individual test: `./y.sh test --test ` -2. libgccjit tests: - ```bash - cd gcc-build/gcc - make check-jit - # For a specific test: - make check-jit RUNTESTFLAGS="-v -v -v jit.exp=jit.dg/test-asm.cc" - ``` +To run specific tests, use appropriate flags such as: +- `./y.sh test --test-libcore` +- `./y.sh test --std-tests` + +Additional test running options: +```bash +# libgccjit tests +cd gcc-build/gcc +make check-jit +# For a specific test: +make check-jit RUNTESTFLAGS="-v -v -v jit.exp=jit.dg/test-asm.cc" +``` #### Debugging Tools The project provides several environment variables for debugging: +- `CG_GCCJIT_DUMP_GIMPLE`: Most commonly used debug dump +- `CG_RUSTFLAGS`: Additional Rust compiler flags - `CG_GCCJIT_DUMP_MODULE`: Dumps a specific module - `CG_GCCJIT_DUMP_TO_FILE`: Creates C-like representation -- `CG_GCCJIT_DUMP_RTL`: Shows Register Transfer Language output Full list of debugging options can be found in the README. @@ -77,8 +54,8 @@ Full list of debugging options can be found in the README. ### Finding Issues to Work On 1. Look for issues labeled with `good-first-issue` or `help-wanted` -2. Check the project roadmap for larger initiatives -3. Consider improving documentation or tests +2. Check the [progress report](https://blog.antoyo.xyz/rustc_codegen_gcc-progress-report-34#state_of_rustc_codegen_gcc) for larger initiatives +3. Consider improving documentation or investigate [failing tests](https://github.com/rust-lang/rustc_codegen_gcc/tree/master/tests)(except failing-ui-tests12.txt) ### Pull Request Process 1. Fork the repository and create a new branch @@ -97,10 +74,12 @@ Full list of debugging options can be found in the README. - [Rustc Dev Guide](https://rustc-dev-guide.rust-lang.org/) - [GCC Internals Documentation](https://gcc.gnu.org/onlinedocs/gccint/) - Project-specific documentation in the `doc/` directory: - - Common errors - - Debugging GCC LTO - - Git subtree sync - - Sending patches to GCC + - [Common errors](./doc/errors.md) + - [Debugging GCC LTO](./doc/debugging-gcc-lto.md) + - [Debugging libgccjit](./doc/debugging-libgccjit.md) + - [Git subtree sync](./doc/subtree.md) + - [List of useful commands](./doc/tips.md) + - [Send a patch to GCC](./doc/sending-gcc-patch.md) ## Getting Help @@ -110,4 +89,4 @@ If you're stuck or unsure about anything: 3. Open a GitHub issue for technical problems 4. Comment on the issue you're working on if you need guidance -Remember that all contributions, including documentation improvements, bug reports, and feature requests, are valuable to the project. \ No newline at end of file +Remember that all contributions, including documentation improvements, bug reports, and feature requests, are valuable to the project. diff --git a/Readme.md b/Readme.md index d0e4dbba6d35..89e7e24bd327 100644 --- a/Readme.md +++ b/Readme.md @@ -12,22 +12,35 @@ This is a GCC codegen for rustc, which means it can be loaded by the existing ru The primary goal of this project is to be able to compile Rust code on platforms unsupported by LLVM. A secondary goal is to check if using the gcc backend will provide any run-time speed improvement for the programs compiled using rustc. +## Getting Started + +Note: **This requires a patched libgccjit in order to work. +You need to use my [fork of gcc](https://github.com/antoyo/gcc) which already includes these patches.** + ### Dependencies + - rustup: follow instructions on the [official website](https://rustup.rs) + - consider to install DejaGnu which is necessary for running the libgccjit test suite. [website](https://www.gnu.org/software/dejagnu/#downloading) + - additional packages: `flex`, `libmpfr-dev`, `libgmp-dev`, `libmpc3`, `libmpc-dev` + +### Quick start +1. Clone and configure the repository: + ```bash + git clone https://github.com/rust-lang/rust_codegen_gcc + cd rust_codegen_gcc + cp config.example.toml config.toml + ``` -**rustup:** Follow the instructions on the official [website](https://www.rust-lang.org/tools/install) - -**DejaGnu:** Consider to install DejaGnu which is necessary for running the libgccjit test suite. [website](https://www.gnu.org/software/dejagnu/#downloading) - - - -## Building - -**This requires a patched libgccjit in order to work. -You need to use my [fork of gcc](https://github.com/rust-lang/gcc) which already includes these patches.** - -```bash -$ cp config.example.toml config.toml -``` +2. Build and test: + ```bash + ./y.sh prepare # downloads and patches sysroot + ./y.sh build --sysroot --release + + # Verify setup with a simple test + ./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml + + # Run full test suite (expect ~100 failing UI tests) + ./y.sh test --release + ``` If don't need to test GCC patches you wrote in our GCC fork, then the default configuration should be all you need. You can update the `rustc_codegen_gcc` without worrying about GCC. @@ -40,7 +53,7 @@ to do a few more things. To build it (most of these instructions come from [here](https://gcc.gnu.org/onlinedocs/jit/internals/index.html), so don't hesitate to take a look there if you encounter an issue): ```bash -$ git clone https://github.com/rust-lang/gcc +$ git clone https://github.com/antoyo/gcc $ sudo apt install flex libmpfr-dev libgmp-dev libmpc3 libmpc-dev $ mkdir gcc-build gcc-install $ cd gcc-build From f150171b6cc6b1e8393fd9b057accb6b5ea0f093 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 29 Apr 2025 18:12:08 -0400 Subject: [PATCH 029/245] Some improvements --- CONTRIBUTING.md | 21 +++++++++++---------- Readme.md | 5 +++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 85ddb6c8f46c..1bfa6e435db3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,8 +31,9 @@ We encourage new contributors to join our communication channels and introduce t To run specific tests, use appropriate flags such as: - `./y.sh test --test-libcore` - `./y.sh test --std-tests` +- `cargo test -- ` -Additional test running options: +Additionally, you can run the tests of `libgccjit`: ```bash # libgccjit tests cd gcc-build/gcc @@ -48,14 +49,14 @@ The project provides several environment variables for debugging: - `CG_GCCJIT_DUMP_MODULE`: Dumps a specific module - `CG_GCCJIT_DUMP_TO_FILE`: Creates C-like representation -Full list of debugging options can be found in the README. +Full list of debugging options can be found in the [README](/rust-lang/rustc_codegen_gcc#env-vars). ## Making Contributions ### Finding Issues to Work On -1. Look for issues labeled with `good-first-issue` or `help-wanted` +1. Look for issues labeled with [`good first issue`](/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue state%3Aopen label%3A"good first issue") or [`help wanted`](/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue state%3Aopen label%3A"help wanted") 2. Check the [progress report](https://blog.antoyo.xyz/rustc_codegen_gcc-progress-report-34#state_of_rustc_codegen_gcc) for larger initiatives -3. Consider improving documentation or investigate [failing tests](https://github.com/rust-lang/rustc_codegen_gcc/tree/master/tests)(except failing-ui-tests12.txt) +3. Consider improving documentation or investigating [failing tests](https://github.com/rust-lang/rustc_codegen_gcc/tree/master/tests)(except `failing-ui-tests12.txt`) ### Pull Request Process 1. Fork the repository and create a new branch @@ -74,12 +75,12 @@ Full list of debugging options can be found in the README. - [Rustc Dev Guide](https://rustc-dev-guide.rust-lang.org/) - [GCC Internals Documentation](https://gcc.gnu.org/onlinedocs/gccint/) - Project-specific documentation in the `doc/` directory: - - [Common errors](./doc/errors.md) - - [Debugging GCC LTO](./doc/debugging-gcc-lto.md) - - [Debugging libgccjit](./doc/debugging-libgccjit.md) - - [Git subtree sync](./doc/subtree.md) - - [List of useful commands](./doc/tips.md) - - [Send a patch to GCC](./doc/sending-gcc-patch.md) + - [Common errors](/rust-lang/rustc_codegen_gcc/blob/master/doc/errors.md) + - [Debugging](/rust-lang/rustc_codegen_gcc/blob/master/doc/debugging.md) + - [Debugging libgccjit](/rust-lang/rustc_codegen_gcc/blob/master/doc/debugging-libgccjit.md) + - [Git subtree sync](/rust-lang/rustc_codegen_gcc/blob/master/doc/subtree.md) + - [List of useful commands](/rust-lang/rustc_codegen_gcc/blob/master/doc/tips.md) + - [Send a patch to GCC](/rust-lang/rustc_codegen_gcc/blob/master/doc/sending-gcc-patch.md) ## Getting Help diff --git a/Readme.md b/Readme.md index 89e7e24bd327..10f9a85d72a3 100644 --- a/Readme.md +++ b/Readme.md @@ -15,7 +15,8 @@ A secondary goal is to check if using the gcc backend will provide any run-time ## Getting Started Note: **This requires a patched libgccjit in order to work. -You need to use my [fork of gcc](https://github.com/antoyo/gcc) which already includes these patches.** +You need to use my [fork of gcc](https://github.com/rust-lang/gcc) which already includes these patches.** +The default configuration (see below in the [Quick start](#quick-start) section) will download a `libgccjit` built in the CI that already contains these patches, so you don't need to build this fork yourself if you use the default configuration. ### Dependencies - rustup: follow instructions on the [official website](https://rustup.rs) @@ -53,7 +54,7 @@ to do a few more things. To build it (most of these instructions come from [here](https://gcc.gnu.org/onlinedocs/jit/internals/index.html), so don't hesitate to take a look there if you encounter an issue): ```bash -$ git clone https://github.com/antoyo/gcc +$ git clone https://github.com/rust-lang/gcc $ sudo apt install flex libmpfr-dev libgmp-dev libmpc3 libmpc-dev $ mkdir gcc-build gcc-install $ cd gcc-build From d11bfe9c187e72147ce520a3d5f2c0ea28eb98ad Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 29 Apr 2025 18:23:23 -0400 Subject: [PATCH 030/245] Use the correct name of the project --- CONTRIBUTING.md | 4 ++-- Readme.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1bfa6e435db3..fecd20209344 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ -# Contributing to rust_codegen_gcc +# Contributing to rustc_codegen_gcc -Welcome to the `rust_codegen_gcc` project! This guide will help you get started as a contributor. The project aims to provide a GCC codegen backend for rustc, allowing Rust compilation on platforms unsupported by LLVM and potentially improving runtime performance through GCC's optimizations. +Welcome to the `rustc_codegen_gcc` project! This guide will help you get started as a contributor. The project aims to provide a GCC codegen backend for rustc, allowing Rust compilation on platforms unsupported by LLVM and potentially improving runtime performance through GCC's optimizations. ## Getting Started diff --git a/Readme.md b/Readme.md index 10f9a85d72a3..bacad151c965 100644 --- a/Readme.md +++ b/Readme.md @@ -26,8 +26,8 @@ The default configuration (see below in the [Quick start](#quick-start) section) ### Quick start 1. Clone and configure the repository: ```bash - git clone https://github.com/rust-lang/rust_codegen_gcc - cd rust_codegen_gcc + git clone https://github.com/rust-lang/rustc_codegen_gcc + cd rustc_codegen_gcc cp config.example.toml config.toml ``` From 62814f0995c855c4a2effc1cb318da9fc9810adb Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 29 Apr 2025 18:36:29 -0400 Subject: [PATCH 031/245] Improve the doc --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fecd20209344..73f13f5bd86b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,8 +44,8 @@ make check-jit RUNTESTFLAGS="-v -v -v jit.exp=jit.dg/test-asm.cc" #### Debugging Tools The project provides several environment variables for debugging: -- `CG_GCCJIT_DUMP_GIMPLE`: Most commonly used debug dump -- `CG_RUSTFLAGS`: Additional Rust compiler flags +- `CG_GCCJIT_DUMP_GIMPLE`: Dump the GIMPLE IR +- `CG_RUSTFLAGS`: Additional Rust flags - `CG_GCCJIT_DUMP_MODULE`: Dumps a specific module - `CG_GCCJIT_DUMP_TO_FILE`: Creates C-like representation From 2e9ec931dae240e4845c16c2f54153bf01b63fcd Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 29 Apr 2025 18:42:25 -0400 Subject: [PATCH 032/245] Fix links in CONTRIBUTING.md --- CONTRIBUTING.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 73f13f5bd86b..db1bee285ead 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ Welcome to the `rustc_codegen_gcc` project! This guide will help you get started ### Setting Up Your Development Environment -For detailed setup instructions including dependencies, build steps, and initial testing, please refer to our [README](https://github.com/rust-lang/rustc_codegen_gcc/blob/master/Readme.md). The README contains the most up-to-date information on: +For detailed setup instructions including dependencies, build steps, and initial testing, please refer to our [README](Readme.md). The README contains the most up-to-date information on: - Required dependencies and system packages - Repository setup and configuration @@ -49,14 +49,14 @@ The project provides several environment variables for debugging: - `CG_GCCJIT_DUMP_MODULE`: Dumps a specific module - `CG_GCCJIT_DUMP_TO_FILE`: Creates C-like representation -Full list of debugging options can be found in the [README](/rust-lang/rustc_codegen_gcc#env-vars). +Full list of debugging options can be found in the [README](Readme.md#env-vars). ## Making Contributions ### Finding Issues to Work On -1. Look for issues labeled with [`good first issue`](/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue state%3Aopen label%3A"good first issue") or [`help wanted`](/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue state%3Aopen label%3A"help wanted") +1. Look for issues labeled with [`good first issue`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue state%3Aopen label%3A"good first issue") or [`help wanted`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue state%3Aopen label%3A"help wanted") 2. Check the [progress report](https://blog.antoyo.xyz/rustc_codegen_gcc-progress-report-34#state_of_rustc_codegen_gcc) for larger initiatives -3. Consider improving documentation or investigating [failing tests](https://github.com/rust-lang/rustc_codegen_gcc/tree/master/tests)(except `failing-ui-tests12.txt`) +3. Consider improving documentation or investigating [failing tests](https://github.com/rust-lang/rustc_codegen_gcc/tree/master/tests) (except `failing-ui-tests12.txt`) ### Pull Request Process 1. Fork the repository and create a new branch @@ -75,12 +75,12 @@ Full list of debugging options can be found in the [README](/rust-lang/rustc_cod - [Rustc Dev Guide](https://rustc-dev-guide.rust-lang.org/) - [GCC Internals Documentation](https://gcc.gnu.org/onlinedocs/gccint/) - Project-specific documentation in the `doc/` directory: - - [Common errors](/rust-lang/rustc_codegen_gcc/blob/master/doc/errors.md) - - [Debugging](/rust-lang/rustc_codegen_gcc/blob/master/doc/debugging.md) - - [Debugging libgccjit](/rust-lang/rustc_codegen_gcc/blob/master/doc/debugging-libgccjit.md) - - [Git subtree sync](/rust-lang/rustc_codegen_gcc/blob/master/doc/subtree.md) - - [List of useful commands](/rust-lang/rustc_codegen_gcc/blob/master/doc/tips.md) - - [Send a patch to GCC](/rust-lang/rustc_codegen_gcc/blob/master/doc/sending-gcc-patch.md) + - [Common errors](doc/errors.md) + - [Debugging](doc/debugging.md) + - [Debugging libgccjit](doc/debugging-libgccjit.md) + - [Git subtree sync](doc/subtree.md) + - [List of useful commands](doc/tips.md) + - [Send a patch to GCC](doc/sending-gcc-patch.md) ## Getting Help From 43747cecfbe9a7f3c60a712256cfd6131bad7a0c Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 29 Apr 2025 18:45:26 -0400 Subject: [PATCH 033/245] Fix links in CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index db1bee285ead..f0fd5c630528 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,7 +54,7 @@ Full list of debugging options can be found in the [README](Readme.md#env-vars). ## Making Contributions ### Finding Issues to Work On -1. Look for issues labeled with [`good first issue`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue state%3Aopen label%3A"good first issue") or [`help wanted`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue state%3Aopen label%3A"help wanted") +1. Look for issues labeled with [`good first issue`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue%20state%3Aopen%20label%3A"good%20first%20issue") or [`help wanted`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue%20state%3Aopen%20label%3A"help%20wanted") 2. Check the [progress report](https://blog.antoyo.xyz/rustc_codegen_gcc-progress-report-34#state_of_rustc_codegen_gcc) for larger initiatives 3. Consider improving documentation or investigating [failing tests](https://github.com/rust-lang/rustc_codegen_gcc/tree/master/tests) (except `failing-ui-tests12.txt`) From 8b15bfbe8c43b6143acb67554573fc8637ce652a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 30 Apr 2025 13:13:02 +0200 Subject: [PATCH 034/245] Clean up docs --- CONTRIBUTING.md | 12 ++++++++++-- Readme.md | 10 ++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f0fd5c630528..8e313ab08b59 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ Once you've completed the setup process outlined in the README, you can proceed - Matrix: Join our [Matrix channel](https://matrix.to/#/#rustc_codegen_gcc:matrix.org) - IRC: Join us on [IRC](https://web.libera.chat/#rustc_codegen_gcc) -- GitHub Issues: For bug reports and feature discussions +- [GitHub Issues](https://github.com/rust-lang/rustc_codegen_gcc/issues): For bug reports and feature discussions We encourage new contributors to join our communication channels and introduce themselves. Feel free to ask questions about where to start or discuss potential contributions. @@ -28,12 +28,15 @@ We encourage new contributors to join our communication channels and introduce t ### Common Development Tasks #### Running Specific Tests + To run specific tests, use appropriate flags such as: + - `./y.sh test --test-libcore` - `./y.sh test --std-tests` - `cargo test -- ` Additionally, you can run the tests of `libgccjit`: + ```bash # libgccjit tests cd gcc-build/gcc @@ -43,8 +46,10 @@ make check-jit RUNTESTFLAGS="-v -v -v jit.exp=jit.dg/test-asm.cc" ``` #### Debugging Tools + The project provides several environment variables for debugging: -- `CG_GCCJIT_DUMP_GIMPLE`: Dump the GIMPLE IR + +- `CG_GCCJIT_DUMP_GIMPLE`: Dumps the GIMPLE IR - `CG_RUSTFLAGS`: Additional Rust flags - `CG_GCCJIT_DUMP_MODULE`: Dumps a specific module - `CG_GCCJIT_DUMP_TO_FILE`: Creates C-like representation @@ -54,11 +59,13 @@ Full list of debugging options can be found in the [README](Readme.md#env-vars). ## Making Contributions ### Finding Issues to Work On + 1. Look for issues labeled with [`good first issue`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue%20state%3Aopen%20label%3A"good%20first%20issue") or [`help wanted`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue%20state%3Aopen%20label%3A"help%20wanted") 2. Check the [progress report](https://blog.antoyo.xyz/rustc_codegen_gcc-progress-report-34#state_of_rustc_codegen_gcc) for larger initiatives 3. Consider improving documentation or investigating [failing tests](https://github.com/rust-lang/rustc_codegen_gcc/tree/master/tests) (except `failing-ui-tests12.txt`) ### Pull Request Process + 1. Fork the repository and create a new branch 2. Make your changes with clear commit messages 3. Add tests for new functionality @@ -66,6 +73,7 @@ Full list of debugging options can be found in the [README](Readme.md#env-vars). 5. Submit a PR with a description of your changes ### Code Style Guidelines + - Follow Rust standard coding conventions - Ensure your code passes `rustfmt` and `clippy` - Add comments explaining complex logic, especially in GCC interface code diff --git a/Readme.md b/Readme.md index bacad151c965..859bb1568f4e 100644 --- a/Readme.md +++ b/Readme.md @@ -19,11 +19,13 @@ You need to use my [fork of gcc](https://github.com/rust-lang/gcc) which already The default configuration (see below in the [Quick start](#quick-start) section) will download a `libgccjit` built in the CI that already contains these patches, so you don't need to build this fork yourself if you use the default configuration. ### Dependencies - - rustup: follow instructions on the [official website](https://rustup.rs) - - consider to install DejaGnu which is necessary for running the libgccjit test suite. [website](https://www.gnu.org/software/dejagnu/#downloading) - - additional packages: `flex`, `libmpfr-dev`, `libgmp-dev`, `libmpc3`, `libmpc-dev` + +- rustup: follow instructions on the [official website](https://rustup.rs) +- consider to install DejaGnu which is necessary for running the libgccjit test suite. [website](https://www.gnu.org/software/dejagnu/#downloading) +- additional packages: `flex`, `libmpfr-dev`, `libgmp-dev`, `libmpc3`, `libmpc-dev` ### Quick start + 1. Clone and configure the repository: ```bash git clone https://github.com/rust-lang/rustc_codegen_gcc @@ -157,7 +159,7 @@ You can do the same manually (although we don't recommend it): $ LIBRARY_PATH="[gcc-path value]" LD_LIBRARY_PATH="[gcc-path value]" rustc +$(cat $CG_GCCJIT_DIR/rust-toolchain | grep 'channel' | cut -d '=' -f 2 | sed 's/"//g' | sed 's/ //g') -Cpanic=abort -Zcodegen-backend=$CG_GCCJIT_DIR/target/release/librustc_codegen_gcc.so --sysroot $CG_GCCJIT_DIR/build_sysroot/sysroot my_crate.rs ``` -## Env vars +## Environment variables * _**CG_GCCJIT_DUMP_ALL_MODULES**_: Enables dumping of all compilation modules. When set to "1", a dump is created for each module during compilation and stored in `/tmp/reproducers/`. * _**CG_GCCJIT_DUMP_MODULE**_: Enables dumping of a specific module. When set with the module name, e.g., `CG_GCCJIT_DUMP_MODULE=module_name`, a dump of that specific module is created in `/tmp/reproducers/`. From e83a0a4bbe9cb89bb24f76b4422a3a9a23e45dc5 Mon Sep 17 00:00:00 2001 From: Mathis B Date: Wed, 30 Apr 2025 14:42:15 +0200 Subject: [PATCH 035/245] Stabilize `#![feature(non_null_from_ref)]` --- library/core/src/lib.rs | 1 - library/core/src/ptr/non_null.rs | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 64a7ec8906b6..4e8d1dc1152e 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -111,7 +111,6 @@ #![feature(is_ascii_octdigit)] #![feature(lazy_get)] #![feature(link_cfg)] -#![feature(non_null_from_ref)] #![feature(offset_of_enum)] #![feature(panic_internals)] #![feature(ptr_alignment_type)] diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 0864cc457b65..be23dc53880b 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -262,7 +262,8 @@ impl NonNull { } /// Converts a reference to a `NonNull` pointer. - #[unstable(feature = "non_null_from_ref", issue = "130823")] + #[stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn from_ref(r: &T) -> Self { // SAFETY: A reference cannot be null. @@ -270,7 +271,8 @@ impl NonNull { } /// Converts a mutable reference to a `NonNull` pointer. - #[unstable(feature = "non_null_from_ref", issue = "130823")] + #[stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn from_mut(r: &mut T) -> Self { // SAFETY: A mutable reference cannot be null. From 6fad1bac72dafc414bd7083f83dccb50a35d08d8 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Wed, 30 Apr 2025 11:11:17 -0400 Subject: [PATCH 036/245] Support more calling convention attributes --- Cargo.lock | 8 +++--- Cargo.toml | 2 +- libgccjit.version | 2 +- src/abi.rs | 65 ++++++++++++++++++++++++++++++----------------- 4 files changed, 48 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 832603aa7925..967a51a1cc64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,18 +56,18 @@ dependencies = [ [[package]] name = "gccjit" -version = "2.5.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2895ddec764de7ac76fe6c056050c4801a80109c066f177a00a9cc8dee02b29b" +checksum = "ae99a89184220d967dd300139f2d2ae7d52c1a69d632b24aacc57c54625254ce" dependencies = [ "gccjit_sys", ] [[package]] name = "gccjit_sys" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac133db68db8a6a8b2c51ef4b18d8ea16682d5814c4641272fe37bbbc223d5f3" +checksum = "24edb7bfe2b7b27c6d09ed23eebfcab0b359c8fe978433f902943e6f127a0f1b" dependencies = [ "libc", ] diff --git a/Cargo.toml b/Cargo.toml index b50f2a626d57..717eaf9e0580 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ master = ["gccjit/master"] default = ["master"] [dependencies] -gccjit = "2.5" +gccjit = "2.7" #gccjit = { git = "https://github.com/rust-lang/gccjit.rs" } # Local copy. diff --git a/libgccjit.version b/libgccjit.version index 125b04004b07..d06646dacc34 100644 --- a/libgccjit.version +++ b/libgccjit.version @@ -1 +1 @@ -0ea98a1365b81f7488073512c850e8ee951a4afd +8b194529188f9d3a98cc211caa805a5355bfa8f0 diff --git a/src/abi.rs b/src/abi.rs index a96b18e01c08..d882d3eecf49 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -9,9 +9,9 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] use rustc_session::config; -#[cfg(feature = "master")] -use rustc_target::callconv::Conv; use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; +#[cfg(feature = "master")] +use rustc_target::callconv::{Conv, RiscvInterruptKind}; use crate::builder::Builder; use crate::context::CodegenCx; @@ -240,38 +240,57 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { #[cfg(feature = "master")] pub fn conv_to_fn_attribute<'gcc>(conv: Conv, arch: &str) -> Option> { - // TODO: handle the calling conventions returning None. let attribute = match conv { - Conv::C - | Conv::Rust - | Conv::CCmseNonSecureCall - | Conv::CCmseNonSecureEntry - | Conv::RiscvInterrupt { .. } => return None, - Conv::Cold => return None, + Conv::C | Conv::Rust => return None, + Conv::CCmseNonSecureCall => { + if arch == "arm" { + FnAttribute::ArmCmseNonsecureCall + } else { + return None; + } + } + Conv::CCmseNonSecureEntry => { + if arch == "arm" { + FnAttribute::ArmCmseNonsecureEntry + } else { + return None; + } + } + Conv::Cold => FnAttribute::Cold, + // NOTE: the preserve attributes are not yet implemented in GCC: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899 Conv::PreserveMost => return None, Conv::PreserveAll => return None, Conv::GpuKernel => { - // TODO(antoyo): remove clippy allow attribute when this is implemented. - #[allow(clippy::if_same_then_else)] if arch == "amdgpu" { - return None; + FnAttribute::GcnAmdGpuHsaKernel } else if arch == "nvptx64" { - return None; + FnAttribute::NvptxKernel } else { panic!("Architecture {} does not support GpuKernel calling convention", arch); } } - Conv::AvrInterrupt => return None, - Conv::AvrNonBlockingInterrupt => return None, - Conv::ArmAapcs => return None, - Conv::Msp430Intr => return None, - Conv::X86Fastcall => return None, - Conv::X86Intr => return None, - Conv::X86Stdcall => return None, - Conv::X86ThisCall => return None, + // TODO(antoyo): check if those AVR attributes are mapped correctly. + Conv::AvrInterrupt => FnAttribute::AvrSignal, + Conv::AvrNonBlockingInterrupt => FnAttribute::AvrInterrupt, + Conv::ArmAapcs => FnAttribute::ArmPcs("aapcs"), + Conv::Msp430Intr => FnAttribute::Msp430Interrupt, + Conv::RiscvInterrupt { kind } => { + let kind = match kind { + RiscvInterruptKind::Machine => "machine", + RiscvInterruptKind::Supervisor => "supervisor", + }; + FnAttribute::RiscvInterrupt(kind) + } + Conv::X86Fastcall => FnAttribute::X86FastCall, + Conv::X86Intr => FnAttribute::X86Interrupt, + Conv::X86Stdcall => FnAttribute::X86Stdcall, + Conv::X86ThisCall => FnAttribute::X86ThisCall, + // NOTE: the vectorcall calling convention is not yet implemented in GCC: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485 Conv::X86VectorCall => return None, - Conv::X86_64SysV => FnAttribute::SysvAbi, - Conv::X86_64Win64 => FnAttribute::MsAbi, + Conv::X86_64SysV => FnAttribute::X86SysvAbi, + Conv::X86_64Win64 => FnAttribute::X86MsAbi, }; Some(attribute) } From 36c3617bb13b3608170a617eb173e4df96ad3e52 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 30 Apr 2025 17:56:14 -0400 Subject: [PATCH 037/245] Remove `is_normalizable`. `layout_of` no longer contains a `delay_bug`. --- clippy_lints/src/casts/manual_dangling_ptr.rs | 15 ++--- clippy_lints/src/transmute/eager_transmute.rs | 3 - clippy_lints/src/zero_sized_map_values.rs | 4 +- clippy_utils/src/ty/mod.rs | 55 +------------------ 4 files changed, 8 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index 8ace27eca895..6dbaa5cb5079 100644 --- a/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_normalizable; use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -71,12 +70,10 @@ fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned, to: &Ty<'_>) return false; } let to_mid_ty = cx.typeck_results().node_type(to.hir_id); - is_normalizable(cx, cx.param_env, to_mid_ty) - && cx - .tcx - .layout_of(cx.typing_env().as_query_input(to_mid_ty)) - .is_ok_and(|layout| { - let align = u128::from(layout.align.abi.bytes()); - u128::from(val) <= align - }) + cx.tcx + .layout_of(cx.typing_env().as_query_input(to_mid_ty)) + .is_ok_and(|layout| { + let align = u128::from(layout.align.abi.bytes()); + u128::from(val) <= align + }) } diff --git a/clippy_lints/src/transmute/eager_transmute.rs b/clippy_lints/src/transmute/eager_transmute.rs index 1ccab62708b1..535c044f49e6 100644 --- a/clippy_lints/src/transmute/eager_transmute.rs +++ b/clippy_lints/src/transmute/eager_transmute.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_normalizable; use clippy_utils::{eq_expr_value, path_to_local, sym}; use rustc_abi::WrappingRange; use rustc_errors::Applicability; @@ -84,8 +83,6 @@ pub(super) fn check<'tcx>( && path.ident.name == sym::then_some && is_local_with_projections(transmutable) && binops_with_local(cx, transmutable, receiver) - && is_normalizable(cx, cx.param_env, from_ty) - && is_normalizable(cx, cx.param_env, to_ty) // we only want to lint if the target type has a niche that is larger than the one of the source type // e.g. `u8` to `NonZero` should lint, but `NonZero` to `u8` should not && let Ok(from_layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(from_ty)) diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index a97643e0eaca..24b1381ba458 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item, ty_from_hir_ty}; +use clippy_utils::ty::{is_type_diagnostic_item, ty_from_hir_ty}; use rustc_hir::{self as hir, AmbigArg, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf as _; @@ -54,8 +54,6 @@ impl LateLintPass<'_> for ZeroSizedMapValues { // Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of // https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968 && !ty.has_escaping_bound_vars() - // Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`. - && is_normalizable(cx, cx.param_env, ty) && let Ok(layout) = cx.layout_of(ty) && layout.is_zst() { diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 8db9cd593b33..d30acfb75f3e 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -20,7 +20,7 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; @@ -359,56 +359,6 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } -// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize -// this function can be removed once the `normalize` method does not panic when normalization does -// not succeed -/// Checks if `Ty` is normalizable. This function is useful -/// to avoid crashes on `layout_of`. -pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { - is_normalizable_helper(cx, param_env, ty, 0, &mut FxHashMap::default()) -} - -fn is_normalizable_helper<'tcx>( - cx: &LateContext<'tcx>, - param_env: ParamEnv<'tcx>, - ty: Ty<'tcx>, - depth: usize, - cache: &mut FxHashMap, bool>, -) -> bool { - if let Some(&cached_result) = cache.get(&ty) { - return cached_result; - } - if !cx.tcx.recursion_limit().value_within_limit(depth) { - return false; - } - // Prevent recursive loops by answering `true` to recursive requests with the same - // type. This will be adjusted when the outermost call analyzes all the type - // components. - cache.insert(ty, true); - let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); - let cause = ObligationCause::dummy(); - let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() { - match ty.kind() { - ty::Adt(def, args) => def.variants().iter().all(|variant| { - variant - .fields - .iter() - .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), depth + 1, cache)) - }), - _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { - GenericArgKind::Type(inner_ty) if inner_ty != ty => { - is_normalizable_helper(cx, param_env, inner_ty, depth + 1, cache) - }, - _ => true, // if inner_ty == ty, we've already checked it - }), - } - } else { - false - }; - cache.insert(ty, result); - result -} - /// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any /// integer or floating-point number type). /// @@ -993,9 +943,6 @@ pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option< /// account the layout of type parameters. pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { use rustc_middle::ty::layout::LayoutOf; - if !is_normalizable(cx, cx.param_env, ty) { - return 0; - } match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) { (Ok(size), _) => size, (Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(), From 782c6065c40369db84a8e05ad3c8299b30a06b0a Mon Sep 17 00:00:00 2001 From: yanglsh Date: Thu, 1 May 2025 14:50:23 +0800 Subject: [PATCH 038/245] fix: `unused_async` FP on default impl --- clippy_lints/src/unused_async.rs | 18 ++++++++++++++++-- tests/ui/unused_async.rs | 8 ++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 1c1c841e9641..8ceaa3dc58ec 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_def_id_trait_method; use rustc_hir::def::DefKind; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Node, YieldSource}; +use rustc_hir::{Body, Defaultness, Expr, ExprKind, FnDecl, HirId, Node, TraitItem, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; @@ -116,7 +116,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { span: Span, def_id: LocalDefId, ) { - if !span.from_expansion() && fn_kind.asyncness().is_async() && !is_def_id_trait_method(cx, def_id) { + if !span.from_expansion() + && fn_kind.asyncness().is_async() + && !is_def_id_trait_method(cx, def_id) + && !is_default_trait_impl(cx, def_id) + { let mut visitor = AsyncFnVisitor { cx, found_await: false, @@ -189,3 +193,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { } } } + +fn is_default_trait_impl(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + matches!( + cx.tcx.hir_node_by_def_id(def_id), + Node::TraitItem(TraitItem { + defaultness: Defaultness::Default { .. }, + .. + }) + ) +} diff --git a/tests/ui/unused_async.rs b/tests/ui/unused_async.rs index 5aaf7b9f5b59..433459253dd7 100644 --- a/tests/ui/unused_async.rs +++ b/tests/ui/unused_async.rs @@ -119,3 +119,11 @@ fn main() { foo(); bar(); } + +mod issue14704 { + use std::sync::Arc; + + trait Action { + async fn cancel(self: Arc) {} + } +} From dd5113daab0758bdd150bdbf6f16d6f2f86c52cd Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 1 May 2025 10:22:55 +0200 Subject: [PATCH 039/245] Merge commit '03a5b6b976ac121f4233775c49a4bce026065b47' into clippy-subtree-update --- clippy_lints/src/assigning_clones.rs | 11 +- .../attrs/blanket_clippy_restriction_lints.rs | 8 +- clippy_lints/src/attrs/deprecated_cfg_attr.rs | 2 +- clippy_lints/src/attrs/deprecated_semver.rs | 3 +- .../src/casts/cast_abs_to_unsigned.rs | 3 +- .../src/casts/cast_possible_truncation.rs | 4 +- clippy_lints/src/casts/cast_ptr_alignment.rs | 5 +- clippy_lints/src/casts/unnecessary_cast.rs | 55 +++++-- clippy_lints/src/crate_in_macro_def.rs | 5 +- clippy_lints/src/equatable_if_let.rs | 34 +++- clippy_lints/src/floating_point_arithmetic.rs | 4 +- clippy_lints/src/from_raw_with_void_ptr.rs | 5 +- clippy_lints/src/from_str_radix_10.rs | 5 +- clippy_lints/src/implicit_hasher.rs | 21 ++- clippy_lints/src/infinite_iter.rs | 9 +- clippy_lints/src/iter_without_into_iter.rs | 5 +- clippy_lints/src/len_zero.rs | 8 +- clippy_lints/src/lib.rs | 1 + clippy_lints/src/loops/same_item_push.rs | 5 +- clippy_lints/src/manual_div_ceil.rs | 6 +- clippy_lints/src/manual_hash_one.rs | 7 +- clippy_lints/src/manual_is_ascii_check.rs | 6 +- clippy_lints/src/manual_option_as_slice.rs | 6 +- .../src/methods/double_ended_iterator_last.rs | 6 +- clippy_lints/src/methods/filter_map.rs | 4 +- clippy_lints/src/methods/mod.rs | 5 +- .../methods/needless_character_iteration.rs | 6 +- clippy_lints/src/methods/needless_collect.rs | 6 +- .../src/methods/read_line_without_trim.rs | 7 +- clippy_lints/src/methods/return_and_then.rs | 11 +- clippy_lints/src/methods/str_split.rs | 3 +- .../src/methods/unnecessary_filter_map.rs | 5 +- .../src/methods/unnecessary_to_owned.rs | 12 +- clippy_lints/src/minmax.rs | 13 +- clippy_lints/src/missing_fields_in_debug.rs | 6 +- .../src/mixed_read_write_in_expression.rs | 4 +- clippy_lints/src/needless_for_each.rs | 6 +- clippy_lints/src/needless_question_mark.rs | 70 ++++---- .../src/non_octal_unix_permissions.rs | 6 +- clippy_lints/src/non_zero_suggestions.rs | 4 +- clippy_lints/src/operators/float_cmp.rs | 6 +- .../src/permissions_set_readonly_false.rs | 4 +- clippy_lints/src/ptr.rs | 22 ++- clippy_lints/src/ptr_offset_with_cast.rs | 4 +- clippy_lints/src/question_mark.rs | 15 +- clippy_lints/src/question_mark_used.rs | 18 +- .../src/slow_vector_initialization.rs | 9 +- clippy_lints/src/strings.rs | 6 +- clippy_lints/src/to_digit_is_some.rs | 6 +- clippy_lints/src/transmute/eager_transmute.rs | 6 +- clippy_lints/src/types/vec_box.rs | 105 ++++++------ clippy_lints/src/uninit_vec.rs | 8 +- clippy_lints/src/unused_self.rs | 11 +- clippy_lints/src/unused_unit.rs | 155 +++++++++++------- clippy_lints/src/unwrap.rs | 4 +- clippy_lints/src/write.rs | 12 +- clippy_lints/src/zombie_processes.rs | 5 +- clippy_lints_internal/src/lib.rs | 8 +- .../src/{interning_literals.rs => symbols.rs} | 99 +++++++++-- clippy_utils/README.md | 2 +- clippy_utils/src/higher.rs | 6 +- clippy_utils/src/hir_utils.rs | 4 +- clippy_utils/src/lib.rs | 9 +- clippy_utils/src/sym.rs | 44 +++++ rust-toolchain.toml | 2 +- tests/ui-internal/symbol_as_str.fixed | 21 +++ tests/ui-internal/symbol_as_str.rs | 21 +++ tests/ui-internal/symbol_as_str.stderr | 76 +++++++++ tests/ui-internal/symbol_as_str_unfixable.rs | 15 ++ .../symbol_as_str_unfixable.stderr | 40 +++++ tests/ui/blocks_in_conditions.fixed | 1 - tests/ui/blocks_in_conditions.rs | 1 - tests/ui/blocks_in_conditions.stderr | 6 +- tests/ui/equatable_if_let.fixed | 36 ++++ tests/ui/equatable_if_let.rs | 36 ++++ tests/ui/equatable_if_let.stderr | 20 ++- tests/ui/manual_div_ceil.fixed | 23 +++ tests/ui/manual_div_ceil.rs | 23 +++ tests/ui/manual_div_ceil.stderr | 62 +++++-- tests/ui/needless_if.fixed | 4 +- tests/ui/needless_if.rs | 4 +- tests/ui/needless_if.stderr | 8 +- tests/ui/needless_late_init.fixed | 4 +- tests/ui/needless_late_init.rs | 4 +- tests/ui/needless_late_init.stderr | 2 +- tests/ui/needless_question_mark.stderr | 148 +++++++++++++---- tests/ui/ptr_eq.fixed | 28 ++-- tests/ui/ptr_eq.rs | 18 +- tests/ui/ptr_eq.stderr | 44 ++--- tests/ui/ptr_eq_no_std.fixed | 20 ++- tests/ui/ptr_eq_no_std.rs | 12 +- tests/ui/ptr_eq_no_std.stderr | 26 +-- tests/ui/question_mark.fixed | 5 + tests/ui/question_mark.rs | 5 + tests/ui/question_mark.stderr | 20 +-- tests/ui/question_mark_used.stderr | 2 +- tests/ui/return_and_then.stderr | 14 +- tests/ui/unnecessary_cast.fixed | 16 +- tests/ui/unnecessary_cast.rs | 14 ++ tests/ui/unnecessary_cast.stderr | 22 ++- tests/ui/unused_unit.edition2021.fixed | 146 +++++++++++++++++ tests/ui/unused_unit.edition2021.stderr | 128 +++++++++++++++ tests/ui/unused_unit.edition2024.fixed | 146 +++++++++++++++++ tests/ui/unused_unit.edition2024.stderr | 122 ++++++++++++++ tests/ui/unused_unit.fixed | 21 +++ tests/ui/unused_unit.rs | 26 ++- tests/ui/unused_unit.stderr | 32 ++-- tests/ui/zombie_processes.rs | 22 +++ util/etc/pre-commit.sh | 1 - 109 files changed, 1822 insertions(+), 585 deletions(-) rename clippy_lints_internal/src/{interning_literals.rs => symbols.rs} (52%) create mode 100644 tests/ui-internal/symbol_as_str.fixed create mode 100644 tests/ui-internal/symbol_as_str.rs create mode 100644 tests/ui-internal/symbol_as_str.stderr create mode 100644 tests/ui-internal/symbol_as_str_unfixable.rs create mode 100644 tests/ui-internal/symbol_as_str_unfixable.stderr create mode 100644 tests/ui/unused_unit.edition2021.fixed create mode 100644 tests/ui/unused_unit.edition2021.stderr create mode 100644 tests/ui/unused_unit.edition2024.fixed create mode 100644 tests/ui/unused_unit.edition2024.stderr diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 9acff676d4f6..8b8b42bbf722 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -3,14 +3,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local}; +use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir; use rustc_middle::ty::{self, Instance, Mutability}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::sym; use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { @@ -86,9 +85,9 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { && ctxt.is_root() && let which_trait = match fn_name { sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone, - _ if fn_name.as_str() == "to_owned" - && is_diag_trait_item(cx, fn_id, sym::ToOwned) - && self.msrv.meets(cx, msrvs::CLONE_INTO) => + sym::to_owned + if is_diag_trait_item(cx, fn_id, sym::ToOwned) + && self.msrv.meets(cx, msrvs::CLONE_INTO) => { CloneTrait::ToOwned }, @@ -112,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { && resolved_assoc_items.in_definition_order().any(|assoc| match which_trait { CloneTrait::Clone => assoc.name() == sym::clone_from, - CloneTrait::ToOwned => assoc.name().as_str() == "clone_into", + CloneTrait::ToOwned => assoc.name() == sym::clone_into, } ) && !clone_source_borrows_from_dest(cx, lhs, rhs.span) diff --git a/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs index 457692ed5dc5..4d64eec25d27 100644 --- a/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs +++ b/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -1,17 +1,15 @@ use super::BLANKET_CLIPPY_RESTRICTION_LINTS; use super::utils::extract_clippy_lint; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::sym; use rustc_ast::MetaItemInner; use rustc_lint::{EarlyContext, Level, LintContext}; +use rustc_span::DUMMY_SP; use rustc_span::symbol::Symbol; -use rustc_span::{DUMMY_SP, sym}; pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) { for lint in items { - if let Some(lint_name) = extract_clippy_lint(lint) - && lint_name.as_str() == "restriction" - && name != sym::allow - { + if name != sym::allow && extract_clippy_lint(lint) == Some(sym::restriction) { span_lint_and_help( cx, BLANKET_CLIPPY_RESTRICTION_LINTS, diff --git a/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/clippy_lints/src/attrs/deprecated_cfg_attr.rs index 7fab97d3ea14..0edb50be8c77 100644 --- a/clippy_lints/src/attrs/deprecated_cfg_attr.rs +++ b/clippy_lints/src/attrs/deprecated_cfg_attr.rs @@ -73,7 +73,7 @@ fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::Met } fn check_cargo_clippy_attr(cx: &EarlyContext<'_>, item: &rustc_ast::MetaItem) { - if item.has_name(sym::feature) && item.value_str().is_some_and(|v| v.as_str() == "cargo-clippy") { + if item.has_name(sym::feature) && item.value_str() == Some(sym::cargo_clippy) { span_lint_and_sugg( cx, DEPRECATED_CLIPPY_CFG_ATTR, diff --git a/clippy_lints/src/attrs/deprecated_semver.rs b/clippy_lints/src/attrs/deprecated_semver.rs index 50943b36809d..bd6459d6f9db 100644 --- a/clippy_lints/src/attrs/deprecated_semver.rs +++ b/clippy_lints/src/attrs/deprecated_semver.rs @@ -1,5 +1,6 @@ use super::DEPRECATED_SEMVER; use clippy_utils::diagnostics::span_lint; +use clippy_utils::sym; use rustc_ast::{LitKind, MetaItemLit}; use rustc_lint::EarlyContext; use rustc_span::Span; @@ -7,7 +8,7 @@ use semver::Version; pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) { if let LitKind::Str(is, _) = lit.kind - && (is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok()) + && (is == sym::TBD || Version::parse(is.as_str()).is_ok()) { return; } diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 164d3540253a..ba31a51f738a 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -19,7 +20,7 @@ pub(super) fn check( if let ty::Int(from) = cast_from.kind() && let ty::Uint(to) = cast_to.kind() && let ExprKind::MethodCall(method_path, receiver, [], _) = cast_expr.kind - && method_path.ident.name.as_str() == "abs" + && method_path.ident.name == sym::abs && msrv.meets(cx, msrvs::UNSIGNED_ABS) { let span = if from.bit_width() == to.bit_width() { diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 8742f5f1a0e0..e92879b853d7 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,9 +1,9 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::expr_or_init; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; +use clippy_utils::{expr_or_init, sym}; use rustc_abi::IntegerType; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; @@ -73,7 +73,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b nbits }, ExprKind::MethodCall(method, _value, [], _) => { - if method.ident.name.as_str() == "signum" { + if method.ident.name == sym::signum { 0 // do not lint if cast comes from a `signum` function } else { nbits diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 3fca0f897077..01020f3eee21 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_c_void; -use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant}; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, sym}; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; use super::CAST_PTR_ALIGNMENT; @@ -20,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind - && method_path.ident.name.as_str() == "cast" + && method_path.ident.name == sym::cast && let Some(generic_args) = method_path.args && let [GenericArg::Type(cast_to)] = generic_args.args // There probably is no obvious reason to do this, just to be consistent with `as` cases. diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index ae994e94a32b..8e8c55cf3832 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -8,7 +8,9 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; +use rustc_span::{Symbol, sym}; use std::ops::ControlFlow; use super::UNNECESSARY_CAST; @@ -142,6 +144,33 @@ pub(super) fn check<'tcx>( } if cast_from.kind() == cast_to.kind() && !expr.span.in_external_macro(cx.sess().source_map()) { + enum MaybeParenOrBlock { + Paren, + Block, + Nothing, + } + + fn is_borrow_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::AddrOf(..)) + || cx + .typeck_results() + .expr_adjustments(expr) + .first() + .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_))) + } + + fn is_in_allowed_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + const ALLOWED_MACROS: &[Symbol] = &[ + sym::format_args_macro, + sym::assert_eq_macro, + sym::debug_assert_eq_macro, + sym::assert_ne_macro, + sym::debug_assert_ne_macro, + ]; + matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if + cx.tcx.get_diagnostic_name(def_id).is_some_and(|sym| ALLOWED_MACROS.contains(&sym))) + } + if let Some(id) = path_to_local(cast_expr) && !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span) { @@ -150,15 +179,15 @@ pub(super) fn check<'tcx>( return false; } - // If the whole cast expression is a unary expression (`(*x as T)`) or an addressof - // expression (`(&x as T)`), then not surrounding the suggestion into a block risks us - // changing the precedence of operators if the cast expression is followed by an operation - // with higher precedence than the unary operator (`(*x as T).foo()` would become - // `*x.foo()`, which changes what the `*` applies on). - // The same is true if the expression encompassing the cast expression is a unary - // expression or an addressof expression. - let needs_block = matches!(cast_expr.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)) - || get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))); + // Changing `&(x as i32)` to `&x` would change the meaning of the code because the previous creates + // a reference to the temporary while the latter creates a reference to the original value. + let surrounding = match cx.tcx.parent_hir_node(expr.hir_id) { + Node::Expr(parent) if is_borrow_expr(cx, parent) && !is_in_allowed_macro(cx, parent) => { + MaybeParenOrBlock::Block + }, + Node::Expr(parent) if cast_expr.precedence() < parent.precedence() => MaybeParenOrBlock::Paren, + _ => MaybeParenOrBlock::Nothing, + }; span_lint_and_sugg( cx, @@ -166,10 +195,10 @@ pub(super) fn check<'tcx>( expr.span, format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), "try", - if needs_block { - format!("{{ {cast_str} }}") - } else { - cast_str + match surrounding { + MaybeParenOrBlock::Paren => format!("({cast_str})"), + MaybeParenOrBlock::Block => format!("{{ {cast_str} }}"), + MaybeParenOrBlock::Nothing => cast_str, }, Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index c2aac7ca090b..19f62e8bf79c 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -5,8 +5,8 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::{Span, kw}; declare_clippy_lint! { /// ### What it does @@ -105,12 +105,11 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { fn is_crate_keyword(tt: &TokenTree) -> Option { if let TokenTree::Token( Token { - kind: TokenKind::Ident(symbol, _), + kind: TokenKind::Ident(kw::Crate, _), span, }, _, ) = tt - && symbol.as_str() == "crate" { Some(*span) } else { diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index 3afb687040f4..72f5eaf8a4bc 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -68,6 +68,38 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T } } +/// Check if the pattern has any type mismatch that would prevent it from being used in an equality +/// check. This can happen if the expr has a reference type and the corresponding pattern is a +/// literal. +fn contains_type_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + let mut result = false; + pat.walk(|p| { + if result { + return false; + } + + if p.span.in_external_macro(cx.sess().source_map()) { + return true; + } + + let adjust_pat = match p.kind { + PatKind::Or([p, ..]) => p, + _ => p, + }; + + if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) + && adjustments.first().is_some_and(|first| first.source.is_ref()) + { + result = true; + return false; + } + + true + }); + + result +} + impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Let(let_expr) = expr.kind @@ -78,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); let mut applicability = Applicability::MachineApplicable; - if is_structural_partial_eq(cx, exp_ty, pat_ty) { + if is_structural_partial_eq(cx, exp_ty, pat_ty) && !contains_type_mismatch(cx, let_expr.pat) { let pat_str = match let_expr.pat.kind { PatKind::Struct(..) => format!( "({})", diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index d5f0659f8427..553a00ed868d 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, - numeric_literal, peel_blocks, sugg, + numeric_literal, peel_blocks, sugg, sym, }; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -435,7 +435,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = expr.kind && let ExprKind::MethodCall(path, self_arg, [], _) = &lhs.kind - && path.ident.name.as_str() == "exp" + && path.ident.name == sym::exp && cx.typeck_results().expr_ty(lhs).is_floating_point() && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs) && (F32(1.0) == value || F64(1.0) == value) diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs index c8828c936157..5e2e2c9dbf72 100644 --- a/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_def_id; use clippy_utils::ty::is_c_void; +use clippy_utils::{path_def_id, sym}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -41,7 +40,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(box_from_raw, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind - && seg.ident.name.as_str() == "from_raw" + && seg.ident.name == sym::from_raw && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) && let arg_kind = cx.typeck_results().expr_ty(arg).kind() && let ty::RawPtr(ty, _) = arg_kind diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 25b087e8484f..b816963cc825 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{is_in_const_context, is_integer_literal}; +use clippy_utils::{is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -53,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { // check if the second part of the path indeed calls the associated // function `from_str_radix` - && pathseg.ident.name.as_str() == "from_str_radix" + && pathseg.ident.name == sym::from_str_radix // check if the first part of the path is some integer primitive && let TyKind::Path(ty_qpath) = &ty.kind diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index d2545e57652a..4c17834c3adf 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -10,10 +10,10 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; use rustc_span::Span; -use rustc_span::symbol::sym; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; declare_clippy_lint! { @@ -326,6 +326,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { if let ExprKind::Call(fun, args) = e.kind && let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind + && matches!(method.ident.name, sym::new | sym::with_capacity) && let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind && let Some(ty_did) = ty_path.res.opt_def_id() { @@ -333,10 +334,11 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { return; } - if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) { - if method.ident.name == sym::new { + match (self.cx.tcx.get_diagnostic_name(ty_did), method.ident.name) { + (Some(sym::HashMap), sym::new) => { self.suggestions.insert(e.span, "HashMap::default()".to_string()); - } else if method.ident.name.as_str() == "with_capacity" { + }, + (Some(sym::HashMap), sym::with_capacity) => { self.suggestions.insert( e.span, format!( @@ -344,11 +346,11 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { snippet(self.cx, args[0].span, "capacity"), ), ); - } - } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) { - if method.ident.name == sym::new { + }, + (Some(sym::HashSet), sym::new) => { self.suggestions.insert(e.span, "HashSet::default()".to_string()); - } else if method.ident.name.as_str() == "with_capacity" { + }, + (Some(sym::HashSet), sym::with_capacity) => { self.suggestions.insert( e.span, format!( @@ -356,7 +358,8 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { snippet(self.cx, args[0].span, "capacity"), ), ); - } + }, + _ => {}, } } diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 427a1f825555..c4e10837bf19 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::higher; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; +use clippy_utils::{higher, sym}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -156,7 +155,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { .and(cap); } } - if method.ident.name.as_str() == "flat_map" + if method.ident.name == sym::flat_map && args.len() == 1 && let ExprKind::Closure(&Closure { body, .. }) = args[0].kind { @@ -224,7 +223,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { return MaybeInfinite.and(is_infinite(cx, receiver)); } } - if method.ident.name.as_str() == "last" && args.is_empty() { + if method.ident.name == sym::last && args.is_empty() { let not_double_ended = cx .tcx .get_diagnostic_item(sym::DoubleEndedIterator) @@ -232,7 +231,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { if not_double_ended { return is_infinite(cx, receiver); } - } else if method.ident.name.as_str() == "collect" { + } else if method.ident.name == sym::collect { let ty = cx.typeck_results().expr_ty(expr); if matches!( get_type_diagnostic_name(cx, ty), diff --git a/clippy_lints/src/iter_without_into_iter.rs b/clippy_lints/src/iter_without_into_iter.rs index 173232c511a5..900b20aa9cfb 100644 --- a/clippy_lints/src/iter_without_into_iter.rs +++ b/clippy_lints/src/iter_without_into_iter.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_parent_as_impl; use clippy_utils::source::snippet; use clippy_utils::ty::{deref_chain, get_adt_inherent_method, implements_trait, make_normalized_projection}; +use clippy_utils::{get_parent_as_impl, sym}; use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -141,7 +140,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter { ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some() }) && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| { - if item.ident.name.as_str() == "IntoIter" { + if item.ident.name == sym::IntoIter { Some(cx.tcx.hir_impl_item(item.id).expect_type().span) } else { None diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 8c71d34c95f6..aded31971cec 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -2,7 +2,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; -use clippy_utils::{fulfill_or_allowed, get_item_name, get_parent_as_impl, is_trait_method, peel_ref_operators, sym}; +use clippy_utils::{ + fulfill_or_allowed, get_parent_as_impl, is_trait_method, parent_item_name, peel_ref_operators, sym, +}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -533,9 +535,7 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method - if let Some(name) = get_item_name(cx, method) - && name.as_str() == "is_empty" - { + if parent_item_name(cx, method) == Some(sym::is_empty) { return; } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5fa8f6f4bf3d..bc7fc60827a0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -729,6 +729,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints)); store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall)); store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); + store.register_late_pass(|_| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(returns::Return)); store.register_late_pass(move |tcx| Box::new(collapsible_if::CollapsibleIf::new(tcx, conf))); store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 661b4b590d8f..388034c39f52 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{msrvs, path_to_local, std_or_core}; +use clippy_utils::{msrvs, path_to_local, std_or_core, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -11,7 +11,6 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::SyntaxContext; -use rustc_span::symbol::sym; /// Detects for loop pushing the same item into a Vec pub(super) fn check<'tcx>( @@ -187,8 +186,8 @@ fn get_vec_push<'tcx>( // Extract method being called and figure out the parameters for the method call && let ExprKind::MethodCall(path, self_expr, [pushed_item], _) = &semi_stmt.kind // Check that the method being called is push() on a Vec + && path.ident.name == sym::push && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec) - && path.ident.name.as_str() == "push" { return Some((self_expr, pushed_item, semi_stmt.span.ctxt())); } diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index 444ecd5d2bb9..ed0cce754b95 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::{SpanlessEq, sym}; use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; @@ -199,9 +199,9 @@ fn build_suggestion( } else { format!("{dividend_sugg_str}{type_suffix}") }; - let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability); + let divisor_snippet = snippet_with_context(cx, rhs.span, expr.span.ctxt(), "..", applicability); - let sugg = format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"); + let sugg = format!("{suggestion_before_div_ceil}.div_ceil({})", divisor_snippet.0); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index f71264a93ca8..b3ee45cc0209 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -3,12 +3,11 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::visitors::{is_local_used, local_used_once}; -use clippy_utils::{is_trait_method, path_to_local_id}; +use clippy_utils::{is_trait_method, path_to_local_id, sym}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ExprKind, LetStmt, Node, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -66,7 +65,7 @@ impl LateLintPass<'_> for ManualHashOne { && let Some(init) = local.init && !init.span.from_expansion() && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind - && seg.ident.name.as_str() == "build_hasher" + && seg.ident.name == sym::build_hasher && let Node::Stmt(local_stmt) = cx.tcx.parent_hir_node(local.hir_id) && let Node::Block(block) = cx.tcx.parent_hir_node(local_stmt.hir_id) @@ -94,7 +93,7 @@ impl LateLintPass<'_> for ManualHashOne { && let Node::Expr(finish_expr) = cx.tcx.parent_hir_node(path_expr.hir_id) && !finish_expr.span.from_expansion() && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind - && seg.ident.name.as_str() == "finish" + && seg.ident.name == sym::finish && self.msrv.meets(cx, msrvs::BUILD_HASHER_HASH_ONE) { diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 8ab49bd2ea8e..ac8c88f02057 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators}; +use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators, sym}; use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -11,7 +11,7 @@ use rustc_hir::{Expr, ExprKind, Lit, Node, Param, PatExpr, PatExprKind, PatKind, use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { check_is_ascii(cx, macro_call.span, recv, &range, None); } } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind - && path.ident.name.as_str() == "contains" + && path.ident.name == sym::contains && let Some(higher::Range { start: Some(start), end: Some(end), diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index e4ad3953b671..b365dbf088f5 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,14 +1,14 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; -use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs}; +use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -76,7 +76,7 @@ impl LateLintPass<'_> for ManualOptionAsSlice { } }, ExprKind::MethodCall(seg, callee, [], _) => { - if seg.ident.name.as_str() == "unwrap_or_default" { + if seg.ident.name == sym::unwrap_or_default { check_map(cx, callee, span, self.msrv); } }, diff --git a/clippy_lints/src/methods/double_ended_iterator_last.rs b/clippy_lints/src/methods/double_ended_iterator_last.rs index e666f31217cc..6d841853fbe5 100644 --- a/clippy_lints/src/methods/double_ended_iterator_last.rs +++ b/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::{has_non_owning_mutable_access, implements_trait}; -use clippy_utils::{is_mutable, is_trait_method, path_to_local}; +use clippy_utils::{is_mutable, is_trait_method, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::Instance; -use rustc_span::{Span, sym}; +use rustc_span::Span; use super::DOUBLE_ENDED_ITERATOR_LAST; @@ -24,7 +24,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp && let Ok(Some(fn_def)) = Instance::try_resolve(cx.tcx, cx.typing_env(), id, args) // find the provided definition of Iterator::last && let Some(item) = cx.tcx.get_diagnostic_item(sym::Iterator) - && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name().as_str() == "last") + && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name() == sym::last) // if the resolved method is the same as the provided definition && fn_def.def_id() == last_def.def_id && let self_ty = cx.typeck_results().expr_ty(self_expr) diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index da123f13d46f..4dd54cf19745 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -233,12 +233,12 @@ impl<'tcx> OffendingFilterExpr<'tcx> { // the latter only calls `effect` once let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span); - if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name.as_str() == "is_some" { + if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym::is_some { Some(Self::IsSome { receiver, side_effect_expr_span, }) - } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name.as_str() == "is_ok" { + } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym::is_ok { Some(Self::IsOk { receiver, side_effect_expr_span, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ad374dee516c..10f4637d08f6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4370,11 +4370,10 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// - /// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using a question mark (`?`) instead. + /// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using + /// the `?` operator instead. /// /// ### Why is this bad? - /// /// The `and_then` method is used to chain a computation that returns an `Option` or a `Result`. /// This can be replaced with the `?` operator, which is more concise and idiomatic. /// diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index 743aacf05885..f528f7f065c6 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -9,7 +9,7 @@ use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::CHAR_IS_ASCII; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; +use clippy_utils::{match_def_path, path_to_local_id, peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { while let ExprKind::AddrOf(_, _, e) = expr.kind { @@ -32,7 +32,7 @@ fn handle_expr( // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is // `is_ascii`, then only `.all()` should warn. if revert != is_all - && method.ident.name.as_str() == "is_ascii" + && method.ident.name == sym::is_ascii && path_to_local_id(receiver, first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char @@ -102,7 +102,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, && let body = cx.tcx.hir_body(body) && let Some(first_param) = body.params.first() && let ExprKind::MethodCall(method, mut recv, [], _) = recv.kind - && method.ident.name.as_str() == "chars" + && method.ident.name == sym::chars && let str_ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() && *str_ty.kind() == ty::Str { diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 6efaba525e3e..cd22583b8a25 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -9,7 +9,7 @@ use clippy_utils::ty::{ }; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, - path_to_local_id, + path_to_local_id, sym, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; @@ -20,8 +20,8 @@ use rustc_hir::{ use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, AssocTag, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_span::Span; use rustc_span::symbol::Ident; -use rustc_span::{Span, sym}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; @@ -339,7 +339,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { // Check function calls on our collection if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind { if args.is_empty() - && method_name.ident.name.as_str() == "collect" + && method_name.ident.name == sym::collect && is_trait_method(self.cx, expr, sym::Iterator) { self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv)); diff --git a/clippy_lints/src/methods/read_line_without_trim.rs b/clippy_lints/src/methods/read_line_without_trim.rs index fe999a3b5f8f..407f2e80aff2 100644 --- a/clippy_lints/src/methods/read_line_without_trim.rs +++ b/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,17 +1,16 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_parent_expr; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_local_use_after_expr; +use clippy_utils::{get_parent_expr, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; use super::READ_LINE_WITHOUT_TRIM; @@ -44,7 +43,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< if let Some(parent) = get_parent_expr(cx, expr) { let data = if let ExprKind::MethodCall(segment, recv, args, span) = parent.kind { if args.is_empty() - && segment.ident.name.as_str() == "parse" + && segment.ident.name == sym::parse && let parse_result_ty = cx.typeck_results().expr_ty(parent) && is_type_diagnostic_item(cx, parse_result_ty, sym::Result) && let ty::Adt(_, substs) = parse_result_ty.kind() @@ -58,7 +57,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< "calling `.parse()` on a string without trimming the trailing newline character", "checking", )) - } else if segment.ident.name.as_str() == "ends_with" + } else if segment.ident.name == sym::ends_with && recv.span == expr.span && let [arg] = args && expr_is_string_literal_without_trailing_newline(arg) diff --git a/clippy_lints/src/methods/return_and_then.rs b/clippy_lints/src/methods/return_and_then.rs index e8861935d421..91643b0dfefd 100644 --- a/clippy_lints/src/methods/return_and_then.rs +++ b/clippy_lints/src/methods/return_and_then.rs @@ -55,7 +55,6 @@ pub(super) fn check<'tcx>( None => &body_snip, }; - let msg = "use the question mark operator instead of an `and_then` call"; let sugg = format!( "let {} = {}?;\n{}", arg_snip, @@ -63,5 +62,13 @@ pub(super) fn check<'tcx>( reindent_multiline(inner, false, indent_of(cx, expr.span)) ); - span_lint_and_sugg(cx, RETURN_AND_THEN, expr.span, msg, "try", sugg, applicability); + span_lint_and_sugg( + cx, + RETURN_AND_THEN, + expr.span, + "use the `?` operator instead of an `and_then` call", + "try", + sugg, + applicability, + ); } diff --git a/clippy_lints/src/methods/str_split.rs b/clippy_lints/src/methods/str_split.rs index 3586e11f56ab..fb4ac7b3613d 100644 --- a/clippy_lints/src/methods/str_split.rs +++ b/clippy_lints/src/methods/str_split.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; +use clippy_utils::sym; use clippy_utils::visitors::is_const_evaluatable; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -19,7 +20,7 @@ pub(super) fn check<'a>(cx: &LateContext<'a>, expr: &'_ Expr<'_>, split_recv: &' && !is_const_evaluatable(cx, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind && (matches!(split_lit.node, LitKind::Char('\n')) - || matches!(split_lit.node, LitKind::Str(sym, _) if (sym.as_str() == "\n" || sym.as_str() == "\r\n"))) + || matches!(split_lit.node, LitKind::Str(sym::LF | sym::CRLF, _))) { let mut app = Applicability::MaybeIncorrect; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index f920f306bc1e..79ed352193fd 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -3,13 +3,12 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; +use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::sym; use super::{UNNECESSARY_FILTER_MAP, UNNECESSARY_FIND_MAP}; @@ -95,7 +94,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc (true, true) }, hir::ExprKind::MethodCall(segment, recv, [arg], _) => { - if segment.ident.name.as_str() == "then_some" + if segment.ident.name == sym::then_some && cx.typeck_results().expr_ty(recv).is_bool() && path_to_local_id(arg, arg_id) { diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 206b0a8ae3cd..87bb8d46a1d6 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -7,7 +7,7 @@ use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_ use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs, - return_ty, + return_ty, sym, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -20,7 +20,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{ self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{Obligation, ObligationCause}; @@ -312,8 +312,7 @@ fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, /// call of a `to_owned`-like function is unnecessary. 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((fn_name, argument_expr)) = get_fn_name_and_arg(cx, parent) - && fn_name.as_str() == "split" + && 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) { @@ -614,8 +613,7 @@ fn has_lifetime(ty: Ty<'_>) -> bool { /// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`. fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - (method_name.as_str() == "cloned" || method_name.as_str() == "copied") - && is_diag_trait_item(cx, method_def_id, sym::Iterator) + matches!(method_name, sym::cloned | sym::copied) && is_diag_trait_item(cx, method_def_id, sym::Iterator) } /// Returns true if the named method can be used to convert the receiver to its "owned" @@ -628,7 +626,7 @@ fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: /// Returns true if the named method is `Cow::into_owned`. fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow) + method_name == sym::into_owned && is_diag_item_method(cx, method_def_id, sym::Cow) } /// Returns true if the named method is `ToString::to_string` and it's called on a type that diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index ed89b3b34386..64eafc0ebccd 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,10 +1,9 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::{is_trait_method, sym}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; use std::cmp::Ordering::{Equal, Greater, Less}; declare_clippy_lint! { @@ -79,12 +78,10 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { - if path.ident.name.as_str() == "max" { - fetch_const(cx, Some(receiver), args, MinMax::Max) - } else if path.ident.name.as_str() == "min" { - fetch_const(cx, Some(receiver), args, MinMax::Min) - } else { - None + match path.ident.name { + sym::max => fetch_const(cx, Some(receiver), args, MinMax::Max), + sym::min => fetch_const(cx, Some(receiver), args, MinMax::Min), + _ => None, } } else { None diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 1932d2d5f978..be7dd74fd62b 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -1,9 +1,9 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_path_lang_item; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Visitable, for_each_expr}; +use clippy_utils::{is_path_lang_item, sym}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; @@ -13,7 +13,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -116,7 +116,7 @@ fn should_lint<'tcx>( if path.ident.name == sym::debug_struct && is_type_diagnostic_item(cx, recv_ty, sym::Formatter) { has_debug_struct = true; - } else if path.ident.name.as_str() == "finish_non_exhaustive" + } else if path.ident.name == sym::finish_non_exhaustive && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) { has_finish_non_exhaustive = true; diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 0e0855859628..d9f4fb271fb4 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; +use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id, sym}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -136,7 +136,7 @@ impl<'tcx> DivergenceVisitor<'_, 'tcx> { fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(self.cx, e) - && self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" + && self.cx.tcx.is_diagnostic_item(sym::todo_macro, macro_call.def_id) { return; } diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 90b27f5dbac8..7dd96f1f037f 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -3,12 +3,12 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::has_iter_method; +use clippy_utils::{is_trait_method, sym}; declare_clippy_lint! { /// ### What it does @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { iter_recv.kind, ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ) - && method_name.ident.name.as_str() == "for_each" + && method_name.ident.name == sym::for_each && is_trait_method(cx, expr, sym::Iterator) // Checks the type of the `iter` method receiver is NOT a user defined type. && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 72b0a80260e9..2a2160c3be2d 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,6 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::path_res; -use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Block, Body, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -9,52 +8,38 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Suggests alternatives for useless applications of `?` in terminating expressions + /// Suggests replacing `Ok(x?)` or `Some(x?)` with `x` in return positions where the `?` operator + /// is not needed to convert the type of `x`. /// /// ### Why is this bad? /// There's no reason to use `?` to short-circuit when execution of the body will end there anyway. /// /// ### Example /// ```no_run - /// struct TO { - /// magic: Option, + /// # use std::num::ParseIntError; + /// fn f(s: &str) -> Option { + /// Some(s.find('x')?) /// } /// - /// fn f(to: TO) -> Option { - /// Some(to.magic?) + /// fn g(s: &str) -> Result { + /// Ok(s.parse()?) /// } - /// - /// struct TR { - /// magic: Result, - /// } - /// - /// fn g(tr: Result) -> Result { - /// tr.and_then(|t| Ok(t.magic?)) - /// } - /// /// ``` /// Use instead: /// ```no_run - /// struct TO { - /// magic: Option, + /// # use std::num::ParseIntError; + /// fn f(s: &str) -> Option { + /// s.find('x') /// } /// - /// fn f(to: TO) -> Option { - /// to.magic - /// } - /// - /// struct TR { - /// magic: Result, - /// } - /// - /// fn g(tr: Result) -> Result { - /// tr.and_then(|t| t.magic) + /// fn g(s: &str) -> Result { + /// s.parse() /// } /// ``` #[clippy::version = "1.51.0"] pub NEEDLESS_QUESTION_MARK, complexity, - "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result`." + "using `Ok(x?)` or `Some(x?)` where `x` would be equivalent" } declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); @@ -111,10 +96,10 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(path, [arg]) = expr.kind && let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) - && let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { - "Some()" + && let variant = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { + "Some" } else if cx.tcx.lang_items().result_ok_variant() == Some(variant_id) { - "Ok()" + "Ok" } else { return; } @@ -126,14 +111,25 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { && let inner_ty = cx.typeck_results().expr_ty(inner_expr) && expr_ty == inner_ty { - span_lint_and_sugg( + span_lint_hir_and_then( cx, NEEDLESS_QUESTION_MARK, + expr.hir_id, expr.span, - "question mark operator is useless here", - format!("try removing question mark and `{sugg_remove}`"), - format!("{}", snippet(cx, inner_expr.span, r#""...""#)), - Applicability::MachineApplicable, + format!("enclosing `{variant}` and `?` operator are unneeded"), + |diag| { + diag.multipart_suggestion( + format!("remove the enclosing `{variant}` and `?` operator"), + vec![ + (expr.span.until(inner_expr.span), String::new()), + ( + inner_expr.span.shrink_to_hi().to(expr.span.shrink_to_hi()), + String::new(), + ), + ], + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 852c3885f568..23a1622f30ff 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -43,12 +43,12 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { match &expr.kind { ExprKind::MethodCall(path, func, [param], _) => { if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def() - && ((path.ident.name.as_str() == "mode" + && ((path.ident.name == sym::mode && matches!( cx.tcx.get_diagnostic_name(adt.did()), Some(sym::FsOpenOptions | sym::DirBuilder) )) - || (path.ident.name.as_str() == "set_mode" + || (path.ident.name == sym::set_mode && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()))) && let ExprKind::Lit(_) = param.kind && param.span.eq_ctxt(expr.span) diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs index 635f5678e2a6..1b8ab1bdedf8 100644 --- a/clippy_lints/src/non_zero_suggestions.rs +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::sym; use rustc_ast::ast::BinOpKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -72,7 +72,7 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit && let ExprKind::Path(qpath) = &func.kind && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && let ExprKind::MethodCall(rcv_path, receiver, [], _) = &arg.kind - && rcv_path.ident.name.as_str() == "get" + && rcv_path.ident.name == sym::get { let fn_name = cx.tcx.item_name(def_id); let target_ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index 01dc6a27c33e..ded161c8576a 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; +use clippy_utils::{parent_item_name, sym}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -34,7 +34,7 @@ pub(crate) fn check<'tcx>( return; } - if let Some(name) = get_item_name(cx, expr) { + if let Some(name) = parent_item_name(cx, expr) { let name = name.as_str(); if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") { return; @@ -106,7 +106,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } if let ExprKind::MethodCall(method_name, self_arg, [], _) = expr.kind - && method_name.ident.name.as_str() == "signum" + && method_name.ident.name == sym::signum // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) { diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs index dc142b6e1577..da56a785007c 100644 --- a/clippy_lints/src/permissions_set_readonly_false.rs +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -33,7 +33,7 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind && let ExprKind::Lit(lit) = &arg.kind && LitKind::Bool(false) == lit.node - && path.ident.name.as_str() == "set_readonly" + && path.ident.name == sym::set_readonly && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) { span_lint_and_then( diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 491961408adb..9149406642d6 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -743,9 +743,9 @@ fn check_ptr_eq<'tcx>( } // Remove one level of usize conversion if any - let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { - (Some(lhs), Some(rhs)) => (lhs, rhs), - _ => (left, right), + let (left, right, usize_peeled) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { + (Some(lhs), Some(rhs)) => (lhs, rhs, true), + _ => (left, right, false), }; // This lint concerns raw pointers @@ -754,7 +754,12 @@ fn check_ptr_eq<'tcx>( return; } - let (left_var, right_var) = (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); + let ((left_var, left_casts_peeled), (right_var, right_casts_peeled)) = + (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); + + if !(usize_peeled || left_casts_peeled || right_casts_peeled) { + return; + } let mut app = Applicability::MachineApplicable; let left_snip = Sugg::hir_with_context(cx, left_var, expr.span.ctxt(), "_", &mut app); @@ -787,8 +792,9 @@ fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_> } } -// Peel raw casts if the remaining expression can be coerced to it -fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> &'tcx Expr<'tcx> { +// Peel raw casts if the remaining expression can be coerced to it, and whether casts have been +// peeled or not. +fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> (&'tcx Expr<'tcx>, bool) { if !expr.span.from_expansion() && let ExprKind::Cast(inner, _) = expr.kind && let ty::RawPtr(target_ty, _) = expr_ty.kind() @@ -796,8 +802,8 @@ fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: && let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind() && target_ty == inner_target_ty { - peel_raw_casts(cx, inner, inner_ty) + (peel_raw_casts(cx, inner, inner_ty).0, true) } else { - expr + (expr, false) } } diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 7f74a2fff9f2..d8d813f9846d 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; use std::fmt; declare_clippy_lint! { @@ -97,7 +97,7 @@ fn expr_as_ptr_offset_call<'tcx>( if path_segment.ident.name == sym::offset { return Some((arg_0, arg_1, Method::Offset)); } - if path_segment.ident.name.as_str() == "wrapping_offset" { + if path_segment.ident.name == sym::wrapping_offset { return Some((arg_0, arg_1, Method::WrappingOffset)); } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index d318897443da..c02e5e0621c9 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -10,7 +10,7 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, - span_contains_cfg, span_contains_comment, + span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -22,15 +22,14 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::sym; use rustc_span::symbol::Symbol; declare_clippy_lint! { /// ### What it does - /// Checks for expressions that could be replaced by the question mark operator. + /// Checks for expressions that could be replaced by the `?` operator. /// /// ### Why is this bad? - /// Question mark usage is more idiomatic. + /// Using the `?` operator is shorter and more idiomatic. /// /// ### Example /// ```ignore @@ -47,7 +46,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub QUESTION_MARK, style, - "checks for expressions that could be replaced by the question mark operator" + "checks for expressions that could be replaced by the `?` operator" } pub struct QuestionMark { @@ -207,8 +206,8 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ is_type_diagnostic_item(cx, caller_ty, smbl) && expr_return_none_or_err(smbl, cx, if_then, caller, None) && match smbl { - sym::Option => call_sym.as_str() == "is_none", - sym::Result => call_sym.as_str() == "is_err", + sym::Option => call_sym == sym::is_none, + sym::Result => call_sym == sym::is_err, _ => false, } }, @@ -280,7 +279,7 @@ fn expr_return_none_or_err( /// } /// ``` /// -/// If it matches, it will suggest to use the question mark operator instead +/// If it matches, it will suggest to use the `?` operator instead fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) && !is_else_clause(cx.tcx, expr) diff --git a/clippy_lints/src/question_mark_used.rs b/clippy_lints/src/question_mark_used.rs index 0a974bf9d2f7..96ea485d7693 100644 --- a/clippy_lints/src/question_mark_used.rs +++ b/clippy_lints/src/question_mark_used.rs @@ -7,10 +7,10 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for expressions that use the question mark operator and rejects them. + /// Checks for expressions that use the `?` operator and rejects them. /// /// ### Why restrict this? - /// Sometimes code wants to avoid the question mark operator because for instance a local + /// Sometimes code wants to avoid the `?` operator because for instance a local /// block requires a macro to re-throw errors to attach additional information to the /// error. /// @@ -27,7 +27,7 @@ declare_clippy_lint! { #[clippy::version = "1.69.0"] pub QUESTION_MARK_USED, restriction, - "complains if the question mark operator is used" + "checks if the `?` operator is used" } declare_lint_pass!(QuestionMarkUsed => [QUESTION_MARK_USED]); @@ -40,15 +40,9 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed { } #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - QUESTION_MARK_USED, - expr.span, - "question mark operator was used", - |diag| { - diag.help("consider using a custom macro or match expression"); - }, - ); + span_lint_and_then(cx, QUESTION_MARK_USED, expr.span, "the `?` operator was used", |diag| { + diag.help("consider using a custom macro or match expression"); + }); } } } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index d26288adb391..30a5fe4db27e 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -3,14 +3,13 @@ use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, - span_contains_comment, + span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -248,7 +247,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind && path_to_local_id(self_arg, self.vec_alloc.local_id) - && path.ident.name.as_str() == "extend" + && path.ident.name == sym::extend && self.is_repeat_take(extend_arg) { self.slow_expression = Some(InitializationType::Extend(expr)); @@ -260,7 +259,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind && path_to_local_id(self_arg, self.vec_alloc.local_id) - && path.ident.name.as_str() == "resize" + && path.ident.name == sym::resize // Check that is filled with 0 && is_integer_literal(fill_arg, 0) { @@ -282,7 +281,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { /// Returns `true` if give expression is `repeat(0).take(...)` fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool { if let ExprKind::MethodCall(take_path, recv, [len_arg], _) = expr.kind - && take_path.ident.name.as_str() == "take" + && take_path.ident.name == sym::take // Check that take is applied to `repeat(0)` && self.is_repeat_zero(recv) { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 43a3e6961051..af4d0d541f17 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -286,7 +286,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if !e.span.in_external_macro(cx.sess().source_map()) && let ExprKind::MethodCall(path, receiver, ..) = &e.kind - && path.ident.name.as_str() == "as_bytes" + && path.ident.name == sym::as_bytes && let ExprKind::Lit(lit) = &receiver.kind && let LitKind::Str(lit_content, _) = &lit.node { @@ -332,7 +332,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { } if let ExprKind::MethodCall(path, recv, [], _) = &e.kind - && path.ident.name.as_str() == "into_bytes" + && path.ident.name == sym::into_bytes && let ExprKind::MethodCall(path, recv, [], _) = &recv.kind && matches!(path.ident.name.as_str(), "to_owned" | "to_string") && let ExprKind::Lit(lit) = &recv.kind @@ -556,7 +556,7 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { let tyckres = cx.typeck_results(); if let ExprKind::MethodCall(path, split_recv, [], split_ws_span) = expr.kind - && path.ident.name.as_str() == "split_whitespace" + && path.ident.name == sym::split_whitespace && let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id) && cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id) && let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 9993e6ae18b9..bb969bc802fe 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::match_def_path; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{match_def_path, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -38,11 +38,11 @@ declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind - && is_some_path.ident.name.as_str() == "is_some" + && is_some_path.ident.name == sym::is_some { let match_result = match &to_digit_expr.kind { hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => { - if to_digits_path.ident.name.as_str() == "to_digit" + if to_digits_path.ident.name == sym::to_digit && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg) && *char_arg_ty.kind() == ty::Char { diff --git a/clippy_lints/src/transmute/eager_transmute.rs b/clippy_lints/src/transmute/eager_transmute.rs index 81c0a57083e8..1ccab62708b1 100644 --- a/clippy_lints/src/transmute/eager_transmute.rs +++ b/clippy_lints/src/transmute/eager_transmute.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_normalizable; -use clippy_utils::{eq_expr_value, path_to_local}; +use clippy_utils::{eq_expr_value, path_to_local, sym}; use rustc_abi::WrappingRange; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node}; @@ -43,7 +43,7 @@ fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_ binops_with_local(cx, local_expr, lhs) || binops_with_local(cx, local_expr, rhs) }, ExprKind::MethodCall(path, receiver, [arg], _) - if path.ident.name.as_str() == "contains" + if path.ident.name == sym::contains // ... `contains` called on some kind of range && let Some(receiver_adt) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def() && let lang_items = cx.tcx.lang_items() @@ -81,7 +81,7 @@ pub(super) fn check<'tcx>( if let Some(then_some_call) = peel_parent_unsafe_blocks(cx, expr) && let ExprKind::MethodCall(path, receiver, [arg], _) = then_some_call.kind && cx.typeck_results().expr_ty(receiver).is_bool() - && path.ident.name.as_str() == "then_some" + && path.ident.name == sym::then_some && is_local_with_projections(transmutable) && binops_with_local(cx, transmutable, receiver) && is_normalizable(cx, cx.param_env, from_ty) diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 769244c675e1..f13042a6fa6b 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -19,61 +19,58 @@ pub(super) fn check<'tcx>( def_id: DefId, box_size_threshold: u64, ) -> bool { - if cx.tcx.is_diagnostic_item(sym::Vec, def_id) { - if let Some(last) = last_path_segment(qpath).args - // Get the _ part of Vec<_> - && let Some(GenericArg::Type(ty)) = last.args.first() - // extract allocator from the Vec for later - && let vec_alloc_ty = last.args.get(1) - // ty is now _ at this point - && let TyKind::Path(ref ty_qpath) = ty.kind - && let res = cx.qpath_res(ty_qpath, ty.hir_id) - && let Some(def_id) = res.opt_def_id() - && Some(def_id) == cx.tcx.lang_items().owned_box() - // At this point, we know ty is Box, now get T - && let Some(last) = last_path_segment(ty_qpath).args - && let Some(GenericArg::Type(boxed_ty)) = last.args.first() - // extract allocator from the Box for later - && let boxed_alloc_ty = last.args.get(1) - // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay - && let ty_ty = lower_ty(cx.tcx, boxed_ty.as_unambig_ty()) - && !ty_ty.has_escaping_bound_vars() - && ty_ty.is_sized(cx.tcx, cx.typing_env()) - && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) - && ty_ty_size < box_size_threshold - // https://github.com/rust-lang/rust-clippy/issues/7114 - && match (vec_alloc_ty, boxed_alloc_ty) { - (None, None) => true, - // this is in the event that we have something like - // Vec<_, Global>, in which case is equivalent to - // Vec<_> - (None, Some(GenericArg::Type(inner))) | (Some(GenericArg::Type(inner)), None) => { - if let TyKind::Path(path) = inner.kind - && let Some(did) = cx.qpath_res(&path, inner.hir_id).opt_def_id() { - cx.tcx.lang_items().get(LangItem::GlobalAlloc) == Some(did) - } else { - false - } - }, - (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) => - // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay - lower_ty(cx.tcx, l.as_unambig_ty()) == lower_ty(cx.tcx, r.as_unambig_ty()), - _ => false - } - { - span_lint_and_sugg( - cx, - VEC_BOX, - hir_ty.span, - "`Vec` is already on the heap, the boxing is unnecessary", - "try", - format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), - Applicability::Unspecified, - ); - true - } else { - false + if cx.tcx.is_diagnostic_item(sym::Vec, def_id) + && let Some(last) = last_path_segment(qpath).args + // Get the _ part of Vec<_> + && let Some(GenericArg::Type(ty)) = last.args.first() + // extract allocator from the Vec for later + && let vec_alloc_ty = last.args.get(1) + // ty is now _ at this point + && let TyKind::Path(ref ty_qpath) = ty.kind + && let res = cx.qpath_res(ty_qpath, ty.hir_id) + && let Some(def_id) = res.opt_def_id() + && Some(def_id) == cx.tcx.lang_items().owned_box() + // At this point, we know ty is Box, now get T + && let Some(last) = last_path_segment(ty_qpath).args + && let Some(GenericArg::Type(boxed_ty)) = last.args.first() + // extract allocator from the Box for later + && let boxed_alloc_ty = last.args.get(1) + // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay + && let ty_ty = lower_ty(cx.tcx, boxed_ty.as_unambig_ty()) + && !ty_ty.has_escaping_bound_vars() + && ty_ty.is_sized(cx.tcx, cx.typing_env()) + && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) + && ty_ty_size < box_size_threshold + // https://github.com/rust-lang/rust-clippy/issues/7114 + && match (vec_alloc_ty, boxed_alloc_ty) { + (None, None) => true, + // this is in the event that we have something like + // Vec<_, Global>, in which case is equivalent to + // Vec<_> + (None, Some(GenericArg::Type(inner))) | (Some(GenericArg::Type(inner)), None) => { + if let TyKind::Path(path) = inner.kind + && let Some(did) = cx.qpath_res(&path, inner.hir_id).opt_def_id() { + cx.tcx.lang_items().get(LangItem::GlobalAlloc) == Some(did) + } else { + false + } + }, + (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) => + // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay + lower_ty(cx.tcx, l.as_unambig_ty()) == lower_ty(cx.tcx, r.as_unambig_ty()), + _ => false } + { + span_lint_and_sugg( + cx, + VEC_BOX, + hir_ty.span, + "`Vec` is already on the heap, the boxing is unnecessary", + "try", + format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), + Applicability::Unspecified, + ); + true } else { false } diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 7803d5115c97..cee4a53f03cb 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; -use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while}; +use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; // TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std declare_clippy_lint! { @@ -187,7 +187,7 @@ fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>) -> bool { is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::Vec) - && path.ident.name.as_str() == "reserve" + && path.ident.name == sym::reserve } /// Returns self if the expression is `Vec::set_len()` @@ -209,7 +209,7 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt ExprKind::MethodCall(path, self_expr, [arg], _) => { let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs(); if is_type_diagnostic_item(cx, self_type, sym::Vec) - && path.ident.name.as_str() == "set_len" + && path.ident.name == sym::set_len && !is_integer_literal(arg, 0) { Some((self_expr, expr.span)) diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index d0067b1a65e7..12da891a71b1 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::sym; use clippy_utils::visitors::is_local_used; use rustc_hir::{Body, Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -61,12 +62,10 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { let assoc_item = cx.tcx.associated_item(impl_item.owner_id); let contains_todo = |cx, body: &'_ Body<'_>| -> bool { clippy_utils::visitors::for_each_expr_without_closures(body.value, |e| { - if let Some(macro_call) = root_macro_call_first_node(cx, e) { - if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } + if let Some(macro_call) = root_macro_call_first_node(cx, e) + && cx.tcx.is_diagnostic_item(sym::todo_macro, macro_call.def_id) + { + ControlFlow::Break(()) } else { ControlFlow::Continue(()) } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index d5309aade7aa..9859ddfdf7bd 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,11 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, position_before_rarrow}; -use rustc_ast::visit::FnKind; -use rustc_ast::{ClosureBinder, ast}; +use clippy_utils::{is_never_expr, is_unit_expr}; +use rustc_ast::{Block, StmtKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, Node, PolyTraitRef, Term, + Ty, TyKind, +}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{BytePos, Span}; +use rustc_span::edition::Edition; +use rustc_span::{BytePos, Span, sym}; declare_clippy_lint! { /// ### What it does @@ -34,27 +41,89 @@ declare_clippy_lint! { declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]); -impl EarlyLintPass for UnusedUnit { - fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if let ast::FnRetTy::Ty(ref ty) = kind.decl().output - && let ast::TyKind::Tup(ref vals) = ty.kind - && vals.is_empty() - && !ty.span.from_expansion() - && get_def(span) == get_def(ty.span) +impl<'tcx> LateLintPass<'tcx> for UnusedUnit { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + span: Span, + def_id: LocalDefId, + ) { + if let FnRetTy::Return(hir_ty) = decl.output + && is_unit_ty(hir_ty) + && !hir_ty.span.from_expansion() + && get_def(span) == get_def(hir_ty.span) { // implicit types in closure signatures are forbidden when `for<...>` is present - if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { + if let FnKind::Closure = kind + && let Node::Expr(expr) = cx.tcx.hir_node_by_def_id(def_id) + && let ExprKind::Closure(closure) = expr.kind + && !closure.bound_generic_params.is_empty() + { return; } - lint_unneeded_unit_return(cx, ty, span); + // unit never type fallback is no longer supported since Rust 2024. For more information, + // see + if cx.tcx.sess.edition() >= Edition::Edition2024 + && let ExprKind::Block(block, _) = body.value.kind + && let Some(expr) = block.expr + && is_never_expr(cx, expr).is_some() + { + return; + } + + lint_unneeded_unit_return(cx, hir_ty.span, span); } } - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if let Some(stmt) = block.stmts.last() - && let ast::StmtKind::Expr(ref expr) = stmt.kind + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Ret(Some(expr)) | ExprKind::Break(_, Some(expr)) = expr.kind && is_unit_expr(expr) + && !expr.span.from_expansion() + { + span_lint_and_sugg( + cx, + UNUSED_UNIT, + expr.span, + "unneeded `()`", + "remove the `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + } + + fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { + let segments = &poly.trait_ref.path.segments; + + if segments.len() == 1 + && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()) + && let Some(args) = segments[0].args + && args.parenthesized == GenericArgsParentheses::ParenSugar + && let constraints = &args.constraints + && constraints.len() == 1 + && constraints[0].ident.name == sym::Output + && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraints[0].kind + && args.span_ext.hi() != poly.span.hi() + && !hir_ty.span.from_expansion() + && is_unit_ty(hir_ty) + { + lint_unneeded_unit_return(cx, hir_ty.span, poly.span); + } + } +} + +impl EarlyLintPass for UnusedUnit { + /// Check for unit expressions in blocks. This is left in the early pass because some macros + /// expand its inputs as-is, making it invisible to the late pass. See #4076. + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { + if let Some(stmt) = block.stmts.last() + && let StmtKind::Expr(expr) = &stmt.kind + && let rustc_ast::ExprKind::Tup(inner) = &expr.kind + && inner.is_empty() && let ctxt = block.span.ctxt() && stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt @@ -72,39 +141,10 @@ impl EarlyLintPass for UnusedUnit { ); } } +} - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - match e.kind { - ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { - if is_unit_expr(expr) && !expr.span.from_expansion() { - span_lint_and_sugg( - cx, - UNUSED_UNIT, - expr.span, - "unneeded `()`", - "remove the `()`", - String::new(), - Applicability::MachineApplicable, - ); - } - }, - _ => (), - } - } - - fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef) { - let segments = &poly.trait_ref.path.segments; - - if segments.len() == 1 - && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()) - && let Some(args) = &segments[0].args - && let ast::GenericArgs::Parenthesized(generic_args) = &**args - && let ast::FnRetTy::Ty(ty) = &generic_args.output - && ty.kind.is_unit() - { - lint_unneeded_unit_return(cx, ty, generic_args.span); - } - } +fn is_unit_ty(ty: &Ty<'_>) -> bool { + matches!(ty.kind, TyKind::Tup([])) } // get the def site @@ -117,24 +157,15 @@ fn get_def(span: Span) -> Option { } } -// is this expr a `()` unit? -fn is_unit_expr(expr: &ast::Expr) -> bool { - if let ast::ExprKind::Tup(ref vals) = expr.kind { - vals.is_empty() - } else { - false - } -} - -fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { +fn lint_unneeded_unit_return(cx: &LateContext<'_>, ty_span: Span, span: Span) { let (ret_span, appl) = - span.with_hi(ty.span.hi()) + span.with_hi(ty_span.hi()) .get_source_text(cx) - .map_or((ty.span, Applicability::MaybeIncorrect), |src| { - position_before_rarrow(&src).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + .map_or((ty_span, Applicability::MaybeIncorrect), |src| { + position_before_rarrow(&src).map_or((ty_span, Applicability::MaybeIncorrect), |rpos| { ( #[expect(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + ty_span.with_lo(BytePos(span.lo().0 + rpos as u32)), Applicability::MachineApplicable, ) }) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index ce82b56eb946..ba140788bb54 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -208,7 +208,7 @@ fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id) && let ExprKind::MethodCall(path, _, [], _) = mutating_expr.kind { - path.ident.name.as_str() == "as_mut" + path.ident.name == sym::as_mut } else { false } @@ -278,7 +278,7 @@ fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Opt if let ExprKind::MethodCall(path, recv, [], _) = expr.kind { if path.ident.name == sym::as_ref { (recv, Some(AsRefKind::AsRef)) - } else if path.ident.name.as_str() == "as_mut" { + } else if path.ident.name == sym::as_mut { (recv, Some(AsRefKind::AsMut)) } else { (expr, None) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 11c14c147776..f24c127c4521 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::is_in_test; use clippy_utils::macros::{FormatArgsStorage, MacroCall, format_arg_removal_span, root_macro_call_first_node}; use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; +use clippy_utils::{is_in_test, sym}; use rustc_ast::token::LitKind; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, @@ -12,7 +12,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -359,7 +359,7 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { } fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { - let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { + let Some(&FormatArgsPiece::Literal(last)) = format_args.template.last() else { return; }; @@ -401,7 +401,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma return; }; - if format_args.template.len() == 1 && last.as_str() == "\n" { + if format_args.template.len() == 1 && last == sym::LF { // print!("\n"), write!(f, "\n") diag.multipart_suggestion( @@ -427,9 +427,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma } fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { - if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..] - && literal.as_str() == "\n" - { + if let [FormatArgsPiece::Literal(sym::LF)] = &format_args.template[..] { let mut span = format_args.span; let lint = if name == "writeln" { diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index 39c1aab8967a..09f1084fe700 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -4,6 +4,7 @@ use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; use rustc_errors::Applicability; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local}; use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -68,6 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { let mut vis = WaitFinder { cx, local_id, + body_id: cx.tcx.hir_enclosing_body_owner(expr.hir_id), state: VisitorState::WalkUpToLocal, early_return: None, missing_wait_branch: None, @@ -129,6 +131,7 @@ struct MaybeWait(Span); struct WaitFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, local_id: HirId, + body_id: LocalDefId, state: VisitorState, early_return: Option, // When joining two if branches where one of them doesn't call `wait()`, stores its span for more targeted help @@ -186,7 +189,7 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { } } else { match ex.kind { - ExprKind::Ret(e) => { + ExprKind::Ret(e) if self.cx.tcx.hir_enclosing_body_owner(ex.hir_id) == self.body_id => { visit_opt!(self, visit_expr, e); if self.early_return.is_none() { self.early_return = Some(ex.span); diff --git a/clippy_lints_internal/src/lib.rs b/clippy_lints_internal/src/lib.rs index 1c42f4112f9a..b02d378619ca 100644 --- a/clippy_lints_internal/src/lib.rs +++ b/clippy_lints_internal/src/lib.rs @@ -2,6 +2,7 @@ #![allow( clippy::missing_docs_in_private_items, clippy::must_use_candidate, + clippy::symbol_as_str, rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic )] @@ -31,12 +32,12 @@ extern crate rustc_span; mod almost_standard_lint_formulation; mod collapsible_calls; -mod interning_literals; mod invalid_paths; mod lint_without_lint_pass; mod msrv_attr_impl; mod outer_expn_data_pass; mod produce_ice; +mod symbols; mod unnecessary_def_path; mod unsorted_clippy_utils_paths; @@ -45,7 +46,6 @@ use rustc_lint::{Lint, LintStore}; static LINTS: &[&Lint] = &[ almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION, collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, - interning_literals::INTERNING_LITERALS, invalid_paths::INVALID_PATHS, lint_without_lint_pass::DEFAULT_LINT, lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, @@ -54,6 +54,8 @@ static LINTS: &[&Lint] = &[ msrv_attr_impl::MISSING_MSRV_ATTR_IMPL, outer_expn_data_pass::OUTER_EXPN_EXPN_DATA, produce_ice::PRODUCE_ICE, + symbols::INTERNING_LITERALS, + symbols::SYMBOL_AS_STR, unnecessary_def_path::UNNECESSARY_DEF_PATH, unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS, ]; @@ -65,7 +67,7 @@ pub fn register_lints(store: &mut LintStore) { store.register_early_pass(|| Box::new(produce_ice::ProduceIce)); store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls)); store.register_late_pass(|_| Box::new(invalid_paths::InvalidPaths)); - store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); diff --git a/clippy_lints_internal/src/interning_literals.rs b/clippy_lints_internal/src/symbols.rs similarity index 52% rename from clippy_lints_internal/src/interning_literals.rs rename to clippy_lints_internal/src/symbols.rs index 6cee37442349..c64e5821916b 100644 --- a/clippy_lints_internal/src/interning_literals.rs +++ b/clippy_lints_internal/src/symbols.rs @@ -1,7 +1,7 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::match_type; -use clippy_utils::{def_path_def_ids, paths}; +use clippy_utils::{def_path_def_ids, match_def_path, paths}; +use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -11,8 +11,8 @@ use rustc_lint_defs::declare_tool_lint; use rustc_middle::mir::ConstValue; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_span::{Span, sym}; declare_tool_lint! { /// ### What it does @@ -36,15 +36,37 @@ declare_tool_lint! { report_in_external_macro: true } +declare_tool_lint! { + /// ### What it does + /// Checks for calls to `Symbol::as_str` + /// + /// ### Why is this bad? + /// It's faster and easier to use the symbol constant. If one doesn't exist it can be added to `clippy_utils/src/sym.rs` + /// + /// ### Example + /// ```rust,ignore + /// symbol.as_str() == "foo" + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// symbol == sym::foo + /// ``` + pub clippy::SYMBOL_AS_STR, + Warn, + "calls to `Symbol::as_str`", + report_in_external_macro: true +} + #[derive(Default)] -pub struct InterningDefinedSymbol { +pub struct Symbols { // Maps the symbol to the import path symbol_map: FxHashMap, } -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_LITERALS]); +impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]); -impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { +impl<'tcx> LateLintPass<'tcx> for Symbols { fn check_crate(&mut self, cx: &LateContext<'_>) { let modules = [ ("kw", &paths::KW_MODULE[..]), @@ -77,7 +99,8 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if let ExprKind::Call(func, [arg]) = &expr.kind && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id) - && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg) + && let ExprKind::Lit(lit) = arg.kind + && let LitKind::Str(name, _) = lit.node { span_lint_and_then( cx, @@ -85,18 +108,62 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { expr.span, "interning a string literal", |diag| { - let value = Symbol::intern(&arg).as_u32(); - let (message, path) = if let Some((prefix, name)) = self.symbol_map.get(&value) { - ("use the preinterned symbol", format!("{prefix}::{name}")) - } else { - ( - "add the symbol to `clippy_utils/src/sym.rs` and use it", - format!("sym::{}", arg.replace(|ch: char| !ch.is_alphanumeric(), "_")), - ) - }; + let (message, path) = suggestion(&mut self.symbol_map, name); diag.span_suggestion_verbose(expr.span, message, path, Applicability::MaybeIncorrect); }, ); } + + if let ExprKind::Binary(_, lhs, rhs) = expr.kind { + check_binary(cx, lhs, rhs, &mut self.symbol_map); + check_binary(cx, rhs, lhs, &mut self.symbol_map); + } + } +} + +fn check_binary( + cx: &LateContext<'_>, + lhs: &Expr<'_>, + rhs: &Expr<'_>, + symbols: &mut FxHashMap, +) { + if let Some(removal_span) = as_str_span(cx, lhs) + && let ExprKind::Lit(lit) = rhs.kind + && let LitKind::Str(name, _) = lit.node + { + span_lint_and_then(cx, SYMBOL_AS_STR, lhs.span, "converting a Symbol to a string", |diag| { + let (message, path) = suggestion(symbols, name); + diag.multipart_suggestion_verbose( + message, + vec![(removal_span, String::new()), (rhs.span, path)], + Applicability::MachineApplicable, + ); + }); + } +} + +fn suggestion(symbols: &mut FxHashMap, name: Symbol) -> (&'static str, String) { + if let Some((prefix, name)) = symbols.get(&name.as_u32()) { + ("use the preinterned symbol", format!("{prefix}::{name}")) + } else { + ( + "add the symbol to `clippy_utils/src/sym.rs` and use it", + format!("sym::{}", name.as_str().replace(|ch: char| !ch.is_alphanumeric(), "_")), + ) + } +} + +/// ```ignore +/// symbol.as_str() +/// // ^^^^^^^^ +/// ``` +fn as_str_span(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if let ExprKind::MethodCall(_, recv, [], _) = expr.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && match_def_path(cx, method_def_id, &paths::SYMBOL_AS_STR) + { + Some(recv.span.shrink_to_hi().to(expr.span.shrink_to_hi())) + } else { + None } } diff --git a/clippy_utils/README.md b/clippy_utils/README.md index aceff14a1599..66192f866fa0 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-04-22 +nightly-2025-05-01 ``` diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index d4e66ebd8e1f..dbb993482902 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -3,14 +3,14 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::consts::{ConstEvalCtxt, Constant}; -use crate::is_expn_of; use crate::ty::is_type_diagnostic_item; +use crate::{is_expn_of, sym}; use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StructTailExpr}; use rustc_lint::LateContext; -use rustc_span::{Span, sym, symbol}; +use rustc_span::{Span, symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Returns `(pat, arg, body, span)`. @@ -474,7 +474,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::New); } else if name.ident.name == symbol::kw::Default { return Some(VecInitKind::Default); - } else if name.ident.name.as_str() == "with_capacity" { + } else if name.ident.name == sym::with_capacity { let arg = args.first()?; return match ConstEvalCtxt::new(cx).eval_simple(arg) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 17368a7530d7..c37231d09312 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1118,8 +1118,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_const_arg(e); }, TyPatKind::Or(variants) => { - for variant in variants.iter() { - self.hash_ty_pat(variant) + for variant in variants { + self.hash_ty_pat(variant); } }, TyPatKind::Err(_) => {}, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 264b9b0406d0..187dfa4dda84 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1412,7 +1412,7 @@ pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { } /// Gets the name of the item the expression is in, if available. -pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id; match cx.tcx.hir_node_by_def_id(parent_id) { Node::Item(item) => item.kind.ident().map(|ident| ident.name), @@ -2088,7 +2088,7 @@ pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool { let path = cx.get_def_path(did); // libc is meant to be used as a flat list of names, but they're all actually defined in different // modules based on the target platform. Ignore everything but crate name and the item name. - path.first().is_some_and(|s| s.as_str() == "libc") && path.last().is_some_and(|s| s.as_str() == name) + path.first().is_some_and(|s| *s == sym::libc) && path.last().is_some_and(|s| s.as_str() == name) } /// Returns the list of condition expressions and the list of blocks in a @@ -3101,7 +3101,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { sm.span_take_while(span, |&ch| ch == ' ' || ch == ';') } -/// Returns whether the given let pattern and else body can be turned into a question mark +/// Returns whether the given let pattern and else body can be turned into the `?` operator /// /// For this example: /// ```ignore @@ -3124,8 +3124,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { /// ``` /// /// We output `Some(a)` in the first instance, and `Some(FooBar { a, b })` in the second, because -/// the question mark operator is applicable here. Callers have to check whether we are in a -/// constant or not. +/// the `?` operator is applicable here. Callers have to check whether we are in a constant or not. pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( cx: &LateContext<'_>, pat: &'a Pat<'hir>, diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 1a30b473d106..38f077134c03 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -30,33 +30,75 @@ macro_rules! generate { } generate! { + abs, as_bytes, as_deref_mut, as_deref, as_mut, Binary, + build_hasher, + cargo_clippy: "cargo-clippy", Cargo_toml: "Cargo.toml", + cast, + chars, CLIPPY_ARGS, CLIPPY_CONF_DIR, + clone_into, cloned, + collect, contains, copied, + CRLF: "\r\n", Current, + ends_with, + exp, + extend, + finish_non_exhaustive, + finish, + flat_map, + for_each, + from_raw, + from_str_radix, get, insert, int_roundings, + into_bytes, + into_owned, IntoIter, + is_ascii, is_empty, + is_err, + is_none, is_ok, is_some, + last, + LF: "\n", LowerExp, LowerHex, + max, + min, + mode, msrv, Octal, or_default, + parse, + push, regex, + reserve, + resize, + restriction, rustfmt_skip, + set_len, + set_mode, + set_readonly, + signum, + split_whitespace, + split, Start, + take, + TBD, + then_some, + to_digit, to_owned, unused_extern_crates, unwrap_err, @@ -66,4 +108,6 @@ generate! { V4, V6, Weak, + with_capacity, + wrapping_offset, } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d2f79da1a541..39c7f0e4ad5a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-04-22" +channel = "nightly-2025-05-01" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/tests/ui-internal/symbol_as_str.fixed b/tests/ui-internal/symbol_as_str.fixed new file mode 100644 index 000000000000..3e26732836ca --- /dev/null +++ b/tests/ui-internal/symbol_as_str.fixed @@ -0,0 +1,21 @@ +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn f(s: Symbol) { + s == sym::f32; + //~^ symbol_as_str + s == sym::proc_dash_macro; + //~^ symbol_as_str + s == kw::SelfLower; + //~^ symbol_as_str + s == sym::msrv; + //~^ symbol_as_str + s == sym::Cargo_toml; + //~^ symbol_as_str + sym::get == s; + //~^ symbol_as_str +} diff --git a/tests/ui-internal/symbol_as_str.rs b/tests/ui-internal/symbol_as_str.rs new file mode 100644 index 000000000000..334c32d18983 --- /dev/null +++ b/tests/ui-internal/symbol_as_str.rs @@ -0,0 +1,21 @@ +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn f(s: Symbol) { + s.as_str() == "f32"; + //~^ symbol_as_str + s.as_str() == "proc-macro"; + //~^ symbol_as_str + s.as_str() == "self"; + //~^ symbol_as_str + s.as_str() == "msrv"; + //~^ symbol_as_str + s.as_str() == "Cargo.toml"; + //~^ symbol_as_str + "get" == s.as_str(); + //~^ symbol_as_str +} diff --git a/tests/ui-internal/symbol_as_str.stderr b/tests/ui-internal/symbol_as_str.stderr new file mode 100644 index 000000000000..39f81f3833c4 --- /dev/null +++ b/tests/ui-internal/symbol_as_str.stderr @@ -0,0 +1,76 @@ +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:9:5 + | +LL | s.as_str() == "f32"; + | ^^^^^^^^^^ + | + = note: `-D clippy::symbol-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` +help: use the preinterned symbol + | +LL - s.as_str() == "f32"; +LL + s == sym::f32; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:11:5 + | +LL | s.as_str() == "proc-macro"; + | ^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - s.as_str() == "proc-macro"; +LL + s == sym::proc_dash_macro; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:13:5 + | +LL | s.as_str() == "self"; + | ^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - s.as_str() == "self"; +LL + s == kw::SelfLower; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:15:5 + | +LL | s.as_str() == "msrv"; + | ^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - s.as_str() == "msrv"; +LL + s == sym::msrv; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:17:5 + | +LL | s.as_str() == "Cargo.toml"; + | ^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - s.as_str() == "Cargo.toml"; +LL + s == sym::Cargo_toml; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:19:14 + | +LL | "get" == s.as_str(); + | ^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - "get" == s.as_str(); +LL + sym::get == s; + | + +error: aborting due to 6 previous errors + diff --git a/tests/ui-internal/symbol_as_str_unfixable.rs b/tests/ui-internal/symbol_as_str_unfixable.rs new file mode 100644 index 000000000000..635f28007e9a --- /dev/null +++ b/tests/ui-internal/symbol_as_str_unfixable.rs @@ -0,0 +1,15 @@ +//@no-rustfix: paths that don't exist yet +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::Symbol; + +fn f(s: Symbol) { + s.as_str() == "xyz123"; + //~^ symbol_as_str + s.as_str() == "with-dash"; + //~^ symbol_as_str + s.as_str() == "with.dot"; + //~^ symbol_as_str +} diff --git a/tests/ui-internal/symbol_as_str_unfixable.stderr b/tests/ui-internal/symbol_as_str_unfixable.stderr new file mode 100644 index 000000000000..5349983ca519 --- /dev/null +++ b/tests/ui-internal/symbol_as_str_unfixable.stderr @@ -0,0 +1,40 @@ +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:9:5 + | +LL | s.as_str() == "xyz123"; + | ^^^^^^^^^^ + | + = note: `-D clippy::symbol-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` +help: add the symbol to `clippy_utils/src/sym.rs` and use it + | +LL - s.as_str() == "xyz123"; +LL + s == sym::xyz123; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:11:5 + | +LL | s.as_str() == "with-dash"; + | ^^^^^^^^^^ + | +help: add the symbol to `clippy_utils/src/sym.rs` and use it + | +LL - s.as_str() == "with-dash"; +LL + s == sym::with_dash; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:13:5 + | +LL | s.as_str() == "with.dot"; + | ^^^^^^^^^^ + | +help: add the symbol to `clippy_utils/src/sym.rs` and use it + | +LL - s.as_str() == "with.dot"; +LL + s == sym::with_dot; + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/blocks_in_conditions.fixed b/tests/ui/blocks_in_conditions.fixed index e696896538e5..6ae5b0cb2f04 100644 --- a/tests/ui/blocks_in_conditions.fixed +++ b/tests/ui/blocks_in_conditions.fixed @@ -4,7 +4,6 @@ #![allow( unused, unnecessary_transmutes, - clippy::let_and_return, clippy::needless_if, clippy::missing_transmute_annotations )] diff --git a/tests/ui/blocks_in_conditions.rs b/tests/ui/blocks_in_conditions.rs index 8c8f3249b8a7..3fd060620728 100644 --- a/tests/ui/blocks_in_conditions.rs +++ b/tests/ui/blocks_in_conditions.rs @@ -4,7 +4,6 @@ #![allow( unused, unnecessary_transmutes, - clippy::let_and_return, clippy::needless_if, clippy::missing_transmute_annotations )] diff --git a/tests/ui/blocks_in_conditions.stderr b/tests/ui/blocks_in_conditions.stderr index 41ff59c683e8..282c42a98bfc 100644 --- a/tests/ui/blocks_in_conditions.stderr +++ b/tests/ui/blocks_in_conditions.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions.rs:31:5 + --> tests/ui/blocks_in_conditions.rs:30:5 | LL | / if { LL | | @@ -20,13 +20,13 @@ LL ~ }; if res { | error: omit braces around single expression condition - --> tests/ui/blocks_in_conditions.rs:43:8 + --> tests/ui/blocks_in_conditions.rs:42:8 | LL | if { true } { 6 } else { 10 } | ^^^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> tests/ui/blocks_in_conditions.rs:49:8 + --> tests/ui/blocks_in_conditions.rs:48:8 | LL | if true && x == 3 { 6 } else { 10 } | ^^^^^^^^^^^^^^ help: try: `x == 3` diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 166b1387ba26..ce8b67f9ca7b 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -103,3 +103,39 @@ fn main() { external!({ if let 2 = $a {} }); } + +mod issue8710 { + fn str_ref(cs: &[char]) { + if matches!(cs.iter().next(), Some('i')) { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn i32_ref(cs: &[i32]) { + if matches!(cs.iter().next(), Some(1)) { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn enum_ref() { + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + enum MyEnum { + A(i32), + B, + } + + fn get_enum() -> Option<&'static MyEnum> { + todo!() + } + + if matches!(get_enum(), Some(MyEnum::B)) { + //~^ equatable_if_let + } else { + todo!(); + } + } +} diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 09c2483ae6d4..ff09533f2651 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -103,3 +103,39 @@ fn main() { external!({ if let 2 = $a {} }); } + +mod issue8710 { + fn str_ref(cs: &[char]) { + if let Some('i') = cs.iter().next() { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn i32_ref(cs: &[i32]) { + if let Some(1) = cs.iter().next() { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn enum_ref() { + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + enum MyEnum { + A(i32), + B, + } + + fn get_enum() -> Option<&'static MyEnum> { + todo!() + } + + if let Some(MyEnum::B) = get_enum() { + //~^ equatable_if_let + } else { + todo!(); + } + } +} diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index 81e0e15a5c74..dd1832ad68b2 100644 --- a/tests/ui/equatable_if_let.stderr +++ b/tests/ui/equatable_if_let.stderr @@ -85,5 +85,23 @@ error: this pattern matching can be expressed using equality LL | if let inline!("abc") = "abc" { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"abc" == inline!("abc")` -error: aborting due to 14 previous errors +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:109:12 + | +LL | if let Some('i') = cs.iter().next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some('i'))` + +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:117:12 + | +LL | if let Some(1) = cs.iter().next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some(1))` + +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:135:12 + | +LL | if let Some(MyEnum::B) = get_enum() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(get_enum(), Some(MyEnum::B))` + +error: aborting due to 17 previous errors diff --git a/tests/ui/manual_div_ceil.fixed b/tests/ui/manual_div_ceil.fixed index 57fe8917afe8..58ee6978fc12 100644 --- a/tests/ui/manual_div_ceil.fixed +++ b/tests/ui/manual_div_ceil.fixed @@ -1,5 +1,21 @@ #![warn(clippy::manual_div_ceil)] +macro_rules! y { + () => { + let x = 33u32; + let _ = x.div_ceil(8); + //~^ manual_div_ceil + let _ = x.div_ceil(8); + //~^ manual_div_ceil + }; +} + +macro_rules! eight { + () => { + 8 + }; +} + fn main() { let x = 7_u32; let y = 4_u32; @@ -32,6 +48,13 @@ fn main() { let _ = (z as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; + + // Test lint with macro + y!(); + + // Also test if RHS should be result of macro expansion + let _ = 33u32.div_ceil(eight!()); + //~^ manual_div_ceil } fn issue_13843() { diff --git a/tests/ui/manual_div_ceil.rs b/tests/ui/manual_div_ceil.rs index ec343513e5ce..aa0d81b22a0e 100644 --- a/tests/ui/manual_div_ceil.rs +++ b/tests/ui/manual_div_ceil.rs @@ -1,5 +1,21 @@ #![warn(clippy::manual_div_ceil)] +macro_rules! y { + () => { + let x = 33u32; + let _ = (x + 7) / 8; + //~^ manual_div_ceil + let _ = (7 + x) / 8; + //~^ manual_div_ceil + }; +} + +macro_rules! eight { + () => { + 8 + }; +} + fn main() { let x = 7_u32; let y = 4_u32; @@ -32,6 +48,13 @@ fn main() { let _ = (z as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; + + // Test lint with macro + y!(); + + // Also test if RHS should be result of macro expansion + let _ = (33u32 + 7) / eight!(); + //~^ manual_div_ceil } fn issue_13843() { diff --git a/tests/ui/manual_div_ceil.stderr b/tests/ui/manual_div_ceil.stderr index 8e14ab274269..9be5a19bf391 100644 --- a/tests/ui/manual_div_ceil.stderr +++ b/tests/ui/manual_div_ceil.stderr @@ -1,5 +1,5 @@ error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:9:13 + --> tests/ui/manual_div_ceil.rs:25:13 | LL | let _ = (x + (y - 1)) / y; | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` @@ -8,94 +8,122 @@ LL | let _ = (x + (y - 1)) / y; = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:11:13 + --> tests/ui/manual_div_ceil.rs:27:13 | LL | let _ = ((y - 1) + x) / y; | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:13:13 + --> tests/ui/manual_div_ceil.rs:29:13 | LL | let _ = (x + y - 1) / y; | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:16:13 + --> tests/ui/manual_div_ceil.rs:32:13 | LL | let _ = (7_u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_u32.div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:18:13 + --> tests/ui/manual_div_ceil.rs:34:13 | LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:39:13 + --> tests/ui/manual_div_ceil.rs:6:17 + | +LL | let _ = (x + 7) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` +... +LL | y!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:8:17 + | +LL | let _ = (7 + x) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` +... +LL | y!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:56:13 + | +LL | let _ = (33u32 + 7) / eight!(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `33u32.div_ceil(eight!())` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:62:13 | LL | let _ = (2048 + x - 1) / x; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:43:13 + --> tests/ui/manual_div_ceil.rs:66:13 | LL | let _ = (2048usize + x - 1) / x; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:47:13 + --> tests/ui/manual_div_ceil.rs:70:13 | LL | let _ = (2048_usize + x - 1) / x; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:51:13 + --> tests/ui/manual_div_ceil.rs:74:13 | LL | let _ = (x + 4 - 1) / 4; | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:54:18 + --> tests/ui/manual_div_ceil.rs:77:18 | LL | let _: u32 = (2048 + 6 - 1) / 6; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:56:20 + --> tests/ui/manual_div_ceil.rs:79:20 | LL | let _: usize = (2048 + 6 - 1) / 6; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:58:18 + --> tests/ui/manual_div_ceil.rs:81:18 | LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:61:13 + --> tests/ui/manual_div_ceil.rs:84:13 | LL | let _ = (2048 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:64:13 + --> tests/ui/manual_div_ceil.rs:87:13 | LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:70:13 + --> tests/ui/manual_div_ceil.rs:93:13 | LL | let _ = (x + 7) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:72:13 + --> tests/ui/manual_div_ceil.rs:95:13 | LL | let _ = (7 + x) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` -error: aborting due to 16 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/needless_if.fixed b/tests/ui/needless_if.fixed index 6208ca19b82b..347dbff7c595 100644 --- a/tests/ui/needless_if.fixed +++ b/tests/ui/needless_if.fixed @@ -46,9 +46,7 @@ fn main() { if let true = true && true {} - if true - && let true = true - {} + if true && let true = true {} // Can lint nested `if let`s ({ //~^ needless_if diff --git a/tests/ui/needless_if.rs b/tests/ui/needless_if.rs index b459ff877be6..5e0f2a14408b 100644 --- a/tests/ui/needless_if.rs +++ b/tests/ui/needless_if.rs @@ -46,9 +46,7 @@ fn main() { if let true = true && true {} - if true - && let true = true - {} + if true && let true = true {} // Can lint nested `if let`s if { //~^ needless_if diff --git a/tests/ui/needless_if.stderr b/tests/ui/needless_if.stderr index eeb8d044526d..62cdf2459448 100644 --- a/tests/ui/needless_if.stderr +++ b/tests/ui/needless_if.stderr @@ -31,7 +31,7 @@ LL + }); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:53:5 + --> tests/ui/needless_if.rs:51:5 | LL | / if { LL | | @@ -57,19 +57,19 @@ LL + } && true); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:98:5 + --> tests/ui/needless_if.rs:96:5 | LL | if { maybe_side_effect() } {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() });` error: this `if` branch is empty - --> tests/ui/needless_if.rs:101:5 + --> tests/ui/needless_if.rs:99:5 | LL | if { maybe_side_effect() } && true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() } && true);` error: this `if` branch is empty - --> tests/ui/needless_if.rs:106:5 + --> tests/ui/needless_if.rs:104:5 | LL | if true {} | ^^^^^^^^^^ help: you can remove it: `true;` diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index 391d4bc3fcc7..f832752ccd79 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -246,9 +246,7 @@ fn does_not_lint() { } let x; - if true - && let Some(n) = Some("let chains too") - { + if true && let Some(n) = Some("let chains too") { x = 1; } else { x = 2; diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 6096e8300e1a..a52fbf529234 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -246,9 +246,7 @@ fn does_not_lint() { } let x; - if true - && let Some(n) = Some("let chains too") - { + if true && let Some(n) = Some("let chains too") { x = 1; } else { x = 2; diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index e7c36136847b..b24c12758816 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -276,7 +276,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:302:5 + --> tests/ui/needless_late_init.rs:300:5 | LL | let r; | ^^^^^^ created here diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index 55da4f28976c..8516cee48e67 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -1,100 +1,188 @@ -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:20:12 | LL | return Some(to.magic?); - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-question-mark` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_question_mark)]` +help: remove the enclosing `Some` and `?` operator + | +LL - return Some(to.magic?); +LL + return to.magic; + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:29:12 | LL | return Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - return Some(to.magic?) +LL + return to.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:35:5 | LL | Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(to.magic?) +LL + to.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:41:21 | LL | to.and_then(|t| Some(t.magic?)) - | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` + | ^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - to.and_then(|t| Some(t.magic?)) +LL + to.and_then(|t| t.magic) + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:51:9 | LL | Some(t.magic?) - | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` + | ^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(t.magic?) +LL + t.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:57:12 | LL | return Ok(tr.magic?); - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(tr.magic?); +LL + return tr.magic; + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:65:12 | LL | return Ok(tr.magic?) - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(tr.magic?) +LL + return tr.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:70:5 | LL | Ok(tr.magic?) - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(tr.magic?) +LL + tr.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:75:21 | LL | tr.and_then(|t| Ok(t.magic?)) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - tr.and_then(|t| Ok(t.magic?)) +LL + tr.and_then(|t| t.magic) + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:84:9 | LL | Ok(t.magic?) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(t.magic?) +LL + t.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:92:16 | LL | return Ok(t.magic?); - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(t.magic?); +LL + return t.magic; + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:128:27 | LL | || -> Option<_> { Some(Some($expr)?) }() - | ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)` + | ^^^^^^^^^^^^^^^^^^ ... LL | let _x = some_and_qmark_in_macro!(x?); | ---------------------------- in this macro invocation | = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) +help: remove the enclosing `Some` and `?` operator + | +LL - || -> Option<_> { Some(Some($expr)?) }() +LL + || -> Option<_> { Some($expr) }() + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:140:5 | LL | Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(to.magic?) +LL + to.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:149:5 | LL | Ok(s.magic?) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(s.magic?) +LL + s.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:154:7 | LL | { Some(a?) } - | ^^^^^^^^ help: try removing question mark and `Some()`: `a` + | ^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - { Some(a?) } +LL + { a } + | error: aborting due to 15 previous errors diff --git a/tests/ui/ptr_eq.fixed b/tests/ui/ptr_eq.fixed index 484ff3073239..9629b3eea587 100644 --- a/tests/ui/ptr_eq.fixed +++ b/tests/ui/ptr_eq.fixed @@ -23,23 +23,25 @@ fn main() { //~^ ptr_eq let _ = std::ptr::eq(a, b); //~^ ptr_eq - let _ = std::ptr::eq(a.as_ptr(), b as *const _); - //~^ ptr_eq - let _ = std::ptr::eq(a.as_ptr(), b.as_ptr()); - //~^ ptr_eq + + // Do not lint: the rhs conversion is needed + let _ = a.as_ptr() == b as *const _; + + // Do not lint: we have two raw pointers already + let _ = a.as_ptr() == b.as_ptr(); // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; - let _ = std::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _); - //~^ ptr_eq - let _ = std::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr()); - //~^ ptr_eq + // Do not lint: the rhs conversion is needed + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + + // Do not lint: we have two raw pointers already + let _ = a.as_mut_ptr() == b.as_mut_ptr(); let _ = a == b; let _ = core::ptr::eq(a, b); @@ -51,8 +53,12 @@ fn main() { let _ = !std::ptr::eq(x, y); //~^ ptr_eq - #[allow(clippy::eq_op)] - let _issue14337 = std::ptr::eq(main as *const (), main as *const ()); + #[expect(clippy::eq_op)] + // Do not lint: casts are needed to not change type + let _issue14337 = main as *const () == main as *const (); + + // Do not peel the content of macros + let _ = std::ptr::eq(mac!(cast a), mac!(cast b)); //~^ ptr_eq // Do not peel the content of macros diff --git a/tests/ui/ptr_eq.rs b/tests/ui/ptr_eq.rs index f28707cc3e92..2b741d8df468 100644 --- a/tests/ui/ptr_eq.rs +++ b/tests/ui/ptr_eq.rs @@ -23,23 +23,25 @@ fn main() { //~^ ptr_eq let _ = a as *const _ == b as *const _; //~^ ptr_eq + + // Do not lint: the rhs conversion is needed let _ = a.as_ptr() == b as *const _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_ptr() == b.as_ptr(); - //~^ ptr_eq // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; + // Do not lint: the rhs conversion is needed let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_mut_ptr() == b.as_mut_ptr(); - //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); @@ -51,8 +53,12 @@ fn main() { let _ = x as *const u32 != y as *mut u32 as *const u32; //~^ ptr_eq - #[allow(clippy::eq_op)] + #[expect(clippy::eq_op)] + // Do not lint: casts are needed to not change type let _issue14337 = main as *const () == main as *const (); + + // Do not peel the content of macros + let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; //~^ ptr_eq // Do not peel the content of macros diff --git a/tests/ui/ptr_eq.stderr b/tests/ui/ptr_eq.stderr index 906831b9e031..e7340624b595 100644 --- a/tests/ui/ptr_eq.stderr +++ b/tests/ui/ptr_eq.stderr @@ -14,52 +14,28 @@ LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:26:13 - | -LL | let _ = a.as_ptr() == b as *const _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b as *const _)` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:28:13 - | -LL | let _ = a.as_ptr() == b.as_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b.as_ptr())` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:39:13 - | -LL | let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _)` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:41:13 - | -LL | let _ = a.as_mut_ptr() == b.as_mut_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr())` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:48:13 + --> tests/ui/ptr_eq.rs:50:13 | LL | let _ = x as *const u32 == y as *mut u32 as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(x, y)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:51:13 + --> tests/ui/ptr_eq.rs:53:13 | LL | let _ = x as *const u32 != y as *mut u32 as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!std::ptr::eq(x, y)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:55:23 - | -LL | let _issue14337 = main as *const () == main as *const (); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(main as *const (), main as *const ())` - -error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:59:13 + --> tests/ui/ptr_eq.rs:61:13 | LL | let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(mac!(cast a), mac!(cast b))` -error: aborting due to 10 previous errors +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:65:13 + | +LL | let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(mac!(cast a), mac!(cast b))` + +error: aborting due to 6 previous errors diff --git a/tests/ui/ptr_eq_no_std.fixed b/tests/ui/ptr_eq_no_std.fixed index d8ee4ea88f84..48cbad62e1a3 100644 --- a/tests/ui/ptr_eq_no_std.fixed +++ b/tests/ui/ptr_eq_no_std.fixed @@ -32,23 +32,25 @@ fn main() { //~^ ptr_eq let _ = core::ptr::eq(a, b); //~^ ptr_eq - let _ = core::ptr::eq(a.as_ptr(), b as *const _); - //~^ ptr_eq - let _ = core::ptr::eq(a.as_ptr(), b.as_ptr()); - //~^ ptr_eq + + // Do not lint: the rhs conversion is needed + let _ = a.as_ptr() == b as *const _; + + // Do not lint: we have two raw pointers already + let _ = a.as_ptr() == b.as_ptr(); // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; - let _ = core::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _); - //~^ ptr_eq - let _ = core::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr()); - //~^ ptr_eq + // Do not lint: the rhs conversion is needed + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + + // Do not lint: we have two raw pointers already + let _ = a.as_mut_ptr() == b.as_mut_ptr(); let _ = a == b; let _ = core::ptr::eq(a, b); diff --git a/tests/ui/ptr_eq_no_std.rs b/tests/ui/ptr_eq_no_std.rs index a236314c29b7..3827178640ee 100644 --- a/tests/ui/ptr_eq_no_std.rs +++ b/tests/ui/ptr_eq_no_std.rs @@ -32,23 +32,25 @@ fn main() { //~^ ptr_eq let _ = a as *const _ == b as *const _; //~^ ptr_eq + + // Do not lint: the rhs conversion is needed let _ = a.as_ptr() == b as *const _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_ptr() == b.as_ptr(); - //~^ ptr_eq // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; + // Do not lint: the rhs conversion is needed let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - //~^ ptr_eq + + // Do not lint: we have two raw pointers already let _ = a.as_mut_ptr() == b.as_mut_ptr(); - //~^ ptr_eq let _ = a == b; let _ = core::ptr::eq(a, b); diff --git a/tests/ui/ptr_eq_no_std.stderr b/tests/ui/ptr_eq_no_std.stderr index 5b8135dc8e8b..8c7b1ff76661 100644 --- a/tests/ui/ptr_eq_no_std.stderr +++ b/tests/ui/ptr_eq_no_std.stderr @@ -13,29 +13,5 @@ error: use `core::ptr::eq` when comparing raw pointers LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a, b)` -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:35:13 - | -LL | let _ = a.as_ptr() == b as *const _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_ptr(), b as *const _)` - -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:37:13 - | -LL | let _ = a.as_ptr() == b.as_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_ptr(), b.as_ptr())` - -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:48:13 - | -LL | let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _)` - -error: use `core::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq_no_std.rs:50:13 - | -LL | let _ = a.as_mut_ptr() == b.as_mut_ptr(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr())` - -error: aborting due to 6 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 6bd071d07f52..507bc2b29d86 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -301,6 +301,11 @@ fn pattern() -> Result<(), PatternedError> { res } +fn expect_expr(a: Option) -> Option { + #[expect(clippy::needless_question_mark)] + Some(a?) +} + fn main() {} // `?` is not the same as `return None;` if inside of a try block diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index dd093c9bf480..64b51b849ede 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -371,6 +371,11 @@ fn pattern() -> Result<(), PatternedError> { res } +fn expect_expr(a: Option) -> Option { + #[expect(clippy::needless_question_mark)] + Some(a?) +} + fn main() {} // `?` is not the same as `return None;` if inside of a try block diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 8fe04b895cea..d8ce4420aeeb 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -198,7 +198,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:390:13 + --> tests/ui/question_mark.rs:395:13 | LL | / if a.is_none() { LL | | @@ -208,7 +208,7 @@ LL | | } | |_____________^ help: replace it with: `a?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:451:5 + --> tests/ui/question_mark.rs:456:5 | LL | / let Some(v) = bar.foo.owned.clone() else { LL | | return None; @@ -216,7 +216,7 @@ LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:466:5 + --> tests/ui/question_mark.rs:471:5 | LL | / let Some(ref x) = foo.opt_x else { LL | | return None; @@ -224,7 +224,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:476:5 + --> tests/ui/question_mark.rs:481:5 | LL | / let Some(ref mut x) = foo.opt_x else { LL | | return None; @@ -232,7 +232,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:487:5 + --> tests/ui/question_mark.rs:492:5 | LL | / let Some(ref x @ ref y) = foo.opt_x else { LL | | return None; @@ -240,7 +240,7 @@ LL | | }; | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:491:5 + --> tests/ui/question_mark.rs:496:5 | LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -248,7 +248,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:495:5 + --> tests/ui/question_mark.rs:500:5 | LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -256,7 +256,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:517:5 + --> tests/ui/question_mark.rs:522:5 | LL | / if arg.is_none() { LL | | @@ -265,7 +265,7 @@ LL | | } | |_____^ help: replace it with: `arg?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:521:15 + --> tests/ui/question_mark.rs:526:15 | LL | let val = match arg { | _______________^ @@ -276,7 +276,7 @@ LL | | }; | |_____^ help: try instead: `arg?` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:531:5 + --> tests/ui/question_mark.rs:536:5 | LL | / let Some(a) = *a else { LL | | return None; diff --git a/tests/ui/question_mark_used.stderr b/tests/ui/question_mark_used.stderr index 53cb59c02166..82f0d3250407 100644 --- a/tests/ui/question_mark_used.stderr +++ b/tests/ui/question_mark_used.stderr @@ -1,4 +1,4 @@ -error: question mark operator was used +error: the `?` operator was used --> tests/ui/question_mark_used.rs:11:5 | LL | other_function()?; diff --git a/tests/ui/return_and_then.stderr b/tests/ui/return_and_then.stderr index cc611c3dba67..a7acbe7b3401 100644 --- a/tests/ui/return_and_then.stderr +++ b/tests/ui/return_and_then.stderr @@ -1,4 +1,4 @@ -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:5:9 | LL | / opt.and_then(|n| { @@ -20,7 +20,7 @@ LL + ret += n; LL + if n > 1 { Some(ret) } else { None } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:14:9 | LL | opt.and_then(|n| test_opt_block(Some(n))) @@ -32,7 +32,7 @@ LL ~ let n = opt?; LL + test_opt_block(Some(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:19:9 | LL | gen_option(1).and_then(|n| test_opt_block(Some(n))) @@ -44,7 +44,7 @@ LL ~ let n = gen_option(1)?; LL + test_opt_block(Some(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:24:9 | LL | opt.and_then(|n| if n > 1 { Ok(n + 1) } else { Err(n) }) @@ -56,7 +56,7 @@ LL ~ let n = opt?; LL + if n > 1 { Ok(n + 1) } else { Err(n) } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:29:9 | LL | opt.and_then(|n| test_res_block(Ok(n))) @@ -68,7 +68,7 @@ LL ~ let n = opt?; LL + test_res_block(Ok(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:35:9 | LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) @@ -80,7 +80,7 @@ LL ~ let x = Some("")?; LL + if x.len() > 2 { Some(3) } else { None } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:41:9 | LL | / Some(match (vec![1, 2, 3], vec![1, 2, 4]) { diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index ba167e79a308..91ff4b9ee771 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -266,7 +266,21 @@ mod fixable { // Issue #11968: The suggestion for this lint removes the parentheses and leave the code as // `*x.pow(2)` which tries to dereference the return value rather than `x`. fn issue_11968(x: &usize) -> usize { - { *x }.pow(2) + (*x).pow(2) + //~^ unnecessary_cast + } + + #[allow(clippy::cast_lossless)] + fn issue_14640() { + let x = 5usize; + let vec: Vec = vec![1, 2, 3, 4, 5]; + assert_eq!(vec.len(), x); + //~^ unnecessary_cast + + let _ = (5i32 as i64).abs(); + //~^ unnecessary_cast + + let _ = 5i32 as i64; //~^ unnecessary_cast } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 0f90a8b05965..5444a914db16 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -269,4 +269,18 @@ mod fixable { (*x as usize).pow(2) //~^ unnecessary_cast } + + #[allow(clippy::cast_lossless)] + fn issue_14640() { + let x = 5usize; + let vec: Vec = vec![1, 2, 3, 4, 5]; + assert_eq!(vec.len(), x as usize); + //~^ unnecessary_cast + + let _ = (5i32 as i64 as i64).abs(); + //~^ unnecessary_cast + + let _ = 5i32 as i64 as i64; + //~^ unnecessary_cast + } } diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index c83770c1a299..3e3c5eb81c10 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -245,7 +245,25 @@ error: casting to the same type is unnecessary (`usize` -> `usize`) --> tests/ui/unnecessary_cast.rs:269:9 | LL | (*x as usize).pow(2) - | ^^^^^^^^^^^^^ help: try: `{ *x }` + | ^^^^^^^^^^^^^ help: try: `(*x)` -error: aborting due to 41 previous errors +error: casting to the same type is unnecessary (`usize` -> `usize`) + --> tests/ui/unnecessary_cast.rs:277: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:280: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:283:17 + | +LL | let _ = 5i32 as i64 as i64; + | ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64` + +error: aborting due to 44 previous errors diff --git a/tests/ui/unused_unit.edition2021.fixed b/tests/ui/unused_unit.edition2021.fixed new file mode 100644 index 000000000000..93dd58b8e9d7 --- /dev/null +++ b/tests/ui/unused_unit.edition2021.fixed @@ -0,0 +1,146 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +#![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] +#![allow(clippy::from_over_into)] + +struct Unitter; +impl Unitter { + #[allow(clippy::no_effect)] + pub fn get_unit(&self, f: F, _g: G) + //~^ unused_unit + //~| unused_unit + where G: Fn() { + //~^ unused_unit + let _y: &dyn Fn() = &f; + //~^ unused_unit + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) { + //~^ unused_unit + + //~^ unused_unit + } +} + +trait Trait { + fn redundant(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn(); + //~^ unused_unit +} + +impl Trait for Unitter { + fn redundant(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn() {} + //~^ unused_unit +} + +fn return_unit() { } +//~^ unused_unit +//~| unused_unit + +#[allow(clippy::needless_return)] +#[allow(clippy::never_loop)] +#[allow(clippy::unit_cmp)] +fn main() { + let u = Unitter; + assert_eq!(u.get_unit(|| {}, return_unit), u.into()); + return_unit(); + loop { + break; + //~^ unused_unit + } + return; + //~^ unused_unit +} + +// https://github.com/rust-lang/rust-clippy/issues/4076 +fn foo() { + macro_rules! foo { + (recv($r:expr) -> $res:pat => $body:expr) => { + $body + } + } + + foo! { + recv(rx) -> _x => () + } +} + +#[rustfmt::skip] +fn test(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test2(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test3(){} +//~^ unused_unit + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} + +mod issue9949 { + fn main() { + #[doc = "documentation"] + () + } +} + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/tests/ui/unused_unit.edition2021.stderr b/tests/ui/unused_unit.edition2021.stderr new file mode 100644 index 000000000000..13cc20d4d7ad --- /dev/null +++ b/tests/ui/unused_unit.edition2021.stderr @@ -0,0 +1,128 @@ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:37:9 + | +LL | () + | ^^ help: remove the final `()` + | +note: the lint level is defined here + --> tests/ui/unused_unit.rs:15:9 + | +LL | #![deny(clippy::unused_unit)] + | ^^^^^^^^^^^^^^^^^^^ + +error: unneeded unit expression + --> tests/ui/unused_unit.rs:62:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:28 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:25:18 + | +LL | where G: Fn() -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:58 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:27:26 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:35:18 + | +LL | fn into(self) -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:43:29 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:46:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:48:16 + | +LL | H: Fn() -> (); + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:53:29 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:56:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:58:16 + | +LL | H: Fn() -> () {} + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:62:17 + | +LL | fn return_unit() -> () { () } + | ^^^^^^ help: remove the `-> ()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:74:14 + | +LL | break(); + | ^^ help: remove the `()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:77:11 + | +LL | return(); + | ^^ help: remove the `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:95:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:99:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:103:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:136:15 + | +LL | run(|| -> () { todo!() }); + | ^^^^^^ help: remove the `-> ()` + +error: aborting due to 20 previous errors + diff --git a/tests/ui/unused_unit.edition2024.fixed b/tests/ui/unused_unit.edition2024.fixed new file mode 100644 index 000000000000..987d901b97df --- /dev/null +++ b/tests/ui/unused_unit.edition2024.fixed @@ -0,0 +1,146 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +#![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] +#![allow(clippy::from_over_into)] + +struct Unitter; +impl Unitter { + #[allow(clippy::no_effect)] + pub fn get_unit(&self, f: F, _g: G) + //~^ unused_unit + //~| unused_unit + where G: Fn() { + //~^ unused_unit + let _y: &dyn Fn() = &f; + //~^ unused_unit + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) { + //~^ unused_unit + + //~^ unused_unit + } +} + +trait Trait { + fn redundant(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn(); + //~^ unused_unit +} + +impl Trait for Unitter { + fn redundant(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn() {} + //~^ unused_unit +} + +fn return_unit() { } +//~^ unused_unit +//~| unused_unit + +#[allow(clippy::needless_return)] +#[allow(clippy::never_loop)] +#[allow(clippy::unit_cmp)] +fn main() { + let u = Unitter; + assert_eq!(u.get_unit(|| {}, return_unit), u.into()); + return_unit(); + loop { + break; + //~^ unused_unit + } + return; + //~^ unused_unit +} + +// https://github.com/rust-lang/rust-clippy/issues/4076 +fn foo() { + macro_rules! foo { + (recv($r:expr) -> $res:pat => $body:expr) => { + $body + } + } + + foo! { + recv(rx) -> _x => () + } +} + +#[rustfmt::skip] +fn test(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test2(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test3(){} +//~^ unused_unit + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} + +mod issue9949 { + fn main() { + #[doc = "documentation"] + () + } +} + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| -> () { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/tests/ui/unused_unit.edition2024.stderr b/tests/ui/unused_unit.edition2024.stderr new file mode 100644 index 000000000000..a79e70e066bd --- /dev/null +++ b/tests/ui/unused_unit.edition2024.stderr @@ -0,0 +1,122 @@ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:37:9 + | +LL | () + | ^^ help: remove the final `()` + | +note: the lint level is defined here + --> tests/ui/unused_unit.rs:15:9 + | +LL | #![deny(clippy::unused_unit)] + | ^^^^^^^^^^^^^^^^^^^ + +error: unneeded unit expression + --> tests/ui/unused_unit.rs:62:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:28 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:25:18 + | +LL | where G: Fn() -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:58 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:27:26 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:35:18 + | +LL | fn into(self) -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:43:29 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:46:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:48:16 + | +LL | H: Fn() -> (); + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:53:29 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:56:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:58:16 + | +LL | H: Fn() -> () {} + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:62:17 + | +LL | fn return_unit() -> () { () } + | ^^^^^^ help: remove the `-> ()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:74:14 + | +LL | break(); + | ^^ help: remove the `()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:77:11 + | +LL | return(); + | ^^ help: remove the `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:95:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:99:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:103:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: aborting due to 19 previous errors + diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index e3c02681c9fd..6668bf90c092 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -120,3 +120,24 @@ mod issue9949 { () } } + +#[clippy::msrv = "1.85"] +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run(f: impl FnOnce() -> R) { + f(); + } + + fn bar() { + run(|| -> () { todo!() }); + } + + struct UnitStruct; + impl UnitStruct { + fn apply Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 4353026c594c..b7645f7b6a26 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -1,4 +1,6 @@ - +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 // The output for humans should just highlight the whole span without showing // the suggested replacement, but we also want to test that suggested @@ -120,3 +122,25 @@ mod issue9949 { () } } + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| -> () { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index 172fe0655028..366f2142095f 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,8 +1,8 @@ -error: unneeded unit return type - --> tests/ui/unused_unit.rs:20:58 +error: unneeded unit expression + --> tests/ui/unused_unit.rs:35:9 | -LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^^ help: remove the `-> ()` +LL | () + | ^^ help: remove the final `()` | note: the lint level is defined here --> tests/ui/unused_unit.rs:13:9 @@ -10,6 +10,12 @@ note: the lint level is defined here LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:60:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + error: unneeded unit return type --> tests/ui/unused_unit.rs:20:28 | @@ -22,6 +28,12 @@ error: unneeded unit return type LL | where G: Fn() -> () { | ^^^^^^ help: remove the `-> ()` +error: unneeded unit return type + --> tests/ui/unused_unit.rs:20:58 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + error: unneeded unit return type --> tests/ui/unused_unit.rs:25:26 | @@ -34,12 +46,6 @@ error: unneeded unit return type LL | fn into(self) -> () { | ^^^^^^ help: remove the `-> ()` -error: unneeded unit expression - --> tests/ui/unused_unit.rs:35:9 - | -LL | () - | ^^ help: remove the final `()` - error: unneeded unit return type --> tests/ui/unused_unit.rs:41:29 | @@ -82,12 +88,6 @@ error: unneeded unit return type LL | fn return_unit() -> () { () } | ^^^^^^ help: remove the `-> ()` -error: unneeded unit expression - --> tests/ui/unused_unit.rs:60:26 - | -LL | fn return_unit() -> () { () } - | ^^ help: remove the final `()` - error: unneeded `()` --> tests/ui/unused_unit.rs:72:14 | diff --git a/tests/ui/zombie_processes.rs b/tests/ui/zombie_processes.rs index 25bbc02ffb76..395f9dd2defb 100644 --- a/tests/ui/zombie_processes.rs +++ b/tests/ui/zombie_processes.rs @@ -176,3 +176,25 @@ fn return_wait() -> ExitStatus { let mut x = Command::new("").spawn().unwrap(); return x.wait().unwrap(); } + +mod issue14677 { + use std::io; + use std::process::Command; + + fn do_something Result<(), ()>>(f: F) { + todo!() + } + + fn foo() { + let mut child = Command::new("true").spawn().unwrap(); + let some_condition = true; + do_something(|| { + if some_condition { + return Err(()); + } + Ok(()) + }); + child.kill().unwrap(); + child.wait().unwrap(); + } +} diff --git a/util/etc/pre-commit.sh b/util/etc/pre-commit.sh index 5dd2ba3d5f53..528f8953b25d 100755 --- a/util/etc/pre-commit.sh +++ b/util/etc/pre-commit.sh @@ -6,7 +6,6 @@ set -e # Update lints cargo dev update_lints git add clippy_lints/src/lib.rs -git add clippy_lints/src/lib.*.rs # Formatting: # Git will not automatically add the formatted code to the staged changes once From 1047a7c43281453f30eb30f39000f69bdfe74e8f Mon Sep 17 00:00:00 2001 From: ALBIN BABU VARGHESE Date: Thu, 1 May 2025 17:13:58 -0400 Subject: [PATCH 040/245] Added a .gitattributes file for showing cargo.lock diffs (#659) --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000000..b9cd1111c8d0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +Cargo.lock linguist-generated=false \ No newline at end of file From 33ea0b482c6f21c655102c2f6f3e9ec19a4623a1 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Thu, 1 May 2025 14:04:42 +0800 Subject: [PATCH 041/245] fix: `manual_slice_fill` FP on `IndexMut` overload --- clippy_lints/src/loops/manual_slice_fill.rs | 4 ++- clippy_utils/src/ty/mod.rs | 7 ++++ tests/ui/manual_slice_fill.fixed | 37 +++++++++++++++++++++ tests/ui/manual_slice_fill.rs | 37 +++++++++++++++++++++ 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops/manual_slice_fill.rs b/clippy_lints/src/loops/manual_slice_fill.rs index 343f7c5d2d12..15c656cc7bc7 100644 --- a/clippy_lints/src/loops/manual_slice_fill.rs +++ b/clippy_lints/src/loops/manual_slice_fill.rs @@ -2,7 +2,7 @@ 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::source::{HasSession, snippet_with_applicability}; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{implements_trait, is_slice_like}; use clippy_utils::visitors::is_local_used; use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment}; use rustc_ast::ast::LitKind; @@ -58,6 +58,8 @@ pub(super) fn check<'tcx>( && let Res::Local(idx_hir) = idx_path.res && !is_local_used(cx, assignval, idx_hir) && msrv.meets(cx, msrvs::SLICE_FILL) + && let slice_ty = cx.typeck_results().expr_ty(slice).peel_refs() + && is_slice_like(cx, slice_ty) { sugg(cx, body, expr, slice.span, assignval.span); } diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 8db9cd593b33..ff11e24c4216 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -1422,3 +1422,10 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<' let mut phantoms = FxHashSet::default(); has_non_owning_mutable_access_inner(cx, &mut phantoms, iter_ty) } + +/// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec`. +pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_slice() + || ty.is_array() + || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())) +} diff --git a/tests/ui/manual_slice_fill.fixed b/tests/ui/manual_slice_fill.fixed index bba863247f5d..d07d1d60e2c1 100644 --- a/tests/ui/manual_slice_fill.fixed +++ b/tests/ui/manual_slice_fill.fixed @@ -123,3 +123,40 @@ fn issue14189() { *b = !*b; } } + +mod issue14685 { + use std::ops::{Index, IndexMut}; + + #[derive(Clone)] + struct ZipList(T); + + impl ZipList { + fn len(&self) -> usize { + todo!() + } + + fn is_empty(&self) -> bool { + todo!() + } + } + + impl Index for ZipList { + type Output = T; + + fn index(&self, _: usize) -> &Self::Output { + todo!() + } + } + + impl IndexMut for ZipList { + fn index_mut(&mut self, _: usize) -> &mut Self::Output { + todo!() + } + } + + fn index_mut(mut zl: ZipList) { + for i in 0..zl.len() { + zl[i] = 6; + } + } +} diff --git a/tests/ui/manual_slice_fill.rs b/tests/ui/manual_slice_fill.rs index 44c60dc40f07..c74ab2225c0a 100644 --- a/tests/ui/manual_slice_fill.rs +++ b/tests/ui/manual_slice_fill.rs @@ -136,3 +136,40 @@ fn issue14189() { *b = !*b; } } + +mod issue14685 { + use std::ops::{Index, IndexMut}; + + #[derive(Clone)] + struct ZipList(T); + + impl ZipList { + fn len(&self) -> usize { + todo!() + } + + fn is_empty(&self) -> bool { + todo!() + } + } + + impl Index for ZipList { + type Output = T; + + fn index(&self, _: usize) -> &Self::Output { + todo!() + } + } + + impl IndexMut for ZipList { + fn index_mut(&mut self, _: usize) -> &mut Self::Output { + todo!() + } + } + + fn index_mut(mut zl: ZipList) { + for i in 0..zl.len() { + zl[i] = 6; + } + } +} From 3564afaa774785ab1400a16b38b0514aa7ab7894 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Sat, 12 Apr 2025 17:49:53 -0700 Subject: [PATCH 042/245] Implement Iterator::last for vec::IntoIter --- tests/ui/double_ended_iterator_last.fixed | 15 ++++++++++++++- tests/ui/double_ended_iterator_last.rs | 15 ++++++++++++++- tests/ui/double_ended_iterator_last.stderr | 4 ++-- tests/ui/double_ended_iterator_last_unfixable.rs | 15 ++++++++++++++- .../double_ended_iterator_last_unfixable.stderr | 4 ++-- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/tests/ui/double_ended_iterator_last.fixed b/tests/ui/double_ended_iterator_last.fixed index 2ce0c04c3017..be31ee5fb486 100644 --- a/tests/ui/double_ended_iterator_last.fixed +++ b/tests/ui/double_ended_iterator_last.fixed @@ -84,6 +84,19 @@ fn issue_14139() { } fn drop_order() { + struct DropDeIterator(std::vec::IntoIter); + impl Iterator for DropDeIterator { + type Item = S; + fn next(&mut self) -> Option { + self.0.next() + } + } + impl DoubleEndedIterator for DropDeIterator { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } + struct S(&'static str); impl std::ops::Drop for S { fn drop(&mut self) { @@ -92,7 +105,7 @@ fn drop_order() { } let v = vec![S("one"), S("two"), S("three")]; - let mut v = v.into_iter(); + let mut v = DropDeIterator(v.into_iter()); println!("Last element is {}", v.next_back().unwrap().0); //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator` println!("Done"); diff --git a/tests/ui/double_ended_iterator_last.rs b/tests/ui/double_ended_iterator_last.rs index a4eb9b3337b9..30864e15bce7 100644 --- a/tests/ui/double_ended_iterator_last.rs +++ b/tests/ui/double_ended_iterator_last.rs @@ -84,6 +84,19 @@ fn issue_14139() { } fn drop_order() { + struct DropDeIterator(std::vec::IntoIter); + impl Iterator for DropDeIterator { + type Item = S; + fn next(&mut self) -> Option { + self.0.next() + } + } + impl DoubleEndedIterator for DropDeIterator { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } + struct S(&'static str); impl std::ops::Drop for S { fn drop(&mut self) { @@ -92,7 +105,7 @@ fn drop_order() { } let v = vec![S("one"), S("two"), S("three")]; - let v = v.into_iter(); + let v = DropDeIterator(v.into_iter()); println!("Last element is {}", v.last().unwrap().0); //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator` println!("Done"); diff --git a/tests/ui/double_ended_iterator_last.stderr b/tests/ui/double_ended_iterator_last.stderr index fe8cf2dcb259..72a6ead47a93 100644 --- a/tests/ui/double_ended_iterator_last.stderr +++ b/tests/ui/double_ended_iterator_last.stderr @@ -18,7 +18,7 @@ LL | let _ = DeIterator.last(); | help: try: `next_back()` error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:96:36 + --> tests/ui/double_ended_iterator_last.rs:109:36 | LL | println!("Last element is {}", v.last().unwrap().0); | ^^^^^^^^ @@ -26,7 +26,7 @@ LL | println!("Last element is {}", v.last().unwrap().0); = note: this change will alter drop order which may be undesirable help: try | -LL ~ let mut v = v.into_iter(); +LL ~ let mut v = DropDeIterator(v.into_iter()); LL ~ println!("Last element is {}", v.next_back().unwrap().0); | diff --git a/tests/ui/double_ended_iterator_last_unfixable.rs b/tests/ui/double_ended_iterator_last_unfixable.rs index 7c5de8832d69..e9218bbb4094 100644 --- a/tests/ui/double_ended_iterator_last_unfixable.rs +++ b/tests/ui/double_ended_iterator_last_unfixable.rs @@ -11,6 +11,19 @@ fn main() { } fn drop_order() { + struct DropDeIterator(std::vec::IntoIter); + impl Iterator for DropDeIterator { + type Item = S; + fn next(&mut self) -> Option { + self.0.next() + } + } + impl DoubleEndedIterator for DropDeIterator { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } + struct S(&'static str); impl std::ops::Drop for S { fn drop(&mut self) { @@ -19,7 +32,7 @@ fn drop_order() { } let v = vec![S("one"), S("two"), S("three")]; - let v = (v.into_iter(), 42); + let v = (DropDeIterator(v.into_iter()), 42); println!("Last element is {}", v.0.last().unwrap().0); //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator` println!("Done"); diff --git a/tests/ui/double_ended_iterator_last_unfixable.stderr b/tests/ui/double_ended_iterator_last_unfixable.stderr index 845afc11f042..e330a22a3548 100644 --- a/tests/ui/double_ended_iterator_last_unfixable.stderr +++ b/tests/ui/double_ended_iterator_last_unfixable.stderr @@ -1,5 +1,5 @@ error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last_unfixable.rs:23:36 + --> tests/ui/double_ended_iterator_last_unfixable.rs:36:36 | LL | println!("Last element is {}", v.0.last().unwrap().0); | ^^^^------ @@ -8,7 +8,7 @@ LL | println!("Last element is {}", v.0.last().unwrap().0); | = note: this change will alter drop order which may be undesirable note: this must be made mutable to use `.next_back()` - --> tests/ui/double_ended_iterator_last_unfixable.rs:23:36 + --> tests/ui/double_ended_iterator_last_unfixable.rs:36:36 | LL | println!("Last element is {}", v.0.last().unwrap().0); | ^^^ From 7106e21227634768597a4283166f20ee7a9c037b Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sat, 3 May 2025 23:38:52 +0800 Subject: [PATCH 043/245] fix: `manual_unwrap_or_default` FP on ref binding --- clippy_lints/src/matches/manual_unwrap_or.rs | 3 ++- tests/ui/manual_unwrap_or_default.fixed | 13 +++++++++++++ tests/ui/manual_unwrap_or_default.rs | 13 +++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index b64ae0b24d81..3ac2c9fc2b36 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; +use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, QPath}; @@ -16,7 +17,7 @@ use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; fn get_some(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind - && let PatKind::Binding(_, pat_id, _, _) = pat.kind + && let PatKind::Binding(BindingMode(ByRef::No, _), pat_id, _, _) = pat.kind && let Some(def_id) = path.res.opt_def_id() // Since it comes from a pattern binding, we need to get the parent to actually match // against it. diff --git a/tests/ui/manual_unwrap_or_default.fixed b/tests/ui/manual_unwrap_or_default.fixed index 9dae9fcae079..41ca44ceef4e 100644 --- a/tests/ui/manual_unwrap_or_default.fixed +++ b/tests/ui/manual_unwrap_or_default.fixed @@ -106,3 +106,16 @@ fn issue_12928() { fn allowed_manual_unwrap_or_zero() -> u32 { Some(42).unwrap_or_default() } + +mod issue14716 { + struct Foo { + name: Option, + } + + fn bar(project: &Foo) { + let _name = match project.name { + Some(ref x) => x, + None => "", + }; + } +} diff --git a/tests/ui/manual_unwrap_or_default.rs b/tests/ui/manual_unwrap_or_default.rs index 539d7a8bbae5..343fbc4879ce 100644 --- a/tests/ui/manual_unwrap_or_default.rs +++ b/tests/ui/manual_unwrap_or_default.rs @@ -147,3 +147,16 @@ fn allowed_manual_unwrap_or_zero() -> u32 { 0 } } + +mod issue14716 { + struct Foo { + name: Option, + } + + fn bar(project: &Foo) { + let _name = match project.name { + Some(ref x) => x, + None => "", + }; + } +} From c46855209755c737e919f69c2409782f1689b682 Mon Sep 17 00:00:00 2001 From: Quang Duong Nguyen Date: Sun, 23 Mar 2025 05:06:34 -0400 Subject: [PATCH 044/245] add allow_unused config to missing_docs_in_private_items add allow_unused config to missing_docs_in_private_items for underscored field --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 10 +++++++++ clippy_config/src/conf.rs | 3 +++ clippy_lints/src/missing_doc.rs | 11 ++++++++++ .../missing_docs_allow_unused/clippy.toml | 1 + .../missing_docs_allow_unused.rs | 19 +++++++++++++++++ .../missing_docs_allow_unused.stderr | 21 +++++++++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 3 +++ 8 files changed, 69 insertions(+) create mode 100644 tests/ui-toml/missing_docs_allow_unused/clippy.toml create mode 100644 tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs create mode 100644 tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b62c9a59aa5..9d698c7c4721 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6435,6 +6435,7 @@ Released 2018-09-13 [`max-suggested-slice-pattern-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-suggested-slice-pattern-length [`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds [`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold +[`missing-docs-allow-unused`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-allow-unused [`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items [`module-item-order-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-item-order-groupings [`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 2314d1beac7e..cc8e362e836c 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -734,6 +734,16 @@ Minimum chars an ident can have, anything below or equal to this will be linted. * [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars) +## `missing-docs-allow-unused` +Whether to allow fields starting with underscore to skip documentation requirements + +**Default Value:** `false` + +--- +**Affected lints:** +* [`missing_docs_in_private_items`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items) + + ## `missing-docs-in-crate-items` Whether to **only** check for missing documentation in items visible within the current crate. For example, `pub(crate)` items. diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 511cb84527d8..c1a561aa407d 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -675,6 +675,9 @@ define_Conf! { /// Minimum chars an ident can have, anything below or equal to this will be linted. #[lints(min_ident_chars)] min_ident_chars_threshold: u64 = 1, + /// Whether to allow fields starting with underscore to skip documentation requirements + #[lints(missing_docs_in_private_items)] + missing_docs_allow_unused: bool = false, /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. #[lints(missing_docs_in_private_items)] diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index b234b190153b..3ac9adfa4f33 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -48,6 +48,8 @@ pub struct MissingDoc { /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. crate_items_only: bool, + /// Whether to allow fields starting with underscore to skip documentation requirements + allow_unused: bool, /// Stack of whether #[doc(hidden)] is set /// at each level which has lint attributes. doc_hidden_stack: Vec, @@ -59,6 +61,7 @@ impl MissingDoc { pub fn new(conf: &'static Conf) -> Self { Self { crate_items_only: conf.missing_docs_in_crate_items, + allow_unused: conf.missing_docs_allow_unused, doc_hidden_stack: vec![false], prev_span: None, } @@ -88,6 +91,14 @@ impl MissingDoc { article: &'static str, desc: &'static str, ) { + // Skip checking if the item starts with underscore and allow_unused is enabled + if self.allow_unused { + if let Some(name) = cx.tcx.opt_item_name(def_id.to_def_id()) { + if name.as_str().starts_with('_') { + return; + } + } + } // If we're building a test harness, then warning about // documentation is probably not really relevant right now. if cx.sess().opts.test { diff --git a/tests/ui-toml/missing_docs_allow_unused/clippy.toml b/tests/ui-toml/missing_docs_allow_unused/clippy.toml new file mode 100644 index 000000000000..2fe64b2755b6 --- /dev/null +++ b/tests/ui-toml/missing_docs_allow_unused/clippy.toml @@ -0,0 +1 @@ +missing-docs-allow-unused = true diff --git a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs new file mode 100644 index 000000000000..bda3fbc928b0 --- /dev/null +++ b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs @@ -0,0 +1,19 @@ +//! Test file for missing_docs_in_private_items lint with allow_unused configuration +#![warn(clippy::missing_docs_in_private_items)] +#![allow(dead_code)] + +/// A struct with some documented and undocumented fields +struct Test { + /// This field is documented + field1: i32, + _unused: i32, // This should not trigger a warning because it starts with underscore + field3: i32, //~ missing_docs_in_private_items +} + +struct Test2 { + //~^ missing_docs_in_private_items + _field1: i32, // This should not trigger a warning + _field2: i32, // This should not trigger a warning +} + +fn main() {} diff --git a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr new file mode 100644 index 000000000000..59b22cd47d44 --- /dev/null +++ b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr @@ -0,0 +1,21 @@ +error: missing documentation for a struct field + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:10:5 + | +LL | field3: i32, + | ^^^^^^^^^^^ + | + = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:13:1 + | +LL | / struct Test2 { +LL | | +LL | | _field1: i32, // This should not trigger a warning +LL | | _field2: i32, // This should not trigger a warning +LL | | } + | |_^ + +error: aborting due to 2 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 f2eaa66a4ae4..0a36cd3cf26d 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -57,6 +57,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings module-items-ordered-within-groupings @@ -149,6 +150,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings module-items-ordered-within-groupings @@ -241,6 +243,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings module-items-ordered-within-groupings From 91a06226a1bb0fdee3ab2c12225ba7b901659383 Mon Sep 17 00:00:00 2001 From: Quang Duong Nguyen Date: Mon, 24 Mar 2025 06:13:14 -0400 Subject: [PATCH 045/245] move the implementation under check_def_field --- clippy_lints/src/missing_doc.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 3ac9adfa4f33..5d228f7ea436 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -91,14 +91,6 @@ impl MissingDoc { article: &'static str, desc: &'static str, ) { - // Skip checking if the item starts with underscore and allow_unused is enabled - if self.allow_unused { - if let Some(name) = cx.tcx.opt_item_name(def_id.to_def_id()) { - if name.as_str().starts_with('_') { - return; - } - } - } // If we're building a test harness, then warning about // documentation is probably not really relevant right now. if cx.sess().opts.test { @@ -272,6 +264,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) { if !sf.is_positional() { + // Skip checking if the field starts with underscore and allow_unused is enabled + if self.allow_unused && sf.ident.as_str().starts_with('_') { + self.prev_span = Some(sf.span); + return; + } + let attrs = cx.tcx.hir_attrs(sf.hir_id); if !is_from_proc_macro(cx, sf) { self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); From 481abc136113185840b80355bad58f28a4717515 Mon Sep 17 00:00:00 2001 From: Quang Duong Nguyen Date: Sat, 3 May 2025 14:06:26 -0700 Subject: [PATCH 046/245] fix doc --- book/src/lint_configuration.md | 2 +- clippy_config/src/conf.rs | 2 +- clippy_lints/src/missing_doc.rs | 4 ++-- .../missing_docs_allow_unused/missing_docs_allow_unused.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index cc8e362e836c..58c79c119ccf 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -735,7 +735,7 @@ Minimum chars an ident can have, anything below or equal to this will be linted. ## `missing-docs-allow-unused` -Whether to allow fields starting with underscore to skip documentation requirements +Whether to allow fields starting with an underscore to skip documentation requirements **Default Value:** `false` diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index c1a561aa407d..aef0516b75be 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -675,7 +675,7 @@ define_Conf! { /// Minimum chars an ident can have, anything below or equal to this will be linted. #[lints(min_ident_chars)] min_ident_chars_threshold: u64 = 1, - /// Whether to allow fields starting with underscore to skip documentation requirements + /// Whether to allow fields starting with an underscore to skip documentation requirements #[lints(missing_docs_in_private_items)] missing_docs_allow_unused: bool = false, /// Whether to **only** check for missing documentation in items visible within the current diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 5d228f7ea436..1707f9d4fc99 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -48,7 +48,7 @@ pub struct MissingDoc { /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. crate_items_only: bool, - /// Whether to allow fields starting with underscore to skip documentation requirements + /// Whether to allow fields starting with an underscore to skip documentation requirements allow_unused: bool, /// Stack of whether #[doc(hidden)] is set /// at each level which has lint attributes. @@ -264,7 +264,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) { if !sf.is_positional() { - // Skip checking if the field starts with underscore and allow_unused is enabled + // Skip checking if the field starts with an underscore and allow_unused is enabled if self.allow_unused && sf.ident.as_str().starts_with('_') { self.prev_span = Some(sf.span); return; diff --git a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs index bda3fbc928b0..a738ff959fe5 100644 --- a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs +++ b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs @@ -6,7 +6,7 @@ struct Test { /// This field is documented field1: i32, - _unused: i32, // This should not trigger a warning because it starts with underscore + _unused: i32, // This should not trigger a warning because it starts with an underscore field3: i32, //~ missing_docs_in_private_items } From 64880cad753c97f1c765dba16a719fec42bf6620 Mon Sep 17 00:00:00 2001 From: Kaido Aethermai Date: Sat, 3 May 2025 14:51:36 -0700 Subject: [PATCH 047/245] add test 3, fmt and lint --- clippy_lints/src/missing_doc.rs | 15 +++++---------- .../missing_docs_allow_unused.rs | 7 +++++++ .../missing_docs_allow_unused.stderr | 19 ++++++++++++++++++- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 1707f9d4fc99..7772051eb5c6 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -263,17 +263,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) { - if !sf.is_positional() { - // Skip checking if the field starts with an underscore and allow_unused is enabled - if self.allow_unused && sf.ident.as_str().starts_with('_') { - self.prev_span = Some(sf.span); - return; - } - + if !(sf.is_positional() + || is_from_proc_macro(cx, sf) + || self.allow_unused && sf.ident.as_str().starts_with('_')) + { let attrs = cx.tcx.hir_attrs(sf.hir_id); - if !is_from_proc_macro(cx, sf) { - self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); - } + self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); } self.prev_span = Some(sf.span); } diff --git a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs index a738ff959fe5..155f680c7b13 100644 --- a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs +++ b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs @@ -16,4 +16,11 @@ struct Test2 { _field2: i32, // This should not trigger a warning } +struct Test3 { + //~^ missing_docs_in_private_items + /// This field is documented although this is not mandatory + _unused: i32, // This should not trigger a warning because it starts with an underscore + field2: i32, //~ missing_docs_in_private_items +} + fn main() {} diff --git a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr index 59b22cd47d44..8f511883e900 100644 --- a/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr +++ b/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr @@ -17,5 +17,22 @@ LL | | _field2: i32, // This should not trigger a warning LL | | } | |_^ -error: aborting due to 2 previous errors +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:19:1 + | +LL | / struct Test3 { +LL | | +LL | | /// This field is documented although this is not mandatory +LL | | _unused: i32, // This should not trigger a warning because it starts with an underscore +LL | | field2: i32, +LL | | } + | |_^ + +error: missing documentation for a struct field + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:23:5 + | +LL | field2: i32, + | ^^^^^^^^^^^ + +error: aborting due to 4 previous errors From 7b7a9a6ecefed92114062af8a84e6b3fcead6a53 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 4 May 2025 09:09:39 -0400 Subject: [PATCH 048/245] Don't warn about unloaded crates Fixes https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221 --- clippy_config/src/types.rs | 13 +++++++++---- tests/ui-toml/toml_unloaded_crate/clippy.toml | 10 ++++++++++ .../toml_unloaded_crate/conf_unloaded_crate.rs | 6 ++++++ .../conf_unloaded_crate.stderr | 16 ++++++++++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 tests/ui-toml/toml_unloaded_crate/clippy.toml create mode 100644 tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs create mode 100644 tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index 5949eaca7bc1..70cf0915bb6f 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -4,7 +4,7 @@ use rustc_hir::PrimTy; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_middle::ty::TyCtxt; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use serde::de::{self, Deserializer, Visitor}; use serde::{Deserialize, Serialize, ser}; use std::collections::HashMap; @@ -145,7 +145,8 @@ pub fn create_disallowed_map( FxHashMap::default(); for disallowed_path in disallowed_paths { let path = disallowed_path.path(); - let mut resolutions = clippy_utils::def_path_res(tcx, &path.split("::").collect::>()); + let path_split = path.split("::").collect::>(); + let mut resolutions = clippy_utils::def_path_res(tcx, &path_split); let mut found_def_id = None; let mut found_prim_ty = false; @@ -160,8 +161,12 @@ pub fn create_disallowed_map( }, _ => false, }); - - if resolutions.is_empty() { + if resolutions.is_empty() + // Don't warn about unloaded crates: + // https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221 + && (path_split.len() < 2 + || !clippy_utils::find_crates(tcx, Symbol::intern(path_split[0])).is_empty()) + { let span = disallowed_path.span(); if let Some(def_id) = found_def_id { diff --git a/tests/ui-toml/toml_unloaded_crate/clippy.toml b/tests/ui-toml/toml_unloaded_crate/clippy.toml new file mode 100644 index 000000000000..e664256d2a2f --- /dev/null +++ b/tests/ui-toml/toml_unloaded_crate/clippy.toml @@ -0,0 +1,10 @@ +# The first two `disallowed-methods` paths should generate warnings, but the third should not. + +[[disallowed-methods]] +path = "regex::Regex::new_" + +[[disallowed-methods]] +path = "regex::Regex_::new" + +[[disallowed-methods]] +path = "regex_::Regex::new" diff --git a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs new file mode 100644 index 000000000000..2dbc5eca32ec --- /dev/null +++ b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs @@ -0,0 +1,6 @@ +//@error-in-other-file: `regex::Regex::new_` does not refer to an existing function +//@error-in-other-file: `regex::Regex_::new` does not refer to an existing function + +extern crate regex; + +fn main() {} diff --git a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr new file mode 100644 index 000000000000..5d28e5fa970e --- /dev/null +++ b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr @@ -0,0 +1,16 @@ +warning: `regex::Regex::new_` does not refer to an existing function + --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:3:1 + | +LL | / [[disallowed-methods]] +LL | | path = "regex::Regex::new_" + | |___________________________^ + +warning: `regex::Regex_::new` does not refer to an existing function + --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:6:1 + | +LL | / [[disallowed-methods]] +LL | | path = "regex::Regex_::new" + | |___________________________^ + +warning: 2 warnings emitted + From b768fbe4bccbfd4c4ecbd4726805facb6769eb4f Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 28 Apr 2025 17:12:16 +0000 Subject: [PATCH 049/245] Replace str path utils with new `PathLookup` type --- CONTRIBUTING.md | 2 +- .../development/common_tools_writing_lints.md | 4 +- book/src/development/trait_checking.md | 16 +- clippy.toml | 3 - clippy_config/src/types.rs | 85 ++--- clippy_lints/src/await_holding_invalid.rs | 13 +- clippy_lints/src/casts/manual_dangling_ptr.rs | 4 +- clippy_lints/src/derive.rs | 4 +- clippy_lints/src/disallowed_macros.rs | 2 + clippy_lints/src/disallowed_methods.rs | 2 + clippy_lints/src/disallowed_types.rs | 10 +- clippy_lints/src/functions/mod.rs | 4 +- clippy_lints/src/let_underscore.rs | 12 +- clippy_lints/src/manual_option_as_slice.rs | 4 +- clippy_lints/src/methods/io_other_error.rs | 9 +- .../methods/manual_saturating_arithmetic.rs | 20 +- .../methods/needless_character_iteration.rs | 7 +- clippy_lints/src/methods/open_options.rs | 20 +- clippy_lints/src/methods/str_splitn.rs | 4 +- clippy_lints/src/methods/useless_asref.rs | 9 +- .../src/missing_enforced_import_rename.rs | 10 +- clippy_lints/src/non_std_lazy_statics.rs | 40 +- clippy_lints/src/regex.rs | 17 +- clippy_lints/src/serde_api.rs | 6 +- clippy_lints/src/single_range_in_vec_init.rs | 4 +- clippy_lints/src/to_digit_is_some.rs | 20 +- clippy_lints/src/unused_io_amount.rs | 41 +- clippy_lints/src/utils/author.rs | 130 +++---- .../src/collapsible_calls.rs | 6 +- clippy_lints_internal/src/internal_paths.rs | 17 + clippy_lints_internal/src/invalid_paths.rs | 108 ------ clippy_lints_internal/src/lib.rs | 6 +- .../src/lint_without_lint_pass.rs | 9 +- clippy_lints_internal/src/msrv_attr_impl.rs | 7 +- .../src/outer_expn_data_pass.rs | 6 +- clippy_lints_internal/src/symbols.rs | 15 +- .../src/unnecessary_def_path.rs | 316 +++------------- clippy_utils/src/lib.rs | 357 +++++++----------- clippy_utils/src/paths.rs | 173 ++++++--- clippy_utils/src/sym.rs | 56 ++- clippy_utils/src/ty/mod.rs | 22 +- clippy_utils/src/ty/type_certainty/mod.rs | 23 +- tests/ui-internal/auxiliary/paths.rs | 4 - tests/ui-internal/invalid_paths.rs | 30 -- tests/ui-internal/invalid_paths.stderr | 26 -- tests/ui-internal/unnecessary_def_path.fixed | 77 ---- tests/ui-internal/unnecessary_def_path.rs | 85 +---- tests/ui-internal/unnecessary_def_path.stderr | 120 ++---- .../unnecessary_def_path_hardcoded_path.rs | 19 - ...unnecessary_def_path_hardcoded_path.stderr | 31 -- .../ui-toml/toml_disallowed_types/clippy.toml | 2 +- .../conf_disallowed_types.stderr | 8 +- tests/ui-toml/toml_invalid_path/clippy.toml | 9 +- .../toml_invalid_path/conf_invalid_path.rs | 5 +- .../conf_invalid_path.stderr | 27 +- .../conf_unloaded_crate.rs | 4 +- .../conf_unloaded_crate.stderr | 8 +- tests/ui/author.stdout | 2 - tests/ui/author/blocks.stdout | 10 +- tests/ui/author/call.stdout | 3 +- tests/ui/author/if.stdout | 4 +- tests/ui/author/issue_3849.stdout | 5 +- tests/ui/author/loop.stdout | 8 +- tests/ui/author/macro_in_closure.stdout | 11 +- tests/ui/author/macro_in_loop.stdout | 11 +- tests/ui/author/matches.stdout | 6 +- tests/ui/author/struct.stdout | 9 +- tests/ui/manual_saturating_arithmetic.fixed | 2 - tests/ui/manual_saturating_arithmetic.rs | 2 - tests/ui/manual_saturating_arithmetic.stderr | 48 +-- 70 files changed, 799 insertions(+), 1400 deletions(-) create mode 100644 clippy_lints_internal/src/internal_paths.rs delete mode 100644 clippy_lints_internal/src/invalid_paths.rs delete mode 100644 tests/ui-internal/auxiliary/paths.rs delete mode 100644 tests/ui-internal/invalid_paths.rs delete mode 100644 tests/ui-internal/invalid_paths.stderr delete mode 100644 tests/ui-internal/unnecessary_def_path.fixed delete mode 100644 tests/ui-internal/unnecessary_def_path_hardcoded_path.rs delete mode 100644 tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3b33f7190638..45ba2f078be7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,7 +77,7 @@ debugging to find the actual problem behind the issue. [`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of -an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful. +an AST expression). [`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue [`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 2e39f279eae4..e23b32039c90 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -86,7 +86,7 @@ arguments have to be checked separately. ```rust use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{paths, match_def_path}; +use clippy_utils::paths; use rustc_span::symbol::sym; use rustc_hir::LangItem; @@ -108,7 +108,7 @@ impl LateLintPass<'_> for MyStructLint { // 3. Using the type path // This method should be avoided if possible - if match_def_path(cx, def_id, &paths::RESULT) { + if paths::RESULT.matches_ty(cx, ty) { // The type is a `core::result::Result` } } diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index b7d229ccc193..cc4eb966f596 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -73,22 +73,24 @@ impl LateLintPass<'_> for CheckDropTraitLint { ## Using Type Path If neither diagnostic item nor a language item is available, we can use -[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait -implementation. +[`clippy_utils::paths`][paths] to determine get a trait's `DefId`. > **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item. -Below, we check if the given `expr` implements the `Iterator`'s trait method `cloned` : +Below, we check if the given `expr` implements [`core::iter::Step`](https://doc.rust-lang.org/std/iter/trait.Step.html): ```rust -use clippy_utils::{match_trait_method, paths}; +use clippy_utils::{implements_trait, paths}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; -impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait { +impl LateLintPass<'_> for CheckIterStep { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if match_trait_method(cx, expr, &paths::CORE_ITER_CLONED) { - println!("`expr` implements `CORE_ITER_CLONED` trait!"); + let ty = cx.typeck_results().expr_ty(expr); + if let Some(trait_def_id) = paths::ITER_STEP.first(cx) + && implements_trait(cx, ty, trait_def_id, &[]) + { + println!("`expr` implements the `core::iter::Step` trait!"); } } } diff --git a/clippy.toml b/clippy.toml index 0a7724bbe4e6..77573105d86a 100644 --- a/clippy.toml +++ b/clippy.toml @@ -7,14 +7,11 @@ lint-commented-code = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" -allow-invalid = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" -allow-invalid = true [[disallowed-methods]] path = "rustc_middle::ty::context::TyCtxt::node_span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" -allow-invalid = true diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index 70cf0915bb6f..2d51c104da1a 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -1,7 +1,8 @@ +use clippy_utils::PathNS; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diag}; use rustc_hir::PrimTy; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_middle::ty::TyCtxt; use rustc_span::{Span, Symbol}; @@ -133,6 +134,7 @@ impl DisallowedPathEnum { pub fn create_disallowed_map( tcx: TyCtxt<'_>, disallowed_paths: &'static [DisallowedPath], + ns: PathNS, def_kind_predicate: impl Fn(DefKind) -> bool, predicate_description: &str, allow_prim_tys: bool, @@ -145,62 +147,47 @@ pub fn create_disallowed_map( FxHashMap::default(); for disallowed_path in disallowed_paths { let path = disallowed_path.path(); - let path_split = path.split("::").collect::>(); - let mut resolutions = clippy_utils::def_path_res(tcx, &path_split); + let sym_path: Vec = path.split("::").map(Symbol::intern).collect(); + let mut resolutions = clippy_utils::lookup_path(tcx, ns, &sym_path); + resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id))); + + let (prim_ty, found_prim_ty) = if let &[name] = sym_path.as_slice() + && let Some(prim) = PrimTy::from_name(name) + { + (allow_prim_tys.then_some(prim), true) + } else { + (None, false) + }; - let mut found_def_id = None; - let mut found_prim_ty = false; - resolutions.retain(|res| match res { - Res::Def(def_kind, def_id) => { - found_def_id = Some(*def_id); - def_kind_predicate(*def_kind) - }, - Res::PrimTy(_) => { - found_prim_ty = true; - allow_prim_tys - }, - _ => false, - }); if resolutions.is_empty() + && prim_ty.is_none() + && !disallowed_path.allow_invalid // Don't warn about unloaded crates: // https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221 - && (path_split.len() < 2 - || !clippy_utils::find_crates(tcx, Symbol::intern(path_split[0])).is_empty()) + && (sym_path.len() < 2 || !clippy_utils::find_crates(tcx, sym_path[0]).is_empty()) { - let span = disallowed_path.span(); - - if let Some(def_id) = found_def_id { - tcx.sess.dcx().span_warn( - span, - format!( - "expected a {predicate_description}, found {} {}", - tcx.def_descr_article(def_id), - tcx.def_descr(def_id) - ), - ); + // Relookup the path in an arbitrary namespace to get a good `expected, found` message + let found_def_ids = clippy_utils::lookup_path(tcx, PathNS::Arbitrary, &sym_path); + let message = if let Some(&def_id) = found_def_ids.first() { + let (article, description) = tcx.article_and_description(def_id); + format!("expected a {predicate_description}, found {article} {description}") } else if found_prim_ty { - tcx.sess.dcx().span_warn( - span, - format!("expected a {predicate_description}, found a primitive type",), - ); - } else if !disallowed_path.allow_invalid { - tcx.sess.dcx().span_warn( - span, - format!("`{path}` does not refer to an existing {predicate_description}"), - ); - } + format!("expected a {predicate_description}, found a primitive type") + } else { + format!("`{path}` does not refer to a reachable {predicate_description}") + }; + tcx.sess + .dcx() + .struct_span_warn(disallowed_path.span(), message) + .with_help("add `allow-invalid = true` to the entry to suppress this warning") + .emit(); } - for res in resolutions { - match res { - Res::Def(_, def_id) => { - def_ids.insert(def_id, (path, disallowed_path)); - }, - Res::PrimTy(ty) => { - prim_tys.insert(ty, (path, disallowed_path)); - }, - _ => unreachable!(), - } + for def_id in resolutions { + def_ids.insert(def_id, (path, disallowed_path)); + } + if let Some(ty) = prim_ty { + prim_tys.insert(ty, (path, disallowed_path)); } } diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 52d1d5b4c67a..4c47d180d3f5 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{match_def_path, paths}; +use clippy_utils::{PathNS, paths}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_lint::{LateContext, LateLintPass}; @@ -182,6 +182,7 @@ impl AwaitHolding { let (def_ids, _) = create_disallowed_map( tcx, &conf.await_holding_invalid_types, + PathNS::Type, crate::disallowed_types::def_kind_predicate, "type", false, @@ -275,12 +276,10 @@ fn emit_invalid_type( } fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { - cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id) - || cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id) - || cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id) - || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) + match cx.tcx.get_diagnostic_name(def_id) { + Some(name) => matches!(name, sym::MutexGuard | sym::RwLockReadGuard | sym::RwLockWriteGuard), + None => paths::PARKING_LOT_GUARDS.iter().any(|guard| guard.matches(cx, def_id)), + } } fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index 6dbaa5cb5079..61dfc0fc0425 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::source::SpanRangeExt; -use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core}; +use clippy_utils::{expr_or_init, path_def_id, paths, std_or_core}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind}; @@ -54,7 +54,7 @@ fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool { if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind && let Some(fun_id) = path_def_id(cx, fun) - && match_def_path(cx, fun_id, &paths::ALIGN_OF) + && paths::ALIGN_OF.matches(cx, fun_id) && let Some(args) = path.segments.last().and_then(|seg| seg.args) && let [GenericArg::Type(generic_ty)] = args.args { diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 06528f875a29..3443b36eb4f3 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; -use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths}; +use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, paths}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; @@ -377,7 +377,7 @@ fn check_unsafe_derive_deserialize<'tcx>( } if let Some(trait_def_id) = trait_ref.trait_def_id() - && match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE) + && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) && let ty::Adt(def, _) = ty.kind() && let Some(local_def_id) = def.did().as_local() && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index fa33fef23062..c58cfb022e49 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,5 +1,6 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_utils::PathNS; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; use rustc_data_structures::fx::FxHashSet; @@ -75,6 +76,7 @@ impl DisallowedMacros { let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_macros, + PathNS::Macro, |def_kind| matches!(def_kind, DefKind::Macro(_)), "macro", false, diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 1382dafa931e..4c32a6002041 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,5 +1,6 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_utils::PathNS; use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; @@ -66,6 +67,7 @@ impl DisallowedMethods { let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_methods, + PathNS::Value, |def_kind| { matches!( def_kind, diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 2bae82648ac7..2cf1fd018b83 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,5 +1,6 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_utils::PathNS; use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; @@ -60,7 +61,14 @@ pub struct DisallowedTypes { impl DisallowedTypes { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (def_ids, prim_tys) = create_disallowed_map(tcx, &conf.disallowed_types, def_kind_predicate, "type", true); + let (def_ids, prim_tys) = create_disallowed_map( + tcx, + &conf.disallowed_types, + PathNS::Type, + def_kind_predicate, + "type", + true, + ); Self { def_ids, prim_tys } } diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 5f3fc5100e75..6be4845e2e76 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -9,8 +9,8 @@ mod too_many_arguments; mod too_many_lines; use clippy_config::Conf; -use clippy_utils::def_path_def_ids; use clippy_utils::msrvs::Msrv; +use clippy_utils::{PathNS, lookup_path_str}; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; @@ -469,7 +469,7 @@ impl Functions { trait_ids: conf .allow_renamed_params_for .iter() - .flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::>())) + .flat_map(|p| lookup_path_str(tcx, PathNS::Type, p)) .collect(), msrv: conf.msrv, } diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index bdbf5b37c5f0..916191b2a7b0 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; +use clippy_utils::ty::{implements_trait, is_must_use_ty}; use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths}; use rustc_hir::{LetStmt, LocalSource, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -129,12 +129,6 @@ declare_clippy_lint! { declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]); -const SYNC_GUARD_PATHS: [&[&str]; 3] = [ - &paths::PARKING_LOT_MUTEX_GUARD, - &paths::PARKING_LOT_RWLOCK_READ_GUARD, - &paths::PARKING_LOT_RWLOCK_WRITE_GUARD, -]; - impl<'tcx> LateLintPass<'tcx> for LetUnderscore { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if matches!(local.source, LocalSource::Normal) @@ -144,7 +138,9 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)), + GenericArgKind::Type(inner_ty) => inner_ty + .ty_adt_def() + .is_some_and(|adt| paths::PARKING_LOT_GUARDS.iter().any(|path| path.matches(cx, adt.did()))), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }); if contains_sync_guard { diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index b365dbf088f5..04e00f841035 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; -use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym}; +use clippy_utils::{is_none_arm, msrvs, paths, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; @@ -220,5 +220,5 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - clippy_utils::is_expr_path_def_path(cx, expr, &["core", "slice", "raw", "from_ref"]) + paths::SLICE_FROM_REF.matches_path(cx, expr) } diff --git a/clippy_lints/src/methods/io_other_error.rs b/clippy_lints/src/methods/io_other_error.rs index bdc834bd47a5..ec4b9c7ae2ee 100644 --- a/clippy_lints/src/methods/io_other_error.rs +++ b/clippy_lints/src/methods/io_other_error.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{expr_or_init, paths}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -8,13 +9,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args if let [error_kind, error] = args && !expr.span.from_expansion() && !error_kind.span.from_expansion() - && clippy_utils::is_expr_path_def_path(cx, path, &clippy_utils::paths::IO_ERROR_NEW) - && clippy_utils::is_expr_path_def_path( - cx, - clippy_utils::expr_or_init(cx, error_kind), - &clippy_utils::paths::IO_ERRORKIND_OTHER, - ) && let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind + && paths::IO_ERROR_NEW.matches_path(cx, path) + && paths::IO_ERRORKIND_OTHER_CTOR.matches_path(cx, expr_or_init(cx, error_kind)) && msrv.meets(cx, msrvs::IO_ERROR_OTHER) { span_lint_and_then( diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 18978a1d2bc8..e2df8ce1513c 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{match_def_path, path_def_id}; +use clippy_utils::{path_res, sym}; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def::Res; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; @@ -79,16 +80,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { } let ty = cx.typeck_results().expr_ty(expr); - let ty_str = ty.to_string(); - // `std::T::MAX` `std::T::MIN` constants - if let Some(id) = path_def_id(cx, expr) { - if match_def_path(cx, id, &["core", &ty_str, "MAX"]) { - return Some(MinMax::Max); - } - - if match_def_path(cx, id, &["core", &ty_str, "MIN"]) { - return Some(MinMax::Min); + // `T::MAX` and `T::MIN` constants + if let hir::ExprKind::Path(hir::QPath::TypeRelative(base, seg)) = expr.kind + && let Res::PrimTy(_) = path_res(cx, base) + { + match seg.ident.name { + sym::MAX => return Some(MinMax::Max), + sym::MIN => return Some(MinMax::Min), + _ => {}, } } diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index f528f7f065c6..71c1576cd57d 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -7,9 +7,8 @@ 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::paths::CHAR_IS_ASCII; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{match_def_path, path_to_local_id, peel_blocks, sym}; +use clippy_utils::{is_path_diagnostic_item, path_to_local_id, peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { while let ExprKind::AddrOf(_, _, e) = expr.kind { @@ -76,9 +75,7 @@ fn handle_expr( // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is // `is_ascii`, then only `.all()` should warn. if revert != is_all - && let ExprKind::Path(path) = fn_path.kind - && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() - && match_def_path(cx, fn_def_id, &CHAR_IS_ASCII) + && is_path_diagnostic_item(cx, fn_path, sym::char_is_ascii) && path_to_local_id(peels_expr_ref(arg), first_param) && let Some(snippet) = before_chars.get_source_text(cx) { diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs index da084871402a..bce314e64f05 100644 --- a/clippy_lints/src/methods/open_options.rs +++ b/clippy_lints/src/methods/open_options.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxHashMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::ty::{is_type_diagnostic_item, match_type}; -use clippy_utils::{match_any_def_paths, paths}; +use clippy_utils::paths; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -13,7 +13,7 @@ use rustc_span::{Span, sym}; use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS}; fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || match_type(cx, ty, &paths::TOKIO_IO_OPEN_OPTIONS) + is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty) } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { @@ -126,14 +126,14 @@ fn get_open_options( && let ExprKind::Path(path) = callee.kind && let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id() { - let std_file_options = [sym::file_options, sym::open_options_new]; + let is_std_options = matches!( + cx.tcx.get_diagnostic_name(did), + Some(sym::file_options | sym::open_options_new) + ); - let tokio_file_options: &[&[&str]] = &[&paths::TOKIO_IO_OPEN_OPTIONS_NEW, &paths::TOKIO_FILE_OPTIONS]; - - let is_std_options = std_file_options - .into_iter() - .any(|sym| cx.tcx.is_diagnostic_item(sym, did)); - is_std_options || match_any_def_paths(cx, did, tokio_file_options).is_some() + is_std_options + || paths::TOKIO_IO_OPEN_OPTIONS_NEW.matches(cx, did) + || paths::TOKIO_FILE_OPTIONS.matches(cx, did) } else { false } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index d183457da25a..c8efb600f576 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -4,7 +4,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; +use clippy_utils::{is_diag_item_method, path_to_local_id, paths}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -288,7 +288,7 @@ fn parse_iter_usage<'tcx>( match (name.ident.as_str(), args) { ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), ("next_tuple", []) => { - return if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE) + return if paths::ITERTOOLS_NEXT_TUPLE.matches(cx, did) && let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind() && cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) && let ty::Tuple(subs) = subs.type_at(0).kind() diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index 0cbf6004be3a..17e2620d9dd4 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,9 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth}; -use clippy_utils::{ - get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs, -}; +use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; @@ -81,8 +79,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, applicability, ); } - } else if match_def_path(cx, def_id, &["core", "option", "Option", call_name]) - || match_def_path(cx, def_id, &["core", "result", "Result", call_name]) + } else if let Some(impl_id) = cx.tcx.opt_parent(def_id) + && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def() + && (cx.tcx.lang_items().option_type() == Some(adt.did()) || cx.tcx.is_diagnostic_item(sym::Result, adt.did())) { let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 66631a692063..dd0cb01cc338 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::def_path_def_ids; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; +use clippy_utils::{PathNS, lookup_path_str}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdMap; @@ -56,8 +56,12 @@ impl ImportRename { renames: conf .enforced_import_renames .iter() - .map(|x| (x.path.split("::").collect::>(), Symbol::intern(&x.rename))) - .flat_map(|(path, rename)| def_path_def_ids(tcx, &path).map(move |id| (id, rename))) + .map(|x| (&x.path, Symbol::intern(&x.rename))) + .flat_map(|(path, rename)| { + lookup_path_str(tcx, PathNS::Arbitrary, path) + .into_iter() + .map(move |id| (id, rename)) + }) .collect(), } } diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index f6bc9428d65f..6f70854767d2 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{def_path_def_ids, fn_def_id, is_no_std_crate, path_def_id}; +use clippy_utils::{PathNS, find_crates, fn_def_id, is_no_std_crate, lookup_path_str, path_def_id, paths, sym}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -62,10 +62,7 @@ static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[ pub struct NonStdLazyStatic { msrv: Msrv, - lazy_static_lazy_static: Vec, - once_cell_crate: Vec, - once_cell_sync_lazy: Vec, - once_cell_sync_lazy_new: Vec, + once_cell_crates: Vec, sugg_map: FxIndexMap>, lazy_type_defs: FxIndexMap, uses_other_once_cell_types: bool, @@ -76,10 +73,7 @@ impl NonStdLazyStatic { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv, - lazy_static_lazy_static: Vec::new(), - once_cell_crate: Vec::new(), - once_cell_sync_lazy: Vec::new(), - once_cell_sync_lazy_new: Vec::new(), + once_cell_crates: Vec::new(), sugg_map: FxIndexMap::default(), lazy_type_defs: FxIndexMap::default(), uses_other_once_cell_types: false, @@ -95,17 +89,15 @@ fn can_use_lazy_cell(cx: &LateContext<'_>, msrv: Msrv) -> bool { impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { fn check_crate(&mut self, cx: &LateContext<'hir>) { - // Fetch def_ids for external paths - self.lazy_static_lazy_static = def_path_def_ids(cx.tcx, &["lazy_static", "lazy_static"]).collect(); - self.once_cell_sync_lazy = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy"]).collect(); - self.once_cell_sync_lazy_new = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy", "new"]).collect(); - // And CrateNums for `once_cell` crate - self.once_cell_crate = self.once_cell_sync_lazy.iter().map(|d| d.krate).collect(); + // Add CrateNums for `once_cell` crate + self.once_cell_crates = find_crates(cx.tcx, sym::once_cell) + .iter() + .map(|def_id| def_id.krate) + .collect(); // Convert hardcoded fn replacement list into a map with def_id for (path, sugg) in FUNCTION_REPLACEMENTS { - let path_vec: Vec<&str> = path.split("::").collect(); - for did in def_path_def_ids(cx.tcx, &path_vec) { + for did in lookup_path_str(cx.tcx, PathNS::Value, path) { self.sugg_map.insert(did, sugg.map(ToOwned::to_owned)); } } @@ -114,7 +106,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) { if let ItemKind::Static(..) = item.kind && let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span) - && self.lazy_static_lazy_static.contains(¯o_call.def_id) + && paths::LAZY_STATIC.matches(cx, macro_call.def_id) && can_use_lazy_cell(cx, self.msrv) { span_lint( @@ -130,7 +122,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { return; } - if let Some(lazy_info) = LazyInfo::from_item(self, cx, item) + if let Some(lazy_info) = LazyInfo::from_item(cx, item) && can_use_lazy_cell(cx, self.msrv) { self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info); @@ -155,9 +147,9 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { if let rustc_hir::TyKind::Path(qpath) = ty.peel_refs().kind && let Some(ty_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id() // Is from `once_cell` crate - && self.once_cell_crate.contains(&ty_def_id.krate) + && self.once_cell_crates.contains(&ty_def_id.krate) // And is NOT `once_cell::sync::Lazy` - && !self.once_cell_sync_lazy.contains(&ty_def_id) + && !paths::ONCE_CELL_SYNC_LAZY.matches(cx, ty_def_id) { self.uses_other_once_cell_types = true; } @@ -190,12 +182,12 @@ struct LazyInfo { } impl LazyInfo { - fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) -> Option { + fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Option { // Check if item is a `once_cell:sync::Lazy` static. if let ItemKind::Static(_, ty, _, body_id) = item.kind && let Some(path_def_id) = path_def_id(cx, ty) && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && state.once_cell_sync_lazy.contains(&path_def_id) + && paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id) { let ty_span_no_args = path_span_without_args(path); let body = cx.tcx.hir_body(body_id); @@ -204,7 +196,7 @@ impl LazyInfo { let mut new_fn_calls = FxIndexMap::default(); for_each_expr::<(), ()>(cx, body, |ex| { if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id) - && state.once_cell_sync_lazy_new.contains(&fn_did) + && paths::ONCE_CELL_SYNC_LAZY_NEW.matches(cx, fn_did) { new_fn_calls.insert(call_span, fn_did); } diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 834ff2af0e88..89d945161f62 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -2,8 +2,9 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::paths::PathLookup; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths, sym}; +use clippy_utils::{path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; @@ -121,17 +122,9 @@ impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX, REGEX_CREATION_IN_LOOPS] impl<'tcx> LateLintPass<'tcx> for Regex { fn check_crate(&mut self, cx: &LateContext<'tcx>) { - // We don't use `match_def_path` here because that relies on matching the exact path, which changed - // between regex 1.8 and 1.9 - // - // `def_path_res_with_base` will resolve through re-exports but is relatively heavy, so we only - // perform the operation once and store the results - let regex_crates = find_crates(cx.tcx, sym::regex); - let mut resolve = |path: &[&str], kind: RegexKind| { - for res in def_path_res_with_base(cx.tcx, regex_crates.clone(), &path[1..]) { - if let Some(id) = res.opt_def_id() { - self.definitions.insert(id, kind); - } + let mut resolve = |path: &PathLookup, kind: RegexKind| { + for &id in path.get(cx) { + self.definitions.insert(id, kind); } }; diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index a8c6518b592b..a64b9b223786 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{get_trait_def_id, paths}; +use clippy_utils::paths; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -32,9 +32,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi { }) = item.kind { let did = trait_ref.path.res.def_id(); - if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR) - && did == visit_did - { + if paths::SERDE_DE_VISITOR.matches(cx, did) { let mut seen_str = None; let mut seen_string = None; for item in *items { diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs index 2d989b1cf0ba..54d09ff9ee40 100644 --- a/clippy_lints/src/single_range_in_vec_init.rs +++ b/clippy_lints/src/single_range_in_vec_init.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, is_no_std_crate}; +use clippy_utils::{is_no_std_crate, paths}; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr}; @@ -100,7 +100,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { && let Some(start_snippet) = start.span.get_source_text(cx) && let Some(end_snippet) = end.span.get_source_text(cx) { - let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"]) + let should_emit_every_value = if let Some(step_def_id) = paths::ITER_STEP.only(cx) && implements_trait(cx, ty, step_def_id, &[]) { true diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index bb969bc802fe..c8a6a41d6d8d 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{match_def_path, sym}; +use clippy_utils::{paths, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -40,27 +39,18 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind && is_some_path.ident.name == sym::is_some { - let match_result = match &to_digit_expr.kind { + let match_result = match to_digit_expr.kind { hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => { if to_digits_path.ident.name == sym::to_digit - && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg) - && *char_arg_ty.kind() == ty::Char + && cx.typeck_results().expr_ty_adjusted(char_arg).is_char() { - Some((true, *char_arg, radix_arg)) + Some((true, char_arg, radix_arg)) } else { None } }, hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => { - if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind - && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id) - && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id() - && match_def_path( - cx, - to_digits_def_id, - &["core", "char", "methods", "", "to_digit"], - ) - { + if paths::CHAR_TO_DIGIT.matches_path(cx, to_digits_call) { Some((false, char_arg, radix_arg)) } else { None diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 2d88c490b1ab..e3f28908ff83 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, paths, peel_blocks}; +use clippy_utils::{is_res_lang_ctor, paths, peel_blocks}; use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -93,14 +93,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { return; } - let async_paths: [&[&str]; 4] = [ + let async_paths = [ &paths::TOKIO_IO_ASYNCREADEXT, &paths::TOKIO_IO_ASYNCWRITEEXT, &paths::FUTURES_IO_ASYNCREADEXT, &paths::FUTURES_IO_ASYNCWRITEEXT, ]; - if async_paths.into_iter().any(|path| match_def_path(cx, trait_id, path)) { + if async_paths.into_iter().any(|path| path.matches(cx, trait_id)) { return; } } @@ -291,19 +291,28 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option { }, }; - match ( - is_trait_method(cx, call, sym::IoRead), - is_trait_method(cx, call, sym::IoWrite), - match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) - || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT), - match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) - || match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT), - ) { - (true, _, _, _) => Some(IoOp::SyncRead(vectorized)), - (_, true, _, _) => Some(IoOp::SyncWrite(vectorized)), - (_, _, true, _) => Some(IoOp::AsyncRead(vectorized)), - (_, _, _, true) => Some(IoOp::AsyncWrite(vectorized)), - _ => None, + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(call.hir_id) + && let Some(trait_def_id) = cx.tcx.trait_of_item(method_def_id) + { + if let Some(diag_name) = cx.tcx.get_diagnostic_name(trait_def_id) { + match diag_name { + sym::IoRead => Some(IoOp::SyncRead(vectorized)), + sym::IoWrite => Some(IoOp::SyncWrite(vectorized)), + _ => None, + } + } else if paths::FUTURES_IO_ASYNCREADEXT.matches(cx, trait_def_id) + || paths::TOKIO_IO_ASYNCREADEXT.matches(cx, trait_def_id) + { + Some(IoOp::AsyncRead(vectorized)) + } else if paths::TOKIO_IO_ASYNCWRITEEXT.matches(cx, trait_def_id) + || paths::FUTURES_IO_ASYNCWRITEEXT.matches(cx, trait_def_id) + { + Some(IoOp::AsyncWrite(vectorized)) + } else { + None + } + } else { + None } } diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index b7dcd2ffb0ee..812c4df4ddde 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -1,16 +1,18 @@ -use clippy_utils::{get_attr, higher}; +use clippy_utils::{MaybePath, get_attr, higher, path_def_id}; +use itertools::Itertools; use rustc_ast::LitIntType; 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, - FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind, + FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::symbol::{Ident, Symbol}; use std::cell::Cell; -use std::fmt::{Display, Formatter, Write as _}; +use std::fmt::{Display, Formatter}; declare_lint_pass!( /// ### What it does @@ -148,6 +150,15 @@ fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, } } +fn paths_static_name(cx: &LateContext<'_>, id: DefId) -> String { + cx.get_def_path(id) + .iter() + .map(Symbol::as_str) + .filter(|s| !s.starts_with('<')) + .join("_") + .to_uppercase() +} + struct Binding { name: String, value: T, @@ -257,11 +268,44 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } - fn qpath(&self, qpath: &Binding<&QPath<'_>>) { + fn qpath<'p>(&self, qpath: &Binding<&QPath<'_>>, has_hir_id: &Binding<&impl MaybePath<'p>>) { if let QPath::LangItem(lang_item, ..) = *qpath.value { chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); - } else if let Ok(path) = path_to_string(qpath.value) { - chain!(self, "match_qpath({qpath}, &[{}])", path); + } else if let Some(def_id) = self.cx.qpath_res(qpath.value, has_hir_id.value.hir_id()).opt_def_id() + && !def_id.is_local() + { + bind!(self, def_id); + chain!( + self, + "let Some({def_id}) = cx.qpath_res({qpath}, {has_hir_id}.hir_id).opt_def_id()" + ); + if let Some(name) = self.cx.tcx.get_diagnostic_name(def_id.value) { + chain!(self, "cx.tcx.is_diagnostic_item(sym::{name}, {def_id})"); + } else { + chain!( + self, + "paths::{}.matches(cx, {def_id}) // Add the path to `clippy_utils::paths` if needed", + paths_static_name(self.cx, def_id.value) + ); + } + } + } + + fn maybe_path<'p>(&self, path: &Binding<&impl MaybePath<'p>>) { + if let Some(id) = path_def_id(self.cx, path.value) + && !id.is_local() + { + if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { + chain!(self, "is_path_lang_item(cx, {path}, LangItem::{}", lang.name()); + } else if let Some(name) = self.cx.tcx.get_diagnostic_name(id) { + chain!(self, "is_path_diagnostic_item(cx, {path}, sym::{name})"); + } else { + chain!( + self, + "paths::{}.matches_path(cx, {path}) // Add the path to `clippy_utils::paths` if needed", + paths_static_name(self.cx, id) + ); + } } } @@ -270,7 +314,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ConstArgKind::Path(ref qpath) => { bind!(self, qpath); chain!(self, "let ConstArgKind::Path(ref {qpath}) = {const_arg}.kind"); - self.qpath(qpath); }, ConstArgKind::Anon(anon_const) => { bind!(self, anon_const); @@ -394,12 +437,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, let_expr); kind!("Let({let_expr})"); self.pat(field!(let_expr.pat)); - // Does what ExprKind::Cast does, only adds a clause for the type - // if it's a path - if let Some(TyKind::Path(qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { - bind!(self, qpath); - chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); - self.qpath(qpath); + if let Some(ty) = let_expr.value.ty { + bind!(self, ty); + chain!(self, "let Some({ty}) = {let_expr}.ty"); + self.maybe_path(ty); } self.expr(field!(let_expr.init)); }, @@ -451,11 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::Cast(expr, cast_ty) => { bind!(self, expr, cast_ty); kind!("Cast({expr}, {cast_ty})"); - if let TyKind::Path(ref qpath) = cast_ty.value.kind { - bind!(self, qpath); - chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); - self.qpath(qpath); - } + self.maybe_path(cast_ty); self.expr(expr); }, ExprKind::Type(expr, _ty) => { @@ -561,10 +598,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(object); self.expr(index); }, - ExprKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); + ExprKind::Path(_) => { + self.maybe_path(expr); }, ExprKind::AddrOf(kind, mutability, inner) => { bind!(self, inner); @@ -608,7 +643,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { StructTailExpr::None | StructTailExpr::DefaultFields(_) => None, }); kind!("Struct({qpath}, {fields}, {base})"); - self.qpath(qpath); + self.qpath(qpath, expr); self.slice(fields, |field| { self.ident(field!(field.ident)); self.expr(field!(field.expr)); @@ -648,7 +683,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(expr); } - fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) { + fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>, pat: &Binding<&hir::Pat<'_>>) { let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); @@ -657,15 +692,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatExprKind::Lit { lit, negated } => { bind!(self, lit); bind!(self, negated); - kind!("Lit{{ref {lit}, {negated} }}"); + kind!("Lit {{ ref {lit}, {negated} }}"); self.lit(lit); }, PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), - PatExprKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); - }, + PatExprKind::Path(_) => self.maybe_path(pat), } } @@ -697,7 +728,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::Struct(ref qpath, fields, ignore) => { bind!(self, qpath, fields); kind!("Struct(ref {qpath}, {fields}, {ignore})"); - self.qpath(qpath); + self.qpath(qpath, pat); self.slice(fields, |field| { self.ident(field!(field.ident)); self.pat(field!(field.pat)); @@ -711,7 +742,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::TupleStruct(ref qpath, fields, skip_pos) => { bind!(self, qpath, fields); kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})"); - self.qpath(qpath); + self.qpath(qpath, pat); self.slice(fields, |pat| self.pat(pat)); }, PatKind::Tuple(fields, skip_pos) => { @@ -743,13 +774,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::Expr(lit_expr) => { bind!(self, lit_expr); kind!("Expr({lit_expr})"); - self.pat_expr(lit_expr); + self.pat_expr(lit_expr, pat); }, PatKind::Range(start, end, end_kind) => { opt_bind!(self, start, end); kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); - start.if_some(|e| self.pat_expr(e)); - end.if_some(|e| self.pat_expr(e)); + start.if_some(|e| self.pat_expr(e, pat)); + end.if_some(|e| self.pat_expr(e, pat)); }, PatKind::Slice(start, middle, end) => { bind!(self, start, end); @@ -797,32 +828,3 @@ fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { let attrs = cx.tcx.hir_attrs(hir_id); get_attr(cx.sess(), attrs, "author").count() > 0 } - -fn path_to_string(path: &QPath<'_>) -> Result { - fn inner(s: &mut String, path: &QPath<'_>) -> Result<(), ()> { - match *path { - QPath::Resolved(_, path) => { - for (i, segment) in path.segments.iter().enumerate() { - if i > 0 { - *s += ", "; - } - write!(s, "{:?}", segment.ident.as_str()).unwrap(); - } - }, - QPath::TypeRelative(ty, segment) => match &ty.kind { - TyKind::Path(inner_path) => { - inner(s, inner_path)?; - *s += ", "; - write!(s, "{:?}", segment.ident.as_str()).unwrap(); - }, - other => write!(s, "/* unimplemented: {other:?}*/").unwrap(), - }, - QPath::LangItem(..) => return Err(()), - } - - Ok(()) - } - let mut s = String::new(); - inner(&mut s, path)?; - Ok(s) -} diff --git a/clippy_lints_internal/src/collapsible_calls.rs b/clippy_lints_internal/src/collapsible_calls.rs index d7967a0cc022..407deb45db0a 100644 --- a/clippy_lints_internal/src/collapsible_calls.rs +++ b/clippy_lints_internal/src/collapsible_calls.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{SpanlessEq, is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt}; +use clippy_utils::{SpanlessEq, is_lint_allowed, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -10,6 +10,8 @@ use rustc_span::Span; use std::borrow::{Borrow, Cow}; +use crate::internal_paths; + declare_tool_lint! { /// ### What it does /// Lints `span_lint_and_then` function calls, where the @@ -80,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { } if let ExprKind::Call(func, [call_cx, call_lint, call_sp, call_msg, call_f]) = expr.kind - && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]) + && internal_paths::SPAN_LINT_AND_THEN.matches_path(cx, func) && let ExprKind::Closure(&Closure { body, .. }) = call_f.kind && let body = cx.tcx.hir_body(body) && let only_expr = peel_blocks_with_stmt(body.value) diff --git a/clippy_lints_internal/src/internal_paths.rs b/clippy_lints_internal/src/internal_paths.rs new file mode 100644 index 000000000000..29ad3b85da11 --- /dev/null +++ b/clippy_lints_internal/src/internal_paths.rs @@ -0,0 +1,17 @@ +use clippy_utils::paths::PathLookup; +use clippy_utils::{PathNS, sym, type_path, value_path}; + +// Paths inside rustc +pub static EARLY_LINT_PASS: PathLookup = type_path!(rustc_lint::passes::EarlyLintPass); +pub static KW_MODULE: PathLookup = type_path!(rustc_span::symbol::kw); +pub static LINT: PathLookup = type_path!(rustc_lint_defs::Lint); +pub static SYMBOL: PathLookup = type_path!(rustc_span::symbol::Symbol); +pub static SYMBOL_AS_STR: PathLookup = value_path!(rustc_span::symbol::Symbol::as_str); +pub static SYM_MODULE: PathLookup = type_path!(rustc_span::symbol::sym); +pub static SYNTAX_CONTEXT: PathLookup = type_path!(rustc_span::hygiene::SyntaxContext); + +// Paths in clippy itself +pub static CLIPPY_SYM_MODULE: PathLookup = type_path!(clippy_utils::sym); +pub static MSRV_STACK: PathLookup = type_path!(clippy_utils::msrvs::MsrvStack); +pub static PATH_LOOKUP_NEW: PathLookup = value_path!(clippy_utils::paths::PathLookup::new); +pub static SPAN_LINT_AND_THEN: PathLookup = value_path!(clippy_utils::diagnostics::span_lint_and_then); diff --git a/clippy_lints_internal/src/invalid_paths.rs b/clippy_lints_internal/src/invalid_paths.rs deleted file mode 100644 index bee87efa3fcd..000000000000 --- a/clippy_lints_internal/src/invalid_paths.rs +++ /dev/null @@ -1,108 +0,0 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::def_path_res; -use clippy_utils::diagnostics::span_lint; -use rustc_hir as hir; -use rustc_hir::Item; -use rustc_hir::def::DefKind; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; -use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, FloatTy}; -use rustc_session::declare_lint_pass; -use rustc_span::symbol::Symbol; - -declare_tool_lint! { - /// ### What it does - /// Checks the paths module for invalid paths. - /// - /// ### Why is this bad? - /// It indicates a bug in the code. - /// - /// ### Example - /// None. - pub clippy::INVALID_PATHS, - Warn, - "invalid path", - report_in_external_macro: true -} - -declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); - -impl<'tcx> LateLintPass<'tcx> for InvalidPaths { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let local_def_id = &cx.tcx.parent_module(item.hir_id()); - let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if mod_name.as_str() == "paths" - && let hir::ItemKind::Const(.., body_id) = item.kind - && let Some(Constant::Vec(path)) = ConstEvalCtxt::with_env( - cx.tcx, - ty::TypingEnv::post_analysis(cx.tcx, item.owner_id), - cx.tcx.typeck(item.owner_id), - ) - .eval_simple(cx.tcx.hir_body(body_id).value) - && let Some(path) = path - .iter() - .map(|x| { - if let Constant::Str(s) = x { - Some(s.as_str()) - } else { - None - } - }) - .collect::>>() - && !check_path(cx, &path[..]) - { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } - } -} - -// This is not a complete resolver for paths. It works on all the paths currently used in the paths -// module. That's all it does and all it needs to do. -pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if !def_path_res(cx.tcx, path).is_empty() { - return true; - } - - // Some implementations can't be found by `path_to_res`, particularly inherent - // implementations of native types. Check lang items. - let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); - let lang_items = cx.tcx.lang_items(); - // This list isn't complete, but good enough for our current list of paths. - let incoherent_impls = [ - SimplifiedType::Float(FloatTy::F32), - SimplifiedType::Float(FloatTy::F64), - SimplifiedType::Slice, - SimplifiedType::Str, - SimplifiedType::Bool, - SimplifiedType::Char, - ] - .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter()) - .copied(); - for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) { - let lang_item_path = cx.get_def_path(item_def_id); - if path_syms.starts_with(&lang_item_path) - && let [item] = &path_syms[lang_item_path.len()..] - { - if matches!( - cx.tcx.def_kind(item_def_id), - DefKind::Mod | DefKind::Enum | DefKind::Trait - ) { - for child in cx.tcx.module_children(item_def_id) { - if child.ident.name == *item { - return true; - } - } - } else { - for child in cx.tcx.associated_item_def_ids(item_def_id) { - if cx.tcx.item_name(*child) == *item { - return true; - } - } - } - } - } - - false -} diff --git a/clippy_lints_internal/src/lib.rs b/clippy_lints_internal/src/lib.rs index b02d378619ca..da37fd3d8271 100644 --- a/clippy_lints_internal/src/lib.rs +++ b/clippy_lints_internal/src/lib.rs @@ -32,7 +32,7 @@ extern crate rustc_span; mod almost_standard_lint_formulation; mod collapsible_calls; -mod invalid_paths; +mod internal_paths; mod lint_without_lint_pass; mod msrv_attr_impl; mod outer_expn_data_pass; @@ -46,7 +46,6 @@ use rustc_lint::{Lint, LintStore}; static LINTS: &[&Lint] = &[ almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION, collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, - invalid_paths::INVALID_PATHS, lint_without_lint_pass::DEFAULT_LINT, lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, @@ -66,10 +65,9 @@ pub fn register_lints(store: &mut LintStore) { store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths)); store.register_early_pass(|| Box::new(produce_ice::ProduceIce)); store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls)); - store.register_late_pass(|_| Box::new(invalid_paths::InvalidPaths)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath)); store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl)); store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new())); diff --git a/clippy_lints_internal/src/lint_without_lint_pass.rs b/clippy_lints_internal/src/lint_without_lint_pass.rs index 6a75defcce34..655d8fb8d1bf 100644 --- a/clippy_lints_internal/src/lint_without_lint_pass.rs +++ b/clippy_lints_internal/src/lint_without_lint_pass.rs @@ -1,6 +1,7 @@ +use crate::internal_paths; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::is_lint_allowed; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::{is_lint_allowed, match_def_path, paths}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir as hir; @@ -209,10 +210,10 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { && let TyKind::Path(ref path) = inner.kind && let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - return match_def_path(cx, def_id, &paths::LINT); + internal_paths::LINT.matches(cx, def_id) + } else { + false } - - false } fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { diff --git a/clippy_lints_internal/src/msrv_attr_impl.rs b/clippy_lints_internal/src/msrv_attr_impl.rs index dda054546e26..d48d8dc57b24 100644 --- a/clippy_lints_internal/src/msrv_attr_impl.rs +++ b/clippy_lints_internal/src/msrv_attr_impl.rs @@ -1,7 +1,6 @@ +use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::match_type; -use clippy_utils::{match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -31,7 +30,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .tcx .impl_trait_ref(item.owner_id) .map(EarlyBinder::instantiate_identity) - && match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS) + && internal_paths::EARLY_LINT_PASS.matches(cx, trait_ref.def_id) && let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind() && self_ty_def.is_struct() && self_ty_def.all_fields().any(|f| { @@ -40,7 +39,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .instantiate_identity() .walk() .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV_STACK)) + .any(|t| internal_paths::MSRV_STACK.matches_ty(cx, t.expect_ty())) }) && !items.iter().any(|item| item.ident.name.as_str() == "check_attributes") { diff --git a/clippy_lints_internal/src/outer_expn_data_pass.rs b/clippy_lints_internal/src/outer_expn_data_pass.rs index e94419647978..40951443a48a 100644 --- a/clippy_lints_internal/src/outer_expn_data_pass.rs +++ b/clippy_lints_internal/src/outer_expn_data_pass.rs @@ -1,6 +1,6 @@ +use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::match_type; -use clippy_utils::{is_lint_allowed, method_calls, paths}; +use clippy_utils::{is_lint_allowed, method_calls}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { && let (self_arg, args) = arg_lists[1] && args.is_empty() && let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs() - && match_type(cx, self_ty, &paths::SYNTAX_CONTEXT) + && internal_paths::SYNTAX_CONTEXT.matches_ty(cx, self_ty) { span_lint_and_sugg( cx, diff --git a/clippy_lints_internal/src/symbols.rs b/clippy_lints_internal/src/symbols.rs index c64e5821916b..bf166988a0c7 100644 --- a/clippy_lints_internal/src/symbols.rs +++ b/clippy_lints_internal/src/symbols.rs @@ -1,6 +1,5 @@ +use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::match_type; -use clippy_utils::{def_path_def_ids, match_def_path, paths}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -69,12 +68,12 @@ impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]); impl<'tcx> LateLintPass<'tcx> for Symbols { fn check_crate(&mut self, cx: &LateContext<'_>) { let modules = [ - ("kw", &paths::KW_MODULE[..]), - ("sym", &paths::SYM_MODULE), - ("sym", &paths::CLIPPY_SYM_MODULE), + ("kw", &internal_paths::KW_MODULE), + ("sym", &internal_paths::SYM_MODULE), + ("sym", &internal_paths::CLIPPY_SYM_MODULE), ]; for (prefix, module) in modules { - for def_id in def_path_def_ids(cx.tcx, module) { + for def_id in module.get(cx) { // When linting `clippy_utils` itself we can't use `module_children` as it's a local def id. It will // still lint but the suggestion will say to add it to `sym.rs` even if it's already there if def_id.is_local() { @@ -84,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for Symbols { for item in cx.tcx.module_children(def_id) { if let Res::Def(DefKind::Const, item_def_id) = item.res && let ty = cx.tcx.type_of(item_def_id).instantiate_identity() - && match_type(cx, ty, &paths::SYMBOL) + && internal_paths::SYMBOL.matches_ty(cx, ty) && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id) && let Some(value) = value.to_u32().discard_err() { @@ -160,7 +159,7 @@ fn suggestion(symbols: &mut FxHashMap, name: Symbol fn as_str_span(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let ExprKind::MethodCall(_, recv, [], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && match_def_path(cx, method_def_id, &paths::SYMBOL_AS_STR) + && internal_paths::SYMBOL_AS_STR.matches(cx, method_def_id) { Some(recv.span.shrink_to_hi().to(expr.span.shrink_to_hi())) } else { diff --git a/clippy_lints_internal/src/unnecessary_def_path.rs b/clippy_lints_internal/src/unnecessary_def_path.rs index 6bdfbed55b06..2e9b8c2d53d6 100644 --- a/clippy_lints_internal/src/unnecessary_def_path.rs +++ b/clippy_lints_internal/src/unnecessary_def_path.rs @@ -1,23 +1,13 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; -use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; +use crate::internal_paths; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{PathNS, lookup_path, path_def_id, peel_ref_operators}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind, LetStmt, Mutability, Node}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_lint_defs::declare_tool_lint; +use rustc_lint_defs::{declare_lint_pass, declare_tool_lint}; use rustc_middle::mir::ConstValue; -use rustc_middle::mir::interpret::{Allocation, GlobalAlloc}; -use rustc_middle::ty::{self, Ty}; -use rustc_session::impl_lint_pass; -use rustc_span::Span; use rustc_span::symbol::Symbol; -use std::str; - declare_tool_lint! { /// ### What it does /// Checks for usage of def paths when a diagnostic item or a `LangItem` could be used. @@ -28,12 +18,14 @@ declare_tool_lint! { /// /// ### Example /// ```rust,ignore - /// utils::match_type(cx, ty, &paths::VEC) + /// pub static VEC: PathLookup = path!(alloc::vec::Vec); + /// + /// VEC.contains_ty(cx, ty) /// ``` /// /// Use instead: /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) + /// is_type_diagnostic_item(cx, ty, sym::Vec) /// ``` pub clippy::UNNECESSARY_DEF_PATH, Warn, @@ -41,257 +33,65 @@ declare_tool_lint! { report_in_external_macro: true } -impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); - -#[derive(Default)] -pub struct UnnecessaryDefPath { - array_def_ids: FxIndexSet<(DefId, Span)>, - linted_def_ids: FxHashSet, -} +declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { - return; - } - - match expr.kind { - ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span), - ExprKind::Array(elements) => self.check_array(cx, elements, expr.span), - _ => {}, - } - } - - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - for &(def_id, span) in &self.array_def_ids { - if self.linted_def_ids.contains(&def_id) { - continue; - } - - let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) { - ("diagnostic item", format!("sym::{sym}")) - } else if let Some(sym) = get_lang_item_name(cx, def_id) { - ("language item", format!("LangItem::{sym}")) - } else { - continue; - }; - - span_lint_and_help( - cx, - UNNECESSARY_DEF_PATH, - span, - format!("hardcoded path to a {msg}"), - None, - format!("convert all references to use `{sugg}`"), - ); - } - } -} - -impl UnnecessaryDefPath { - #[allow(clippy::too_many_lines)] - fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) { - enum Item { - LangItem(&'static str), - DiagnosticItem(Symbol), - } - static PATHS: &[&[&str]] = &[ - &["clippy_utils", "match_def_path"], - &["clippy_utils", "match_trait_method"], - &["clippy_utils", "ty", "match_type"], - &["clippy_utils", "is_expr_path_def_path"], - ]; - - if let [cx_arg, def_arg, args @ ..] = args - && let ExprKind::Path(path) = &func.kind - && let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id() - && let Some(which_path) = match_any_def_paths(cx, id, PATHS) - && let item_arg = if which_path == 4 { &args[1] } else { &args[0] } - // Extract the path to the matched type - && let Some(segments) = path_to_matched_type(cx, item_arg) - && let segments = segments.iter().map(|sym| &**sym).collect::>() - && let Some(def_id) = def_path_def_ids(cx.tcx, &segments[..]).next() + if let ExprKind::Call(ctor, [_, path]) = expr.kind + && internal_paths::PATH_LOOKUP_NEW.matches_path(cx, ctor) + && let ExprKind::Array(segments) = peel_ref_operators(cx, path).kind + && let Some(macro_id) = expr.span.ctxt().outer_expn_data().macro_def_id { - // Check if the target item is a diagnostic item or LangItem. - #[rustfmt::skip] - let (msg, item) = if let Some(item_name) - = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) - { - ( - "use of a def path to a diagnostic item", - Item::DiagnosticItem(*item_name), - ) - } else if let Some(item_name) = get_lang_item_name(cx, def_id) { - ( - "use of a def path to a `LangItem`", - Item::LangItem(item_name), - ) - } else { - return; + let ns = match cx.tcx.item_name(macro_id).as_str() { + "type_path" => PathNS::Type, + "value_path" => PathNS::Value, + "macro_path" => PathNS::Macro, + _ => unreachable!(), }; - let has_ctor = match cx.tcx.def_kind(def_id) { - DefKind::Struct => { - let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - DefKind::Variant => { - let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - _ => false, - }; + let path: Vec = segments + .iter() + .map(|segment| { + if let Some(const_def_id) = path_def_id(cx, segment) + && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(const_def_id) + && let Some(value) = value.to_u32().discard_err() + { + Symbol::new(value) + } else { + panic!("failed to resolve path {:?}", expr.span); + } + }) + .collect(); - let mut app = Applicability::MachineApplicable; - let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); - let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); - let (sugg, with_note) = match (which_path, item) { - // match_def_path - (0, Item::DiagnosticItem(item)) => ( - format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), - has_ctor, - ), - (0, Item::LangItem(item)) => ( - format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"), - has_ctor, - ), - // match_trait_method - (1, Item::DiagnosticItem(item)) => { - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) - }, - // match_type - (2, Item::DiagnosticItem(item)) => ( - format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (2, Item::LangItem(item)) => ( - format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), - false, - ), - // is_expr_path_def_path - (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), - false, - ), - (3, Item::LangItem(item)) if has_ctor => ( - format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), - false, - ), - (3, Item::DiagnosticItem(item)) => ( - format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (3, Item::LangItem(item)) => ( - format!( - "path_res({cx_snip}, {def_snip}).opt_def_id()\ - .map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))", - ), - false, - ), - _ => return, - }; - - span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { - diag.span_suggestion(span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead", + for def_id in lookup_path(cx.tcx, ns, &path) { + if let Some(name) = cx.tcx.get_diagnostic_name(def_id) { + span_lint_and_then( + cx, + UNNECESSARY_DEF_PATH, + expr.span.source_callsite(), + format!("a diagnostic name exists for this path: sym::{name}"), + |diag| { + diag.help( + "remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead", + ); + diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils"); + }, + ); + } else if let Some(item_name) = get_lang_item_name(cx, def_id) { + span_lint_and_then( + cx, + UNNECESSARY_DEF_PATH, + expr.span.source_callsite(), + format!("a language item exists for this path: LangItem::{item_name}"), + |diag| { + diag.help("remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead"); + diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils"); + }, ); } - }); - - self.linted_def_ids.insert(def_id); - } - } - - fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { - let Some(path) = path_from_array(elements) else { return }; - - for def_id in def_path_def_ids(cx.tcx, &path.iter().map(AsRef::as_ref).collect::>()) { - self.array_def_ids.insert((def_id, span)); - } - } -} - -fn path_to_matched_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option> { - match peel_hir_expr_refs(expr).0.kind { - ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { - Res::Local(hir_id) => { - if let Node::LetStmt(LetStmt { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { - path_to_matched_type(cx, init) - } else { - None - } - }, - Res::Def(DefKind::Static { .. }, def_id) => read_mir_alloc_def_path( - cx, - cx.tcx.eval_static_initializer(def_id).ok()?.inner(), - cx.tcx.type_of(def_id).instantiate_identity(), - ), - Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::Indirect { alloc_id, offset } if offset.bytes() == 0 => { - let alloc = cx.tcx.global_alloc(alloc_id).unwrap_memory(); - read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity()) - }, - _ => None, - }, - _ => None, - }, - ExprKind::Array(exprs) => path_from_array(exprs), - _ => None, - } -} - -fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { - let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { - let &alloc = alloc.provenance().ptrs().values().next()?; - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) { - (alloc.inner(), ty) - } else { - return None; - } - } else { - (alloc, ty) - }; - - if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() - && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() - && ty.is_str() - { - alloc - .provenance() - .ptrs() - .values() - .map(|&alloc| { - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) { - let alloc = alloc.inner(); - str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) - .ok() - .map(ToOwned::to_owned) - } else { - None - } - }) - .collect() - } else { - None - } -} - -fn path_from_array(exprs: &[Expr<'_>]) -> Option> { - exprs - .iter() - .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind - && let LitKind::Str(sym, _) = lit.node - { - return Some((*sym.as_str()).to_owned()); } - - None - }) - .collect() + } + } } fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 187dfa4dda84..44e72a8d16a5 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -96,8 +96,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnhashMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; +use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; @@ -105,8 +106,8 @@ use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, CoroutineDesugaring, CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, - Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, - Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def, + Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, + StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -347,14 +348,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { } } -/// Checks if the method call given in `expr` belongs to the given trait. -/// This is a deprecated function, consider using [`is_trait_method`]. -pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { - let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); - let trt_id = cx.tcx.trait_of_item(def_id); - trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path)) -} - /// Checks if the given method call expression calls an inherent method. pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { @@ -438,44 +431,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator, segments: &[&str]) -> bool { - match *path { - QPath::Resolved(_, path) => match_path(path, segments), - QPath::TypeRelative(ty, segment) => match ty.kind { - TyKind::Path(ref inner_path) => { - if let [prefix @ .., end] = segments - && match_qpath(inner_path, prefix) - { - return segment.ident.name.as_str() == *end; - } - false - }, - _ => false, - }, - QPath::LangItem(..) => false, - } -} - -/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path. -/// -/// Please use `is_path_diagnostic_item` if the target is a diagnostic item. -pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool { - path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments)) -} - /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if /// it matches the given lang item. pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool { @@ -492,34 +447,6 @@ pub fn is_path_diagnostic_item<'tcx>( path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id)) } -/// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals. -/// -/// This method is deprecated and will eventually be removed since it does not match against the -/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from -/// `QPath::Resolved.1.res.opt_def_id()`. -/// -/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a -/// `rustc_hir::Path`. -/// -/// # Examples -/// -/// ```rust,ignore -/// if match_path(&trait_ref.path, &paths::HASH) { -/// // This is the `std::hash::Hash` trait. -/// } -/// -/// if match_path(ty_path, &["rustc", "lint", "Lint"]) { -/// // This is a `rustc_middle::lint::Lint`. -/// } -/// ``` -pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool { - path.segments - .iter() - .rev() - .zip(segments.iter().rev()) - .all(|(a, b)| a.ident.name.as_str() == *b) -} - /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option { if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind @@ -586,60 +513,57 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { +fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] { let ty = match name { - "bool" => SimplifiedType::Bool, - "char" => SimplifiedType::Char, - "str" => SimplifiedType::Str, - "array" => SimplifiedType::Array, - "slice" => SimplifiedType::Slice, + sym::bool => SimplifiedType::Bool, + sym::char => SimplifiedType::Char, + sym::str => SimplifiedType::Str, + sym::array => SimplifiedType::Array, + sym::slice => SimplifiedType::Slice, // FIXME: rustdoc documents these two using just `pointer`. // // Maybe this is something we should do here too. - "const_ptr" => SimplifiedType::Ptr(Mutability::Not), - "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut), - "isize" => SimplifiedType::Int(IntTy::Isize), - "i8" => SimplifiedType::Int(IntTy::I8), - "i16" => SimplifiedType::Int(IntTy::I16), - "i32" => SimplifiedType::Int(IntTy::I32), - "i64" => SimplifiedType::Int(IntTy::I64), - "i128" => SimplifiedType::Int(IntTy::I128), - "usize" => SimplifiedType::Uint(UintTy::Usize), - "u8" => SimplifiedType::Uint(UintTy::U8), - "u16" => SimplifiedType::Uint(UintTy::U16), - "u32" => SimplifiedType::Uint(UintTy::U32), - "u64" => SimplifiedType::Uint(UintTy::U64), - "u128" => SimplifiedType::Uint(UintTy::U128), - "f32" => SimplifiedType::Float(FloatTy::F32), - "f64" => SimplifiedType::Float(FloatTy::F64), - _ => { - return [].iter().copied(); - }, + sym::const_ptr => SimplifiedType::Ptr(Mutability::Not), + sym::mut_ptr => SimplifiedType::Ptr(Mutability::Mut), + sym::isize => SimplifiedType::Int(IntTy::Isize), + sym::i8 => SimplifiedType::Int(IntTy::I8), + sym::i16 => SimplifiedType::Int(IntTy::I16), + sym::i32 => SimplifiedType::Int(IntTy::I32), + sym::i64 => SimplifiedType::Int(IntTy::I64), + sym::i128 => SimplifiedType::Int(IntTy::I128), + sym::usize => SimplifiedType::Uint(UintTy::Usize), + sym::u8 => SimplifiedType::Uint(UintTy::U8), + sym::u16 => SimplifiedType::Uint(UintTy::U16), + sym::u32 => SimplifiedType::Uint(UintTy::U32), + sym::u64 => SimplifiedType::Uint(UintTy::U64), + sym::u128 => SimplifiedType::Uint(UintTy::U128), + sym::f32 => SimplifiedType::Float(FloatTy::F32), + sym::f64 => SimplifiedType::Float(FloatTy::F64), + _ => return &[], }; - tcx.incoherent_impls(ty).iter().copied() + tcx.incoherent_impls(ty) } -fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { +fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { match tcx.def_kind(def_id) { - DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx - .module_children(def_id) - .iter() - .filter(|item| item.ident.name == name) - .map(|child| child.res.expect_non_local()) - .collect(), + DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx.module_children(def_id).iter().find_map(|child| { + if child.ident.name == name && ns.matches(child.res.ns()) { + child.res.opt_def_id() + } else { + None + } + }), DefKind::Impl { .. } => tcx .associated_item_def_ids(def_id) .iter() .copied() - .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name) - .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)) - .collect(), - _ => Vec::new(), + .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())), + _ => None, } } -fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec { +fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, name: Symbol) -> Option { let root_mod; let item_kind = match tcx.hir_node_by_def_id(local_id) { Node::Crate(r#mod) => { @@ -647,138 +571,147 @@ fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symb &root_mod }, Node::Item(item) => &item.kind, - _ => return Vec::new(), + _ => return None, }; let res = |ident: Ident, owner_id: OwnerId| { - if ident.name == name { - let def_id = owner_id.to_def_id(); - Some(Res::Def(tcx.def_kind(def_id), def_id)) + if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) { + Some(owner_id.to_def_id()) } else { None } }; match item_kind { - ItemKind::Mod(_, r#mod) => r#mod - .item_ids - .iter() - .filter_map(|&item_id| { - let ident = tcx.hir_item(item_id).kind.ident()?; - res(ident, item_id.owner_id) - }) - .collect(), + ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| { + let ident = tcx.hir_item(item_id).kind.ident()?; + res(ident, item_id.owner_id) + }), ItemKind::Impl(r#impl) => r#impl .items .iter() - .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)) - .collect(), + .find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)), ItemKind::Trait(.., trait_item_refs) => trait_item_refs .iter() - .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)) - .collect(), - _ => Vec::new(), + .find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)), + _ => None, } } -fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { +fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { if let Some(local_id) = def_id.as_local() { - local_item_children_by_name(tcx, local_id, name) + local_item_child_by_name(tcx, local_id, ns, name) } else { - non_local_item_children_by_name(tcx, def_id, name) + non_local_item_child_by_name(tcx, def_id, ns, name) } } /// Finds the crates called `name`, may be multiple due to multiple major versions. -pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec { - tcx.crates(()) - .iter() - .copied() - .filter(move |&num| tcx.crate_name(num) == name) - .map(CrateNum::as_def_id) - .map(|id| Res::Def(tcx.def_kind(id), id)) - .collect() +pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> &'static [DefId] { + static BY_NAME: OnceLock>> = OnceLock::new(); + let map = BY_NAME.get_or_init(|| { + let mut map = FxHashMap::default(); + map.insert(tcx.crate_name(LOCAL_CRATE), vec![LOCAL_CRATE.as_def_id()]); + for &num in tcx.crates(()) { + map.entry(tcx.crate_name(num)).or_default().push(num.as_def_id()); + } + map + }); + match map.get(&name) { + Some(def_ids) => def_ids, + None => &[], + } +} + +/// Specifies whether to resolve a path in the [`TypeNS`], [`ValueNS`], [`MacroNS`] or in an +/// arbitrary namespace +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum PathNS { + Type, + Value, + Macro, + + /// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return + /// either the macro or the module but **not** both + /// + /// Must only be used when the specific resolution is unimportant such as in + /// `missing_enforced_import_renames` + Arbitrary, +} + +impl PathNS { + fn matches(self, ns: Option) -> bool { + let required = match self { + PathNS::Type => TypeNS, + PathNS::Value => ValueNS, + PathNS::Macro => MacroNS, + PathNS::Arbitrary => return true, + }; + + ns == Some(required) + } } /// Resolves a def path like `std::vec::Vec`. /// -/// Can return multiple resolutions when there are multiple versions of the same crate, e.g. -/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0. -/// -/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec` -/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. +/// Typically it will return one [`DefId`] or none, but in some situations there can be multiple: +/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 +/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls +/// ([1], [2], [3]) /// /// This function is expensive and should be used sparingly. -pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec { - let (base, path) = match path { - [primitive] => { - return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; - }, - [base, path @ ..] => (base, path), - _ => return Vec::new(), +/// +/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast +/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 +/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 +pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec { + let (root, rest) = match *path { + [] | [_] => return Vec::new(), + [root, ref rest @ ..] => (root, rest), }; - let base_sym = Symbol::intern(base); - - let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym { - Some(LOCAL_CRATE.as_def_id()) - } else { - None - }; - - let crates = find_primitive_impls(tcx, base) - .chain(local_crate) - .map(|id| Res::Def(tcx.def_kind(id), id)) - .chain(find_crates(tcx, base_sym)) - .collect(); - - def_path_res_with_base(tcx, crates, path) + let mut out = Vec::new(); + for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) { + lookup_path_with_base(tcx, base, ns, rest, &mut out); + } + out } /// Resolves a def path like `vec::Vec` with the base `std`. -/// -/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up -/// items from the same crate repeatedly, although should still be used sparingly. -pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec, mut path: &[&str]) -> Vec { - while let [segment, rest @ ..] = path { - path = rest; - let segment = Symbol::intern(segment); +fn lookup_path_with_base(tcx: TyCtxt<'_>, mut base: DefId, ns: PathNS, mut path: &[Symbol], out: &mut Vec) { + loop { + match *path { + [segment] => { + out.extend(item_child_by_name(tcx, base, ns, segment)); - base = base - .into_iter() - .filter_map(|res| res.opt_def_id()) - .flat_map(|def_id| { // When the current def_id is e.g. `struct S`, check the impl items in // `impl S { ... }` let inherent_impl_children = tcx - .inherent_impls(def_id) + .inherent_impls(base) .iter() - .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); + .filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment)); + out.extend(inherent_impl_children); - let direct_children = item_children_by_name(tcx, def_id, segment); - - inherent_impl_children.chain(direct_children) - }) - .collect(); + return; + }, + [segment, ref rest @ ..] => { + path = rest; + let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else { + return; + }; + base = child; + }, + [] => unreachable!(), + } } - - base } -/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`]. -pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator + use<> { - def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id()) -} - -/// Convenience function to get the `DefId` of a trait by path. -/// It could be a trait or trait alias. +/// Equivalent to a [`lookup_path`] after splitting the input string on `::` /// /// This function is expensive and should be used sparingly. -pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option { - def_path_res(tcx, path).into_iter().find_map(|res| match res { - Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), - _ => None, - }) +pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec { + let path: Vec = path.split("::").map(Symbol::intern).collect(); + lookup_path(tcx, ns, &path) } /// Gets the `hir::TraitRef` of the trait the given method is implemented for. @@ -2065,24 +1998,6 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool { }) } -/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if -/// any. -/// -/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items. -pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option { - let search_path = cx.get_def_path(did); - paths - .iter() - .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied())) -} - -/// Checks if the given `DefId` matches the path. -pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool { - // We should probably move to Symbols in Clippy as well rather than interning every time. - let path = cx.get_def_path(did); - syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied()) -} - /// Checks if the given `DefId` matches the `libc` item. pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool { let path = cx.get_def_path(did); diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 7f64ebd3b643..984230178830 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -4,62 +4,125 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -// Paths inside rustc -pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; -pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ - ["rustc_lint_defs", "Applicability", "Unspecified"], - ["rustc_lint_defs", "Applicability", "HasPlaceholders"], - ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], - ["rustc_lint_defs", "Applicability", "MachineApplicable"], -]; -pub const DIAG: [&str; 2] = ["rustc_errors", "Diag"]; -pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; -pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"]; -pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; -pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; -pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; -pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; -pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; -pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; -pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; -pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; -pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; -pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; +use crate::{MaybePath, PathNS, lookup_path, path_def_id, sym}; +use rustc_hir::def_id::DefId; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{STDLIB_STABLE_CRATES, Symbol}; +use std::sync::OnceLock; + +/// Lazily resolves a path into a list of [`DefId`]s using [`lookup_path`]. +/// +/// Typically it will contain one [`DefId`] or none, but in some situations there can be multiple: +/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 +/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls +/// ([1], [2], [3]) +/// +/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast +/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 +/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 +pub struct PathLookup { + ns: PathNS, + path: &'static [Symbol], + once: OnceLock>, +} + +impl PathLookup { + /// Only exported for tests and `clippy_lints_internal` + #[doc(hidden)] + pub const fn new(ns: PathNS, path: &'static [Symbol]) -> Self { + Self { + ns, + path, + once: OnceLock::new(), + } + } + + /// Returns the list of [`DefId`]s that the path resolves to + pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] { + self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path)) + } + + /// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into + /// stdlib crates to avoid the issue of multiple [`DefId`]s being returned + /// + /// May return [`None`] in `no_std`/`no_core` environments + pub fn only(&self, cx: &LateContext<'_>) -> Option { + let ids = self.get(cx); + debug_assert!(STDLIB_STABLE_CRATES.contains(&self.path[0])); + debug_assert!(ids.len() <= 1, "{ids:?}"); + ids.first().copied() + } + + /// Checks if the path resolves to the given `def_id` + pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool { + self.get(cx).contains(&def_id) + } + + /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it + pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> bool { + path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id)) + } + + /// Checks if the path resolves to `ty`'s definition, must be an `Adt` + pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did())) + } +} + +macro_rules! path_macros { + ($($name:ident: $ns:expr,)*) => { + $( + /// Only exported for tests and `clippy_lints_internal` + #[doc(hidden)] + #[macro_export] + macro_rules! $name { + ($$($$seg:ident $$(::)?)*) => { + PathLookup::new($ns, &[$$(sym::$$seg,)*]) + }; + } + )* + }; +} + +path_macros! { + type_path: PathNS::Type, + value_path: PathNS::Value, + macro_path: PathNS::Macro, +} // Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. -pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "", "is_ascii"]; -pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"]; -pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"]; -pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"]; - -// Paths in clippy itself -pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"]; -pub const CLIPPY_SYM_MODULE: [&str; 2] = ["clippy_utils", "sym"]; +pub static ALIGN_OF: PathLookup = value_path!(core::mem::align_of); +pub static CHAR_TO_DIGIT: PathLookup = value_path!(char::to_digit); +pub static IO_ERROR_NEW: PathLookup = value_path!(std::io::Error::new); +pub static IO_ERRORKIND_OTHER_CTOR: PathLookup = value_path!(std::io::ErrorKind::Other); +pub static ITER_STEP: PathLookup = type_path!(core::iter::Step); +pub static SLICE_FROM_REF: PathLookup = value_path!(core::slice::from_ref); // Paths in external crates -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; -pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; -pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; -pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"]; -pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"]; -pub const REGEX_BUILDER_NEW: [&str; 3] = ["regex", "RegexBuilder", "new"]; -pub const REGEX_BYTES_BUILDER_NEW: [&str; 4] = ["regex", "bytes", "RegexBuilder", "new"]; -pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "bytes", "Regex", "new"]; -pub const REGEX_BYTES_SET_NEW: [&str; 4] = ["regex", "bytes", "RegexSet", "new"]; -pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"]; -pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"]; -pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; -pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_OPEN_OPTIONS: [&str; 4] = ["tokio", "fs", "open_options", "OpenOptions"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_OPEN_OPTIONS_NEW: [&str; 5] = ["tokio", "fs", "open_options", "OpenOptions", "new"]; +pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt); +pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt); +pub static ITERTOOLS_NEXT_TUPLE: PathLookup = value_path!(itertools::Itertools::next_tuple); +pub static PARKING_LOT_GUARDS: [PathLookup; 3] = [ + type_path!(lock_api::mutex::MutexGuard), + type_path!(lock_api::rwlock::RwLockReadGuard), + type_path!(lock_api::rwlock::RwLockWriteGuard), +]; +pub static REGEX_BUILDER_NEW: PathLookup = value_path!(regex::RegexBuilder::new); +pub static REGEX_BYTES_BUILDER_NEW: PathLookup = value_path!(regex::bytes::RegexBuilder::new); +pub static REGEX_BYTES_NEW: PathLookup = value_path!(regex::bytes::Regex::new); +pub static REGEX_BYTES_SET_NEW: PathLookup = value_path!(regex::bytes::RegexSet::new); +pub static REGEX_NEW: PathLookup = value_path!(regex::Regex::new); +pub static REGEX_SET_NEW: PathLookup = value_path!(regex::RegexSet::new); +pub static SERDE_DESERIALIZE: PathLookup = type_path!(serde::de::Deserialize); +pub static SERDE_DE_VISITOR: PathLookup = type_path!(serde::de::Visitor); +pub static TOKIO_FILE_OPTIONS: PathLookup = value_path!(tokio::fs::File::options); +pub static TOKIO_IO_ASYNCREADEXT: PathLookup = type_path!(tokio::io::AsyncReadExt); +pub static TOKIO_IO_ASYNCWRITEEXT: PathLookup = type_path!(tokio::io::AsyncWriteExt); +pub static TOKIO_IO_OPEN_OPTIONS: PathLookup = type_path!(tokio::fs::OpenOptions); +pub static TOKIO_IO_OPEN_OPTIONS_NEW: PathLookup = value_path!(tokio::fs::OpenOptions::new); +pub static LAZY_STATIC: PathLookup = macro_path!(lazy_static::lazy_static); +pub static ONCE_CELL_SYNC_LAZY: PathLookup = type_path!(once_cell::sync::Lazy); +pub static ONCE_CELL_SYNC_LAZY_NEW: PathLookup = value_path!(once_cell::sync::Lazy::new); + +// Paths for internal lints go in `clippy_lints_internal/src/internal_paths.rs` diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 38f077134c03..94b73f37269f 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -1,6 +1,6 @@ #![allow(non_upper_case_globals)] -use rustc_span::symbol::{PREDEFINED_SYMBOLS_COUNT, Symbol}; +use rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT; #[doc(no_inline)] pub use rustc_span::sym::*; @@ -24,33 +24,45 @@ macro_rules! generate { ]; $( - pub const $name: Symbol = Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()}); + pub const $name: rustc_span::Symbol = rustc_span::Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()}); )* }; } generate! { abs, + align_of, as_bytes, as_deref_mut, as_deref, as_mut, + AsyncReadExt, + AsyncWriteExt, Binary, build_hasher, + bytes, cargo_clippy: "cargo-clippy", Cargo_toml: "Cargo.toml", cast, chars, CLIPPY_ARGS, CLIPPY_CONF_DIR, + clippy_utils, clone_into, cloned, collect, + const_ptr, contains, copied, CRLF: "\r\n", Current, + de, + Deserialize, + diagnostics, + EarlyLintPass, ends_with, + error, + ErrorKind, exp, extend, finish_non_exhaustive, @@ -58,48 +70,87 @@ generate! { flat_map, for_each, from_raw, + from_ref, from_str_radix, + fs, + futures_util, get, + hygiene, insert, int_roundings, into_bytes, into_owned, IntoIter, + io, is_ascii, is_empty, is_err, is_none, is_ok, is_some, + itertools, + Itertools, + kw, last, + lazy_static, + Lazy, LF: "\n", + Lint, + lock_api, LowerExp, LowerHex, max, + MAX, + mem, min, + MIN, mode, msrv, + msrvs, + MsrvStack, + mut_ptr, + mutex, + next_tuple, Octal, + once_cell, + OpenOptions, or_default, + Other, parse, + PathLookup, + paths, push, regex, + Regex, + RegexBuilder, + RegexSet, reserve, resize, restriction, + rustc_lint_defs, + rustc_lint, + rustc_span, rustfmt_skip, + rwlock, + serde, set_len, set_mode, set_readonly, signum, + span_lint_and_then, split_whitespace, split, Start, + Step, + symbol, + Symbol, + SyntaxContext, take, TBD, then_some, to_digit, to_owned, + tokio, unused_extern_crates, unwrap_err, unwrap_or_default, @@ -107,6 +158,7 @@ generate! { UpperHex, V4, V6, + Visitor, Weak, with_capacity, wrapping_offset, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index db7233126955..901b66159c9e 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -32,7 +32,7 @@ use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::iter; -use crate::{def_path_def_ids, match_def_path, path_res}; +use crate::{PathNS, lookup_path_str, path_res}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -229,9 +229,7 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option< /// Checks whether a type implements a trait. /// The function returns false in case the type contains an inference variable. /// -/// See: -/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`]. -/// * [Common tools for writing lints] for an example how to use this function and other options. +/// See [Common tools for writing lints] for an example how to use this function and other options. /// /// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait pub fn implements_trait<'tcx>( @@ -424,17 +422,6 @@ pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) } -/// Checks if type is struct, enum or union type with the given def path. -/// -/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead. -/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` -pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { - match ty.kind() { - ty::Adt(adt, _) => match_def_path(cx, adt.did(), path), - _ => false, - } -} - /// Checks if the drop order for a type matters. /// /// Some std types implement drop solely to deallocate memory. For these types, and composites @@ -1131,10 +1118,7 @@ impl<'tcx> InteriorMut<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, ignore_interior_mutability: &[String]) -> Self { let ignored_def_ids = ignore_interior_mutability .iter() - .flat_map(|ignored_ty| { - let path: Vec<&str> = ignored_ty.split("::").collect(); - def_path_def_ids(tcx, path.as_slice()) - }) + .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty)) .collect(); Self { diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 3398ff8af2f5..2b9f0cbf25ce 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -11,14 +11,14 @@ //! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should //! be considered a bug. -use crate::def_path_res; +use crate::{PathNS, lookup_path}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty}; use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; mod certainty; use certainty::{Certainty, Meet, join, meet}; @@ -194,7 +194,7 @@ fn path_segment_certainty( path_segment: &PathSegment<'_>, resolves_to_type: bool, ) -> Certainty { - let certainty = match update_res(cx, parent_certainty, path_segment).unwrap_or(path_segment.res) { + let certainty = match update_res(cx, parent_certainty, path_segment, resolves_to_type).unwrap_or(path_segment.res) { // A definition's type is certain if it refers to something without generics (e.g., a crate or module, or // an unparameterized type), or the generics are instantiated with arguments that are certain. // @@ -267,17 +267,24 @@ fn path_segment_certainty( /// For at least some `QPath::TypeRelative`, the path segment's `res` can be `Res::Err`. /// `update_res` tries to fix the resolution when `parent_certainty` is `Certain(Some(..))`. -fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &PathSegment<'_>) -> Option { +fn update_res( + cx: &LateContext<'_>, + parent_certainty: Certainty, + path_segment: &PathSegment<'_>, + resolves_to_type: bool, +) -> Option { if path_segment.res == Res::Err && let Some(def_id) = parent_certainty.to_def_id() { let mut def_path = cx.get_def_path(def_id); def_path.push(path_segment.ident.name); - let reses = def_path_res(cx.tcx, &def_path.iter().map(Symbol::as_str).collect::>()); - if let [res] = reses.as_slice() { Some(*res) } else { None } - } else { - None + let ns = if resolves_to_type { PathNS::Type } else { PathNS::Value }; + if let &[id] = lookup_path(cx.tcx, ns, &def_path).as_slice() { + return Some(Res::Def(cx.tcx.def_kind(id), id)); + } } + + None } #[allow(clippy::cast_possible_truncation)] diff --git a/tests/ui-internal/auxiliary/paths.rs b/tests/ui-internal/auxiliary/paths.rs deleted file mode 100644 index f730f564a09c..000000000000 --- a/tests/ui-internal/auxiliary/paths.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![allow(clippy::unnecessary_def_path)] - -pub static OPTION: [&str; 3] = ["core", "option", "Option"]; -pub const RESULT: &[&str] = &["core", "result", "Result"]; diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs deleted file mode 100644 index 7317abc2185a..000000000000 --- a/tests/ui-internal/invalid_paths.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![deny(clippy::invalid_paths)] -#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)] - -mod paths { - // Good path - pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; - - // Path to method on inherent impl of a primitive type - pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; - - // Path to method on inherent impl - pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; - - // Path with empty segment - pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; - //~^ invalid_paths - - // Path with bad crate - pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; - //~^ invalid_paths - - // Path with bad module - pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; - //~^ invalid_paths - - // Path to method on an enum inherent impl - pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"]; -} - -fn main() {} diff --git a/tests/ui-internal/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr deleted file mode 100644 index 7b7b25ce8d8d..000000000000 --- a/tests/ui-internal/invalid_paths.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: invalid path - --> tests/ui-internal/invalid_paths.rs:15:5 - | -LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> tests/ui-internal/invalid_paths.rs:1:9 - | -LL | #![deny(clippy::invalid_paths)] - | ^^^^^^^^^^^^^^^^^^^^^ - -error: invalid path - --> tests/ui-internal/invalid_paths.rs:19:5 - | -LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: invalid path - --> tests/ui-internal/invalid_paths.rs:23:5 - | -LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed deleted file mode 100644 index 89902ebe4e54..000000000000 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ /dev/null @@ -1,77 +0,0 @@ -//@aux-build:paths.rs -#![deny(clippy::unnecessary_def_path)] -#![feature(rustc_private)] -#![allow(clippy::unnecessary_map_or)] - -extern crate clippy_utils; -extern crate paths; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; -extern crate rustc_span; - -#[allow(unused)] -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; -#[allow(unused)] -use clippy_utils::{ - is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, - match_def_path, match_trait_method, path_res, -}; - -#[allow(unused)] -use rustc_hir::LangItem; -#[allow(unused)] -use rustc_span::sym; - -use rustc_hir::Expr; -use rustc_hir::def_id::DefId; -use rustc_lint::LateContext; -use rustc_middle::ty::Ty; - -#[allow(unused, clippy::unnecessary_def_path)] -static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused, clippy::unnecessary_def_path)] -const RESULT: &[&str] = &["core", "result", "Result"]; - -fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { - let _ = is_type_diagnostic_item(cx, ty, sym::Option); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - //~^ unnecessary_def_path - - #[allow(unused, clippy::unnecessary_def_path)] - let rc_path = &["alloc", "rc", "Rc"]; - let _ = is_type_diagnostic_item(cx, ty, sym::Rc); - //~^ unnecessary_def_path - - let _ = is_type_diagnostic_item(cx, ty, sym::Option); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - //~^ unnecessary_def_path - - let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); - //~^ unnecessary_def_path - let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); - //~^ unnecessary_def_path - - let _ = cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did); - //~^ unnecessary_def_path - let _ = cx.tcx.is_diagnostic_item(sym::Option, did); - //~^ unnecessary_def_path - let _ = cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did); - //~^ unnecessary_def_path - - let _ = is_trait_method(cx, expr, sym::AsRef); - //~^ unnecessary_def_path - - let _ = is_path_diagnostic_item(cx, expr, sym::Option); - //~^ unnecessary_def_path - let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id)); - //~^ unnecessary_def_path - let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); - //~^ unnecessary_def_path -} - -fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index cfca15267c19..b17c60d03426 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -1,77 +1,20 @@ -//@aux-build:paths.rs -#![deny(clippy::unnecessary_def_path)] #![feature(rustc_private)] -#![allow(clippy::unnecessary_map_or)] -extern crate clippy_utils; -extern crate paths; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; -extern crate rustc_span; +use clippy_utils::paths::PathLookup; +use clippy_utils::{PathNS, macro_path, sym, type_path, value_path}; -#[allow(unused)] -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; -#[allow(unused)] -use clippy_utils::{ - is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, - match_def_path, match_trait_method, path_res, -}; +static OPTION: PathLookup = type_path!(core::option::Option); +//~^ unnecessary_def_path +static SOME: PathLookup = type_path!(core::option::Option::Some); +//~^ unnecessary_def_path -#[allow(unused)] -use rustc_hir::LangItem; -#[allow(unused)] -use rustc_span::sym; +static RESULT: PathLookup = type_path!(core::result::Result); +//~^ unnecessary_def_path +static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result); +//~^ unnecessary_def_path -use rustc_hir::Expr; -use rustc_hir::def_id::DefId; -use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new); +//~^ unnecessary_def_path -#[allow(unused, clippy::unnecessary_def_path)] -static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused, clippy::unnecessary_def_path)] -const RESULT: &[&str] = &["core", "result", "Result"]; - -fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { - let _ = match_type(cx, ty, &OPTION); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, RESULT); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, &["core", "result", "Result"]); - //~^ unnecessary_def_path - - #[allow(unused, clippy::unnecessary_def_path)] - let rc_path = &["alloc", "rc", "Rc"]; - let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - //~^ unnecessary_def_path - - let _ = match_type(cx, ty, &paths::OPTION); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, paths::RESULT); - //~^ unnecessary_def_path - - let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); - //~^ unnecessary_def_path - let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); - //~^ unnecessary_def_path - - let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); - //~^ unnecessary_def_path - let _ = match_def_path(cx, did, &["core", "option", "Option"]); - //~^ unnecessary_def_path - let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); - //~^ unnecessary_def_path - - let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); - //~^ unnecessary_def_path - - let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); - //~^ unnecessary_def_path - let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); - //~^ unnecessary_def_path - let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); - //~^ unnecessary_def_path -} - -fn main() {} +static VEC_MACRO: PathLookup = macro_path!(std::vec); +//~^ unnecessary_def_path diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr index d7fb4ea551e1..4abb1be7406c 100644 --- a/tests/ui-internal/unnecessary_def_path.stderr +++ b/tests/ui-internal/unnecessary_def_path.stderr @@ -1,100 +1,58 @@ -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:37:13 +error: a diagnostic name exists for this path: sym::Option + --> tests/ui-internal/unnecessary_def_path.rs:6:29 | -LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` +LL | static OPTION: PathLookup = type_path!(core::option::Option); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui-internal/unnecessary_def_path.rs:2:9 - | -LL | #![deny(clippy::unnecessary_def_path)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:39:13 +error: a language item exists for this path: LangItem::OptionSome + --> tests/ui-internal/unnecessary_def_path.rs:8:27 | -LL | let _ = match_type(cx, ty, RESULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:41:13 +LL | static SOME: PathLookup = type_path!(core::option::Option::Some); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:46:13 +error: a diagnostic name exists for this path: sym::Result + --> tests/ui-internal/unnecessary_def_path.rs:11:29 | -LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:49:13 +LL | static RESULT: PathLookup = type_path!(core::result::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_type(cx, ty, &paths::OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:51:13 +error: a diagnostic name exists for this path: sym::Result + --> tests/ui-internal/unnecessary_def_path.rs:13:37 | -LL | let _ = match_type(cx, ty, paths::RESULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:54:13 +LL | static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:56:13 +error: a diagnostic name exists for this path: sym::vec_new + --> tests/ui-internal/unnecessary_def_path.rs:16:30 | -LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:59:13 +LL | static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:61:13 +error: a diagnostic name exists for this path: sym::vec_macro + --> tests/ui-internal/unnecessary_def_path.rs:19:32 | -LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:63:13 +LL | static VEC_MACRO: PathLookup = macro_path!(std::vec); + | ^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)` - | - = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:66:13 - | -LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:69:13 - | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:71:13 - | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:73:13 - | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)` - -error: aborting due to 15 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs deleted file mode 100644 index bd7a55114acb..000000000000 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![feature(rustc_private)] -#![allow(unused)] -#![deny(clippy::unnecessary_def_path)] - -extern crate rustc_hir; - -use rustc_hir::LangItem; - -fn main() { - const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - //~^ unnecessary_def_path - const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - //~^ unnecessary_def_path - const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; - //~^ unnecessary_def_path - - // Don't lint, not a diagnostic or language item - const OPS_MOD: [&str; 2] = ["core", "ops"]; -} diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr deleted file mode 100644 index c49abc516f56..000000000000 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error: hardcoded path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:10:36 - | -LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `sym::Deref` -note: the lint level is defined here - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:3:9 - | -LL | #![deny(clippy::unnecessary_def_path)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: hardcoded path to a language item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:40 - | -LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `LangItem::DerefMut` - -error: hardcoded path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:14:43 - | -LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `sym::deref_method` - -error: aborting due to 3 previous errors - diff --git a/tests/ui-toml/toml_disallowed_types/clippy.toml b/tests/ui-toml/toml_disallowed_types/clippy.toml index 6cb9e2ef9546..08e35017f782 100644 --- a/tests/ui-toml/toml_disallowed_types/clippy.toml +++ b/tests/ui-toml/toml_disallowed_types/clippy.toml @@ -6,7 +6,7 @@ disallowed-types = [ "std::thread::Thread", "std::time::Instant", "std::io::Read", - "std::primitive::usize", + "usize", "bool", # can give path and reason with an inline table { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" }, diff --git a/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr b/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr index 18bc36ca1e33..061cdc7649ad 100644 --- a/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr +++ b/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr @@ -37,7 +37,7 @@ error: use of a disallowed type `std::io::Read` LL | fn trait_obj(_: &dyn std::io::Read) {} | ^^^^^^^^^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:26:33 | LL | fn full_and_single_path_prim(_: usize, _: bool) {} @@ -49,13 +49,13 @@ error: use of a disallowed type `bool` LL | fn full_and_single_path_prim(_: usize, _: bool) {} | ^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:30:28 | LL | fn const_generics() {} | ^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:33:24 | LL | struct GenArg([u8; U]); @@ -123,7 +123,7 @@ error: use of a disallowed type `proc_macro2::Ident` LL | let _ = syn::Ident::new("", todo!()); | ^^^^^^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:61:12 | LL | let _: usize = 64_usize; diff --git a/tests/ui-toml/toml_invalid_path/clippy.toml b/tests/ui-toml/toml_invalid_path/clippy.toml index 6d0d732a9223..997ed47b71cb 100644 --- a/tests/ui-toml/toml_invalid_path/clippy.toml +++ b/tests/ui-toml/toml_invalid_path/clippy.toml @@ -1,12 +1,15 @@ -[[disallowed-types]] -path = "std::result::Result::Err" - [[disallowed-macros]] path = "bool" [[disallowed-methods]] path = "std::process::current_exe" +[[disallowed-methods]] +path = "" + +[[disallowed-types]] +path = "std::result::Result::Err" + # negative test [[disallowed-methods]] diff --git a/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs b/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs index c15203827034..ff4eada39007 100644 --- a/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs +++ b/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs @@ -1,5 +1,6 @@ //@error-in-other-file: expected a macro, found a primitive type -//@error-in-other-file: `std::process::current_exe` does not refer to an existing function -//@error-in-other-file: expected a type, found a tuple variant +//@error-in-other-file: `std::process::current_exe` does not refer to a reachable function +//@error-in-other-file: `` does not refer to a reachable function +//@error-in-other-file: expected a type, found a variant fn main() {} diff --git a/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr b/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr index 82550108eba5..59a427dc99ca 100644 --- a/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr +++ b/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr @@ -1,23 +1,38 @@ warning: expected a macro, found a primitive type - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1 + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1 | LL | / [[disallowed-macros]] LL | | path = "bool" | |_____________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning -warning: `std::process::current_exe` does not refer to an existing function - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1 +warning: `std::process::current_exe` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1 | LL | / [[disallowed-methods]] LL | | path = "std::process::current_exe" | |__________________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning -warning: expected a type, found a tuple variant - --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1 +warning: `` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1 + | +LL | / [[disallowed-methods]] +LL | | path = "" + | |_________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: expected a type, found a variant + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:10:1 | LL | / [[disallowed-types]] LL | | path = "std::result::Result::Err" | |_________________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning -warning: 3 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs index 2dbc5eca32ec..14f15e733111 100644 --- a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs +++ b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs @@ -1,5 +1,5 @@ -//@error-in-other-file: `regex::Regex::new_` does not refer to an existing function -//@error-in-other-file: `regex::Regex_::new` does not refer to an existing function +//@error-in-other-file: `regex::Regex::new_` does not refer to a reachable function +//@error-in-other-file: `regex::Regex_::new` does not refer to a reachable function extern crate regex; diff --git a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr index 5d28e5fa970e..e5fd548b26df 100644 --- a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr +++ b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr @@ -1,16 +1,20 @@ -warning: `regex::Regex::new_` does not refer to an existing function +warning: `regex::Regex::new_` does not refer to a reachable function --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:3:1 | LL | / [[disallowed-methods]] LL | | path = "regex::Regex::new_" | |___________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning -warning: `regex::Regex_::new` does not refer to an existing function +warning: `regex::Regex_::new` does not refer to a reachable function --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:6:1 | LL | / [[disallowed-methods]] LL | | path = "regex::Regex_::new" | |___________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning warning: 2 warnings emitted diff --git a/tests/ui/author.stdout b/tests/ui/author.stdout index eed704e82fe1..88a275302387 100644 --- a/tests/ui/author.stdout +++ b/tests/ui/author.stdout @@ -1,8 +1,6 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Cast(expr, cast_ty) = init.kind - && let TyKind::Path(ref qpath) = cast_ty.kind - && match_qpath(qpath, &["char"]) && let ExprKind::Lit(ref lit) = expr.kind && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index 54325f9776c5..e453299edbcf 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -14,8 +14,6 @@ if let ExprKind::Block(block, None) = expr.kind && name1.as_str() == "_t" && let StmtKind::Semi(e) = block.stmts[2].kind && let ExprKind::Unary(UnOp::Neg, inner) = e.kind - && let ExprKind::Path(ref qpath) = inner.kind - && match_qpath(qpath, &["x"]) && block.expr.is_none() { // report your lint here @@ -25,18 +23,14 @@ if let ExprKind::Block(block, None) = expr.kind && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["String", "new"]) + && is_path_diagnostic_item(cx, func, sym::string_new) && args.is_empty() && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "expr" && let Some(trailing_expr) = block.expr && let ExprKind::Call(func1, args1) = trailing_expr.kind - && let ExprKind::Path(ref qpath1) = func1.kind - && match_qpath(qpath1, &["drop"]) + && is_path_diagnostic_item(cx, func1, sym::mem_drop) && args1.len() == 1 - && let ExprKind::Path(ref qpath2) = args1[0].kind - && match_qpath(qpath2, &["expr"]) { // report your lint here } diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout index 59d4da490fe5..2b179d45112e 100644 --- a/tests/ui/author/call.stdout +++ b/tests/ui/author/call.stdout @@ -1,8 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]) + && is_path_diagnostic_item(cx, func, sym::cmp_min) && args.len() == 2 && let ExprKind::Lit(ref lit) = args[0].kind && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout index 8ffdf8862027..da359866bffc 100644 --- a/tests/ui/author/if.stdout +++ b/tests/ui/author/if.stdout @@ -31,10 +31,8 @@ if let StmtKind::Let(local) = stmt.kind if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind && let ExprKind::Let(let_expr) = cond.kind && let PatKind::Expr(lit_expr) = let_expr.pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node - && let ExprKind::Path(ref qpath) = let_expr.init.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = then.kind && block.stmts.is_empty() && block.expr.is_none() diff --git a/tests/ui/author/issue_3849.stdout b/tests/ui/author/issue_3849.stdout index a5a8c0304ee4..f02ea5bf075f 100644 --- a/tests/ui/author/issue_3849.stdout +++ b/tests/ui/author/issue_3849.stdout @@ -1,11 +1,8 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["std", "mem", "transmute"]) + && is_path_diagnostic_item(cx, func, sym::transmute) && args.len() == 1 - && let ExprKind::Path(ref qpath1) = args[0].kind - && match_qpath(qpath1, &["ZPTR"]) && let PatKind::Wild = local.pat.kind { // report your lint here diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout index c94eb171f52b..79794cec9269 100644 --- a/tests/ui/author/loop.stdout +++ b/tests/ui/author/loop.stdout @@ -14,8 +14,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && block.stmts.len() == 1 && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init - && let ExprKind::Path(ref qpath1) = init.kind - && match_qpath(qpath1, &["y"]) && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind && name1.as_str() == "z" && block.expr.is_none() @@ -64,8 +62,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo // report your lint here } if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr) - && let ExprKind::Path(ref qpath) = condition.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = body.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind @@ -77,10 +73,8 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While: } if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) && let PatKind::Expr(lit_expr) = let_pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node - && let ExprKind::Path(ref qpath) = let_expr.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = if_then.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind diff --git a/tests/ui/author/macro_in_closure.stdout b/tests/ui/author/macro_in_closure.stdout index 3186d0cbc276..5b347aef14fa 100644 --- a/tests/ui/author/macro_in_closure.stdout +++ b/tests/ui/author/macro_in_closure.stdout @@ -7,12 +7,10 @@ if let StmtKind::Let(local) = stmt.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind && let ExprKind::Call(func, args) = e.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["$crate", "io", "_print"]) + && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Call(func1, args1) = args[0].kind - && let ExprKind::Path(ref qpath1) = func1.kind - && match_qpath(qpath1, &["format_arguments", "new_v1"]) + && paths::CORE_FMT_ARGUMENTS_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed && args1.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind && let ExprKind::Array(elements) = inner.kind @@ -27,12 +25,9 @@ if let StmtKind::Let(local) = stmt.kind && let ExprKind::Array(elements1) = inner1.kind && elements1.len() == 1 && let ExprKind::Call(func2, args2) = elements1[0].kind - && let ExprKind::Path(ref qpath2) = func2.kind - && match_qpath(qpath2, &["format_argument", "new_display"]) + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 1 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind - && let ExprKind::Path(ref qpath3) = inner2.kind - && match_qpath(qpath3, &["x"]) && block.expr.is_none() && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "print_text" diff --git a/tests/ui/author/macro_in_loop.stdout b/tests/ui/author/macro_in_loop.stdout index 3f9be297c33c..75dabd57bfea 100644 --- a/tests/ui/author/macro_in_loop.stdout +++ b/tests/ui/author/macro_in_loop.stdout @@ -17,12 +17,10 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && block1.stmts.len() == 1 && let StmtKind::Semi(e1) = block1.stmts[0].kind && let ExprKind::Call(func, args) = e1.kind - && let ExprKind::Path(ref qpath1) = func.kind - && match_qpath(qpath1, &["$crate", "io", "_print"]) + && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Call(func1, args1) = args[0].kind - && let ExprKind::Path(ref qpath2) = func1.kind - && match_qpath(qpath2, &["format_arguments", "new_v1"]) + && paths::CORE_FMT_ARGUMENTS_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed && args1.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind && let ExprKind::Array(elements) = inner.kind @@ -37,12 +35,9 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && let ExprKind::Array(elements1) = inner1.kind && elements1.len() == 1 && let ExprKind::Call(func2, args2) = elements1[0].kind - && let ExprKind::Path(ref qpath3) = func2.kind - && match_qpath(qpath3, &["format_argument", "new_display"]) + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 1 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind - && let ExprKind::Path(ref qpath4) = inner2.kind - && match_qpath(qpath4, &["i"]) && block1.expr.is_none() && block.expr.is_none() { diff --git a/tests/ui/author/matches.stdout b/tests/ui/author/matches.stdout index acb3b140dfa1..9752d7a9f99d 100644 --- a/tests/ui/author/matches.stdout +++ b/tests/ui/author/matches.stdout @@ -5,13 +5,13 @@ if let StmtKind::Let(local) = stmt.kind && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node && arms.len() == 3 && let PatKind::Expr(lit_expr) = arms[0].pat.kind - && let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit1, negated } = lit_expr.kind && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node && arms[0].guard.is_none() && let ExprKind::Lit(ref lit2) = arms[0].body.kind && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node && let PatKind::Expr(lit_expr1) = arms[1].pat.kind - && let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind + && let PatExprKind::Lit { ref lit3, negated1 } = lit_expr1.kind && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node && arms[1].guard.is_none() && let ExprKind::Block(block, None) = arms[1].body.kind @@ -23,8 +23,6 @@ if let StmtKind::Let(local) = stmt.kind && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind && name.as_str() == "x" && let Some(trailing_expr) = block.expr - && let ExprKind::Path(ref qpath) = trailing_expr.kind - && match_qpath(qpath, &["x"]) && let PatKind::Wild = arms[2].pat.kind && arms[2].guard.is_none() && let ExprKind::Lit(ref lit5) = arms[2].body.kind diff --git a/tests/ui/author/struct.stdout b/tests/ui/author/struct.stdout index b66bbccb3cf1..1e8fbafd30c5 100644 --- a/tests/ui/author/struct.stdout +++ b/tests/ui/author/struct.stdout @@ -1,5 +1,4 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind - && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind @@ -20,11 +19,10 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind // report your lint here } if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind - && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" && let PatKind::Expr(lit_expr) = fields[0].pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -34,10 +32,9 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind // report your lint here } if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind - && match_qpath(qpath, &["TestTuple"]) && fields.len() == 1 && let PatKind::Expr(lit_expr) = fields[0].kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -48,8 +45,6 @@ if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind } if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind && method_name.ident.as_str() == "test" - && let ExprKind::Path(ref qpath) = receiver.kind - && match_qpath(qpath, &["test_method_call"]) && args.is_empty() { // report your lint here diff --git a/tests/ui/manual_saturating_arithmetic.fixed b/tests/ui/manual_saturating_arithmetic.fixed index 3f73d6e5a1a6..304be05f6c4c 100644 --- a/tests/ui/manual_saturating_arithmetic.fixed +++ b/tests/ui/manual_saturating_arithmetic.fixed @@ -1,7 +1,5 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i32, i128, u32, u128}; - fn main() { let _ = 1u32.saturating_add(1); //~^ manual_saturating_arithmetic diff --git a/tests/ui/manual_saturating_arithmetic.rs b/tests/ui/manual_saturating_arithmetic.rs index 98246a5cd96c..c2b570e974ac 100644 --- a/tests/ui/manual_saturating_arithmetic.rs +++ b/tests/ui/manual_saturating_arithmetic.rs @@ -1,7 +1,5 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i32, i128, u32, u128}; - fn main() { let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); //~^ manual_saturating_arithmetic diff --git a/tests/ui/manual_saturating_arithmetic.stderr b/tests/ui/manual_saturating_arithmetic.stderr index 9d133d8a073b..2f006a3ae170 100644 --- a/tests/ui/manual_saturating_arithmetic.stderr +++ b/tests/ui/manual_saturating_arithmetic.stderr @@ -1,5 +1,5 @@ error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:6:13 + --> tests/ui/manual_saturating_arithmetic.rs:4:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` @@ -8,19 +8,19 @@ LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); = help: to override `-D warnings` add `#[allow(clippy::manual_saturating_arithmetic)]` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:8:13 + --> tests/ui/manual_saturating_arithmetic.rs:6:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:10:13 + --> tests/ui/manual_saturating_arithmetic.rs:8:13 | LL | let _ = 1u8.checked_add(1).unwrap_or(255); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u8.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:12:13 + --> tests/ui/manual_saturating_arithmetic.rs:10:13 | LL | let _ = 1u128 | _____________^ @@ -30,49 +30,49 @@ LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455); | |_______________________________________________________________________^ help: consider using `saturating_add`: `1u128.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:18:13 + --> tests/ui/manual_saturating_arithmetic.rs:16:13 | LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_mul`: `1u32.saturating_mul(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:21:13 + --> tests/ui/manual_saturating_arithmetic.rs:19:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:23:13 + --> tests/ui/manual_saturating_arithmetic.rs:21:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:25:13 + --> tests/ui/manual_saturating_arithmetic.rs:23:13 | LL | let _ = 1u8.checked_sub(1).unwrap_or(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u8.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:30:13 + --> tests/ui/manual_saturating_arithmetic.rs:28:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:32:13 + --> tests/ui/manual_saturating_arithmetic.rs:30:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:34:13 + --> tests/ui/manual_saturating_arithmetic.rs:32:13 | LL | let _ = 1i8.checked_add(1).unwrap_or(127); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:36:13 + --> tests/ui/manual_saturating_arithmetic.rs:34:13 | LL | let _ = 1i128 | _____________^ @@ -82,25 +82,25 @@ LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); | |_______________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:40:13 + --> tests/ui/manual_saturating_arithmetic.rs:38:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:42:13 + --> tests/ui/manual_saturating_arithmetic.rs:40:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:44:13 + --> tests/ui/manual_saturating_arithmetic.rs:42:13 | LL | let _ = 1i8.checked_add(-1).unwrap_or(-128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:46:13 + --> tests/ui/manual_saturating_arithmetic.rs:44:13 | LL | let _ = 1i128 | _____________^ @@ -110,25 +110,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); | |________________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:54:13 + --> tests/ui/manual_saturating_arithmetic.rs:52:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:56:13 + --> tests/ui/manual_saturating_arithmetic.rs:54:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:58:13 + --> tests/ui/manual_saturating_arithmetic.rs:56:13 | LL | let _ = 1i8.checked_sub(1).unwrap_or(-128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:60:13 + --> tests/ui/manual_saturating_arithmetic.rs:58:13 | LL | let _ = 1i128 | _____________^ @@ -138,25 +138,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); | |________________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:64:13 + --> tests/ui/manual_saturating_arithmetic.rs:62:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:66:13 + --> tests/ui/manual_saturating_arithmetic.rs:64:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:68:13 + --> tests/ui/manual_saturating_arithmetic.rs:66:13 | LL | let _ = 1i8.checked_sub(-1).unwrap_or(127); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:70:13 + --> tests/ui/manual_saturating_arithmetic.rs:68:13 | LL | let _ = 1i128 | _____________^ From f23772ce8cbfe78010fdb27da9dedcdf35180e9f Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 4 May 2025 17:07:20 +0000 Subject: [PATCH 050/245] Move `lookup_path` and similar into `clippy_utils::paths` --- clippy_config/src/types.rs | 8 +- clippy_lints/src/await_holding_invalid.rs | 2 +- clippy_lints/src/disallowed_macros.rs | 2 +- clippy_lints/src/disallowed_methods.rs | 2 +- clippy_lints/src/disallowed_types.rs | 2 +- clippy_lints/src/functions/mod.rs | 2 +- .../src/missing_enforced_import_rename.rs | 2 +- clippy_lints/src/non_std_lazy_statics.rs | 3 +- clippy_lints_internal/src/internal_paths.rs | 4 +- .../src/unnecessary_def_path.rs | 3 +- clippy_utils/src/lib.rs | 225 +----------------- clippy_utils/src/paths.rs | 215 ++++++++++++++++- clippy_utils/src/ty/mod.rs | 3 +- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- tests/ui-internal/unnecessary_def_path.rs | 4 +- 15 files changed, 243 insertions(+), 236 deletions(-) diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index 2d51c104da1a..2cb5493f1a98 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -1,4 +1,4 @@ -use clippy_utils::PathNS; +use clippy_utils::paths::{PathNS, find_crates, lookup_path}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diag}; use rustc_hir::PrimTy; @@ -148,7 +148,7 @@ pub fn create_disallowed_map( for disallowed_path in disallowed_paths { let path = disallowed_path.path(); let sym_path: Vec = path.split("::").map(Symbol::intern).collect(); - let mut resolutions = clippy_utils::lookup_path(tcx, ns, &sym_path); + let mut resolutions = lookup_path(tcx, ns, &sym_path); resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id))); let (prim_ty, found_prim_ty) = if let &[name] = sym_path.as_slice() @@ -164,10 +164,10 @@ pub fn create_disallowed_map( && !disallowed_path.allow_invalid // Don't warn about unloaded crates: // https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221 - && (sym_path.len() < 2 || !clippy_utils::find_crates(tcx, sym_path[0]).is_empty()) + && (sym_path.len() < 2 || !find_crates(tcx, sym_path[0]).is_empty()) { // Relookup the path in an arbitrary namespace to get a good `expected, found` message - let found_def_ids = clippy_utils::lookup_path(tcx, PathNS::Arbitrary, &sym_path); + let found_def_ids = lookup_path(tcx, PathNS::Arbitrary, &sym_path); let message = if let Some(&def_id) = found_def_ids.first() { let (article, description) = tcx.article_and_description(def_id); format!("expected a {predicate_description}, found {article} {description}") diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 4c47d180d3f5..31cc004f6855 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{PathNS, paths}; +use clippy_utils::paths::{self, PathNS}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index c58cfb022e49..25b7099c855d 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::PathNS; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; +use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 4c32a6002041..fb970e17f38f 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::PathNS; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 2cf1fd018b83..d0b2f0c8407f 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::PathNS; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 6be4845e2e76..d0d02a382d15 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -10,7 +10,7 @@ mod too_many_lines; use clippy_config::Conf; use clippy_utils::msrvs::Msrv; -use clippy_utils::{PathNS, lookup_path_str}; +use clippy_utils::paths::{PathNS, lookup_path_str}; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index dd0cb01cc338..a1e621cc9f6b 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::{PathNS, lookup_path_str}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdMap; diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index 6f70854767d2..370ded99a4d0 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::paths::{self, PathNS, find_crates, lookup_path_str}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{PathNS, find_crates, fn_def_id, is_no_std_crate, lookup_path_str, path_def_id, paths, sym}; +use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; diff --git a/clippy_lints_internal/src/internal_paths.rs b/clippy_lints_internal/src/internal_paths.rs index 29ad3b85da11..dc1e30ab2bdd 100644 --- a/clippy_lints_internal/src/internal_paths.rs +++ b/clippy_lints_internal/src/internal_paths.rs @@ -1,5 +1,5 @@ -use clippy_utils::paths::PathLookup; -use clippy_utils::{PathNS, sym, type_path, value_path}; +use clippy_utils::paths::{PathLookup, PathNS}; +use clippy_utils::{sym, type_path, value_path}; // Paths inside rustc pub static EARLY_LINT_PASS: PathLookup = type_path!(rustc_lint::passes::EarlyLintPass); diff --git a/clippy_lints_internal/src/unnecessary_def_path.rs b/clippy_lints_internal/src/unnecessary_def_path.rs index 2e9b8c2d53d6..8877f1faf0ee 100644 --- a/clippy_lints_internal/src/unnecessary_def_path.rs +++ b/clippy_lints_internal/src/unnecessary_def_path.rs @@ -1,6 +1,7 @@ use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{PathNS, lookup_path, path_def_id, peel_ref_operators}; +use clippy_utils::paths::{PathNS, lookup_path}; +use clippy_utils::{path_def_id, peel_ref_operators}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 44e72a8d16a5..bbd0c262c246 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -96,30 +96,29 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnhashMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; -use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ - self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, - CoroutineDesugaring, CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, - GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, - Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, - StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def, + self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, + CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, + ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, + Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, + TraitItemKind, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; +use rustc_middle::hir::nested_filter; use rustc_middle::hir::place::PlaceBase; use rustc_middle::lint::LevelAndSource; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; -use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty, - TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, + self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt, + TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; @@ -132,7 +131,6 @@ use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; 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; -use rustc_middle::hir::nested_filter; #[macro_export] macro_rules! extract_msrv_attr { @@ -240,7 +238,7 @@ pub fn is_in_const_context(cx: &LateContext<'_>) -> bool { /// * const blocks (or inline consts) /// * associated constants pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { - use ConstContext::{Const, ConstFn, Static}; + use rustc_hir::ConstContext::{Const, ConstFn, Static}; let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else { return false; }; @@ -513,207 +511,6 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] { - let ty = match name { - sym::bool => SimplifiedType::Bool, - sym::char => SimplifiedType::Char, - sym::str => SimplifiedType::Str, - sym::array => SimplifiedType::Array, - sym::slice => SimplifiedType::Slice, - // FIXME: rustdoc documents these two using just `pointer`. - // - // Maybe this is something we should do here too. - sym::const_ptr => SimplifiedType::Ptr(Mutability::Not), - sym::mut_ptr => SimplifiedType::Ptr(Mutability::Mut), - sym::isize => SimplifiedType::Int(IntTy::Isize), - sym::i8 => SimplifiedType::Int(IntTy::I8), - sym::i16 => SimplifiedType::Int(IntTy::I16), - sym::i32 => SimplifiedType::Int(IntTy::I32), - sym::i64 => SimplifiedType::Int(IntTy::I64), - sym::i128 => SimplifiedType::Int(IntTy::I128), - sym::usize => SimplifiedType::Uint(UintTy::Usize), - sym::u8 => SimplifiedType::Uint(UintTy::U8), - sym::u16 => SimplifiedType::Uint(UintTy::U16), - sym::u32 => SimplifiedType::Uint(UintTy::U32), - sym::u64 => SimplifiedType::Uint(UintTy::U64), - sym::u128 => SimplifiedType::Uint(UintTy::U128), - sym::f32 => SimplifiedType::Float(FloatTy::F32), - sym::f64 => SimplifiedType::Float(FloatTy::F64), - _ => return &[], - }; - - tcx.incoherent_impls(ty) -} - -fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { - match tcx.def_kind(def_id) { - DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx.module_children(def_id).iter().find_map(|child| { - if child.ident.name == name && ns.matches(child.res.ns()) { - child.res.opt_def_id() - } else { - None - } - }), - DefKind::Impl { .. } => tcx - .associated_item_def_ids(def_id) - .iter() - .copied() - .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())), - _ => None, - } -} - -fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, name: Symbol) -> Option { - let root_mod; - let item_kind = match tcx.hir_node_by_def_id(local_id) { - Node::Crate(r#mod) => { - root_mod = ItemKind::Mod(Ident::dummy(), r#mod); - &root_mod - }, - Node::Item(item) => &item.kind, - _ => return None, - }; - - let res = |ident: Ident, owner_id: OwnerId| { - if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) { - Some(owner_id.to_def_id()) - } else { - None - } - }; - - match item_kind { - ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| { - let ident = tcx.hir_item(item_id).kind.ident()?; - res(ident, item_id.owner_id) - }), - ItemKind::Impl(r#impl) => r#impl - .items - .iter() - .find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)), - ItemKind::Trait(.., trait_item_refs) => trait_item_refs - .iter() - .find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)), - _ => None, - } -} - -fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { - if let Some(local_id) = def_id.as_local() { - local_item_child_by_name(tcx, local_id, ns, name) - } else { - non_local_item_child_by_name(tcx, def_id, ns, name) - } -} - -/// Finds the crates called `name`, may be multiple due to multiple major versions. -pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> &'static [DefId] { - static BY_NAME: OnceLock>> = OnceLock::new(); - let map = BY_NAME.get_or_init(|| { - let mut map = FxHashMap::default(); - map.insert(tcx.crate_name(LOCAL_CRATE), vec![LOCAL_CRATE.as_def_id()]); - for &num in tcx.crates(()) { - map.entry(tcx.crate_name(num)).or_default().push(num.as_def_id()); - } - map - }); - match map.get(&name) { - Some(def_ids) => def_ids, - None => &[], - } -} - -/// Specifies whether to resolve a path in the [`TypeNS`], [`ValueNS`], [`MacroNS`] or in an -/// arbitrary namespace -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum PathNS { - Type, - Value, - Macro, - - /// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return - /// either the macro or the module but **not** both - /// - /// Must only be used when the specific resolution is unimportant such as in - /// `missing_enforced_import_renames` - Arbitrary, -} - -impl PathNS { - fn matches(self, ns: Option) -> bool { - let required = match self { - PathNS::Type => TypeNS, - PathNS::Value => ValueNS, - PathNS::Macro => MacroNS, - PathNS::Arbitrary => return true, - }; - - ns == Some(required) - } -} - -/// Resolves a def path like `std::vec::Vec`. -/// -/// Typically it will return one [`DefId`] or none, but in some situations there can be multiple: -/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 -/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls -/// ([1], [2], [3]) -/// -/// This function is expensive and should be used sparingly. -/// -/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast -/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 -/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 -pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec { - let (root, rest) = match *path { - [] | [_] => return Vec::new(), - [root, ref rest @ ..] => (root, rest), - }; - - let mut out = Vec::new(); - for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) { - lookup_path_with_base(tcx, base, ns, rest, &mut out); - } - out -} - -/// Resolves a def path like `vec::Vec` with the base `std`. -fn lookup_path_with_base(tcx: TyCtxt<'_>, mut base: DefId, ns: PathNS, mut path: &[Symbol], out: &mut Vec) { - loop { - match *path { - [segment] => { - out.extend(item_child_by_name(tcx, base, ns, segment)); - - // When the current def_id is e.g. `struct S`, check the impl items in - // `impl S { ... }` - let inherent_impl_children = tcx - .inherent_impls(base) - .iter() - .filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment)); - out.extend(inherent_impl_children); - - return; - }, - [segment, ref rest @ ..] => { - path = rest; - let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else { - return; - }; - base = child; - }, - [] => unreachable!(), - } - } -} - -/// Equivalent to a [`lookup_path`] after splitting the input string on `::` -/// -/// This function is expensive and should be used sparingly. -pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec { - let path: Vec = path.split("::").map(Symbol::intern).collect(); - lookup_path(tcx, ns, &path) -} - /// Gets the `hir::TraitRef` of the trait the given method is implemented for. /// /// Use this if you want to find the `TraitRef` of the `Add` trait in this example: diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 984230178830..795fb502c9cc 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -4,13 +4,48 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -use crate::{MaybePath, PathNS, lookup_path, path_def_id, sym}; -use rustc_hir::def_id::DefId; +use crate::{MaybePath, path_def_id, sym}; +use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; +use rustc_hir::def::{DefKind, Namespace}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; +use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, TraitItemRef}; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; -use rustc_span::{STDLIB_STABLE_CRATES, Symbol}; +use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy}; +use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol}; use std::sync::OnceLock; +/// Specifies whether to resolve a path in the [`TypeNS`], [`ValueNS`], [`MacroNS`] or in an +/// arbitrary namespace +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum PathNS { + Type, + Value, + Macro, + + /// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return + /// either the macro or the module but **not** both + /// + /// Must only be used when the specific resolution is unimportant such as in + /// `missing_enforced_import_renames` + Arbitrary, +} + +impl PathNS { + fn matches(self, ns: Option) -> bool { + let required = match self { + PathNS::Type => TypeNS, + PathNS::Value => ValueNS, + PathNS::Macro => MacroNS, + PathNS::Arbitrary => return true, + }; + + ns == Some(required) + } +} + /// Lazily resolves a path into a list of [`DefId`]s using [`lookup_path`]. /// /// Typically it will contain one [`DefId`] or none, but in some situations there can be multiple: @@ -126,3 +161,175 @@ pub static ONCE_CELL_SYNC_LAZY: PathLookup = type_path!(once_cell::sync::Lazy); pub static ONCE_CELL_SYNC_LAZY_NEW: PathLookup = value_path!(once_cell::sync::Lazy::new); // Paths for internal lints go in `clippy_lints_internal/src/internal_paths.rs` + +/// Equivalent to a [`lookup_path`] after splitting the input string on `::` +/// +/// This function is expensive and should be used sparingly. +pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec { + let path: Vec = path.split("::").map(Symbol::intern).collect(); + lookup_path(tcx, ns, &path) +} + +/// Resolves a def path like `std::vec::Vec`. +/// +/// Typically it will return one [`DefId`] or none, but in some situations there can be multiple: +/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 +/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls +/// ([1], [2], [3]) +/// +/// This function is expensive and should be used sparingly. +/// +/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast +/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 +/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 +pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec { + let (root, rest) = match *path { + [] | [_] => return Vec::new(), + [root, ref rest @ ..] => (root, rest), + }; + + let mut out = Vec::new(); + for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) { + lookup_with_base(tcx, base, ns, rest, &mut out); + } + out +} + +/// Finds the crates called `name`, may be multiple due to multiple major versions. +pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> &'static [DefId] { + static BY_NAME: OnceLock>> = OnceLock::new(); + let map = BY_NAME.get_or_init(|| { + let mut map = FxHashMap::default(); + map.insert(tcx.crate_name(LOCAL_CRATE), vec![LOCAL_CRATE.as_def_id()]); + for &num in tcx.crates(()) { + map.entry(tcx.crate_name(num)).or_default().push(num.as_def_id()); + } + map + }); + match map.get(&name) { + Some(def_ids) => def_ids, + None => &[], + } +} + +fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] { + let ty = match name { + sym::bool => SimplifiedType::Bool, + sym::char => SimplifiedType::Char, + sym::str => SimplifiedType::Str, + sym::array => SimplifiedType::Array, + sym::slice => SimplifiedType::Slice, + // FIXME: rustdoc documents these two using just `pointer`. + // + // Maybe this is something we should do here too. + sym::const_ptr => SimplifiedType::Ptr(Mutability::Not), + sym::mut_ptr => SimplifiedType::Ptr(Mutability::Mut), + sym::isize => SimplifiedType::Int(IntTy::Isize), + sym::i8 => SimplifiedType::Int(IntTy::I8), + sym::i16 => SimplifiedType::Int(IntTy::I16), + sym::i32 => SimplifiedType::Int(IntTy::I32), + sym::i64 => SimplifiedType::Int(IntTy::I64), + sym::i128 => SimplifiedType::Int(IntTy::I128), + sym::usize => SimplifiedType::Uint(UintTy::Usize), + sym::u8 => SimplifiedType::Uint(UintTy::U8), + sym::u16 => SimplifiedType::Uint(UintTy::U16), + sym::u32 => SimplifiedType::Uint(UintTy::U32), + sym::u64 => SimplifiedType::Uint(UintTy::U64), + sym::u128 => SimplifiedType::Uint(UintTy::U128), + sym::f32 => SimplifiedType::Float(FloatTy::F32), + sym::f64 => SimplifiedType::Float(FloatTy::F64), + _ => return &[], + }; + + tcx.incoherent_impls(ty) +} + +/// Resolves a def path like `vec::Vec` with the base `std`. +fn lookup_with_base(tcx: TyCtxt<'_>, mut base: DefId, ns: PathNS, mut path: &[Symbol], out: &mut Vec) { + loop { + match *path { + [segment] => { + out.extend(item_child_by_name(tcx, base, ns, segment)); + + // When the current def_id is e.g. `struct S`, check the impl items in + // `impl S { ... }` + let inherent_impl_children = tcx + .inherent_impls(base) + .iter() + .filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment)); + out.extend(inherent_impl_children); + + return; + }, + [segment, ref rest @ ..] => { + path = rest; + let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else { + return; + }; + base = child; + }, + [] => unreachable!(), + } + } +} + +fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { + if let Some(local_id) = def_id.as_local() { + local_item_child_by_name(tcx, local_id, ns, name) + } else { + non_local_item_child_by_name(tcx, def_id, ns, name) + } +} + +fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, name: Symbol) -> Option { + let root_mod; + let item_kind = match tcx.hir_node_by_def_id(local_id) { + Node::Crate(r#mod) => { + root_mod = ItemKind::Mod(Ident::dummy(), r#mod); + &root_mod + }, + Node::Item(item) => &item.kind, + _ => return None, + }; + + let res = |ident: Ident, owner_id: OwnerId| { + if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) { + Some(owner_id.to_def_id()) + } else { + None + } + }; + + match item_kind { + ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| { + let ident = tcx.hir_item(item_id).kind.ident()?; + res(ident, item_id.owner_id) + }), + ItemKind::Impl(r#impl) => r#impl + .items + .iter() + .find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)), + ItemKind::Trait(.., trait_item_refs) => trait_item_refs + .iter() + .find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)), + _ => None, + } +} + +fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { + match tcx.def_kind(def_id) { + DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx.module_children(def_id).iter().find_map(|child| { + if child.ident.name == name && ns.matches(child.res.ns()) { + child.res.opt_def_id() + } else { + None + } + }), + DefKind::Impl { .. } => tcx + .associated_item_def_ids(def_id) + .iter() + .copied() + .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())), + _ => None, + } +} diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 901b66159c9e..2683892448dd 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -32,7 +32,8 @@ use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::iter; -use crate::{PathNS, lookup_path_str, path_res}; +use crate::path_res; +use crate::paths::{PathNS, lookup_path_str}; mod type_certainty; pub use type_certainty::expr_type_is_certain; diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 2b9f0cbf25ce..6e3586623277 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -11,7 +11,7 @@ //! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should //! be considered a bug. -use crate::{PathNS, lookup_path}; +use crate::paths::{PathNS, lookup_path}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty}; diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index b17c60d03426..5cd3254188d9 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -1,7 +1,7 @@ #![feature(rustc_private)] -use clippy_utils::paths::PathLookup; -use clippy_utils::{PathNS, macro_path, sym, type_path, value_path}; +use clippy_utils::paths::{PathLookup, PathNS}; +use clippy_utils::{macro_path, sym, type_path, value_path}; static OPTION: PathLookup = type_path!(core::option::Option); //~^ unnecessary_def_path From 7008f351b69262ebcff77b1d134f6c13be39b1a5 Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Mon, 30 Sep 2024 21:07:36 +0300 Subject: [PATCH 051/245] Initial support for dynamically linked crates --- src/back/lto.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/back/lto.rs b/src/back/lto.rs index e5221c7da319..faeb2643ecb8 100644 --- a/src/back/lto.rs +++ b/src/back/lto.rs @@ -44,7 +44,11 @@ use crate::{GccCodegenBackend, GccContext, SyncContext, to_gcc_opt_level}; pub fn crate_type_allows_lto(crate_type: CrateType) -> bool { match crate_type { - CrateType::Executable | CrateType::Dylib | CrateType::Staticlib | CrateType::Cdylib => true, + CrateType::Executable + | CrateType::Dylib + | CrateType::Staticlib + | CrateType::Cdylib + | CrateType::Sdylib => true, CrateType::Rlib | CrateType::ProcMacro => false, } } From afaedbb29b5cb2a1bde4680c8b87d51b62399ef8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 28 Apr 2025 14:40:16 +0000 Subject: [PATCH 052/245] Rename Instance::new to Instance::new_raw and add a note that it is raw --- clippy_lints/src/non_copy_const.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 63859c0396e4..6d3e77b6b6e9 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -263,7 +263,7 @@ impl<'tcx> NonCopyConst<'tcx> { fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { let def_id = body_id.hir_id.owner.to_def_id(); let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); - let instance = ty::Instance::new(def_id, args); + let instance = ty::Instance::new_raw(def_id, args); let cid = GlobalId { instance, promoted: None, From fcb4ef584d88c3a8210ac17b91732fddb00a6036 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 28 Apr 2025 14:40:16 +0000 Subject: [PATCH 053/245] Rename Instance::new to Instance::new_raw and add a note that it is raw --- src/intrinsic/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs index d22f4229e237..2ed5ec4381ed 100644 --- a/src/intrinsic/mod.rs +++ b/src/intrinsic/mod.rs @@ -399,7 +399,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } // Fall back to default body - _ => return Err(Instance::new(instance.def_id(), instance.args)), + _ => return Err(Instance::new_raw(instance.def_id(), instance.args)), }; if !fn_abi.ret.is_ignore() { From 84bb0f07e6c3e920db567ff95a5f8365cf042c75 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 3 May 2025 19:39:13 +0200 Subject: [PATCH 054/245] std: stop using TLS in signal handler TLS is not async-signal-safe, making its use in the signal handler used to detect stack overflows unsound (c.f. #133698). POSIX however lists two thread-specific identifiers that can be obtained in a signal handler: the current `pthread_t` and the address of `errno`. Since `pthread_equal` is not AS-safe, `pthread_t` should be considered opaque, so for our purposes, `&errno` is the only option. This however works nicely: we can use the address as a key into a map that stores information for each thread. This PR uses a `BTreeMap` protected by a spin lock to hold the guard page address and thread name and thus fixes #133698. --- .../std/src/sys/pal/unix/stack_overflow.rs | 92 +++++++------ .../pal/unix/stack_overflow/thread_info.rs | 129 ++++++++++++++++++ 2 files changed, 183 insertions(+), 38 deletions(-) create mode 100644 library/std/src/sys/pal/unix/stack_overflow/thread_info.rs diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 8bf6d8335159..804353178aac 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -25,6 +25,18 @@ impl Drop for Handler { } } +#[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", +))] +mod thread_info; + #[cfg(any( target_os = "linux", target_os = "freebsd", @@ -46,22 +58,13 @@ mod imp { use libc::{mmap64, mprotect, munmap}; use super::Handler; - use crate::cell::Cell; + use super::thread_info::{delete_current_info, set_current_info, with_current_info}; use crate::ops::Range; use crate::sync::OnceLock; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; - use crate::{io, mem, ptr, thread}; - - // We use a TLS variable to store the address of the guard page. While TLS - // variables are not guaranteed to be signal-safe, this works out in practice - // since we make sure to write to the variable before the signal stack is - // installed, thereby ensuring that the variable is always allocated when - // the signal handler is called. - thread_local! { - // FIXME: use `Range` once that implements `Copy`. - static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) }; - } + use crate::thread::with_current_name; + use crate::{io, mem, panic, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages // (unmapped pages) at the end of every thread's stack, so if a thread ends @@ -93,29 +96,35 @@ mod imp { info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { - let (start, end) = GUARD.get(); // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`. - let addr = unsafe { (*info).si_addr().addr() }; + let fault_addr = unsafe { (*info).si_addr().addr() }; - // If the faulting address is within the guard page, then we print a - // message saying so and abort. - if start <= addr && addr < end { - thread::with_current_name(|name| { - let name = name.unwrap_or(""); - rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); - }); - - rtabort!("stack overflow"); - } else { - // Unregister ourselves by reverting back to the default behavior. - // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" - let mut action: sigaction = unsafe { mem::zeroed() }; - action.sa_sigaction = SIG_DFL; - // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction - unsafe { sigaction(signum, &action, ptr::null_mut()) }; - - // See comment above for why this function returns. + // `with_current_info` expects that the process aborts after it is + // called. If the signal was not caused by a memory access, this might + // not be true. We detect this by noticing that the `si_addr` field is + // zero if the signal is synthetic. + if fault_addr != 0 { + with_current_info(|thread_info| { + // If the faulting address is within the guard page, then we print a + // message saying so and abort. + if let Some(thread_info) = thread_info + && thread_info.guard_page_range.contains(&fault_addr) + { + let name = thread_info.thread_name.as_deref().unwrap_or(""); + rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + rtabort!("stack overflow"); + } + }) } + + // Unregister ourselves by reverting back to the default behavior. + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; + action.sa_sigaction = SIG_DFL; + // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction + unsafe { sigaction(signum, &action, ptr::null_mut()) }; + + // See comment above for why this function returns. } static PAGE_SIZE: Atomic = AtomicUsize::new(0); @@ -128,9 +137,7 @@ mod imp { pub unsafe fn init() { PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { install_main_guard().unwrap_or(0..0) }; - GUARD.set((guard.start, guard.end)); + let mut guard_page_range = unsafe { install_main_guard() }; // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" let mut action: sigaction = unsafe { mem::zeroed() }; @@ -145,7 +152,13 @@ mod imp { let handler = unsafe { make_handler(true) }; MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); + + if let Some(guard_page_range) = guard_page_range.take() { + let thread_name = with_current_name(|name| name.map(Box::from)); + set_current_info(guard_page_range, thread_name); + } } + action.sa_flags = SA_SIGINFO | SA_ONSTACK; action.sa_sigaction = signal_handler as sighandler_t; // SAFETY: only overriding signals if the default is set @@ -214,9 +227,10 @@ mod imp { } if !main_thread { - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { current_guard() }.unwrap_or(0..0); - GUARD.set((guard.start, guard.end)); + if let Some(guard_page_range) = unsafe { current_guard() } { + let thread_name = with_current_name(|name| name.map(Box::from)); + set_current_info(guard_page_range, thread_name); + } } // SAFETY: assuming stack_t is zero-initializable @@ -261,6 +275,8 @@ mod imp { // a mapping that started one page earlier, so walk back a page and unmap from there. unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; } + + delete_current_info(); } /// Modern kernels on modern hardware can have dynamic signal stack sizes. diff --git a/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs new file mode 100644 index 000000000000..e81429b98a6c --- /dev/null +++ b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs @@ -0,0 +1,129 @@ +//! TLS, but async-signal-safe. +//! +//! Unfortunately, because thread local storage isn't async-signal-safe, we +//! cannot soundly use it in our stack overflow handler. While this works +//! without problems on most platforms, it can lead to undefined behaviour +//! on others (such as GNU/Linux). Luckily, the POSIX specification documents +//! two thread-specific values that can be accessed in asynchronous signal +//! handlers: the value of `pthread_self()` and the address of `errno`. As +//! `pthread_t` is an opaque platform-specific type, we use the address of +//! `errno` here. As it is thread-specific and does not change over the +//! lifetime of a thread, we can use `&errno` as a key for a `BTreeMap` +//! that stores thread-specific data. +//! +//! Concurrent access to this map is synchronized by two locks – an outer +//! [`Mutex`] and an inner spin lock that also remembers the identity of +//! the lock owner: +//! * The spin lock is the primary means of synchronization: since it only +//! uses native atomics, it can be soundly used inside the signal handle +//! as opposed to [`Mutex`], which might not be async-signal-safe. +//! * The [`Mutex`] prevents busy-waiting in the setup logic, as all accesses +//! there are performed with the [`Mutex`] held, which makes the spin-lock +//! redundant in the common case. +//! * Finally, by using the `errno` address as the locked value of the spin +//! lock, we can detect cases where a SIGSEGV occurred while the thread +//! info is being modified. + +use crate::collections::BTreeMap; +use crate::hint::spin_loop; +use crate::ops::Range; +use crate::sync::Mutex; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sys::os::errno_location; + +pub struct ThreadInfo { + pub guard_page_range: Range, + pub thread_name: Option>, +} + +static LOCK: Mutex<()> = Mutex::new(()); +static SPIN_LOCK: AtomicUsize = AtomicUsize::new(0); +// This uses a `BTreeMap` instead of a hashmap since it supports constant +// initialization and automatically reduces the amount of memory used when +// items are removed. +static mut THREAD_INFO: BTreeMap = BTreeMap::new(); + +struct UnlockOnDrop; + +impl Drop for UnlockOnDrop { + fn drop(&mut self) { + SPIN_LOCK.store(0, Ordering::Release); + } +} + +/// Get the current thread's information, if available. +/// +/// Calling this function might freeze other threads if they attempt to modify +/// their thread information. Thus, the caller should ensure that the process +/// is aborted shortly after this function is called. +/// +/// This function is guaranteed to be async-signal-safe if `f` is too. +pub fn with_current_info(f: impl FnOnce(Option<&ThreadInfo>) -> R) -> R { + let this = errno_location().addr(); + let mut attempt = 0; + let _guard = loop { + // If we are just spinning endlessly, it's very likely that the thread + // modifying the thread info map has a lower priority than us and will + // not continue until we stop running. Just give up in that case. + if attempt == 10_000_000 { + rtprintpanic!("deadlock in SIGSEGV handler"); + return f(None); + } + + match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => break UnlockOnDrop, + Err(owner) if owner == this => { + rtabort!("a thread received SIGSEGV while modifying its stack overflow information") + } + // Spin until the lock can be acquired – there is nothing better to + // do. This is unfortunately a priority hole, but a stack overflow + // is a fatal error anyway. + Err(_) => { + spin_loop(); + attempt += 1; + } + } + }; + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased. + let thread_info = unsafe { &*(&raw const THREAD_INFO) }; + f(thread_info.get(&this)) +} + +fn spin_lock_in_setup(this: usize) -> UnlockOnDrop { + loop { + match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => return UnlockOnDrop, + Err(owner) if owner == this => { + unreachable!("the thread info setup logic isn't recursive") + } + // This function is always called with the outer lock held, + // meaning the only time locking can fail is if another thread has + // encountered a stack overflow. Since that will abort the process, + // we just stop the current thread until that time. We use `pause` + // instead of spinning to avoid priority inversion. + // SAFETY: this doesn't have any safety preconditions. + Err(_) => drop(unsafe { libc::pause() }), + } + } +} + +pub fn set_current_info(guard_page_range: Range, thread_name: Option>) { + let this = errno_location().addr(); + let _lock_guard = LOCK.lock(); + let _spin_guard = spin_lock_in_setup(this); + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot be aliased. + let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) }; + thread_info.insert(this, ThreadInfo { guard_page_range, thread_name }); +} + +pub fn delete_current_info() { + let this = errno_location().addr(); + let _lock_guard = LOCK.lock(); + let _spin_guard = spin_lock_in_setup(this); + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased. + let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) }; + thread_info.remove(&this); +} From 59aaecbbf9e9568bfad1d15f54acd4a3a810ee46 Mon Sep 17 00:00:00 2001 From: ismailarilik Date: Mon, 5 May 2025 16:36:04 +0300 Subject: [PATCH 055/245] Handle rustc_middle cases of rustc::potential_query_instability lint --- clippy_lints/src/wildcard_imports.rs | 2 +- tests/ui/wildcard_imports.fixed | 10 +++++----- tests/ui/wildcard_imports.stderr | 10 +++++----- tests/ui/wildcard_imports_2021.edition2018.fixed | 10 +++++----- tests/ui/wildcard_imports_2021.edition2018.stderr | 10 +++++----- tests/ui/wildcard_imports_2021.edition2021.fixed | 10 +++++----- tests/ui/wildcard_imports_2021.edition2021.stderr | 10 +++++----- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 405310512dff..393060d52609 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -150,7 +150,7 @@ impl LateLintPass<'_> for WildcardImports { (span, false) }; - let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(); + let mut imports: Vec<_> = used_imports.iter().map(ToString::to_string).collect(); let imports_string = if imports.len() == 1 { imports.pop().unwrap() } else if braced_glob { diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index a26b4a34190c..17510683f03e 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -16,7 +16,7 @@ use crate::fn_mod::foo; //~^ wildcard_imports use crate::mod_mod::inner_mod; //~^ wildcard_imports -use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}; //~^ wildcard_imports #[macro_use] use crate::struct_mod::{A, inner_struct_mod}; @@ -26,7 +26,7 @@ use crate::struct_mod::{A, inner_struct_mod}; use wildcard_imports_helper::inner::inner_for_self_import; use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; //~^ wildcard_imports -use wildcard_imports_helper::{ExternA, extern_foo}; +use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports use std::io::prelude::*; @@ -138,7 +138,7 @@ mod in_fn_test { fn test_extern() { use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; //~^ wildcard_imports - use wildcard_imports_helper::{ExternA, extern_foo}; + use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports inner_for_self_import::inner_extern_foo(); @@ -160,7 +160,7 @@ mod in_fn_test { } fn test_extern_reexported() { - use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}; //~^ wildcard_imports extern_exported(); @@ -190,7 +190,7 @@ mod in_fn_test { } fn test_reexported() { - use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}; //~^ wildcard_imports exported(); diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index f774126102bc..26434656a509 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -17,7 +17,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:19:5 | LL | use crate::multi_fn_mod::*; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:22:5 @@ -35,7 +35,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:29:5 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:100:13 @@ -59,7 +59,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:141:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:154:20 @@ -77,13 +77,13 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:163:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:193:9 | LL | use crate::in_fn_test::*; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:203:9 diff --git a/tests/ui/wildcard_imports_2021.edition2018.fixed b/tests/ui/wildcard_imports_2021.edition2018.fixed index a3d1aebba8af..f97b883ea231 100644 --- a/tests/ui/wildcard_imports_2021.edition2018.fixed +++ b/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -14,7 +14,7 @@ use crate::fn_mod::foo; //~^ wildcard_imports use crate::mod_mod::inner_mod; //~^ wildcard_imports -use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}; //~^ wildcard_imports use crate::struct_mod::{A, inner_struct_mod}; //~^ wildcard_imports @@ -23,7 +23,7 @@ use crate::struct_mod::{A, inner_struct_mod}; use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; //~^ wildcard_imports use wildcard_imports_helper::prelude::v1::*; -use wildcard_imports_helper::{ExternA, extern_foo}; +use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports use std::io::prelude::*; @@ -132,7 +132,7 @@ mod in_fn_test { fn test_extern() { use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; //~^ wildcard_imports - use wildcard_imports_helper::{ExternA, extern_foo}; + use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports inner_for_self_import::inner_extern_foo(); @@ -154,7 +154,7 @@ mod in_fn_test { } fn test_extern_reexported() { - use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}; //~^ wildcard_imports extern_exported(); @@ -184,7 +184,7 @@ mod in_fn_test { } fn test_reexported() { - use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}; //~^ wildcard_imports exported(); diff --git a/tests/ui/wildcard_imports_2021.edition2018.stderr b/tests/ui/wildcard_imports_2021.edition2018.stderr index a1b557f39f0d..873ce41b04f4 100644 --- a/tests/ui/wildcard_imports_2021.edition2018.stderr +++ b/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -17,7 +17,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:17:5 | LL | use crate::multi_fn_mod::*; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:19:5 @@ -35,7 +35,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:26:5 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:95:13 @@ -59,7 +59,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:135:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:148:20 @@ -77,13 +77,13 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:157:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:187:9 | LL | use crate::in_fn_test::*; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:197:9 diff --git a/tests/ui/wildcard_imports_2021.edition2021.fixed b/tests/ui/wildcard_imports_2021.edition2021.fixed index a3d1aebba8af..f97b883ea231 100644 --- a/tests/ui/wildcard_imports_2021.edition2021.fixed +++ b/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -14,7 +14,7 @@ use crate::fn_mod::foo; //~^ wildcard_imports use crate::mod_mod::inner_mod; //~^ wildcard_imports -use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}; //~^ wildcard_imports use crate::struct_mod::{A, inner_struct_mod}; //~^ wildcard_imports @@ -23,7 +23,7 @@ use crate::struct_mod::{A, inner_struct_mod}; use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; //~^ wildcard_imports use wildcard_imports_helper::prelude::v1::*; -use wildcard_imports_helper::{ExternA, extern_foo}; +use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports use std::io::prelude::*; @@ -132,7 +132,7 @@ mod in_fn_test { fn test_extern() { use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; //~^ wildcard_imports - use wildcard_imports_helper::{ExternA, extern_foo}; + use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports inner_for_self_import::inner_extern_foo(); @@ -154,7 +154,7 @@ mod in_fn_test { } fn test_extern_reexported() { - use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}; //~^ wildcard_imports extern_exported(); @@ -184,7 +184,7 @@ mod in_fn_test { } fn test_reexported() { - use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}; //~^ wildcard_imports exported(); diff --git a/tests/ui/wildcard_imports_2021.edition2021.stderr b/tests/ui/wildcard_imports_2021.edition2021.stderr index a1b557f39f0d..873ce41b04f4 100644 --- a/tests/ui/wildcard_imports_2021.edition2021.stderr +++ b/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -17,7 +17,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:17:5 | LL | use crate::multi_fn_mod::*; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:19:5 @@ -35,7 +35,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:26:5 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:95:13 @@ -59,7 +59,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:135:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:148:20 @@ -77,13 +77,13 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:157:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:187:9 | LL | use crate::in_fn_test::*; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:197:9 From e21b9367707e239fb2ba8d31b1e98488f7d6a993 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 5 May 2025 15:12:22 +0000 Subject: [PATCH 056/245] Read PR body from event in clippy_changelog action --- .github/workflows/clippy_changelog.yml | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/.github/workflows/clippy_changelog.yml b/.github/workflows/clippy_changelog.yml index 1e97154bf8a3..4d84d6b6dae4 100644 --- a/.github/workflows/clippy_changelog.yml +++ b/.github/workflows/clippy_changelog.yml @@ -15,27 +15,18 @@ jobs: changelog: runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: # Run - name: Check Changelog if: ${{ github.event_name == 'pull_request' }} run: | - body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | \ - python -c "import sys, json; print(json.load(sys.stdin)['body'])") - output=$(awk '/^changelog:\s*\S/ && !/changelog: \[.*\]: your change/' <<< "$body" | sed "s/changelog:\s*//g") - if [ -z "$output" ]; then - echo "ERROR: pull request message must contain 'changelog: ...' with your changelog. Please add it." + if [[ -z $(grep -oP 'changelog: *\K\S+' <<< "$PR_BODY") ]]; then + echo "::error::Pull request message must contain 'changelog: ...' with your changelog. Please add it." exit 1 - else - echo "changelog: $output" fi env: - PYTHONIOENCODING: 'utf-8' - PR_NUMBER: '${{ github.event.number }}' + PR_BODY: ${{ github.event.pull_request.body }}) + # We need to have the "conclusion" job also on PR CI, to make it possible # to add PRs to a merge queue. From 9d5bb00d92ccc5e413cc50d82f40733d02648838 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 5 May 2025 19:02:54 +0200 Subject: [PATCH 057/245] Remove myself from users on vacation --- triagebot.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index f27b109e9953..b2a2a85f61a0 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -42,7 +42,6 @@ new_pr = true contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", - "samueltardieu", ] [assign.owners] From 53459ffa8c0b9060f111b4730d1fe58a110470d9 Mon Sep 17 00:00:00 2001 From: Vilim Lendvaj Date: Mon, 5 May 2025 21:38:25 +0200 Subject: [PATCH 058/245] Simplify `Vec::as_non_null` implementation and make it `const` --- library/alloc/src/raw_vec/mod.rs | 2 +- library/alloc/src/vec/mod.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index a989e5b55b3d..3e006a2d1bdf 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -287,7 +287,7 @@ impl RawVec { } #[inline] - pub(crate) fn non_null(&self) -> NonNull { + pub(crate) const fn non_null(&self) -> NonNull { self.inner.non_null() } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index a97912304c89..59879f23d785 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1816,10 +1816,10 @@ impl Vec { /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + #[rustc_const_unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] #[inline] - pub fn as_non_null(&mut self) -> NonNull { - // SAFETY: A `Vec` always has a non-null pointer. - unsafe { NonNull::new_unchecked(self.as_mut_ptr()) } + pub const fn as_non_null(&mut self) -> NonNull { + self.buf.non_null() } /// Returns a reference to the underlying allocator. From 8c93668a71faaf07813da9ff995481cf312dc950 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 1 May 2025 21:55:21 +0000 Subject: [PATCH 059/245] Gate collapsible_if let_chains lints on edition 2024 and MSRV --- clippy_lints/src/collapsible_if.rs | 17 +-- clippy_lints/src/lib.rs | 2 +- clippy_utils/src/msrvs.rs | 1 + .../collapsible_if_let_chains.fixed | 1 - .../collapsible_if_let_chains.rs | 1 - .../collapsible_if_let_chains.stderr | 6 +- tests/ui/auxiliary/proc_macro_attr.rs | 73 +++++----- tests/ui/auxiliary/proc_macros.rs | 1 - tests/ui/collapsible_if.fixed | 19 --- tests/ui/collapsible_if.rs | 19 --- tests/ui/collapsible_if.stderr | 8 +- ...ollapsible_if_let_chains.edition2024.fixed | 68 +++++++++ ...llapsible_if_let_chains.edition2024.stderr | 132 ++++++++++++++++++ tests/ui/collapsible_if_let_chains.fixed | 29 ---- tests/ui/collapsible_if_let_chains.rs | 52 ++++++- tests/ui/collapsible_if_let_chains.stderr | 58 -------- tests/ui/collapsible_match.rs | 1 + tests/ui/collapsible_match.stderr | 52 +++---- .../if_let_slice_binding.fixed | 2 +- .../if_let_slice_binding.rs | 2 +- tests/ui/needless_late_init.fixed | 2 - tests/ui/needless_late_init.rs | 2 - tests/ui/needless_late_init.stderr | 34 ++--- 23 files changed, 348 insertions(+), 234 deletions(-) create mode 100644 tests/ui/collapsible_if_let_chains.edition2024.fixed create mode 100644 tests/ui/collapsible_if_let_chains.edition2024.stderr delete mode 100644 tests/ui/collapsible_if_let_chains.fixed delete mode 100644 tests/ui/collapsible_if_let_chains.stderr diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 20fae8a6775b..b231f05ed881 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,11 +1,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability}; use rustc_ast::BinOpKind; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -78,14 +78,14 @@ declare_clippy_lint! { } pub struct CollapsibleIf { - let_chains_enabled: bool, + msrv: Msrv, lint_commented_code: bool, } impl CollapsibleIf { - pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - let_chains_enabled: tcx.features().let_chains(), + msrv: conf.msrv, lint_commented_code: conf.lint_commented_code, } } @@ -127,7 +127,7 @@ impl CollapsibleIf { if let Some(inner) = expr_block(then) && cx.tcx.hir_attrs(inner.hir_id).is_empty() && let ExprKind::If(check_inner, _, None) = &inner.kind - && self.eligible_condition(check_inner) + && self.eligible_condition(cx, check_inner) && let ctxt = expr.span.ctxt() && inner.span.ctxt() == ctxt && (self.lint_commented_code || !block_starts_with_comment(cx, then)) @@ -163,8 +163,9 @@ impl CollapsibleIf { } } - pub fn eligible_condition(&self, cond: &Expr<'_>) -> bool { - self.let_chains_enabled || !matches!(cond.kind, ExprKind::Let(..)) + fn eligible_condition(&self, cx: &LateContext<'_>, cond: &Expr<'_>) -> bool { + !matches!(cond.kind, ExprKind::Let(..)) + || (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS)) } } @@ -180,7 +181,7 @@ impl LateLintPass<'_> for CollapsibleIf { { Self::check_collapsible_else_if(cx, then.span, else_); } else if else_.is_none() - && self.eligible_condition(cond) + && self.eligible_condition(cx, cond) && let ExprKind::Block(then, None) = then.kind { self.check_collapsible_if_if(cx, expr, cond, then); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bc7fc60827a0..62e2b565b482 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -731,7 +731,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(returns::Return)); - store.register_late_pass(move |tcx| Box::new(collapsible_if::CollapsibleIf::new(tcx, conf))); + store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))); store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 19061b574ff8..71985cb4b091 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -22,6 +22,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { + 1,88,0 { LET_CHAINS } 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT } 1,85,0 { UINT_FLOAT_MIDPOINT } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed index f12273954c6d..5e189471b000 100644 --- a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed +++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::collapsible_if)] fn main() { diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs index 5a984d7a3cbe..525eebf632a0 100644 --- a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs +++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::collapsible_if)] fn main() { diff --git a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr index c22a65a44730..c9de166a969a 100644 --- a/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr +++ b/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr @@ -1,5 +1,5 @@ error: this `if` statement can be collapsed - --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:5:5 + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:4:5 | LL | / if let Some(a) = Some(3) { LL | | // with comment @@ -21,7 +21,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:13:5 + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:12:5 | LL | / if let Some(a) = Some(3) { LL | | // with comment @@ -41,7 +41,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:21:5 + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:20:5 | LL | / if Some(3) == Some(4).map(|x| x - 1) { LL | | // with comment diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index e72d6b6ceadf..4c61c5accd39 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -51,14 +51,14 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> { let arg = sig.inputs.first_mut()?; - if let FnArg::Typed(PatType { pat, .. }) = arg { - if let Pat::Ident(PatIdent { ident, .. }) = &**pat { - if ident == "self" { - return Some(arg); - } - } + if let FnArg::Typed(PatType { pat, .. }) = arg + && let Pat::Ident(PatIdent { ident, .. }) = &**pat + && ident == "self" + { + Some(arg) + } else { + None } - None } let mut elided = 0; @@ -66,30 +66,29 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea // Look for methods having arbitrary self type taken by &mut ref for inner in &mut item.items { - if let ImplItem::Fn(method) = inner { - if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) { - if let box Type::Reference(reference) = &mut pat_type.ty { - // Target only unnamed lifetimes - let name = match &reference.lifetime { - Some(lt) if lt.ident == "_" => make_name(elided), - None => make_name(elided), - _ => continue, - }; - elided += 1; + if let ImplItem::Fn(method) = inner + && let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) + && let box Type::Reference(reference) = &mut pat_type.ty + { + // Target only unnamed lifetimes + let name = match &reference.lifetime { + Some(lt) if lt.ident == "_" => make_name(elided), + None => make_name(elided), + _ => continue, + }; + elided += 1; - // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. - // In order to avoid adding the dependency, get a default span from a nonexistent token. - // A default span is needed to mark the code as coming from expansion. - let span = Star::default().span(); + // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. + // In order to avoid adding the dependency, get a default span from a nonexistent token. + // A default span is needed to mark the code as coming from expansion. + let span = Star::default().span(); - // Replace old lifetime with the named one - let lifetime = Lifetime::new(&name, span); - reference.lifetime = Some(parse_quote!(#lifetime)); + // Replace old lifetime with the named one + let lifetime = Lifetime::new(&name, span); + reference.lifetime = Some(parse_quote!(#lifetime)); - // Add lifetime to the generics of the method - method.sig.generics.params.push(parse_quote!(#lifetime)); - } - } + // Add lifetime to the generics of the method + method.sig.generics.params.push(parse_quote!(#lifetime)); } } @@ -129,15 +128,15 @@ pub fn fake_desugar_await(_args: TokenStream, input: TokenStream) -> TokenStream let mut async_fn = parse_macro_input!(input as syn::ItemFn); for stmt in &mut async_fn.block.stmts { - if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt { - if let syn::Expr::Await(syn::ExprAwait { base, await_token, .. }) = scrutinee.as_mut() { - let blc = quote_spanned!( await_token.span => { - #[allow(clippy::let_and_return)] - let __pinned = #base; - __pinned - }); - *scrutinee = parse_quote!(#blc); - } + if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt + && let syn::Expr::Await(syn::ExprAwait { base, await_token, .. }) = scrutinee.as_mut() + { + let blc = quote_spanned!( await_token.span => { + #[allow(clippy::let_and_return)] + let __pinned = #base; + __pinned + }); + *scrutinee = parse_quote!(#blc); } } diff --git a/tests/ui/auxiliary/proc_macros.rs b/tests/ui/auxiliary/proc_macros.rs index 7a4cc4fa9ee8..bb55539617fc 100644 --- a/tests/ui/auxiliary/proc_macros.rs +++ b/tests/ui/auxiliary/proc_macros.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![feature(proc_macro_span)] #![allow(clippy::needless_if, dead_code)] diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index e1ceb04f9cb8..d62309fe08b3 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -101,27 +101,8 @@ fn main() { } } - // Test behavior wrt. `let_chains`. - // None of the cases below should be collapsed. fn truth() -> bool { true } - // Prefix: - if let 0 = 1 { - if truth() {} - } - - // Suffix: - if truth() { - if let 0 = 1 {} - } - - // Midfix: - if truth() { - if let 0 = 1 { - if truth() {} - } - } - // Fix #5962 if matches!(true, true) && matches!(true, true) {} diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index 0b996dca22e8..0677b98bb5b2 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -108,27 +108,8 @@ fn main() { } } - // Test behavior wrt. `let_chains`. - // None of the cases below should be collapsed. fn truth() -> bool { true } - // Prefix: - if let 0 = 1 { - if truth() {} - } - - // Suffix: - if truth() { - if let 0 = 1 {} - } - - // Midfix: - if truth() { - if let 0 = 1 { - if truth() {} - } - } - // Fix #5962 if matches!(true, true) { if matches!(true, true) {} diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index 532811462393..32c6b0194030 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -127,7 +127,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:133:5 + --> tests/ui/collapsible_if.rs:114:5 | LL | / if matches!(true, true) { LL | | if matches!(true, true) {} @@ -141,7 +141,7 @@ LL ~ && matches!(true, true) {} | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:139:5 + --> tests/ui/collapsible_if.rs:120:5 | LL | / if matches!(true, true) && truth() { LL | | if matches!(true, true) {} @@ -155,7 +155,7 @@ LL ~ && matches!(true, true) {} | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:151:5 + --> tests/ui/collapsible_if.rs:132:5 | LL | / if true { LL | | if true { @@ -173,7 +173,7 @@ LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:168:5 + --> tests/ui/collapsible_if.rs:149:5 | LL | / if true { LL | | if true { diff --git a/tests/ui/collapsible_if_let_chains.edition2024.fixed b/tests/ui/collapsible_if_let_chains.edition2024.fixed new file mode 100644 index 000000000000..ad08c21ba9ed --- /dev/null +++ b/tests/ui/collapsible_if_let_chains.edition2024.fixed @@ -0,0 +1,68 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2021] check-pass + +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment, so do not lint + if let Some(b) = Some(4) { + let _ = a + b; + } + } + + //~[edition2024]v collapsible_if + if let Some(a) = Some(3) + && let Some(b) = Some(4) { + let _ = a + b; + } + + //~[edition2024]v collapsible_if + if let Some(a) = Some(3) + && a + 1 == 4 { + let _ = a; + } + + //~[edition2024]v collapsible_if + if Some(3) == Some(4).map(|x| x - 1) + && let Some(b) = Some(4) { + let _ = b; + } + + fn truth() -> bool { + true + } + + // Prefix: + //~[edition2024]v collapsible_if + if let 0 = 1 + && truth() {} + + // Suffix: + //~[edition2024]v collapsible_if + if truth() + && let 0 = 1 {} + + // Midfix: + //~[edition2024]vvv collapsible_if + //~[edition2024]v collapsible_if + if truth() + && let 0 = 1 + && truth() {} +} + +#[clippy::msrv = "1.87.0"] +fn msrv_1_87() { + if let 0 = 1 { + if true {} + } +} + +#[clippy::msrv = "1.88.0"] +fn msrv_1_88() { + //~[edition2024]v collapsible_if + if let 0 = 1 + && true {} +} diff --git a/tests/ui/collapsible_if_let_chains.edition2024.stderr b/tests/ui/collapsible_if_let_chains.edition2024.stderr new file mode 100644 index 000000000000..b0aa3cecadb6 --- /dev/null +++ b/tests/ui/collapsible_if_let_chains.edition2024.stderr @@ -0,0 +1,132 @@ +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:17:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = a + b; +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && let Some(b) = Some(4) { +LL | let _ = a + b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:24:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if a + 1 == 4 { +LL | | let _ = a; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && a + 1 == 4 { +LL | let _ = a; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:31:5 + | +LL | / if Some(3) == Some(4).map(|x| x - 1) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = b; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if Some(3) == Some(4).map(|x| x - 1) +LL ~ && let Some(b) = Some(4) { +LL | let _ = b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:43:5 + | +LL | / if let 0 = 1 { +LL | | if truth() {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && truth() {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:49:5 + | +LL | / if truth() { +LL | | if let 0 = 1 {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if truth() +LL ~ && let 0 = 1 {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:56:5 + | +LL | / if truth() { +LL | | if let 0 = 1 { +LL | | if truth() {} +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if truth() +LL ~ && let 0 = 1 { +LL | if truth() {} +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:57:9 + | +LL | / if let 0 = 1 { +LL | | if truth() {} +LL | | } + | |_________^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && truth() {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:73:5 + | +LL | / if let 0 = 1 { +LL | | if true {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && true {} + | + +error: aborting due to 8 previous errors + diff --git a/tests/ui/collapsible_if_let_chains.fixed b/tests/ui/collapsible_if_let_chains.fixed deleted file mode 100644 index 3dd9498a4c9f..000000000000 --- a/tests/ui/collapsible_if_let_chains.fixed +++ /dev/null @@ -1,29 +0,0 @@ -#![feature(let_chains)] -#![warn(clippy::collapsible_if)] - -fn main() { - if let Some(a) = Some(3) { - // with comment, so do not lint - if let Some(b) = Some(4) { - let _ = a + b; - } - } - - if let Some(a) = Some(3) - && let Some(b) = Some(4) { - let _ = a + b; - } - //~^^^^^ collapsible_if - - if let Some(a) = Some(3) - && a + 1 == 4 { - let _ = a; - } - //~^^^^^ collapsible_if - - if Some(3) == Some(4).map(|x| x - 1) - && let Some(b) = Some(4) { - let _ = b; - } - //~^^^^^ collapsible_if -} diff --git a/tests/ui/collapsible_if_let_chains.rs b/tests/ui/collapsible_if_let_chains.rs index 064b9a0be484..b2e88b1a5568 100644 --- a/tests/ui/collapsible_if_let_chains.rs +++ b/tests/ui/collapsible_if_let_chains.rs @@ -1,4 +1,8 @@ -#![feature(let_chains)] +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2021] check-pass + #![warn(clippy::collapsible_if)] fn main() { @@ -9,24 +13,64 @@ fn main() { } } + //~[edition2024]v collapsible_if if let Some(a) = Some(3) { if let Some(b) = Some(4) { let _ = a + b; } } - //~^^^^^ collapsible_if + //~[edition2024]v collapsible_if if let Some(a) = Some(3) { if a + 1 == 4 { let _ = a; } } - //~^^^^^ collapsible_if + //~[edition2024]v collapsible_if if Some(3) == Some(4).map(|x| x - 1) { if let Some(b) = Some(4) { let _ = b; } } - //~^^^^^ collapsible_if + + fn truth() -> bool { + true + } + + // Prefix: + //~[edition2024]v collapsible_if + if let 0 = 1 { + if truth() {} + } + + // Suffix: + //~[edition2024]v collapsible_if + if truth() { + if let 0 = 1 {} + } + + // Midfix: + //~[edition2024]vvv collapsible_if + //~[edition2024]v collapsible_if + if truth() { + if let 0 = 1 { + if truth() {} + } + } +} + +#[clippy::msrv = "1.87.0"] +fn msrv_1_87() { + if let 0 = 1 { + if true {} + } +} + +#[clippy::msrv = "1.88.0"] +fn msrv_1_88() { + //~[edition2024]v collapsible_if + if let 0 = 1 { + if true {} + } } diff --git a/tests/ui/collapsible_if_let_chains.stderr b/tests/ui/collapsible_if_let_chains.stderr deleted file mode 100644 index 64a88114c47a..000000000000 --- a/tests/ui/collapsible_if_let_chains.stderr +++ /dev/null @@ -1,58 +0,0 @@ -error: this `if` statement can be collapsed - --> tests/ui/collapsible_if_let_chains.rs:12:5 - | -LL | / if let Some(a) = Some(3) { -LL | | if let Some(b) = Some(4) { -LL | | let _ = a + b; -LL | | } -LL | | } - | |_____^ - | - = note: `-D clippy::collapsible-if` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` -help: collapse nested if block - | -LL ~ if let Some(a) = Some(3) -LL ~ && let Some(b) = Some(4) { -LL | let _ = a + b; -LL ~ } - | - -error: this `if` statement can be collapsed - --> tests/ui/collapsible_if_let_chains.rs:19:5 - | -LL | / if let Some(a) = Some(3) { -LL | | if a + 1 == 4 { -LL | | let _ = a; -LL | | } -LL | | } - | |_____^ - | -help: collapse nested if block - | -LL ~ if let Some(a) = Some(3) -LL ~ && a + 1 == 4 { -LL | let _ = a; -LL ~ } - | - -error: this `if` statement can be collapsed - --> tests/ui/collapsible_if_let_chains.rs:26:5 - | -LL | / if Some(3) == Some(4).map(|x| x - 1) { -LL | | if let Some(b) = Some(4) { -LL | | let _ = b; -LL | | } -LL | | } - | |_____^ - | -help: collapse nested if block - | -LL ~ if Some(3) == Some(4).map(|x| x - 1) -LL ~ && let Some(b) = Some(4) { -LL | let _ = b; -LL ~ } - | - -error: aborting due to 3 previous errors - diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 55ef55844957..71b82040ff62 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -1,5 +1,6 @@ #![warn(clippy::collapsible_match)] #![allow( + clippy::collapsible_if, clippy::equatable_if_let, clippy::needless_return, clippy::no_effect, diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 5294a9d6975d..c290d84ec297 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -1,5 +1,5 @@ error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:14:20 + --> tests/ui/collapsible_match.rs:15:20 | LL | Ok(val) => match val { | ____________________^ @@ -10,7 +10,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:14:12 + --> tests/ui/collapsible_match.rs:15:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -21,7 +21,7 @@ LL | Some(n) => foo(n), = help: to override `-D warnings` add `#[allow(clippy::collapsible_match)]` error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:24:20 + --> tests/ui/collapsible_match.rs:25:20 | LL | Ok(val) => match val { | ____________________^ @@ -32,7 +32,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:24:12 + --> tests/ui/collapsible_match.rs:25:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -41,7 +41,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:34:9 + --> tests/ui/collapsible_match.rs:35:9 | LL | / if let Some(n) = val { LL | | @@ -51,7 +51,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:33:15 + --> tests/ui/collapsible_match.rs:34:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -59,7 +59,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:43:9 + --> tests/ui/collapsible_match.rs:44:9 | LL | / if let Some(n) = val { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:42:15 + --> tests/ui/collapsible_match.rs:43:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -79,7 +79,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:56:9 + --> tests/ui/collapsible_match.rs:57:9 | LL | / match val { LL | | @@ -89,7 +89,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:55:15 + --> tests/ui/collapsible_match.rs:56:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -98,7 +98,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:66:13 + --> tests/ui/collapsible_match.rs:67:13 | LL | / if let Some(n) = val { LL | | @@ -108,7 +108,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:65:12 + --> tests/ui/collapsible_match.rs:66:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -116,7 +116,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:77:9 + --> tests/ui/collapsible_match.rs:78:9 | LL | / match val { LL | | @@ -126,7 +126,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:76:15 + --> tests/ui/collapsible_match.rs:77:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -135,7 +135,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:89:13 + --> tests/ui/collapsible_match.rs:90:13 | LL | / if let Some(n) = val { LL | | @@ -147,7 +147,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:88:12 + --> tests/ui/collapsible_match.rs:89:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -155,7 +155,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:102:20 + --> tests/ui/collapsible_match.rs:103:20 | LL | Ok(val) => match val { | ____________________^ @@ -166,7 +166,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:102:12 + --> tests/ui/collapsible_match.rs:103:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -175,7 +175,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:112:22 + --> tests/ui/collapsible_match.rs:113:22 | LL | Some(val) => match val { | ______________________^ @@ -186,7 +186,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:112:14 + --> tests/ui/collapsible_match.rs:113:14 | LL | Some(val) => match val { | ^^^ replace this binding @@ -195,7 +195,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:256:22 + --> tests/ui/collapsible_match.rs:257:22 | LL | Some(val) => match val { | ______________________^ @@ -206,7 +206,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:256:14 + --> tests/ui/collapsible_match.rs:257:14 | LL | Some(val) => match val { | ^^^ replace this binding @@ -215,7 +215,7 @@ LL | E::A(val) | E::B(val) => foo(val), | ^^^^^^^^^^^^^^^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:288:9 + --> tests/ui/collapsible_match.rs:289:9 | LL | / if let Some(u) = a { LL | | @@ -225,7 +225,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:287:27 + --> tests/ui/collapsible_match.rs:288:27 | LL | if let Issue9647::A { a, .. } = x { | ^ replace this binding @@ -233,7 +233,7 @@ LL | if let Some(u) = a { | ^^^^^^^ with this pattern, prefixed by `a`: error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:298:9 + --> tests/ui/collapsible_match.rs:299:9 | LL | / if let Some(u) = a { LL | | @@ -243,7 +243,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:297:35 + --> tests/ui/collapsible_match.rs:298:35 | LL | if let Issue9647::A { a: Some(a), .. } = x { | ^ replace this binding diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed index e19aa4acb4c1..050cdfcba966 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)] +#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/tests/ui/index_refutable_slice/if_let_slice_binding.rs index 290393568554..91429bfea276 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)] +#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index f832752ccd79..b686a8e9f1a0 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] -#![allow(unused)] #![allow( clippy::assign_op_pattern, clippy::blocks_in_conditions, diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index a52fbf529234..23772ff70293 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] -#![allow(unused)] #![allow( clippy::assign_op_pattern, clippy::blocks_in_conditions, diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index b24c12758816..e3e25cdc8d76 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initialization - --> tests/ui/needless_late_init.rs:27:5 + --> tests/ui/needless_late_init.rs:25:5 | LL | let a; | ^^^^^^ created here @@ -17,7 +17,7 @@ LL ~ let a = "zero"; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:31:5 + --> tests/ui/needless_late_init.rs:29:5 | LL | let b; | ^^^^^^ created here @@ -35,7 +35,7 @@ LL ~ let b = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:33:5 + --> tests/ui/needless_late_init.rs:31:5 | LL | let c; | ^^^^^^ created here @@ -52,7 +52,7 @@ LL ~ let c = 2; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:38:5 + --> tests/ui/needless_late_init.rs:36:5 | LL | let d: usize; | ^^^^^^^^^^^^^ created here @@ -68,7 +68,7 @@ LL ~ let d: usize = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:42:5 + --> tests/ui/needless_late_init.rs:40:5 | LL | let e; | ^^^^^^ created here @@ -84,7 +84,7 @@ LL ~ let e = format!("{}", d); | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:48:5 + --> tests/ui/needless_late_init.rs:46:5 | LL | let a; | ^^^^^^ @@ -103,7 +103,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:58:5 + --> tests/ui/needless_late_init.rs:56:5 | LL | let b; | ^^^^^^ @@ -120,7 +120,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:66:5 + --> tests/ui/needless_late_init.rs:64:5 | LL | let d; | ^^^^^^ @@ -138,7 +138,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:75:5 + --> tests/ui/needless_late_init.rs:73:5 | LL | let e; | ^^^^^^ @@ -155,7 +155,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:83:5 + --> tests/ui/needless_late_init.rs:81:5 | LL | let f; | ^^^^^^ @@ -169,7 +169,7 @@ LL ~ 1 => "three", | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:90:5 + --> tests/ui/needless_late_init.rs:88:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -186,7 +186,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:99:5 + --> tests/ui/needless_late_init.rs:97:5 | LL | let x; | ^^^^^^ created here @@ -203,7 +203,7 @@ LL ~ let x = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:104:5 + --> tests/ui/needless_late_init.rs:102:5 | LL | let x; | ^^^^^^ created here @@ -220,7 +220,7 @@ LL ~ let x = SignificantDrop; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:109:5 + --> tests/ui/needless_late_init.rs:107:5 | LL | let x; | ^^^^^^ created here @@ -238,7 +238,7 @@ LL ~ let x = SignificantDrop; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:129:5 + --> tests/ui/needless_late_init.rs:127:5 | LL | let a; | ^^^^^^ @@ -257,7 +257,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:147:5 + --> tests/ui/needless_late_init.rs:145:5 | LL | let a; | ^^^^^^ @@ -276,7 +276,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:300:5 + --> tests/ui/needless_late_init.rs:298:5 | LL | let r; | ^^^^^^ created here From e89cf4d3896020fcce2f15bae47ee1e704c6586b Mon Sep 17 00:00:00 2001 From: Aaron Ang Date: Tue, 15 Apr 2025 11:09:46 -0700 Subject: [PATCH 060/245] `item_name_repetitions`: exclude enum variants with identical path components --- clippy_lints/src/item_name_repetitions.rs | 28 +++++++++++++++++++++-- tests/ui/enum_variants.rs | 15 ++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index b1271a264b54..30f61af29e59 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -5,7 +5,7 @@ use clippy_utils::macros::span_is_local; use clippy_utils::source::is_present_in_source; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; +use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Symbol; @@ -405,6 +405,7 @@ fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_> if count_match_start(item_name, name).char_count == item_name_chars && name.chars().nth(item_name_chars).is_some_and(|c| !c.is_lowercase()) && name.chars().nth(item_name_chars + 1).is_some_and(|c| !c.is_numeric()) + && !check_enum_tuple_path_match(name, variant.data) { span_lint_hir( cx, @@ -420,7 +421,9 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) let name = variant.ident.name.as_str(); let item_name_chars = item_name.chars().count(); - if count_match_end(item_name, name).char_count == item_name_chars { + if count_match_end(item_name, name).char_count == item_name_chars + && !check_enum_tuple_path_match(name, variant.data) + { span_lint_hir( cx, ENUM_VARIANT_NAMES, @@ -431,6 +434,27 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) } } +/// Checks if an enum tuple variant contains a single field +/// whose qualified path contains the variant's name. +fn check_enum_tuple_path_match(variant_name: &str, variant_data: VariantData<'_>) -> bool { + // Only check single-field tuple variants + let VariantData::Tuple(fields, ..) = variant_data else { + return false; + }; + if fields.len() != 1 { + return false; + } + // Check if field type is a path and contains the variant name + match fields[0].ty.kind { + TyKind::Path(QPath::Resolved(_, path)) => path + .segments + .iter() + .any(|segment| segment.ident.name.as_str() == variant_name), + TyKind::Path(QPath::TypeRelative(_, segment)) => segment.ident.name.as_str() == variant_name, + _ => false, + } +} + impl LateLintPass<'_> for ItemNameRepetitions { fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { let Some(_ident) = item.kind.ident() else { return }; diff --git a/tests/ui/enum_variants.rs b/tests/ui/enum_variants.rs index f7bbf83654f1..18c80c7aba43 100644 --- a/tests/ui/enum_variants.rs +++ b/tests/ui/enum_variants.rs @@ -220,4 +220,19 @@ mod issue11494 { } } +mod encapsulated { + mod types { + pub struct FooError; + pub struct BarError; + pub struct BazError; + } + + enum Error { + FooError(types::FooError), + BarError(types::BarError), + BazError(types::BazError), + Other, + } +} + fn main() {} From e7acf5c9b91bf9f2bd79fbb1de77f65cdd73c8e5 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sat, 3 May 2025 20:31:47 +0800 Subject: [PATCH 061/245] fix: `collapsible_if` FP on block stmt before expr --- clippy_lints/src/collapsible_if.rs | 15 +++++++-------- tests/ui/collapsible_if.fixed | 11 +++++++++++ tests/ui/collapsible_if.rs | 11 +++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index b231f05ed881..7f6ecea99fb0 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -4,7 +4,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability}; use rustc_ast::BinOpKind; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -203,13 +203,12 @@ fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { match block.stmts { [] => block.expr, - [stmt] => { - if let StmtKind::Semi(expr) = stmt.kind { - Some(expr) - } else { - None - } - }, + [ + Stmt { + kind: StmtKind::Semi(expr), + .. + }, + ] if block.expr.is_none() => Some(expr), _ => None, } } diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index d62309fe08b3..b553182a4454 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -143,3 +143,14 @@ fn layout_check() -> u32 { ; 3 //~^^^^^ collapsible_if } + +fn issue14722() { + let x = if true { + Some(1) + } else { + if true { + println!("Some debug information"); + }; + None + }; +} diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index 0677b98bb5b2..f5998457ca6c 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -153,3 +153,14 @@ fn layout_check() -> u32 { }; 3 //~^^^^^ collapsible_if } + +fn issue14722() { + let x = if true { + Some(1) + } else { + if true { + println!("Some debug information"); + }; + None + }; +} From d0af404b19ebde51add8862b561a91edb53c7add Mon Sep 17 00:00:00 2001 From: yanglsh Date: Tue, 6 May 2025 14:42:18 +0800 Subject: [PATCH 062/245] fix: `manual_let_else` FN when diverges on simple enum variant --- clippy_lints/src/manual_let_else.rs | 71 +++++++++++++++++++++++++++-- tests/ui/manual_let_else.rs | 32 +++++++++++++ tests/ui/manual_let_else.stderr | 22 ++++++++- 3 files changed, 119 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index d6ac6e106b4b..0b3bec714c0e 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -4,11 +4,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; +use clippy_utils::{ + MaybePath, is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, path_res, peel_blocks, +}; use rustc_ast::BindingMode; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::Span; @@ -91,14 +94,15 @@ impl<'tcx> QuestionMark { let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; + + let pat_arm = &arms[1 - idx]; // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement. // However, if it arrives in second position, its pattern may cover some cases already covered // by the diverging one. - // TODO: accept the non-diverging arm as a second position if patterns are disjointed. - if idx == 0 { + if idx == 0 && !is_arms_disjointed(cx, diverging_arm, pat_arm) { return; } - let pat_arm = &arms[1 - idx]; + let Some(ident_map) = expr_simple_identity_map(local.pat, pat_arm.pat, pat_arm.body) else { return; }; @@ -110,6 +114,63 @@ impl<'tcx> QuestionMark { } } +/// Checks if the patterns of the arms are disjointed. Currently, we only support patterns of simple +/// enum variants without nested patterns or bindings. +/// +/// TODO: Support more complex patterns. +fn is_arms_disjointed(cx: &LateContext<'_>, arm1: &Arm<'_>, arm2: &Arm<'_>) -> bool { + if arm1.guard.is_some() || arm2.guard.is_some() { + return false; + } + + if !is_enum_variant(cx, arm1.pat) || !is_enum_variant(cx, arm2.pat) { + return false; + } + + true +} + +/// Returns `true` if the given pattern is a variant of an enum. +pub fn is_enum_variant(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + struct Pat<'hir>(&'hir rustc_hir::Pat<'hir>); + + impl<'hir> MaybePath<'hir> for Pat<'hir> { + fn qpath_opt(&self) -> Option<&QPath<'hir>> { + match self.0.kind { + PatKind::Struct(ref qpath, fields, _) + if fields + .iter() + .all(|field| is_wild(field.pat) || matches!(field.pat.kind, PatKind::Binding(..))) => + { + Some(qpath) + }, + PatKind::TupleStruct(ref qpath, pats, _) + if pats + .iter() + .all(|pat| is_wild(pat) || matches!(pat.kind, PatKind::Binding(..))) => + { + Some(qpath) + }, + PatKind::Expr(&PatExpr { + kind: PatExprKind::Path(ref qpath), + .. + }) => Some(qpath), + _ => None, + } + } + + fn hir_id(&self) -> HirId { + self.0.hir_id + } + } + + let res = path_res(cx, &Pat(pat)); + matches!( + res, + Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) + ) +} + fn emit_manual_let_else( cx: &LateContext<'_>, span: Span, diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index a753566b34cb..3781ba1676f5 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -514,3 +514,35 @@ mod issue13768 { }; } } + +mod issue14598 { + fn bar() -> Result { + let value = match foo() { + //~^ manual_let_else + Err(_) => return Err("abc"), + Ok(value) => value, + }; + + let w = Some(0); + let v = match w { + //~^ manual_let_else + None => return Err("abc"), + Some(x) => x, + }; + + enum Foo { + Foo(T), + } + + let v = match Foo::Foo(Some(())) { + Foo::Foo(Some(_)) => return Err("abc"), + Foo::Foo(v) => v, + }; + + Ok(value == 42) + } + + fn foo() -> Result { + todo!() + } +} diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index ef0421921141..a1eea0419291 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -529,5 +529,25 @@ LL + return; LL + }; | -error: aborting due to 33 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:520:9 + | +LL | / let value = match foo() { +LL | | +LL | | Err(_) => return Err("abc"), +LL | | Ok(value) => value, +LL | | }; + | |__________^ help: consider writing: `let Ok(value) = foo() else { return Err("abc") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:527:9 + | +LL | / let v = match w { +LL | | +LL | | None => return Err("abc"), +LL | | Some(x) => x, +LL | | }; + | |__________^ help: consider writing: `let Some(v) = w else { return Err("abc") };` + +error: aborting due to 35 previous errors From 94aa0d9a7538bd8cbf765ba017243bf0aad2b258 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 6 May 2025 14:07:14 +0000 Subject: [PATCH 063/245] Update clippy_lints_internal to 2024 edition --- clippy_lints_internal/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints_internal/Cargo.toml b/clippy_lints_internal/Cargo.toml index 2a0ceac27a32..a8293a1ad395 100644 --- a/clippy_lints_internal/Cargo.toml +++ b/clippy_lints_internal/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints_internal" version = "0.0.1" -edition = "2021" +edition = "2024" [dependencies] clippy_config = { path = "../clippy_config" } From 737d3b3363ec98d90608b9ef3951f785960ea900 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 6 May 2025 13:20:01 +0000 Subject: [PATCH 064/245] Remove some unused `#![feature]`s --- clippy_config/src/lib.rs | 2 +- clippy_dev/src/lib.rs | 1 - clippy_lints/src/lib.rs | 2 -- clippy_lints_internal/src/lib.rs | 2 +- clippy_utils/src/lib.rs | 3 --- lintcheck/src/main.rs | 1 - src/driver.rs | 1 - tests/compile-test.rs | 2 +- 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/comparison_to_empty.fixed | 1 - tests/ui/comparison_to_empty.rs | 1 - tests/ui/comparison_to_empty.stderr | 26 +++++++++---------- tests/ui/needless_if.fixed | 1 - tests/ui/needless_if.rs | 1 - tests/ui/needless_if.stderr | 14 +++++----- .../redundant_pattern_matching_option.fixed | 2 +- tests/ui/redundant_pattern_matching_option.rs | 2 +- 19 files changed, 36 insertions(+), 50 deletions(-) diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index c227b8900b74..33608591fc74 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(rustc_private, array_windows, let_chains)] +#![feature(rustc_private)] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index c1ffaf269c6f..db4b4d07c156 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![feature(rustc_private)] #![warn( trivial_casts, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 62e2b565b482..ad8b223b3aad 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,5 +1,4 @@ #![feature(array_windows)] -#![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] #![feature(macro_metavar_expr_concat)] #![feature(f128)] @@ -7,7 +6,6 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iter_partition_in_place)] -#![feature(let_chains)] #![feature(never_type)] #![feature(round_char_boundary)] #![feature(rustc_private)] diff --git a/clippy_lints_internal/src/lib.rs b/clippy_lints_internal/src/lib.rs index da37fd3d8271..308d161b9d6d 100644 --- a/clippy_lints_internal/src/lib.rs +++ b/clippy_lints_internal/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(let_chains, rustc_private)] +#![feature(rustc_private)] #![allow( clippy::missing_docs_in_private_items, clippy::must_use_candidate, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index bbd0c262c246..0a9c39c41bd8 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1,9 +1,6 @@ -#![feature(array_chunks)] #![feature(box_patterns)] #![feature(if_let_guard)] -#![feature(macro_metavar_expr_concat)] #![feature(macro_metavar_expr)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustc_private)] #![feature(assert_matches)] diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index fe488ef89da1..d4bf6cd48a15 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -6,7 +6,6 @@ // positives. #![feature(iter_collect_into)] -#![feature(let_chains)] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/src/driver.rs b/src/driver.rs index 87ca9c5beddf..f8acf88cf81c 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,7 +1,6 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![feature(rustc_private)] -#![feature(let_chains)] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] // warn on rustc internal lints diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 6d391bd622a8..78b27e2f6139 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,4 +1,4 @@ -#![feature(rustc_private, let_chains)] +#![feature(rustc_private)] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] diff --git a/tests/ui/bool_to_int_with_if.fixed b/tests/ui/bool_to_int_with_if.fixed index ed6141244b40..7fa7c016f935 100644 --- a/tests/ui/bool_to_int_with_if.fixed +++ b/tests/ui/bool_to_int_with_if.fixed @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] diff --git a/tests/ui/bool_to_int_with_if.rs b/tests/ui/bool_to_int_with_if.rs index 3f1f1c766e46..2295d6f1362d 100644 --- a/tests/ui/bool_to_int_with_if.rs +++ b/tests/ui/bool_to_int_with_if.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] diff --git a/tests/ui/bool_to_int_with_if.stderr b/tests/ui/bool_to_int_with_if.stderr index 94089bc6dc8e..e4ae57304141 100644 --- a/tests/ui/bool_to_int_with_if.stderr +++ b/tests/ui/bool_to_int_with_if.stderr @@ -1,5 +1,5 @@ error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:14:5 + --> tests/ui/bool_to_int_with_if.rs:13:5 | LL | / if a { LL | | @@ -14,7 +14,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::bool_to_int_with_if)]` error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:20:5 + --> tests/ui/bool_to_int_with_if.rs:19:5 | LL | / if a { LL | | @@ -27,7 +27,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:26:5 + --> tests/ui/bool_to_int_with_if.rs:25:5 | LL | / if !a { LL | | @@ -40,7 +40,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:32:5 + --> tests/ui/bool_to_int_with_if.rs:31:5 | LL | / if a || b { LL | | @@ -53,7 +53,7 @@ LL | | }; = note: `(a || b) as i32` or `(a || b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:38:5 + --> tests/ui/bool_to_int_with_if.rs:37:5 | LL | / if cond(a, b) { LL | | @@ -66,7 +66,7 @@ LL | | }; = note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:44:5 + --> tests/ui/bool_to_int_with_if.rs:43:5 | LL | / if x + y < 4 { LL | | @@ -79,7 +79,7 @@ LL | | }; = note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:54:12 + --> tests/ui/bool_to_int_with_if.rs:53:12 | LL | } else if b { | ____________^ @@ -93,7 +93,7 @@ LL | | }; = note: `b as i32` or `b.into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:64:12 + --> tests/ui/bool_to_int_with_if.rs:63:12 | LL | } else if b { | ____________^ @@ -107,7 +107,7 @@ LL | | }; = note: `!b as i32` or `(!b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:130:5 + --> tests/ui/bool_to_int_with_if.rs:129:5 | LL | if a { 1 } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)` @@ -115,7 +115,7 @@ LL | if a { 1 } else { 0 } = note: `a as u8` or `a.into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:174:13 + --> tests/ui/bool_to_int_with_if.rs:173:13 | LL | let _ = if dbg!(4 > 0) { 1 } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `i32::from(dbg!(4 > 0))` @@ -123,7 +123,7 @@ LL | let _ = if dbg!(4 > 0) { 1 } else { 0 }; = note: `dbg!(4 > 0) as i32` or `dbg!(4 > 0).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:177:18 + --> tests/ui/bool_to_int_with_if.rs:176:18 | LL | let _ = dbg!(if 4 > 0 { 1 } else { 0 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `i32::from(4 > 0)` diff --git a/tests/ui/comparison_to_empty.fixed b/tests/ui/comparison_to_empty.fixed index dfbb61683840..7a71829dd62c 100644 --- a/tests/ui/comparison_to_empty.fixed +++ b/tests/ui/comparison_to_empty.fixed @@ -1,6 +1,5 @@ #![warn(clippy::comparison_to_empty)] #![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] -#![feature(let_chains)] fn main() { // Disallow comparisons to empty diff --git a/tests/ui/comparison_to_empty.rs b/tests/ui/comparison_to_empty.rs index 61cdb2bbe9f8..5d213a09e812 100644 --- a/tests/ui/comparison_to_empty.rs +++ b/tests/ui/comparison_to_empty.rs @@ -1,6 +1,5 @@ #![warn(clippy::comparison_to_empty)] #![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] -#![feature(let_chains)] fn main() { // Disallow comparisons to empty diff --git a/tests/ui/comparison_to_empty.stderr b/tests/ui/comparison_to_empty.stderr index 00a50430a3ee..deb3e9388784 100644 --- a/tests/ui/comparison_to_empty.stderr +++ b/tests/ui/comparison_to_empty.stderr @@ -1,5 +1,5 @@ error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:8:13 + --> tests/ui/comparison_to_empty.rs:7:13 | LL | let _ = s == ""; | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` @@ -8,73 +8,73 @@ LL | let _ = s == ""; = help: to override `-D warnings` add `#[allow(clippy::comparison_to_empty)]` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:10:13 + --> tests/ui/comparison_to_empty.rs:9:13 | LL | let _ = s != ""; | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:14:13 + --> tests/ui/comparison_to_empty.rs:13:13 | LL | let _ = v == []; | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:16:13 + --> tests/ui/comparison_to_empty.rs:15:13 | LL | let _ = v != []; | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:18:8 + --> tests/ui/comparison_to_empty.rs:17:8 | LL | if let [] = &*v {} | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(*v).is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:21:8 + --> tests/ui/comparison_to_empty.rs:20:8 | LL | if let [] = s {} | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:23:8 + --> tests/ui/comparison_to_empty.rs:22:8 | LL | if let [] = &*s {} | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:25:8 + --> tests/ui/comparison_to_empty.rs:24:8 | LL | if let [] = &*s | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:27:12 + --> tests/ui/comparison_to_empty.rs:26:12 | LL | && s == [] | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:48:13 + --> tests/ui/comparison_to_empty.rs:47:13 | LL | let _ = s.eq(""); | ^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:50:13 + --> tests/ui/comparison_to_empty.rs:49:13 | LL | let _ = s.ne(""); | ^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:53:13 + --> tests/ui/comparison_to_empty.rs:52:13 | LL | let _ = v.eq(&[]); | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:55:13 + --> tests/ui/comparison_to_empty.rs:54:13 | LL | let _ = v.ne(&[]); | ^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` diff --git a/tests/ui/needless_if.fixed b/tests/ui/needless_if.fixed index 347dbff7c595..c839156bed9b 100644 --- a/tests/ui/needless_if.fixed +++ b/tests/ui/needless_if.fixed @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] #![allow( clippy::blocks_in_conditions, clippy::if_same_then_else, diff --git a/tests/ui/needless_if.rs b/tests/ui/needless_if.rs index 5e0f2a14408b..11103af5c559 100644 --- a/tests/ui/needless_if.rs +++ b/tests/ui/needless_if.rs @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] #![allow( clippy::blocks_in_conditions, clippy::if_same_then_else, diff --git a/tests/ui/needless_if.stderr b/tests/ui/needless_if.stderr index 62cdf2459448..4b56843bd522 100644 --- a/tests/ui/needless_if.stderr +++ b/tests/ui/needless_if.stderr @@ -1,5 +1,5 @@ error: this `if` branch is empty - --> tests/ui/needless_if.rs:27:5 + --> tests/ui/needless_if.rs:26:5 | LL | if (true) {} | ^^^^^^^^^^^^ help: you can remove it @@ -8,13 +8,13 @@ LL | if (true) {} = help: to override `-D warnings` add `#[allow(clippy::needless_if)]` error: this `if` branch is empty - --> tests/ui/needless_if.rs:30:5 + --> tests/ui/needless_if.rs:29:5 | LL | if maybe_side_effect() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `maybe_side_effect();` error: this `if` branch is empty - --> tests/ui/needless_if.rs:36:5 + --> tests/ui/needless_if.rs:35:5 | LL | / if { LL | | @@ -31,7 +31,7 @@ LL + }); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:51:5 + --> tests/ui/needless_if.rs:50:5 | LL | / if { LL | | @@ -57,19 +57,19 @@ LL + } && true); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:96:5 + --> tests/ui/needless_if.rs:95:5 | LL | if { maybe_side_effect() } {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() });` error: this `if` branch is empty - --> tests/ui/needless_if.rs:99:5 + --> tests/ui/needless_if.rs:98:5 | LL | if { maybe_side_effect() } && true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() } && true);` error: this `if` branch is empty - --> tests/ui/needless_if.rs:104:5 + --> tests/ui/needless_if.rs:103:5 | LL | if true {} | ^^^^^^^^^^ help: you can remove it: `true;` diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index 33a5308bd357..dc9d6491691f 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -1,4 +1,4 @@ -#![feature(let_chains, if_let_guard)] +#![feature(if_let_guard)] #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index 60bce2994ea3..2e9714ad8e75 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -1,4 +1,4 @@ -#![feature(let_chains, if_let_guard)] +#![feature(if_let_guard)] #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, From cc5d1a5ab5c551ec763e2e32d1737ef7cddf9669 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 6 May 2025 22:01:47 +0200 Subject: [PATCH 065/245] Post `non_std_lazy_statics` type warnings onto the right node When a `non_std_lazy_statics` warning is generated about an item type which can be replaced by a standard library one, ensure that the lint happens on the item HIR node so that it can be expected. --- clippy_lints/src/non_std_lazy_statics.rs | 10 +++++++--- .../non_std_lazy_static_fixable.fixed | 7 +++++++ .../non_std_lazy_static/non_std_lazy_static_fixable.rs | 7 +++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index 370ded99a4d0..f66b9519317b 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::paths::{self, PathNS, find_crates, lookup_path_str}; use clippy_utils::visitors::for_each_expr; @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_hir::{self as hir, BodyId, Expr, ExprKind, Item, ItemKind}; +use rustc_hir::{self as hir, BodyId, Expr, ExprKind, HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -173,6 +173,8 @@ struct LazyInfo { /// // ^^^^ /// ``` ty_span_no_args: Span, + /// Item on which the lint must be generated. + item_hir_id: HirId, /// `Span` and `DefId` of calls on `Lazy` type. /// i.e.: /// ```ignore @@ -206,6 +208,7 @@ impl LazyInfo { Some(LazyInfo { ty_span_no_args, + item_hir_id: item.hir_id(), calls_span_and_id: new_fn_calls, }) } else { @@ -229,9 +232,10 @@ impl LazyInfo { } } - span_lint_and_then( + span_lint_hir_and_then( cx, NON_STD_LAZY_STATICS, + self.item_hir_id, self.ty_span_no_args, "this type has been superseded by `LazyLock` in the standard library", |diag| { diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed index 2b30c8f984eb..d6c35d8097c5 100644 --- a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed @@ -70,3 +70,10 @@ mod external_macros { once_cell::external!(); lazy_static::external!(); } + +mod issue14729 { + use once_cell::sync::Lazy; + + #[expect(clippy::non_std_lazy_statics)] + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); +} diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs index c52338eee83c..996ef050d691 100644 --- a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs @@ -70,3 +70,10 @@ mod external_macros { once_cell::external!(); lazy_static::external!(); } + +mod issue14729 { + use once_cell::sync::Lazy; + + #[expect(clippy::non_std_lazy_statics)] + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); +} From e608520b12b3f6521312287831156e1ba309c5f2 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Tue, 6 May 2025 17:58:21 -0400 Subject: [PATCH 066/245] Stop ignoring the feature -sse --- src/attributes.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/attributes.rs b/src/attributes.rs index 69b04dd57969..8bc1b7702430 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -88,14 +88,8 @@ pub fn from_fn_attrs<'gcc, 'tcx>( let target_features = function_features .iter() .filter_map(|feature| { - // FIXME(antoyo): for some reasons, disabling SSE results in the following error when - // compiling Rust for Linux: - // SSE register return with SSE disabled - // TODO(antoyo): support soft-float and retpoline-external-thunk. - if feature.contains("soft-float") - || feature.contains("retpoline-external-thunk") - || *feature == "-sse" - { + // TODO(antoyo): support soft-float. + if feature.contains("soft-float") { return None; } From 3007433e2c65e289aa02155debf500d836539890 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 7 May 2025 00:52:58 -0700 Subject: [PATCH 067/245] add a type alias for the pattern bindings stack I'll be modifying it in future commits, so I think it's cleanest to abstract it out. Possibly a newtype would be ideal, but for now this is least disruptive. --- compiler/rustc_resolve/src/late.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index faee0e7dd5ff..1da1bfeaf607 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -111,6 +111,17 @@ enum PatBoundCtx { Or, } +/// Tracks bindings resolved within a pattern. This serves two purposes: +/// +/// - This tracks when identifiers are bound multiple times within a pattern. In a product context, +/// this is an error. In an or-pattern, this lets us reuse the same resolution for each instance. +/// See `fresh_binding` and `resolve_pattern_inner` for more information. +/// +/// - The guard expression of a guard pattern may use bindings from within the guard pattern, but +/// not from elsewhere in the pattern containing it. This allows us to isolate the bindings in the +/// subpattern to construct the scope for the guard. +type PatternBindings = SmallVec<[(PatBoundCtx, FxHashSet); 1]>; + /// Does this the item (from the item rib scope) allow generic parameters? #[derive(Copy, Clone, Debug)] pub(crate) enum HasGenericParams { @@ -3857,7 +3868,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, pat: &'ast Pat, pat_src: PatternSource, - bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, + bindings: &mut PatternBindings, ) { // We walk the pattern before declaring the pattern's inner bindings, // so that we avoid resolving a literal expression to a binding defined @@ -3892,7 +3903,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, pat: &Pat, pat_src: PatternSource, - bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, + bindings: &mut PatternBindings, ) { // Visit all direct subpatterns of this pattern. pat.walk(&mut |pat| { @@ -3988,7 +3999,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ident: Ident, pat_id: NodeId, pat_src: PatternSource, - bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet); 1]>, + bindings: &mut PatternBindings, ) -> Res { // Add the binding to the local ribs, if it doesn't already exist in the bindings map. // (We must not add it if it's in the bindings map because that breaks the assumptions From 999db5cdc66f017e4219c56536e7f7f32c6d10e7 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 9 Apr 2025 12:37:44 +0200 Subject: [PATCH 068/245] `unwrap_used`, `expect_used`: accept macro result as receiver --- clippy_lints/src/methods/mod.rs | 98 +++++++++++++++++------------- tests/ui/unwrap_expect_used.rs | 17 ++++++ tests/ui/unwrap_expect_used.stderr | 34 ++++++++++- 3 files changed, 105 insertions(+), 44 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7e2ce157c737..6c55c27989c7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4661,6 +4661,8 @@ impl_lint_pass!(Methods => [ ]); /// Extracts a method call name, args, and `Span` of the method name. +/// This ensures that neither the receiver nor any of the arguments +/// come from expansion. pub fn method_call<'tcx>( recv: &'tcx Expr<'tcx>, ) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { @@ -4858,6 +4860,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { impl Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { @@ -5000,29 +5003,12 @@ impl Methods { Some(("err", recv, [], err_span, _)) => { err_expect::check(cx, expr, recv, span, err_span, self.msrv); }, - _ => unwrap_expect_used::check( - cx, - expr, - recv, - false, - self.allow_expect_in_consts, - self.allow_expect_in_tests, - unwrap_expect_used::Variant::Expect, - ), + _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("expect_err", [_]) => { + ("expect_err", [_]) | ("unwrap_err" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - true, - self.allow_expect_in_consts, - self.allow_expect_in_tests, - unwrap_expect_used::Variant::Expect, - ); }, ("extend", [arg]) => { string_extend_chars::check(cx, expr, recv, arg); @@ -5388,27 +5374,6 @@ impl Methods { _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - false, - self.allow_unwrap_in_consts, - self.allow_unwrap_in_tests, - unwrap_expect_used::Variant::Unwrap, - ); - }, - ("unwrap_err", []) => { - unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - true, - self.allow_unwrap_in_consts, - self.allow_unwrap_in_tests, - unwrap_expect_used::Variant::Unwrap, - ); }, ("unwrap_or", [u_arg]) => { match method_call(recv) { @@ -5437,9 +5402,6 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_unchecked" | "unwrap_err_unchecked", []) => { - unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - }, ("unwrap_or_else", [u_arg]) => { match method_call(recv) { Some(("map", recv, [map_arg], _, _)) @@ -5477,6 +5439,56 @@ impl Methods { _ => {}, } } + // Handle method calls whose receiver and arguments may come from expansion + if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind { + match (path.ident.name.as_str(), args) { + ("expect", [_]) if !matches!(method_call(recv), Some(("ok" | "err", _, [], _, _))) => { + unwrap_expect_used::check( + cx, + expr, + recv, + false, + self.allow_expect_in_consts, + self.allow_expect_in_tests, + unwrap_expect_used::Variant::Expect, + ); + }, + ("expect_err", [_]) => { + unwrap_expect_used::check( + cx, + expr, + recv, + true, + self.allow_expect_in_consts, + self.allow_expect_in_tests, + unwrap_expect_used::Variant::Expect, + ); + }, + ("unwrap", []) => { + unwrap_expect_used::check( + cx, + expr, + recv, + false, + self.allow_unwrap_in_consts, + self.allow_unwrap_in_tests, + unwrap_expect_used::Variant::Unwrap, + ); + }, + ("unwrap_err", []) => { + unwrap_expect_used::check( + cx, + expr, + recv, + true, + self.allow_unwrap_in_consts, + self.allow_unwrap_in_tests, + unwrap_expect_used::Variant::Unwrap, + ); + }, + _ => {}, + } + } } } diff --git a/tests/ui/unwrap_expect_used.rs b/tests/ui/unwrap_expect_used.rs index d0bb571273b5..b429f3a8a0bb 100644 --- a/tests/ui/unwrap_expect_used.rs +++ b/tests/ui/unwrap_expect_used.rs @@ -66,3 +66,20 @@ fn main() { SOME.expect("Still not three?"); } } + +mod with_expansion { + macro_rules! open { + ($file:expr) => { + std::fs::File::open($file) + }; + } + + fn test(file: &str) { + use std::io::Read; + let mut s = String::new(); + let _ = open!(file).unwrap(); //~ unwrap_used + let _ = open!(file).expect("can open"); //~ expect_used + let _ = open!(file).unwrap_err(); //~ unwrap_used + let _ = open!(file).expect_err("can open"); //~ expect_used + } +} diff --git a/tests/ui/unwrap_expect_used.stderr b/tests/ui/unwrap_expect_used.stderr index 79eac3f58ccb..6fd1b84d8123 100644 --- a/tests/ui/unwrap_expect_used.stderr +++ b/tests/ui/unwrap_expect_used.stderr @@ -50,5 +50,37 @@ LL | a.expect_err("Hello error!"); | = note: if this value is an `Ok`, it will panic -error: aborting due to 6 previous errors +error: used `unwrap()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:80:17 + | +LL | let _ = open!(file).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `expect()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:81:17 + | +LL | let _ = open!(file).expect("can open"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `unwrap_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:82:17 + | +LL | let _ = open!(file).unwrap_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: used `expect_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:83:17 + | +LL | let _ = open!(file).expect_err("can open"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: aborting due to 10 previous errors From 0d5df707f51891b28f62e9264f02e45739731bde Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 May 2025 10:45:25 +0200 Subject: [PATCH 069/245] test suite: use CARGO_TARGET_TMPDIR for temporary build artifacts --- src/tools/miri/tests/ui.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index c37cf15d40ab..a7b889d966c4 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -44,8 +44,7 @@ pub fn flagsplit(flags: &str) -> Vec { fn build_native_lib() -> PathBuf { let cc = env::var("CC").unwrap_or_else(|_| "cc".into()); // Target directory that we can write to. - let so_target_dir = - Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-native-lib"); + let so_target_dir = Path::new(env!("CARGO_TARGET_TMPDIR")).join("miri-native-lib"); // Create the directory if it does not already exist. std::fs::create_dir_all(&so_target_dir) .expect("Failed to create directory for shared object file"); @@ -101,7 +100,7 @@ fn miri_config( let mut config = Config { target: Some(target.to_owned()), program, - out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri_ui"), + out_dir: PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("miri_ui"), threads: std::env::var("MIRI_TEST_THREADS") .ok() .map(|threads| NonZero::new(threads.parse().unwrap()).unwrap()), From 30a0ac66dbb15ee8dd3951499b34df48e1d758a0 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 7 May 2025 02:59:18 -0700 Subject: [PATCH 070/245] delay introducing pattern bindings into scope This splits introduction of bindings into scope (`apply_pattern_bindings`) apart from manipulation of the pattern's binding map (`fresh_binding`). By delaying the latter, we can keep bindings from appearing in-scope in guards. Since `fresh_binding` is now specifically for manipulating a pattern's bindings map, this commit also inlines a use of `fresh_binding` that was only adding to the innermost rib. --- compiler/rustc_resolve/src/late.rs | 82 +++++++++++++++++++----------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 1da1bfeaf607..aa211a8f3c29 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -120,7 +120,9 @@ enum PatBoundCtx { /// - The guard expression of a guard pattern may use bindings from within the guard pattern, but /// not from elsewhere in the pattern containing it. This allows us to isolate the bindings in the /// subpattern to construct the scope for the guard. -type PatternBindings = SmallVec<[(PatBoundCtx, FxHashSet); 1]>; +/// +/// Each identifier must map to at most one distinct [`Res`]. +type PatternBindings = SmallVec<[(PatBoundCtx, FxIndexMap); 1]>; /// Does this the item (from the item rib scope) allow generic parameters? #[derive(Copy, Clone, Debug)] @@ -2308,7 +2310,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn resolve_fn_params( &mut self, has_self: bool, - inputs: impl Iterator, &'ast Ty)>, + inputs: impl Iterator, &'ast Ty)> + Clone, ) -> Result, Vec)> { enum Elision { /// We have not found any candidate. @@ -2330,15 +2332,20 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let mut parameter_info = Vec::new(); let mut all_candidates = Vec::new(); + // Resolve and apply bindings first so diagnostics can see if they're used in types. let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - for (index, (pat, ty)) in inputs.enumerate() { - debug!(?pat, ?ty); + for (pat, _) in inputs.clone() { + debug!("resolving bindings in pat = {pat:?}"); self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { if let Some(pat) = pat { this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); } }); + } + self.apply_pattern_bindings(bindings); + for (index, (pat, ty)) in inputs.enumerate() { + debug!("resolving type for pat = {pat:?}, ty = {ty:?}"); // Record elision candidates only for this parameter. debug_assert_matches!(self.lifetime_elision_candidates, None); self.lifetime_elision_candidates = Some(Default::default()); @@ -3626,16 +3633,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.visit_path(&delegation.path, delegation.id); let Some(body) = &delegation.body else { return }; self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { - // `PatBoundCtx` is not necessary in this context - let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - let span = delegation.path.segments.last().unwrap().ident.span; - this.fresh_binding( - Ident::new(kw::SelfLower, span), - delegation.id, - PatternSource::FnParam, - &mut bindings, - ); + let ident = Ident::new(kw::SelfLower, span.normalize_to_macro_rules()); + let res = Res::Local(delegation.id); + this.innermost_rib_bindings(ValueNS).insert(ident, res); this.visit_block(body); }); } @@ -3646,6 +3647,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { for Param { pat, .. } in params { this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); } + this.apply_pattern_bindings(bindings); }); for Param { ty, .. } in params { self.visit_ty(ty); @@ -3862,8 +3864,27 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn resolve_pattern_top(&mut self, pat: &'ast Pat, pat_src: PatternSource) { let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; self.resolve_pattern(pat, pat_src, &mut bindings); + self.apply_pattern_bindings(bindings); } + /// Apply the bindings from a pattern to the innermost rib of the current scope. + fn apply_pattern_bindings(&mut self, mut pat_bindings: PatternBindings) { + let rib_bindings = self.innermost_rib_bindings(ValueNS); + let Some((_, pat_bindings)) = pat_bindings.pop() else { + bug!("tried applying nonexistent bindings from pattern"); + }; + + if rib_bindings.is_empty() { + // Often, such as for match arms, the bindings are introduced into a new rib. + // In this case, we can move the bindings over directly. + *rib_bindings = pat_bindings; + } else { + rib_bindings.extend(pat_bindings); + } + } + + /// Resolve bindings in a pattern. `apply_pattern_bindings` must be called after to introduce + /// the bindings into scope. fn resolve_pattern( &mut self, pat: &'ast Pat, @@ -4001,18 +4022,15 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { pat_src: PatternSource, bindings: &mut PatternBindings, ) -> Res { - // Add the binding to the local ribs, if it doesn't already exist in the bindings map. + // Add the binding to the bindings map, if it doesn't already exist. // (We must not add it if it's in the bindings map because that breaks the assumptions // later passes make about or-patterns.) let ident = ident.normalize_to_macro_rules(); - let mut bound_iter = bindings.iter().filter(|(_, set)| set.contains(&ident)); // Already bound in a product pattern? e.g. `(a, a)` which is not allowed. - let already_bound_and = bound_iter.clone().any(|(ctx, _)| *ctx == PatBoundCtx::Product); - // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`. - // This is *required* for consistency which is checked later. - let already_bound_or = bound_iter.any(|(ctx, _)| *ctx == PatBoundCtx::Or); - + let already_bound_and = bindings + .iter() + .any(|(ctx, map)| *ctx == PatBoundCtx::Product && map.contains_key(&ident)); if already_bound_and { // Overlap in a product pattern somewhere; report an error. use ResolutionError::*; @@ -4025,19 +4043,23 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.report_error(ident.span, error(ident)); } - // Record as bound. - bindings.last_mut().unwrap().1.insert(ident); - - if already_bound_or { + // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`. + // This is *required* for consistency which is checked later. + let already_bound_or = bindings + .iter() + .find_map(|(ctx, map)| if *ctx == PatBoundCtx::Or { map.get(&ident) } else { None }); + let res = if let Some(&res) = already_bound_or { // `Variant1(a) | Variant2(a)`, ok // Reuse definition from the first `a`. - self.innermost_rib_bindings(ValueNS)[&ident] - } else { - // A completely fresh binding is added to the set. - let res = Res::Local(pat_id); - self.innermost_rib_bindings(ValueNS).insert(ident, res); res - } + } else { + // A completely fresh binding is added to the map. + Res::Local(pat_id) + }; + + // Record as bound. + bindings.last_mut().unwrap().1.insert(ident, res); + res } fn innermost_rib_bindings(&mut self, ns: Namespace) -> &mut FxIndexMap { From 5aac7083985bbb4e595edcdfdbc63f65b4543297 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 7 May 2025 13:52:11 +0000 Subject: [PATCH 071/245] Replace `Symbol::as_str` usage in `match` expressions --- clippy_lints/src/attrs/useless_attribute.rs | 40 +++--- clippy_lints/src/bool_assert_comparison.rs | 9 +- clippy_lints/src/floating_point_arithmetic.rs | 12 +- clippy_lints/src/manual_ignore_case_cmp.rs | 9 +- clippy_lints/src/manual_option_as_slice.rs | 12 +- ...se_sensitive_file_extension_comparisons.rs | 5 +- .../src/methods/manual_c_str_literals.rs | 16 +-- clippy_lints/src/methods/needless_collect.rs | 10 +- clippy_lints/src/mutable_debug_assertion.rs | 11 +- clippy_lints/src/operators/eq_op.rs | 15 +-- clippy_lints/src/panic_in_result_fn.rs | 11 +- clippy_lints/src/panic_unimplemented.rs | 8 +- clippy_lints/src/returns.rs | 8 +- clippy_lints/src/strings.rs | 2 +- clippy_lints/src/unit_types/unit_cmp.rs | 17 ++- clippy_lints/src/unused_io_amount.rs | 2 +- clippy_lints/src/write.rs | 6 +- clippy_lints_internal/src/symbols.rs | 119 ++++++++++++------ clippy_utils/src/sym.rs | 35 ++++++ tests/ui-internal/interning_literals.stderr | 15 ++- .../interning_literals_unfixable.stderr | 9 +- tests/ui-internal/symbol_as_str.fixed | 7 ++ tests/ui-internal/symbol_as_str.rs | 7 ++ tests/ui-internal/symbol_as_str.stderr | 35 ++++-- .../symbol_as_str_unfixable.stderr | 9 +- 25 files changed, 281 insertions(+), 148 deletions(-) diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index d75b73280e63..4059f9603c33 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -26,16 +26,16 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { if namespace.is_none() && matches!( - name.as_str(), - "ambiguous_glob_reexports" - | "dead_code" - | "deprecated" - | "hidden_glob_reexports" - | "unreachable_pub" - | "unused" - | "unused_braces" - | "unused_import_braces" - | "unused_imports" + name, + sym::ambiguous_glob_reexports + | sym::dead_code + | sym::deprecated + | sym::hidden_glob_reexports + | sym::unreachable_pub + | sym::unused + | sym::unused_braces + | sym::unused_import_braces + | sym::unused_imports ) { return; @@ -43,16 +43,16 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { if namespace == Some(sym::clippy) && matches!( - name.as_str(), - "wildcard_imports" - | "enum_glob_use" - | "redundant_pub_crate" - | "macro_use_imports" - | "unsafe_removed_from_name" - | "module_name_repetitions" - | "single_component_path_imports" - | "disallowed_types" - | "unused_trait_names" + name, + sym::wildcard_imports + | sym::enum_glob_use + | sym::redundant_pub_crate + | sym::macro_use_imports + | sym::unsafe_removed_from_name + | sym::module_name_repetitions + | sym::single_component_path_imports + | sym::disallowed_types + | sym::unused_trait_names ) { return; diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 4a876b854165..ae36bb76117d 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use clippy_utils::sugg::Sugg; +use clippy_utils::sym; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -73,10 +74,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; - let macro_name = cx.tcx.item_name(macro_call.def_id); - let eq_macro = match macro_name.as_str() { - "assert_eq" | "debug_assert_eq" => true, - "assert_ne" | "debug_assert_ne" => false, + let eq_macro = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::assert_eq_macro | sym::debug_assert_eq_macro) => true, + Some(sym::assert_ne_macro | sym::debug_assert_ne_macro) => false, _ => return, }; let Some((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { @@ -115,6 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { return; } + let macro_name = cx.tcx.item_name(macro_call.def_id); let macro_name = macro_name.as_str(); let non_eq_mac = ¯o_name[..macro_name.len() - 3]; span_lint_and_then( diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 553a00ed868d..e653a57196d6 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -759,12 +759,12 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { let recv_ty = cx.typeck_results().expr_ty(receiver); if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) { - match path.ident.name.as_str() { - "ln" => check_ln1p(cx, expr, receiver), - "log" => check_log_base(cx, expr, receiver, args), - "powf" => check_powf(cx, expr, receiver, args), - "powi" => check_powi(cx, expr, receiver, args), - "sqrt" => check_hypot(cx, expr, receiver), + match path.ident.name { + sym::ln => check_ln1p(cx, expr, receiver), + sym::log => check_log_base(cx, expr, receiver, args), + sym::powf => check_powf(cx, expr, receiver, args), + sym::powi => check_powi(cx, expr, receiver, args), + sym::sqrt => check_hypot(cx, expr, receiver), _ => {}, } } diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index d92069edb6d0..57c03fbb2ed2 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -1,6 +1,7 @@ use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::{get_type_diagnostic_name, is_type_diagnostic_item, is_type_lang_item}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -10,7 +11,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::{Ty, UintTy}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -47,9 +48,9 @@ enum MatchType<'a, 'b> { fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) -> Option<(Span, MatchType<'a, 'b>)> { if let MethodCall(path, expr, _, _) = kind { - let is_lower = match path.ident.name.as_str() { - "to_ascii_lowercase" => true, - "to_ascii_uppercase" => false, + let is_lower = match path.ident.name { + sym::to_ascii_lowercase => true, + sym::to_ascii_uppercase => false, _ => return None, }; let ty_raw = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 04e00f841035..b55c11f2d5b6 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -80,26 +80,26 @@ impl LateLintPass<'_> for ManualOptionAsSlice { check_map(cx, callee, span, self.msrv); } }, - ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name.as_str() { - "unwrap_or" => { + ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name { + sym::unwrap_or => { if is_empty_slice(cx, or) { check_map(cx, callee, span, self.msrv); } }, - "unwrap_or_else" => { + sym::unwrap_or_else => { if returns_empty_slice(cx, or) { check_map(cx, callee, span, self.msrv); } }, _ => {}, }, - ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name.as_str() { - "map_or" => { + ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name { + sym::map_or => { if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { check_as_ref(cx, callee, span, self.msrv); } }, - "map_or_else" => { + sym::map_or_else => { if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { check_as_ref(cx, callee, span, self.msrv); } 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 d07870d4951e..292fa08b5984 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::sym; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -21,8 +22,8 @@ pub(super) fn check<'tcx>( ) { if let ExprKind::MethodCall(path_segment, ..) = recv.kind && matches!( - path_segment.ident.name.as_str(), - "to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase" + path_segment.ident.name, + sym::to_lowercase | sym::to_uppercase | sym::to_ascii_lowercase | sym::to_ascii_uppercase ) { return; diff --git a/clippy_lints/src/methods/manual_c_str_literals.rs b/clippy_lints/src/methods/manual_c_str_literals.rs index 0274e31b4c33..3fa83cd39d1d 100644 --- a/clippy_lints/src/methods/manual_c_str_literals.rs +++ b/clippy_lints/src/methods/manual_c_str_literals.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; +use clippy_utils::{get_parent_expr, sym}; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::edition::Edition::Edition2021; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; use super::MANUAL_C_STR_LITERALS; @@ -71,15 +71,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args && cx.tcx.sess.edition() >= Edition2021 && msrv.meets(cx, msrvs::C_STR_LITERALS) { - match fn_name.as_str() { - name @ ("from_bytes_with_nul" | "from_bytes_with_nul_unchecked") + match fn_name { + sym::from_bytes_with_nul | sym::from_bytes_with_nul_unchecked if !arg.span.from_expansion() && let ExprKind::Lit(lit) = arg.kind && let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node => { - check_from_bytes(cx, expr, arg, name); + check_from_bytes(cx, expr, arg, fn_name); }, - "from_ptr" => check_from_ptr(cx, expr, arg), + sym::from_ptr => check_from_ptr(cx, expr, arg), _ => {}, } } @@ -106,13 +106,13 @@ fn check_from_ptr(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) { } } /// Checks `CStr::from_bytes_with_nul(b"foo\0")` -fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: &str) { +fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: Symbol) { let (span, applicability) = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(method, ..) = parent.kind && [sym::unwrap, sym::expect].contains(&method.ident.name) { (parent.span, Applicability::MachineApplicable) - } else if method == "from_bytes_with_nul_unchecked" { + } else if method == sym::from_bytes_with_nul_unchecked { // `*_unchecked` returns `&CStr` directly, nothing needs to be changed (expr.span, Applicability::MachineApplicable) } else { diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index cd22583b8a25..4c1ed6a1d833 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -357,20 +357,20 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { if let Some(hir_id) = self.current_statement_hir_id { self.hir_id_uses_map.insert(hir_id, self.uses.len()); } - match method_name.ident.name.as_str() { - "into_iter" => self.uses.push(Some(IterFunction { + match method_name.ident.name { + sym::into_iter => self.uses.push(Some(IterFunction { func: IterFunctionKind::IntoIter(expr.hir_id), span: expr.span, })), - "len" => self.uses.push(Some(IterFunction { + sym::len => self.uses.push(Some(IterFunction { func: IterFunctionKind::Len, span: expr.span, })), - "is_empty" => self.uses.push(Some(IterFunction { + sym::is_empty => self.uses.push(Some(IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span, })), - "contains" => self.uses.push(Some(IterFunction { + sym::contains => self.uses.push(Some(IterFunction { func: IterFunctionKind::Contains(args[0].span), span: expr.span, })), diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index d5bed54f0be2..9b327955608a 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::sym; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -42,10 +43,9 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return; }; - let macro_name = cx.tcx.item_name(macro_call.def_id); if !matches!( - macro_name.as_str(), - "debug_assert" | "debug_assert_eq" | "debug_assert_ne" + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::debug_assert_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro) ) { return; } @@ -60,7 +60,10 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { cx, DEBUG_ASSERT_WITH_MUT_CALL, span, - format!("do not call a function with mutable arguments inside of `{macro_name}!`"), + format!( + "do not call a function with mutable arguments inside of `{}!`", + cx.tcx.item_name(macro_call.def_id) + ), ); } } diff --git a/clippy_lints/src/operators/eq_op.rs b/clippy_lints/src/operators/eq_op.rs index 1421893274f5..d79101a687df 100644 --- a/clippy_lints/src/operators/eq_op.rs +++ b/clippy_lints/src/operators/eq_op.rs @@ -1,20 +1,18 @@ use clippy_utils::ast_utils::is_useless_with_eq_exprs; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; -use clippy_utils::{eq_expr_value, is_in_test_function}; +use clippy_utils::{eq_expr_value, is_in_test_function, sym}; use rustc_hir::{BinOpKind, Expr}; use rustc_lint::LateContext; use super::EQ_OP; pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| { - let name = cx.tcx.item_name(macro_call.def_id); + if let Some(macro_call) = first_node_macro_backtrace(cx, e).find(|macro_call| { matches!( - name.as_str(), - "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne" + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro) ) - .then(|| (macro_call, name)) }) && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) && eq_expr_value(cx, lhs, rhs) && macro_call.is_local() @@ -24,7 +22,10 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { cx, EQ_OP, lhs.span.to(rhs.span), - format!("identical args used in this `{macro_name}!` macro call"), + format!( + "identical args used in this `{}!` macro call", + cx.tcx.item_name(macro_call.def_id) + ), ); } } diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index eebc62e2a5a9..ee1d59490ce9 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{is_inside_always_const_context, return_ty}; @@ -69,10 +69,11 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir return ControlFlow::Continue(Descend::Yes); }; if !is_inside_always_const_context(cx.tcx, e.hir_id) - && matches!( - cx.tcx.item_name(macro_call.def_id).as_str(), - "panic" | "assert" | "assert_eq" | "assert_ne" - ) + && (is_panic(cx, macro_call.def_id) + || matches!( + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) + )) { panics.push(macro_call.span); ControlFlow::Continue(Descend::No) diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 39ae9967e016..8962f36db1e6 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -113,8 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { ); return; } - match cx.tcx.item_name(macro_call.def_id).as_str() { - "todo" => { + match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::todo_macro) => { span_lint( cx, TODO, @@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { "`todo` should not be present in production code", ); }, - "unimplemented" => { + Some(sym::unimplemented_macro) => { span_lint( cx, UNIMPLEMENTED, @@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { "`unimplemented` should not be present in production code", ); }, - "unreachable" => { + Some(sym::unreachable_macro) => { span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); }, _ => {}, diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index d8e8ead29128..122d97fdf819 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -5,7 +5,7 @@ use clippy_utils::visitors::for_each_expr; use clippy_utils::{ binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg, - span_find_starting_semi, + span_find_starting_semi, sym, }; use core::ops::ControlFlow; use rustc_ast::MetaItemInner; @@ -22,7 +22,7 @@ use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::edition::Edition; -use rustc_span::{BytePos, Pos, Span, sym}; +use rustc_span::{BytePos, Pos, Span}; use std::borrow::Cow; use std::fmt::Display; @@ -411,8 +411,8 @@ fn check_final_expr<'tcx>( && let [tool, lint_name] = meta_item.path.segments.as_slice() && tool.ident.name == sym::clippy && matches!( - lint_name.ident.name.as_str(), - "needless_return" | "style" | "all" | "warnings" + lint_name.ident.name, + sym::needless_return | sym::style | sym::all | sym::warnings ) { // This is an expectation of the `needless_return` lint diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index af4d0d541f17..73a9fe71e001 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -334,7 +334,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if let ExprKind::MethodCall(path, recv, [], _) = &e.kind && path.ident.name == sym::into_bytes && let ExprKind::MethodCall(path, recv, [], _) = &recv.kind - && matches!(path.ident.name.as_str(), "to_owned" | "to_string") + && matches!(path.ident.name, sym::to_owned | sym::to_string) && let ExprKind::Lit(lit) = &recv.kind && let LitKind::Str(lit_content, _) = &lit.node && lit_content.as_str().is_ascii() diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index 6dcc1195a705..48b532968cb5 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::sym; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -7,11 +8,12 @@ use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { - if let Some(macro_call) = root_macro_call_first_node(cx, expr) { - let macro_name = cx.tcx.item_name(macro_call.def_id); - let result = match macro_name.as_str() { - "assert_eq" | "debug_assert_eq" => "succeed", - "assert_ne" | "debug_assert_ne" => "fail", + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) + { + let result = match diag_name { + sym::assert_eq_macro | sym::debug_assert_eq_macro => "succeed", + sym::assert_ne_macro | sym::debug_assert_ne_macro => "fail", _ => return, }; let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { @@ -24,7 +26,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, UNIT_CMP, macro_call.span, - format!("`{macro_name}` of unit values detected. This will always {result}"), + format!( + "`{}` of unit values detected. This will always {result}", + cx.tcx.item_name(macro_call.def_id) + ), ); } return; diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index e3f28908ff83..5e1cb9e54f57 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -226,7 +226,7 @@ fn is_unreachable_or_panic(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { if is_panic(cx, macro_call.def_id) { return !cx.tcx.hir_is_inside_const_context(expr.hir_id); } - matches!(cx.tcx.item_name(macro_call.def_id).as_str(), "unreachable") + cx.tcx.is_diagnostic_item(sym::unreachable_macro, macro_call.def_id) } fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index f24c127c4521..a8758b65c919 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -498,9 +498,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { None => return, }, LitKind::Char => ( - match lit.symbol.as_str() { - "\"" => "\\\"", - "\\'" => "'", + match lit.symbol { + sym::DOUBLE_QUOTE => "\\\"", + sym::BACKSLASH_SINGLE_QUOTE => "'", _ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) { Some(stripped) => stripped, None => return, diff --git a/clippy_lints_internal/src/symbols.rs b/clippy_lints_internal/src/symbols.rs index bf166988a0c7..5aee545fb0f5 100644 --- a/clippy_lints_internal/src/symbols.rs +++ b/clippy_lints_internal/src/symbols.rs @@ -4,7 +4,7 @@ use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, Lit, Node, Pat, PatExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_lint_defs::declare_tool_lint; use rustc_middle::mir::ConstValue; @@ -65,6 +65,39 @@ pub struct Symbols { impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]); +impl Symbols { + fn lit_suggestion(&self, lit: &Lit) -> Option<(Span, String)> { + if let LitKind::Str(name, _) = lit.node { + let sugg = if let Some((prefix, name)) = self.symbol_map.get(&name.as_u32()) { + format!("{prefix}::{name}") + } else { + format!("sym::{}", name.as_str().replace(|ch: char| !ch.is_alphanumeric(), "_")) + }; + Some((lit.span, sugg)) + } else { + None + } + } + + fn expr_suggestion(&self, expr: &Expr<'_>) -> Option<(Span, String)> { + if let ExprKind::Lit(lit) = expr.kind { + self.lit_suggestion(lit) + } else { + None + } + } + + fn pat_suggestions(&self, pat: &Pat<'_>, suggestions: &mut Vec<(Span, String)>) { + pat.walk_always(|pat| { + if let PatKind::Expr(pat_expr) = pat.kind + && let PatExprKind::Lit { lit, .. } = pat_expr.kind + { + suggestions.extend(self.lit_suggestion(lit)); + } + }); + } +} + impl<'tcx> LateLintPass<'tcx> for Symbols { fn check_crate(&mut self, cx: &LateContext<'_>) { let modules = [ @@ -75,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for Symbols { for (prefix, module) in modules { for def_id in module.get(cx) { // When linting `clippy_utils` itself we can't use `module_children` as it's a local def id. It will - // still lint but the suggestion will say to add it to `sym.rs` even if it's already there + // still lint but the suggestion may suggest the incorrect name for symbols such as `sym::CRLF` if def_id.is_local() { continue; } @@ -98,8 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for Symbols { if let ExprKind::Call(func, [arg]) = &expr.kind && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id) - && let ExprKind::Lit(lit) = arg.kind - && let LitKind::Str(name, _) = lit.node + && let Some((_, sugg)) = self.expr_suggestion(arg) { span_lint_and_then( cx, @@ -107,48 +139,55 @@ impl<'tcx> LateLintPass<'tcx> for Symbols { expr.span, "interning a string literal", |diag| { - let (message, path) = suggestion(&mut self.symbol_map, name); - diag.span_suggestion_verbose(expr.span, message, path, Applicability::MaybeIncorrect); + diag.span_suggestion_verbose( + expr.span, + "use a preinterned symbol instead", + sugg, + Applicability::MaybeIncorrect, + ); + diag.help("add the symbol to `clippy_utils/src/sym.rs` if needed"); }, ); } - if let ExprKind::Binary(_, lhs, rhs) = expr.kind { - check_binary(cx, lhs, rhs, &mut self.symbol_map); - check_binary(cx, rhs, lhs, &mut self.symbol_map); - } - } -} + if let Some(as_str) = as_str_span(cx, expr) + && let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) + { + let mut suggestions = Vec::new(); -fn check_binary( - cx: &LateContext<'_>, - lhs: &Expr<'_>, - rhs: &Expr<'_>, - symbols: &mut FxHashMap, -) { - if let Some(removal_span) = as_str_span(cx, lhs) - && let ExprKind::Lit(lit) = rhs.kind - && let LitKind::Str(name, _) = lit.node - { - span_lint_and_then(cx, SYMBOL_AS_STR, lhs.span, "converting a Symbol to a string", |diag| { - let (message, path) = suggestion(symbols, name); - diag.multipart_suggestion_verbose( - message, - vec![(removal_span, String::new()), (rhs.span, path)], - Applicability::MachineApplicable, + match parent.kind { + ExprKind::Binary(_, lhs, rhs) => { + suggestions.extend(self.expr_suggestion(lhs)); + suggestions.extend(self.expr_suggestion(rhs)); + }, + ExprKind::Match(_, arms, _) => { + for arm in arms { + self.pat_suggestions(arm.pat, &mut suggestions); + } + }, + _ => {}, + } + + if suggestions.is_empty() { + return; + } + + span_lint_and_then( + cx, + SYMBOL_AS_STR, + expr.span, + "converting a Symbol to a string", + |diag| { + suggestions.push((as_str, String::new())); + diag.multipart_suggestion( + "use preinterned symbols instead", + suggestions, + Applicability::MaybeIncorrect, + ); + diag.help("add the symbols to `clippy_utils/src/sym.rs` if needed"); + }, ); - }); - } -} - -fn suggestion(symbols: &mut FxHashMap, name: Symbol) -> (&'static str, String) { - if let Some((prefix, name)) = symbols.get(&name.as_u32()) { - ("use the preinterned symbol", format!("{prefix}::{name}")) - } else { - ( - "add the symbol to `clippy_utils/src/sym.rs` and use it", - format!("sym::{}", name.as_str().replace(|ch: char| !ch.is_alphanumeric(), "_")), - ) + } } } diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 94b73f37269f..9428262b99aa 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -32,12 +32,14 @@ macro_rules! generate { generate! { abs, align_of, + ambiguous_glob_reexports, as_bytes, as_deref_mut, as_deref, as_mut, AsyncReadExt, AsyncWriteExt, + BACKSLASH_SINGLE_QUOTE: r"\'", Binary, build_hasher, bytes, @@ -59,8 +61,11 @@ generate! { de, Deserialize, diagnostics, + disallowed_types, + DOUBLE_QUOTE: "\"", EarlyLintPass, ends_with, + enum_glob_use, error, ErrorKind, exp, @@ -69,12 +74,16 @@ generate! { finish, flat_map, for_each, + from_bytes_with_nul_unchecked, + from_bytes_with_nul, + from_ptr, from_raw, from_ref, from_str_radix, fs, futures_util, get, + hidden_glob_reexports, hygiene, insert, int_roundings, @@ -96,20 +105,27 @@ generate! { Lazy, LF: "\n", Lint, + ln, lock_api, + log, LowerExp, LowerHex, + macro_use_imports, + map_or_else, + map_or, max, MAX, mem, min, MIN, mode, + module_name_repetitions, msrv, msrvs, MsrvStack, mut_ptr, mutex, + needless_return, next_tuple, Octal, once_cell, @@ -119,7 +135,10 @@ generate! { parse, PathLookup, paths, + powf, + powi, push, + redundant_pub_crate, regex, Regex, RegexBuilder, @@ -137,29 +156,45 @@ generate! { set_mode, set_readonly, signum, + single_component_path_imports, span_lint_and_then, split_whitespace, split, + sqrt, Start, Step, + style, symbol, Symbol, SyntaxContext, take, TBD, then_some, + to_ascii_lowercase, + to_ascii_uppercase, to_digit, + to_lowercase, to_owned, + to_uppercase, tokio, + unreachable_pub, + unsafe_removed_from_name, + unused_braces, unused_extern_crates, + unused_import_braces, + unused_trait_names, + unused, unwrap_err, unwrap_or_default, + unwrap_or_else, UpperExp, UpperHex, V4, V6, Visitor, + warnings, Weak, + wildcard_imports, with_capacity, wrapping_offset, } diff --git a/tests/ui-internal/interning_literals.stderr b/tests/ui-internal/interning_literals.stderr index 628b97eff84d..9ff4194e542b 100644 --- a/tests/ui-internal/interning_literals.stderr +++ b/tests/ui-internal/interning_literals.stderr @@ -4,9 +4,10 @@ error: interning a string literal LL | let _ = Symbol::intern("f32"); | ^^^^^^^^^^^^^^^^^^^^^ | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed = note: `-D clippy::interning-literals` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::interning_literals)]` -help: use the preinterned symbol +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("f32"); LL + let _ = sym::f32; @@ -18,7 +19,8 @@ error: interning a string literal LL | let _ = Symbol::intern("proc-macro"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("proc-macro"); LL + let _ = sym::proc_dash_macro; @@ -30,7 +32,8 @@ error: interning a string literal LL | let _ = Symbol::intern("self"); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("self"); LL + let _ = kw::SelfLower; @@ -42,7 +45,8 @@ error: interning a string literal LL | let _ = Symbol::intern("msrv"); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("msrv"); LL + let _ = sym::msrv; @@ -54,7 +58,8 @@ error: interning a string literal LL | let _ = Symbol::intern("Cargo.toml"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("Cargo.toml"); LL + let _ = sym::Cargo_toml; diff --git a/tests/ui-internal/interning_literals_unfixable.stderr b/tests/ui-internal/interning_literals_unfixable.stderr index 8294453a8f94..879d9e633c23 100644 --- a/tests/ui-internal/interning_literals_unfixable.stderr +++ b/tests/ui-internal/interning_literals_unfixable.stderr @@ -4,9 +4,10 @@ error: interning a string literal LL | let _ = Symbol::intern("xyz123"); | ^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed = note: `-D clippy::interning-literals` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::interning_literals)]` -help: add the symbol to `clippy_utils/src/sym.rs` and use it +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("xyz123"); LL + let _ = sym::xyz123; @@ -18,7 +19,8 @@ error: interning a string literal LL | let _ = Symbol::intern("with-dash"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: add the symbol to `clippy_utils/src/sym.rs` and use it + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("with-dash"); LL + let _ = sym::with_dash; @@ -30,7 +32,8 @@ error: interning a string literal LL | let _ = Symbol::intern("with.dot"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: add the symbol to `clippy_utils/src/sym.rs` and use it + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead | LL - let _ = Symbol::intern("with.dot"); LL + let _ = sym::with_dot; diff --git a/tests/ui-internal/symbol_as_str.fixed b/tests/ui-internal/symbol_as_str.fixed index 3e26732836ca..6a71b16c6049 100644 --- a/tests/ui-internal/symbol_as_str.fixed +++ b/tests/ui-internal/symbol_as_str.fixed @@ -18,4 +18,11 @@ fn f(s: Symbol) { //~^ symbol_as_str sym::get == s; //~^ symbol_as_str + + let _ = match s { + //~^ symbol_as_str + sym::unwrap_err => 1, + sym::unwrap_or_default | sym::unwrap_or_else => 2, + _ => 3, + }; } diff --git a/tests/ui-internal/symbol_as_str.rs b/tests/ui-internal/symbol_as_str.rs index 334c32d18983..43136504bf1a 100644 --- a/tests/ui-internal/symbol_as_str.rs +++ b/tests/ui-internal/symbol_as_str.rs @@ -18,4 +18,11 @@ fn f(s: Symbol) { //~^ symbol_as_str "get" == s.as_str(); //~^ symbol_as_str + + let _ = match s.as_str() { + //~^ symbol_as_str + "unwrap_err" => 1, + "unwrap_or_default" | "unwrap_or_else" => 2, + _ => 3, + }; } diff --git a/tests/ui-internal/symbol_as_str.stderr b/tests/ui-internal/symbol_as_str.stderr index 39f81f3833c4..3eeead4aa8c1 100644 --- a/tests/ui-internal/symbol_as_str.stderr +++ b/tests/ui-internal/symbol_as_str.stderr @@ -4,9 +4,10 @@ error: converting a Symbol to a string LL | s.as_str() == "f32"; | ^^^^^^^^^^ | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed = note: `-D clippy::symbol-as-str` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` -help: use the preinterned symbol +help: use preinterned symbols instead | LL - s.as_str() == "f32"; LL + s == sym::f32; @@ -18,7 +19,8 @@ error: converting a Symbol to a string LL | s.as_str() == "proc-macro"; | ^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "proc-macro"; LL + s == sym::proc_dash_macro; @@ -30,7 +32,8 @@ error: converting a Symbol to a string LL | s.as_str() == "self"; | ^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "self"; LL + s == kw::SelfLower; @@ -42,7 +45,8 @@ error: converting a Symbol to a string LL | s.as_str() == "msrv"; | ^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "msrv"; LL + s == sym::msrv; @@ -54,7 +58,8 @@ error: converting a Symbol to a string LL | s.as_str() == "Cargo.toml"; | ^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "Cargo.toml"; LL + s == sym::Cargo_toml; @@ -66,11 +71,27 @@ error: converting a Symbol to a string LL | "get" == s.as_str(); | ^^^^^^^^^^ | -help: use the preinterned symbol + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - "get" == s.as_str(); LL + sym::get == s; | -error: aborting due to 6 previous errors +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:22:19 + | +LL | let _ = match s.as_str() { + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL ~ let _ = match s { +LL | +LL ~ sym::unwrap_err => 1, +LL ~ sym::unwrap_or_default | sym::unwrap_or_else => 2, + | + +error: aborting due to 7 previous errors diff --git a/tests/ui-internal/symbol_as_str_unfixable.stderr b/tests/ui-internal/symbol_as_str_unfixable.stderr index 5349983ca519..65664ebb451a 100644 --- a/tests/ui-internal/symbol_as_str_unfixable.stderr +++ b/tests/ui-internal/symbol_as_str_unfixable.stderr @@ -4,9 +4,10 @@ error: converting a Symbol to a string LL | s.as_str() == "xyz123"; | ^^^^^^^^^^ | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed = note: `-D clippy::symbol-as-str` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` -help: add the symbol to `clippy_utils/src/sym.rs` and use it +help: use preinterned symbols instead | LL - s.as_str() == "xyz123"; LL + s == sym::xyz123; @@ -18,7 +19,8 @@ error: converting a Symbol to a string LL | s.as_str() == "with-dash"; | ^^^^^^^^^^ | -help: add the symbol to `clippy_utils/src/sym.rs` and use it + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "with-dash"; LL + s == sym::with_dash; @@ -30,7 +32,8 @@ error: converting a Symbol to a string LL | s.as_str() == "with.dot"; | ^^^^^^^^^^ | -help: add the symbol to `clippy_utils/src/sym.rs` and use it + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead | LL - s.as_str() == "with.dot"; LL + s == sym::with_dot; From 772ea35935c8201abaa8c21387c9eda323d9184f Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 7 May 2025 14:32:23 +0000 Subject: [PATCH 072/245] Fix diagnostic paths printed by dogfood test --- tests/dogfood.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 16a1a415102c..4ac2bd532851 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -44,8 +44,8 @@ fn dogfood() { "rustc_tools_util", ] { println!("linting {package}"); - if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) { - failed_packages.push(if package.is_empty() { "root" } else { package }); + if !run_clippy_for_package(package) { + failed_packages.push(package); } } @@ -57,7 +57,7 @@ fn dogfood() { } #[must_use] -fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { +fn run_clippy_for_package(project: &str) -> bool { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); @@ -79,15 +79,17 @@ fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { } } - command.arg("--").args(args); + command.arg("--"); command.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - command.args(["-D", "clippy::dbg_macro"]); - + command.args(["-D", "clippy::all", "-D", "clippy::pedantic", "-D", "clippy::dbg_macro"]); if !cfg!(feature = "internal") { // running a clippy built without internal lints on the clippy source - // that contains e.g. `allow(clippy::invalid_paths)` + // that contains e.g. `allow(clippy::symbol_as_str)` command.args(["-A", "unknown_lints"]); } + // Workaround for not being a workspace, add the crate's directory back to the path + command.args(["--remap-path-prefix", &format!("={project}")]); + command.status().unwrap().success() } From 3278cb554578fadd03a606b45690cc68a8661cfd Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 7 May 2025 16:34:32 +0200 Subject: [PATCH 073/245] Migrate to 2024 edition --- Cargo.toml | 2 +- src/lib.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 717eaf9e0580..c692a90f0a4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "rustc_codegen_gcc" version = "0.1.0" authors = ["Antoni Boucher "] -edition = "2018" +edition = "2024" license = "MIT OR Apache-2.0" [lib] diff --git a/src/lib.rs b/src/lib.rs index 555f164e53fd..9bbf40d4b34a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -413,7 +413,7 @@ impl WriteBackendMethods for GccCodegenBackend { cgcx: &CodegenContext, thin: ThinModule, ) -> Result, FatalError> { - back::lto::optimize_thin_module(thin, cgcx) + unsafe { back::lto::optimize_thin_module(thin, cgcx) } } unsafe fn codegen( @@ -422,7 +422,7 @@ impl WriteBackendMethods for GccCodegenBackend { module: ModuleCodegen, config: &ModuleConfig, ) -> Result { - back::write::codegen(cgcx, dcx, module, config) + unsafe { back::write::codegen(cgcx, dcx, module, config) } } fn prepare_thin( @@ -454,7 +454,7 @@ impl WriteBackendMethods for GccCodegenBackend { } /// This is the entrypoint for a hot plugged rustc_codegen_gccjit -#[no_mangle] +#[unsafe(no_mangle)] pub fn __rustc_codegen_backend() -> Box { #[cfg(feature = "master")] let info = { From 8e9a84011d24e99e3741664143c727f9c8e61964 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 7 May 2025 16:37:50 +0200 Subject: [PATCH 074/245] Migrate build system to 2024 edition --- build_system/Cargo.toml | 2 +- build_system/src/main.rs | 4 +++- build_system/src/utils.rs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build_system/Cargo.toml b/build_system/Cargo.toml index d2600ed5a031..540d82369fdf 100644 --- a/build_system/Cargo.toml +++ b/build_system/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "y" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] boml = "0.3.1" diff --git a/build_system/src/main.rs b/build_system/src/main.rs index 393617183061..c70b00e09ae7 100644 --- a/build_system/src/main.rs +++ b/build_system/src/main.rs @@ -60,7 +60,9 @@ pub enum Command { fn main() { if env::var("RUST_BACKTRACE").is_err() { - env::set_var("RUST_BACKTRACE", "1"); + unsafe { + env::set_var("RUST_BACKTRACE", "1"); + } } let command = match env::args().nth(1).as_deref() { diff --git a/build_system/src/utils.rs b/build_system/src/utils.rs index 401c23948e5d..ca177a5feb86 100644 --- a/build_system/src/utils.rs +++ b/build_system/src/utils.rs @@ -10,7 +10,7 @@ use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus, Output}; #[cfg(unix)] -extern "C" { +unsafe extern "C" { fn raise(signal: c_int) -> c_int; } From dcfd5c30d4bd81b08f893ca245e02359b9210d25 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 7 May 2025 16:45:18 +0200 Subject: [PATCH 075/245] Remove unneeded `let_chains` feature --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9bbf40d4b34a..7ef7d4daf461 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ #![allow(internal_features)] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![feature(rustc_private, decl_macro, never_type, trusted_len, let_chains)] +#![feature(rustc_private, decl_macro, never_type, trusted_len)] #![allow(broken_intra_doc_links)] #![recursion_limit = "256"] #![warn(rust_2018_idioms)] From 7f2f0d2ec13f22eb3057b309d39de134d0fd2848 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 7 May 2025 16:50:47 +0200 Subject: [PATCH 076/245] Update tests to 2024 edition --- tests/lang_tests_common.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/lang_tests_common.rs b/tests/lang_tests_common.rs index d5a0d71c4b29..f0a9e72f2ea2 100644 --- a/tests/lang_tests_common.rs +++ b/tests/lang_tests_common.rs @@ -42,7 +42,9 @@ pub fn main_inner(profile: Profile) { .expect("failed to get absolute path of `gcc-path`") .display() .to_string(); - env::set_var("LD_LIBRARY_PATH", gcc_path); + unsafe { + env::set_var("LD_LIBRARY_PATH", gcc_path); + } fn rust_filter(path: &Path) -> bool { path.is_file() && path.extension().expect("extension").to_str().expect("to_str") == "rs" From 8c045221b59adfb5197f2900a82b496efa5d4740 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 7 May 2025 15:16:32 +0000 Subject: [PATCH 077/245] Make `let_with_type_underscore` help message into a suggestion --- clippy_lints/src/let_with_type_underscore.rs | 15 +++++-- tests/ui/let_with_type_underscore.fixed | 47 ++++++++++++++++++++ tests/ui/let_with_type_underscore.stderr | 34 +++++++------- 3 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 tests/ui/let_with_type_underscore.fixed diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 9c8488ff381b..1917ca24a05b 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; +use rustc_errors::Applicability; use rustc_hir::{LetStmt, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -32,13 +33,19 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { && !local.span.in_external_macro(cx.tcx.sess.source_map()) && !is_from_proc_macro(cx, ty) { - span_lint_and_help( + span_lint_and_then( cx, LET_WITH_TYPE_UNDERSCORE, local.span, "variable declared with type underscore", - Some(ty.span.with_lo(local.pat.span.hi())), - "remove the explicit type `_` declaration", + |diag| { + diag.span_suggestion_verbose( + ty.span.with_lo(local.pat.span.hi()), + "remove the explicit type `_` declaration", + "", + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/tests/ui/let_with_type_underscore.fixed b/tests/ui/let_with_type_underscore.fixed new file mode 100644 index 000000000000..7a4af4e3d1e7 --- /dev/null +++ b/tests/ui/let_with_type_underscore.fixed @@ -0,0 +1,47 @@ +//@aux-build: proc_macros.rs +#![allow(unused)] +#![warn(clippy::let_with_type_underscore)] +#![allow(clippy::let_unit_value, clippy::needless_late_init)] + +extern crate proc_macros; + +fn func() -> &'static str { + "" +} + +#[rustfmt::skip] +fn main() { + // Will lint + let x = 1; + //~^ let_with_type_underscore + let _ = 2; + //~^ let_with_type_underscore + let x = func(); + //~^ let_with_type_underscore + let x; + //~^ let_with_type_underscore + x = (); + + let x = 1; // Will not lint, Rust infers this to an integer before Clippy + let x = func(); + let x: Vec<_> = Vec::::new(); + let x: [_; 1] = [1]; + let x = 1; + //~^ let_with_type_underscore + + // Do not lint from procedural macros + proc_macros::with_span! { + span + let x: _ = (); + // Late initialization + let x: _; + x = (); + // Ensure weird formatting will not break it (hopefully) + let x : _ = 1; + let x +: _ = 1; + let x : + _; + x = (); + }; +} diff --git a/tests/ui/let_with_type_underscore.stderr b/tests/ui/let_with_type_underscore.stderr index 2284d1fe2e48..9179f9922071 100644 --- a/tests/ui/let_with_type_underscore.stderr +++ b/tests/ui/let_with_type_underscore.stderr @@ -4,13 +4,13 @@ error: variable declared with type underscore LL | let x: _ = 1; | ^^^^^^^^^^^^^ | -help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:15:10 - | -LL | let x: _ = 1; - | ^^^ = note: `-D clippy::let-with-type-underscore` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_with_type_underscore)]` +help: remove the explicit type `_` declaration + | +LL - let x: _ = 1; +LL + let x = 1; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:17:5 @@ -19,10 +19,10 @@ LL | let _: _ = 2; | ^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:17:10 | -LL | let _: _ = 2; - | ^^^ +LL - let _: _ = 2; +LL + let _ = 2; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:19:5 @@ -31,10 +31,10 @@ LL | let x: _ = func(); | ^^^^^^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:19:10 | -LL | let x: _ = func(); - | ^^^ +LL - let x: _ = func(); +LL + let x = func(); + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:21:5 @@ -43,10 +43,10 @@ LL | let x: _; | ^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:21:10 | -LL | let x: _; - | ^^^ +LL - let x: _; +LL + let x; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:29:5 @@ -55,10 +55,10 @@ LL | let x : _ = 1; | ^^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:29:10 | -LL | let x : _ = 1; - | ^^^^ +LL - let x : _ = 1; +LL + let x = 1; + | error: aborting due to 5 previous errors From 0bdf0726cf77c95937aea1df408c9b18fe33f010 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 7 May 2025 17:20:25 +0200 Subject: [PATCH 078/245] Mark back::lto::optimize_thin_module` and `back::write::codegen` functions as safe --- src/back/lto.rs | 2 +- src/back/write.rs | 2 +- src/lib.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/back/lto.rs b/src/back/lto.rs index e5221c7da319..62bb58d44fe0 100644 --- a/src/back/lto.rs +++ b/src/back/lto.rs @@ -589,7 +589,7 @@ fn thin_lto( Ok((opt_jobs, copy_jobs)) } -pub unsafe fn optimize_thin_module( +pub fn optimize_thin_module( thin_module: ThinModule, _cgcx: &CodegenContext, ) -> Result, FatalError> { diff --git a/src/back/write.rs b/src/back/write.rs index 16c895322e88..09e955acf390 100644 --- a/src/back/write.rs +++ b/src/back/write.rs @@ -14,7 +14,7 @@ use crate::base::add_pic_option; use crate::errors::CopyBitcode; use crate::{GccCodegenBackend, GccContext}; -pub(crate) unsafe fn codegen( +pub(crate) fn codegen( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, diff --git a/src/lib.rs b/src/lib.rs index 7ef7d4daf461..688487304612 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -413,7 +413,7 @@ impl WriteBackendMethods for GccCodegenBackend { cgcx: &CodegenContext, thin: ThinModule, ) -> Result, FatalError> { - unsafe { back::lto::optimize_thin_module(thin, cgcx) } + back::lto::optimize_thin_module(thin, cgcx) } unsafe fn codegen( @@ -422,7 +422,7 @@ impl WriteBackendMethods for GccCodegenBackend { module: ModuleCodegen, config: &ModuleConfig, ) -> Result { - unsafe { back::write::codegen(cgcx, dcx, module, config) } + back::write::codegen(cgcx, dcx, module, config) } fn prepare_thin( From 390fc73ae737ef99fa109add85bbf60e53f1a983 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 7 May 2025 17:32:31 +0200 Subject: [PATCH 079/245] Fix new clippy lints --- src/consts.rs | 6 ++---- src/debuginfo.rs | 5 ++--- src/declare.rs | 1 + tests/lang_tests_common.rs | 5 ++--- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/consts.rs b/src/consts.rs index 0a67bd7bc71a..033afc0f8fbf 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -191,13 +191,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { // TODO(antoyo): check if it's okay that no link_section is set. let typ = self.val_ty(cv).get_aligned(align.bytes()); - let global = self.declare_private_global(&name[..], typ); - global + self.declare_private_global(&name[..], typ) } _ => { let typ = self.val_ty(cv).get_aligned(align.bytes()); - let global = self.declare_unnamed_global(typ); - global + self.declare_unnamed_global(typ) } }; global.global_set_initializer_rvalue(cv); diff --git a/src/debuginfo.rs b/src/debuginfo.rs index f3ced8643952..e0597d0030d5 100644 --- a/src/debuginfo.rs +++ b/src/debuginfo.rs @@ -289,7 +289,7 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { ) -> Self::DILocation { let pos = span.lo(); let DebugLoc { file, line, col } = self.lookup_debug_loc(pos); - let loc = match file.name { + match file.name { rustc_span::FileName::Real(ref name) => match *name { rustc_span::RealFileName::LocalPath(ref name) => { if let Some(name) = name.to_str() { @@ -314,7 +314,6 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } }, _ => Location::null(), - }; - loc + } } } diff --git a/src/declare.rs b/src/declare.rs index c1ca3eb849e8..bed82073e2c4 100644 --- a/src/declare.rs +++ b/src/declare.rs @@ -157,6 +157,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { /// /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. +#[allow(clippy::let_and_return)] fn declare_raw_fn<'gcc>( cx: &CodegenCx<'gcc, '_>, name: &str, diff --git a/tests/lang_tests_common.rs b/tests/lang_tests_common.rs index f0a9e72f2ea2..bdcf14b4b26d 100644 --- a/tests/lang_tests_common.rs +++ b/tests/lang_tests_common.rs @@ -69,15 +69,14 @@ pub fn main_inner(profile: Profile) { .test_dir("tests/run") .test_path_filter(filter) .test_extract(|path| { - let lines = std::fs::read_to_string(path) + std::fs::read_to_string(path) .expect("read file") .lines() .skip_while(|l| !l.starts_with("//")) .take_while(|l| l.starts_with("//")) .map(|l| &l[2..]) .collect::>() - .join("\n"); - lines + .join("\n") }) .test_cmds(move |path| { // Test command 1: Compile `x.rs` into `tempdir/x`. From 303c4ecfdd0c571d80c5bc151243aee1900cebfd Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 23 Apr 2025 16:03:55 +0000 Subject: [PATCH 080/245] Require T: TypeFoldable in Binder visit --- clippy_utils/src/ty/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 8db9cd593b33..da09edd7f7c0 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -20,8 +20,8 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, + GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; @@ -915,7 +915,7 @@ pub fn for_each_top_level_late_bound_region( ControlFlow::Continue(()) } } - fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result { + fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result { self.index += 1; let res = t.super_visit_with(self); self.index -= 1; From be405344570f68138ecce86f19f3908d6f481ba9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 10 Jan 2025 17:44:47 +0100 Subject: [PATCH 081/245] Add `primitive_method_to_numeric_cast` lint --- CHANGELOG.md | 1 + clippy_lints/src/casts/mod.rs | 29 ++++++++++ .../casts/primitive_method_to_numeric_cast.rs | 57 +++++++++++++++++++ clippy_lints/src/declared_lints.rs | 1 + 4 files changed, 88 insertions(+) create mode 100644 clippy_lints/src/casts/primitive_method_to_numeric_cast.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b62c9a59aa5..ca2e73897903 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6068,6 +6068,7 @@ Released 2018-09-13 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`precedence_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence_bits +[`primitive_method_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#primitive_method_to_numeric_cast [`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal [`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 76931fce209e..7c8fa0b4d921 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -18,6 +18,7 @@ mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; mod manual_dangling_ptr; +mod primitive_method_to_numeric_cast; mod ptr_as_ptr; mod ptr_cast_constness; mod ref_as_ptr; @@ -786,6 +787,32 @@ declare_clippy_lint! { "casting small constant literals to pointers to create dangling pointers" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of a primitive method pointer to any integer type. + /// + /// ### Why restrict this? + /// Casting a function pointer to an integer can have surprising results and can occur + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything + /// low-level with function pointers then you can opt out of casting functions to integers in + /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function + /// pointer casts in your code. + /// + /// ### Example + /// ```no_run + /// let _ = u16::max as usize; + /// ``` + /// + /// Use instead: + /// ```no_run + /// let _ = u16::MAX as usize; + /// ``` + #[clippy::version = "1.86.0"] + pub PRIMITIVE_METHOD_TO_NUMERIC_CAST, + suspicious, + "casting a primitive method pointer to any integer type" +} + pub struct Casts { msrv: Msrv, } @@ -823,6 +850,7 @@ impl_lint_pass!(Casts => [ REF_AS_PTR, AS_POINTER_UNDERSCORE, MANUAL_DANGLING_PTR, + PRIMITIVE_METHOD_TO_NUMERIC_CAST, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -847,6 +875,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); + primitive_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); diff --git a/clippy_lints/src/casts/primitive_method_to_numeric_cast.rs b/clippy_lints/src/casts/primitive_method_to_numeric_cast.rs new file mode 100644 index 000000000000..380fa98cd15f --- /dev/null +++ b/clippy_lints/src/casts/primitive_method_to_numeric_cast.rs @@ -0,0 +1,57 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::match_def_path; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +use super::PRIMITIVE_METHOD_TO_NUMERIC_CAST; + +fn get_primitive_ty_name(ty: Ty<'_>) -> Option<&'static str> { + match ty.kind() { + ty::Char => Some("char"), + ty::Int(int) => Some(int.name_str()), + ty::Uint(uint) => Some(uint.name_str()), + ty::Float(float) => Some(float.name_str()), + _ => None, + } +} + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + // We allow casts from any function type to any function type. + match cast_to.kind() { + ty::FnDef(..) | ty::FnPtr(..) => return, + _ => { /* continue to checks */ }, + } + + if let ty::FnDef(def_id, generics) = cast_from.kind() + && let Some(method_name) = cx.tcx.opt_item_name(*def_id) + && let method_name = method_name.as_str() + && (method_name == "min" || method_name == "max") + // We get the type on which the `min`/`max` method of the `Ord` trait is implemented. + && let [ty] = generics.as_slice() + && let Some(ty) = ty.as_type() + // We get its name in case it's a primitive with an associated MIN/MAX constant. + && let Some(ty_name) = get_primitive_ty_name(ty) + && match_def_path(cx, *def_id, &["core", "cmp", "Ord", method_name]) + { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); + + span_lint_and_then( + cx, + PRIMITIVE_METHOD_TO_NUMERIC_CAST, + expr.span, + format!("casting function pointer `{from_snippet}` to `{cast_to}`"), + |diag| { + diag.span_suggestion_verbose( + expr.span, + "did you mean to use the associated constant?", + format!("{ty_name}::{} as {cast_to}", method_name.to_ascii_uppercase()), + applicability, + ); + }, + ); + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2cccd6ba2702..1d4c75dafa31 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -68,6 +68,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, crate::casts::MANUAL_DANGLING_PTR_INFO, + crate::casts::PRIMITIVE_METHOD_TO_NUMERIC_CAST_INFO, crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::REF_AS_PTR_INFO, From c680419425b736f238fa0142f43c400b3ec26d27 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 10 Jan 2025 17:44:58 +0100 Subject: [PATCH 082/245] Add UI test for `primitive_method_to_numeric_cast` --- tests/ui/primitive_method_to_numeric_cast.fixed | 5 +++++ tests/ui/primitive_method_to_numeric_cast.rs | 5 +++++ tests/ui/primitive_method_to_numeric_cast.stderr | 15 +++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 tests/ui/primitive_method_to_numeric_cast.fixed create mode 100644 tests/ui/primitive_method_to_numeric_cast.rs create mode 100644 tests/ui/primitive_method_to_numeric_cast.stderr diff --git a/tests/ui/primitive_method_to_numeric_cast.fixed b/tests/ui/primitive_method_to_numeric_cast.fixed new file mode 100644 index 000000000000..1ebb52618f13 --- /dev/null +++ b/tests/ui/primitive_method_to_numeric_cast.fixed @@ -0,0 +1,5 @@ +#![warn(clippy::primitive_method_to_numeric_cast)] + +fn main() { + let _ = u16::MAX as usize; //~ primitive_method_to_numeric_cast +} diff --git a/tests/ui/primitive_method_to_numeric_cast.rs b/tests/ui/primitive_method_to_numeric_cast.rs new file mode 100644 index 000000000000..89b0bfa84fee --- /dev/null +++ b/tests/ui/primitive_method_to_numeric_cast.rs @@ -0,0 +1,5 @@ +#![warn(clippy::primitive_method_to_numeric_cast)] + +fn main() { + let _ = u16::max as usize; //~ primitive_method_to_numeric_cast +} diff --git a/tests/ui/primitive_method_to_numeric_cast.stderr b/tests/ui/primitive_method_to_numeric_cast.stderr new file mode 100644 index 000000000000..5515dc646ce7 --- /dev/null +++ b/tests/ui/primitive_method_to_numeric_cast.stderr @@ -0,0 +1,15 @@ +error: casting function pointer `u16::max` to `usize` + --> tests/ui/primitive_method_to_numeric_cast.rs:4:13 + | +LL | let _ = u16::max as usize; + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::primitive-method-to-numeric-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::primitive_method_to_numeric_cast)]` +help: did you mean to use the associated constant? + | +LL | let _ = u16::MAX as usize; + | ~~~~~~~~~~~~~~~~~ + +error: aborting due to 1 previous error + From df479588945119d2e356fdfef21a5d2094ce9499 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 1 May 2025 16:31:47 +0000 Subject: [PATCH 083/245] Remove manual WF hack --- .../src/check/compare_impl_item.rs | 65 +++---------------- .../issue-88022.rs | 6 +- .../dropck/explicit-drop-bounds.bad1.stderr | 18 ++++- .../dropck/explicit-drop-bounds.bad2.stderr | 22 ++++++- tests/ui/dropck/explicit-drop-bounds.rs | 2 + .../rust-call-abi-not-a-tuple-ice-81974.rs | 4 +- ...rust-call-abi-not-a-tuple-ice-81974.stderr | 44 ++++++++++--- 7 files changed, 89 insertions(+), 72 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index bbf36fef1ddb..2a70c711e051 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use std::iter; use hir::def_id::{DefId, DefIdMap, LocalDefId}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir::def::{DefKind, Res}; @@ -356,61 +356,14 @@ fn compare_method_predicate_entailment<'tcx>( } if !(impl_sig, trait_sig).references_error() { - // Select obligations to make progress on inference before processing - // the wf obligation below. - // FIXME(-Znext-solver): Not needed when the hack below is removed. - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(errors); - return Err(reported); - } - - // See #108544. Annoying, we can end up in cases where, because of winnowing, - // we pick param env candidates over a more general impl, leading to more - // stricter lifetime requirements than we would otherwise need. This can - // trigger the lint. Instead, let's only consider type outlives and - // region outlives obligations. - // - // FIXME(-Znext-solver): Try removing this hack again once the new - // solver is stable. We should just be able to register a WF pred for - // the fn sig. - let mut wf_args: smallvec::SmallVec<[_; 4]> = - unnormalized_impl_sig.inputs_and_output.iter().map(|ty| ty.into()).collect(); - // Annoyingly, asking for the WF predicates of an array (with an unevaluated const (only?)) - // will give back the well-formed predicate of the same array. - let mut wf_args_seen: FxHashSet<_> = wf_args.iter().copied().collect(); - while let Some(term) = wf_args.pop() { - let Some(obligations) = rustc_trait_selection::traits::wf::obligations( - infcx, - param_env, - impl_m_def_id, - 0, - term, - impl_m_span, - ) else { - continue; - }; - for obligation in obligations { - debug!(?obligation); - match obligation.predicate.kind().skip_binder() { - // We need to register Projection oblgiations too, because we may end up with - // an implied `X::Item: 'a`, which gets desugared into `X::Item = ?0`, `?0: 'a`. - // If we only register the region outlives obligation, this leads to an unconstrained var. - // See `implied_bounds_entailment_alias_var.rs` test. - ty::PredicateKind::Clause( - ty::ClauseKind::RegionOutlives(..) - | ty::ClauseKind::TypeOutlives(..) - | ty::ClauseKind::Projection(..), - ) => ocx.register_obligation(obligation), - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { - if wf_args_seen.insert(term) { - wf_args.push(term) - } - } - _ => {} - } - } - } + ocx.register_obligation(traits::Obligation::new( + infcx.tcx, + cause, + param_env, + ty::ClauseKind::WellFormed( + Ty::new_fn_ptr(tcx, ty::Binder::dummy(unnormalized_impl_sig)).into(), + ), + )); } // Check that all obligations are satisfied by the implementation's diff --git a/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-88022.rs b/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-88022.rs index 99eb92f432c2..76f6aaee6dce 100644 --- a/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-88022.rs +++ b/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-88022.rs @@ -15,12 +15,14 @@ where impl<'a, T, const S: usize> Iterator for BufferIter<'a, T, S> { //~^ error: the trait bound - //~^^ error: unconstrained generic constant + //~| error: unconstrained generic constant type Item = &'a T; fn next(&mut self) -> Option { //~^ error: the trait bound - //~^^ error: unconstrained generic constant + //~| error: unconstrained generic constant + //~| error: the trait bound + //~| error: unconstrained generic constant None } } diff --git a/tests/ui/dropck/explicit-drop-bounds.bad1.stderr b/tests/ui/dropck/explicit-drop-bounds.bad1.stderr index 12d7f5b6cd30..28d7546d0c9b 100644 --- a/tests/ui/dropck/explicit-drop-bounds.bad1.stderr +++ b/tests/ui/dropck/explicit-drop-bounds.bad1.stderr @@ -14,6 +14,22 @@ help: consider further restricting type parameter `T` with trait `Copy` LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` | ++++++++++++++++++++ +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/explicit-drop-bounds.rs:32:5 + | +LL | fn drop(&mut self) {} + | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +note: required by a bound in `DropMe` + --> $DIR/explicit-drop-bounds.rs:7:18 + | +LL | struct DropMe(T); + | ^^^^ required by this bound in `DropMe` +help: consider further restricting type parameter `T` with trait `Copy` + | +LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` + | ++++++++++++++++++++ + error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/explicit-drop-bounds.rs:32:18 | @@ -30,6 +46,6 @@ help: consider further restricting type parameter `T` with trait `Copy` LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` | ++++++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dropck/explicit-drop-bounds.bad2.stderr b/tests/ui/dropck/explicit-drop-bounds.bad2.stderr index 5851731e8346..c363676edea3 100644 --- a/tests/ui/dropck/explicit-drop-bounds.bad2.stderr +++ b/tests/ui/dropck/explicit-drop-bounds.bad2.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/explicit-drop-bounds.rs:37:18 + --> $DIR/explicit-drop-bounds.rs:38:18 | LL | impl Drop for DropMe | ^^^^^^^^^ the trait `Copy` is not implemented for `T` @@ -15,7 +15,23 @@ LL | impl Drop for DropMe | +++++++++++++++++++ error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/explicit-drop-bounds.rs:40:18 + --> $DIR/explicit-drop-bounds.rs:41:5 + | +LL | fn drop(&mut self) {} + | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +note: required by a bound in `DropMe` + --> $DIR/explicit-drop-bounds.rs:7:18 + | +LL | struct DropMe(T); + | ^^^^ required by this bound in `DropMe` +help: consider restricting type parameter `T` with trait `Copy` + | +LL | impl Drop for DropMe + | +++++++++++++++++++ + +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/explicit-drop-bounds.rs:41:18 | LL | fn drop(&mut self) {} | ^^^^ the trait `Copy` is not implemented for `T` @@ -30,6 +46,6 @@ help: consider restricting type parameter `T` with trait `Copy` LL | impl Drop for DropMe | +++++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dropck/explicit-drop-bounds.rs b/tests/ui/dropck/explicit-drop-bounds.rs index 6ddac4d314f4..cd1d89ed9dbd 100644 --- a/tests/ui/dropck/explicit-drop-bounds.rs +++ b/tests/ui/dropck/explicit-drop-bounds.rs @@ -31,6 +31,7 @@ where { fn drop(&mut self) {} //[bad1]~^ ERROR the trait bound `T: Copy` is not satisfied + //[bad1]~| ERROR the trait bound `T: Copy` is not satisfied } #[cfg(bad2)] @@ -39,6 +40,7 @@ impl Drop for DropMe { fn drop(&mut self) {} //[bad2]~^ ERROR the trait bound `T: Copy` is not satisfied + //[bad2]~| ERROR the trait bound `T: Copy` is not satisfied } fn main() {} diff --git a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs index 6380449124ff..3b297a9a6629 100644 --- a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs +++ b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs @@ -30,7 +30,8 @@ where type Output = B; extern "rust-call" fn call_once(mut self, a: A) -> Self::Output { //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument - self.call_mut(a) + //~| ERROR type parameter to bare `FnOnce` trait must be a tuple + self.call_mut(a) //~^ ERROR `A` is not a tuple } } @@ -43,6 +44,7 @@ where { extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output { //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument + //~| ERROR type parameter to bare `FnOnce` trait must be a tuple self.cache.get(&a).map(|a| a.clone()).unwrap_or_else(|| { let b = (self.fun)(self, a.clone()); self.cache.insert(a, b.clone()); diff --git a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr index 3b051ef9a882..32a564e466be 100644 --- a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr +++ b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr @@ -11,8 +11,21 @@ help: consider further restricting type parameter `A` with unstable trait `Tuple LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ +error[E0059]: type parameter to bare `FnOnce` trait must be a tuple + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:31:5 + | +LL | extern "rust-call" fn call_once(mut self, a: A) -> Self::Output { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` + | +note: required by a bound in `FnOnce` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL +help: consider further restricting type parameter `A` with unstable trait `Tuple` + | +LL | A: Eq + Hash + Clone + std::marker::Tuple, + | ++++++++++++++++++++ + error[E0059]: type parameter to bare `FnMut` trait must be a tuple - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:38:12 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:39:12 | LL | impl FnMut for CachedFun | ^^^^^^^^ the trait `Tuple` is not implemented for `A` @@ -24,6 +37,19 @@ help: consider further restricting type parameter `A` with unstable trait `Tuple LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ +error[E0059]: type parameter to bare `FnOnce` trait must be a tuple + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:45:5 + | +LL | extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` + | +note: required by a bound in `FnOnce` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL +help: consider further restricting type parameter `A` with unstable trait `Tuple` + | +LL | A: Eq + Hash + Clone + std::marker::Tuple, + | ++++++++++++++++++++ + error[E0277]: functions with the "rust-call" ABI must take a single non-self tuple argument --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:31:5 | @@ -36,7 +62,7 @@ LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ error[E0277]: functions with the "rust-call" ABI must take a single non-self tuple argument - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:44:5 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:45:5 | LL | extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` @@ -47,12 +73,12 @@ LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ error[E0277]: `A` is not a tuple - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:33:23 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:34:19 | -LL | self.call_mut(a) - | -------- ^ the trait `Tuple` is not implemented for `A` - | | - | required by a bound introduced by this call +LL | self.call_mut(a) + | -------- ^ the trait `Tuple` is not implemented for `A` + | | + | required by a bound introduced by this call | note: required by a bound in `call_mut` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -62,7 +88,7 @@ LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ error[E0277]: `i32` is not a tuple - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:57:26 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:59:26 | LL | cachedcoso.call_once(1); | --------- ^ the trait `Tuple` is not implemented for `i32` @@ -76,7 +102,7 @@ help: use a unary tuple instead LL | cachedcoso.call_once((1,)); | + ++ -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors Some errors have detailed explanations: E0059, E0277. For more information about an error, try `rustc --explain E0059`. From 06fa0452eb62f675a4e43bde3a4754195e942e23 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 11 Jan 2025 00:43:04 +0100 Subject: [PATCH 084/245] Rename lint into `confusing_method_to_numeric_cast` --- CHANGELOG.md | 2 +- ...rs => confusing_method_to_numeric_cast.rs} | 51 ++++++--- clippy_lints/src/casts/mod.rs | 8 +- clippy_lints/src/declared_lints.rs | 2 +- .../ui/confusing_method_to_numeric_cast.fixed | 14 +++ tests/ui/confusing_method_to_numeric_cast.rs | 14 +++ .../confusing_method_to_numeric_cast.stderr | 100 ++++++++++++++++++ .../ui/primitive_method_to_numeric_cast.fixed | 5 - tests/ui/primitive_method_to_numeric_cast.rs | 5 - .../primitive_method_to_numeric_cast.stderr | 15 --- 10 files changed, 172 insertions(+), 44 deletions(-) rename clippy_lints/src/casts/{primitive_method_to_numeric_cast.rs => confusing_method_to_numeric_cast.rs} (52%) create mode 100644 tests/ui/confusing_method_to_numeric_cast.fixed create mode 100644 tests/ui/confusing_method_to_numeric_cast.rs create mode 100644 tests/ui/confusing_method_to_numeric_cast.stderr delete mode 100644 tests/ui/primitive_method_to_numeric_cast.fixed delete mode 100644 tests/ui/primitive_method_to_numeric_cast.rs delete mode 100644 tests/ui/primitive_method_to_numeric_cast.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ca2e73897903..6ab4cffb0cd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5594,6 +5594,7 @@ Released 2018-09-13 [`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty +[`confusing_method_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#confusing_method_to_numeric_cast [`const_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_is_empty [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator @@ -6068,7 +6069,6 @@ Released 2018-09-13 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`precedence_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence_bits -[`primitive_method_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#primitive_method_to_numeric_cast [`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal [`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr diff --git a/clippy_lints/src/casts/primitive_method_to_numeric_cast.rs b/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs similarity index 52% rename from clippy_lints/src/casts/primitive_method_to_numeric_cast.rs rename to clippy_lints/src/casts/confusing_method_to_numeric_cast.rs index 380fa98cd15f..31cdd078f45a 100644 --- a/clippy_lints/src/casts/primitive_method_to_numeric_cast.rs +++ b/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::match_def_path; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, GenericArg, Ty}; +use rustc_span::def_id::DefId; +use rustc_span::{Symbol, sym}; -use super::PRIMITIVE_METHOD_TO_NUMERIC_CAST; +use super::CONFUSING_METHOD_TO_NUMERIC_CAST; fn get_primitive_ty_name(ty: Ty<'_>) -> Option<&'static str> { match ty.kind() { @@ -18,6 +19,37 @@ fn get_primitive_ty_name(ty: Ty<'_>) -> Option<&'static str> { } } +fn get_const_name_and_ty_name( + cx: &LateContext<'_>, + method_name: Symbol, + method_def_id: DefId, + generics: &[GenericArg<'_>], +) -> Option<(&'static str, &'static str)> { + let method_name = method_name.as_str(); + let diagnostic_name = cx.tcx.get_diagnostic_name(method_def_id); + + let ty_name = if diagnostic_name.is_some_and(|diag| diag == sym::cmp_ord_min || diag == sym::cmp_ord_max) { + // We get the type on which the `min`/`max` method of the `Ord` trait is implemented. + if let [ty] = generics + && let Some(ty) = ty.as_type() + { + get_primitive_ty_name(ty)? + } else { + return None; + } + } else if let Some(impl_id) = cx.tcx.impl_of_method(method_def_id) + && let Some(ty_name) = get_primitive_ty_name(cx.tcx.type_of(impl_id).instantiate_identity()) + && ["min", "max", "minimum", "maximum", "min_value", "max_value"].contains(&method_name) + { + ty_name + } else { + return None; + }; + + let const_name = if method_name.starts_with("max") { "MAX" } else { "MIN" }; + Some((const_name, ty_name)) +} + pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { // We allow casts from any function type to any function type. match cast_to.kind() { @@ -27,28 +59,21 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, if let ty::FnDef(def_id, generics) = cast_from.kind() && let Some(method_name) = cx.tcx.opt_item_name(*def_id) - && let method_name = method_name.as_str() - && (method_name == "min" || method_name == "max") - // We get the type on which the `min`/`max` method of the `Ord` trait is implemented. - && let [ty] = generics.as_slice() - && let Some(ty) = ty.as_type() - // We get its name in case it's a primitive with an associated MIN/MAX constant. - && let Some(ty_name) = get_primitive_ty_name(ty) - && match_def_path(cx, *def_id, &["core", "cmp", "Ord", method_name]) + && let Some((const_name, ty_name)) = get_const_name_and_ty_name(cx, method_name, *def_id, generics.as_slice()) { let mut applicability = Applicability::MaybeIncorrect; let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); span_lint_and_then( cx, - PRIMITIVE_METHOD_TO_NUMERIC_CAST, + CONFUSING_METHOD_TO_NUMERIC_CAST, expr.span, format!("casting function pointer `{from_snippet}` to `{cast_to}`"), |diag| { diag.span_suggestion_verbose( expr.span, "did you mean to use the associated constant?", - format!("{ty_name}::{} as {cast_to}", method_name.to_ascii_uppercase()), + format!("{ty_name}::{const_name} as {cast_to}"), applicability, ); }, diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 7c8fa0b4d921..6eac7ad438bd 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -14,11 +14,11 @@ mod cast_sign_loss; mod cast_slice_different_sizes; mod cast_slice_from_raw_parts; mod char_lit_as_u8; +mod confusing_method_to_numeric_cast; mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; mod manual_dangling_ptr; -mod primitive_method_to_numeric_cast; mod ptr_as_ptr; mod ptr_cast_constness; mod ref_as_ptr; @@ -808,7 +808,7 @@ declare_clippy_lint! { /// let _ = u16::MAX as usize; /// ``` #[clippy::version = "1.86.0"] - pub PRIMITIVE_METHOD_TO_NUMERIC_CAST, + pub CONFUSING_METHOD_TO_NUMERIC_CAST, suspicious, "casting a primitive method pointer to any integer type" } @@ -850,7 +850,7 @@ impl_lint_pass!(Casts => [ REF_AS_PTR, AS_POINTER_UNDERSCORE, MANUAL_DANGLING_PTR, - PRIMITIVE_METHOD_TO_NUMERIC_CAST, + CONFUSING_METHOD_TO_NUMERIC_CAST, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -875,7 +875,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); - primitive_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); + confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 1d4c75dafa31..472d0f732ac0 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -64,11 +64,11 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO, crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO, crate::casts::CHAR_LIT_AS_U8_INFO, + crate::casts::CONFUSING_METHOD_TO_NUMERIC_CAST_INFO, crate::casts::FN_TO_NUMERIC_CAST_INFO, crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, crate::casts::MANUAL_DANGLING_PTR_INFO, - crate::casts::PRIMITIVE_METHOD_TO_NUMERIC_CAST_INFO, crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::REF_AS_PTR_INFO, diff --git a/tests/ui/confusing_method_to_numeric_cast.fixed b/tests/ui/confusing_method_to_numeric_cast.fixed new file mode 100644 index 000000000000..e698b99edd5c --- /dev/null +++ b/tests/ui/confusing_method_to_numeric_cast.fixed @@ -0,0 +1,14 @@ +#![feature(float_minimum_maximum)] +#![warn(clippy::confusing_method_to_numeric_cast)] + +fn main() { + let _ = u16::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MIN as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MIN as usize; //~ confusing_method_to_numeric_cast + + let _ = f32::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MIN as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MIN as usize; //~ confusing_method_to_numeric_cast +} diff --git a/tests/ui/confusing_method_to_numeric_cast.rs b/tests/ui/confusing_method_to_numeric_cast.rs new file mode 100644 index 000000000000..ef65c21563d9 --- /dev/null +++ b/tests/ui/confusing_method_to_numeric_cast.rs @@ -0,0 +1,14 @@ +#![feature(float_minimum_maximum)] +#![warn(clippy::confusing_method_to_numeric_cast)] + +fn main() { + let _ = u16::max as usize; //~ confusing_method_to_numeric_cast + let _ = u16::min as usize; //~ confusing_method_to_numeric_cast + let _ = u16::max_value as usize; //~ confusing_method_to_numeric_cast + let _ = u16::min_value as usize; //~ confusing_method_to_numeric_cast + + let _ = f32::maximum as usize; //~ confusing_method_to_numeric_cast + let _ = f32::max as usize; //~ confusing_method_to_numeric_cast + let _ = f32::minimum as usize; //~ confusing_method_to_numeric_cast + let _ = f32::min as usize; //~ confusing_method_to_numeric_cast +} diff --git a/tests/ui/confusing_method_to_numeric_cast.stderr b/tests/ui/confusing_method_to_numeric_cast.stderr new file mode 100644 index 000000000000..ba90df2059af --- /dev/null +++ b/tests/ui/confusing_method_to_numeric_cast.stderr @@ -0,0 +1,100 @@ +error: casting function pointer `u16::max` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:5:13 + | +LL | let _ = u16::max as usize; + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::confusing-method-to-numeric-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::confusing_method_to_numeric_cast)]` +help: did you mean to use the associated constant? + | +LL - let _ = u16::max as usize; +LL + let _ = u16::MAX as usize; + | + +error: casting function pointer `u16::min` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:6:13 + | +LL | let _ = u16::min as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::min as usize; +LL + let _ = u16::MIN as usize; + | + +error: casting function pointer `u16::max_value` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:7:13 + | +LL | let _ = u16::max_value as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::max_value as usize; +LL + let _ = u16::MAX as usize; + | + +error: casting function pointer `u16::min_value` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:8:13 + | +LL | let _ = u16::min_value as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::min_value as usize; +LL + let _ = u16::MIN as usize; + | + +error: casting function pointer `f32::maximum` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:10:13 + | +LL | let _ = f32::maximum as usize; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::maximum as usize; +LL + let _ = f32::MAX as usize; + | + +error: casting function pointer `f32::max` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:11:13 + | +LL | let _ = f32::max as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::max as usize; +LL + let _ = f32::MAX as usize; + | + +error: casting function pointer `f32::minimum` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:12:13 + | +LL | let _ = f32::minimum as usize; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::minimum as usize; +LL + let _ = f32::MIN as usize; + | + +error: casting function pointer `f32::min` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:13:13 + | +LL | let _ = f32::min as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::min as usize; +LL + let _ = f32::MIN as usize; + | + +error: aborting due to 8 previous errors + diff --git a/tests/ui/primitive_method_to_numeric_cast.fixed b/tests/ui/primitive_method_to_numeric_cast.fixed deleted file mode 100644 index 1ebb52618f13..000000000000 --- a/tests/ui/primitive_method_to_numeric_cast.fixed +++ /dev/null @@ -1,5 +0,0 @@ -#![warn(clippy::primitive_method_to_numeric_cast)] - -fn main() { - let _ = u16::MAX as usize; //~ primitive_method_to_numeric_cast -} diff --git a/tests/ui/primitive_method_to_numeric_cast.rs b/tests/ui/primitive_method_to_numeric_cast.rs deleted file mode 100644 index 89b0bfa84fee..000000000000 --- a/tests/ui/primitive_method_to_numeric_cast.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![warn(clippy::primitive_method_to_numeric_cast)] - -fn main() { - let _ = u16::max as usize; //~ primitive_method_to_numeric_cast -} diff --git a/tests/ui/primitive_method_to_numeric_cast.stderr b/tests/ui/primitive_method_to_numeric_cast.stderr deleted file mode 100644 index 5515dc646ce7..000000000000 --- a/tests/ui/primitive_method_to_numeric_cast.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: casting function pointer `u16::max` to `usize` - --> tests/ui/primitive_method_to_numeric_cast.rs:4:13 - | -LL | let _ = u16::max as usize; - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::primitive-method-to-numeric-cast` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::primitive_method_to_numeric_cast)]` -help: did you mean to use the associated constant? - | -LL | let _ = u16::MAX as usize; - | ~~~~~~~~~~~~~~~~~ - -error: aborting due to 1 previous error - From a94abae9118f004365a2c6b4ec2ae4b0f1ac43ce Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 7 May 2025 21:42:21 +0200 Subject: [PATCH 085/245] Clarify docs of `CONFUSING_METHOD_TO_NUMERIC_CAST` --- clippy_lints/src/casts/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 6eac7ad438bd..16a1552c5eea 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -789,7 +789,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for casts of a primitive method pointer to any integer type. + /// Checks for casts of a primitive method pointer like `max`/`min` to any integer type. /// /// ### Why restrict this? /// Casting a function pointer to an integer can have surprising results and can occur From d17eeb5d8307455d02d23a514c600343747a2c34 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 8 May 2025 00:01:45 +0200 Subject: [PATCH 086/245] =?UTF-8?q?triagebot:=20`canonicalize-issue-links`?= =?UTF-8?q?=20=E2=86=92=20`issue-links`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The feature has been renamed in triagebot (https://forge.rust-lang.org/triagebot/canonicalize-issue-links.html). --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index b2a2a85f61a0..fe558e0ef8f3 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -13,7 +13,7 @@ allow-unauthenticated = [ [note] -[canonicalize-issue-links] +[issue-links] # Prevents mentions in commits to avoid users being spammed [no-mentions] From b6c2a429efd85cbfa5f592c66faac6955729c6ea Mon Sep 17 00:00:00 2001 From: xizheyin Date: Thu, 8 May 2025 18:09:29 +0800 Subject: [PATCH 087/245] std: Make consistence between `From` and `Into` Signed-off-by: xizheyin --- library/core/src/convert/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index e1b10e1074d2..ef184e1ceb4f 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -464,8 +464,8 @@ pub trait Into: Sized { /// orphaning rules. /// See [`Into`] for more details. /// -/// Prefer using [`Into`] over using `From` when specifying trait bounds on a generic function. -/// This way, types that directly implement [`Into`] can be used as arguments as well. +/// Prefer using [`Into`] over [`From`] when specifying trait bounds on a generic function +/// to ensure that types that only implement [`Into`] can be used as well. /// /// The `From` trait is also very useful when performing error handling. When constructing a function /// that is capable of failing, the return type will generally be of the form `Result`. From 4101d90818b7e5574d5366e24c995980fa2de866 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Thu, 8 May 2025 18:12:05 +0800 Subject: [PATCH 088/245] std: Explain prefer `TryInto` over `TryFrom` when specifying traits bounds on generic function Signed-off-by: xizheyin --- library/core/src/convert/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index ef184e1ceb4f..c542a28beb87 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -597,6 +597,9 @@ pub trait From: Sized { /// standard library. For more information on this, see the /// documentation for [`Into`]. /// +/// Prefer using [`TryInto`] over [`TryFrom`] when specifying trait bounds on a generic function +/// to ensure that types that only implement [`TryInto`] can be used as well. +/// /// # Implementing `TryInto` /// /// This suffers the same restrictions and reasoning as implementing @@ -636,6 +639,9 @@ pub trait TryInto: Sized { /// When the [`!`] type is stabilized [`Infallible`] and [`!`] will be /// equivalent. /// +/// Prefer using [`TryInto`] over [`TryFrom`] when specifying trait bounds on a generic function +/// to ensure that types that only implement [`TryInto`] can be used as well. +/// /// `TryFrom` can be implemented as follows: /// /// ``` From 1fbfbb59e9409022ae3b6ddb497ee7b4ca6d725e Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Mon, 21 Apr 2025 21:29:26 +0500 Subject: [PATCH 089/245] Fix integer_division false negative for NonZero denominators --- clippy_lints/src/operators/integer_division.rs | 5 ++++- tests/ui/integer_division.rs | 8 ++++++++ tests/ui/integer_division.stderr | 16 ++++++++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/operators/integer_division.rs b/clippy_lints/src/operators/integer_division.rs index 76eba7327cff..7b98afa9b40b 100644 --- a/clippy_lints/src/operators/integer_division.rs +++ b/clippy_lints/src/operators/integer_division.rs @@ -1,6 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::symbol::sym; use super::INTEGER_DIVISION; @@ -13,7 +15,8 @@ pub(crate) fn check<'tcx>( ) { if op == hir::BinOpKind::Div && cx.typeck_results().expr_ty(left).is_integral() - && cx.typeck_results().expr_ty(right).is_integral() + && let right_ty = cx.typeck_results().expr_ty(right) + && (right_ty.is_integral() || is_type_diagnostic_item(cx, right_ty, sym::NonZero)) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| { diff --git a/tests/ui/integer_division.rs b/tests/ui/integer_division.rs index 632fedc9e8fe..a40956e2672e 100644 --- a/tests/ui/integer_division.rs +++ b/tests/ui/integer_division.rs @@ -1,5 +1,9 @@ #![warn(clippy::integer_division)] +use std::num::NonZeroU32; + +const TWO: NonZeroU32 = NonZeroU32::new(2).unwrap(); + fn main() { let two = 2; let n = 1 / 2; @@ -12,4 +16,8 @@ fn main() { //~^ integer_division let x = 1. / 2.0; + + let a = 1; + let s = a / TWO; + //~^ integer_division } diff --git a/tests/ui/integer_division.stderr b/tests/ui/integer_division.stderr index 0fe2021a1a9c..c0e34a562f65 100644 --- a/tests/ui/integer_division.stderr +++ b/tests/ui/integer_division.stderr @@ -1,5 +1,5 @@ error: integer division - --> tests/ui/integer_division.rs:5:13 + --> tests/ui/integer_division.rs:9:13 | LL | let n = 1 / 2; | ^^^^^ @@ -9,7 +9,7 @@ LL | let n = 1 / 2; = help: to override `-D warnings` add `#[allow(clippy::integer_division)]` error: integer division - --> tests/ui/integer_division.rs:8:13 + --> tests/ui/integer_division.rs:12:13 | LL | let o = 1 / two; | ^^^^^^^ @@ -17,12 +17,20 @@ LL | let o = 1 / two; = help: division of integers may cause loss of precision. consider using floats error: integer division - --> tests/ui/integer_division.rs:11:13 + --> tests/ui/integer_division.rs:15:13 | LL | let p = two / 4; | ^^^^^^^ | = help: division of integers may cause loss of precision. consider using floats -error: aborting due to 3 previous errors +error: integer division + --> tests/ui/integer_division.rs:21:13 + | +LL | let s = a / TWO; + | ^^^^^^^ + | + = help: division of integers may cause loss of precision. consider using floats + +error: aborting due to 4 previous errors From c70dc4554c1866584ad637863750600d6864826d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 8 May 2025 16:13:41 +0200 Subject: [PATCH 090/245] remove stray stderr file --- .../fail/weak_memory/racing_mixed_size.stderr | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr diff --git a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr deleted file mode 100644 index a437ca342585..000000000000 --- a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: Undefined Behavior: Race condition detected between (1) 4-byte atomic store on thread `unnamed-ID` and (2) 2-byte atomic load on thread `unnamed-ID` at ALLOC. (2) just happened here - --> tests/fail/weak_memory/racing_mixed_size.rs:LL:CC - | -LL | std::intrinsics::atomic_load_relaxed(hi); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Race condition detected between (1) 4-byte atomic store on thread `unnamed-ID` and (2) 2-byte atomic load on thread `unnamed-ID` at ALLOC. (2) just happened here - | -help: and (1) occurred earlier here - --> tests/fail/weak_memory/racing_mixed_size.rs:LL:CC - | -LL | x.store(1, Relaxed); - | ^^^^^^^^^^^^^^^^^^^ - = help: overlapping unsynchronized atomic accesses must use the same access size - = help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE (of the first span) on thread `unnamed-ID`: - = note: inside closure at tests/fail/weak_memory/racing_mixed_size.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 8930c41034176644eaede7ecb65f2c31111b9b2e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 8 May 2025 16:14:13 +0200 Subject: [PATCH 091/245] remove commented-out test leftover --- src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs index c2b6a7e68be5..c76e7f2eebd2 100644 --- a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs +++ b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs @@ -12,7 +12,6 @@ fn main() { mut_raw_mut(); partially_invalidate_mut(); drop_after_sharing(); - // direct_mut_to_const_raw(); two_raw(); shr_and_raw(); disjoint_mutable_subborrows(); From 31ebe11f61dc56018c3919dae140d9e89b2238a5 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 6 May 2025 18:39:07 +0000 Subject: [PATCH 092/245] normalization: avoid incompletely constraining GAT args --- .../src/solve/assembly/mod.rs | 23 +++++------ .../src/solve/effect_goals.rs | 5 ++- .../src/solve/normalizes_to/mod.rs | 38 +++++++++++++++++-- .../src/solve/trait_goals.rs | 7 ++-- .../src/traits/select/mod.rs | 7 ++-- compiler/rustc_type_ir/src/inherent.rs | 8 ++++ compiler/rustc_type_ir/src/predicate.rs | 7 ++++ .../guide-inference-in-gat-arg-deeper.rs | 6 ++- .../no-incomplete-gat-arg-inference.rs | 33 ++++++++++++++++ 9 files changed, 111 insertions(+), 23 deletions(-) create mode 100644 tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs 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 345a272895d3..579be70fccf1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -117,6 +117,8 @@ where ) -> Result, NoSolution> { Self::fast_reject_assumption(ecx, goal, assumption)?; + // Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily + // check whether the candidate is global. ecx.probe(|candidate: &Result, NoSolution>| match candidate { Ok(candidate) => inspect::ProbeKind::TraitCandidate { source: candidate.source, @@ -128,12 +130,12 @@ where }, }) .enter(|ecx| { - Self::match_assumption(ecx, goal, assumption)?; - let source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?; - Ok(Candidate { - source, - result: ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)?, - }) + let mut source = CandidateSource::ParamEnv(ParamEnvSource::Global); + let result = Self::match_assumption(ecx, goal, assumption, |ecx| { + source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + })?; + Ok(Candidate { source, result }) }) } @@ -150,10 +152,8 @@ where ) -> Result, NoSolution> { Self::fast_reject_assumption(ecx, goal, assumption)?; - ecx.probe_trait_candidate(source).enter(|ecx| { - Self::match_assumption(ecx, goal, assumption)?; - then(ecx) - }) + ecx.probe_trait_candidate(source) + .enter(|ecx| Self::match_assumption(ecx, goal, assumption, then)) } /// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`] @@ -169,7 +169,8 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - ) -> Result<(), NoSolution>; + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult; fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 84a83d79cf04..8413c2abbb96 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -61,13 +61,14 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - ) -> Result<(), NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult { let host_clause = assumption.as_host_effect_clause().unwrap(); let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause); ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?; - Ok(()) + then(ecx) } /// Register additional assumptions for aliases corresponding to `~const` item bounds. diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index b90e34e78101..2fddc0044cb9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -129,7 +129,40 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - ) -> Result<(), NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult { + let cx = ecx.cx(); + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. + // + // If this type is a GAT with currently unconstrained arguments, we do not + // want to normalize it via a candidate which only applies for a specific + // instantiation. We could otherwise keep the GAT as rigid and succeed this way. + // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. + // + // This only avoids normalization if the GAT arguments are fully unconstrained. + // This is quite arbitrary but fixing it causes some ambiguity, see #125196. + match goal.predicate.alias.kind(cx) { + ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { + for arg in goal.predicate.alias.own_args(cx).iter() { + let Some(term) = arg.as_term() else { + continue; + }; + let term = ecx.structurally_normalize_term(goal.param_env, term)?; + if term.is_infer() { + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + } + } + ty::AliasTermKind::OpaqueTy + | ty::AliasTermKind::InherentTy + | ty::AliasTermKind::InherentConst + | ty::AliasTermKind::FreeTy + | ty::AliasTermKind::FreeConst + | ty::AliasTermKind::UnevaluatedConst => {} + } + let projection_pred = assumption.as_projection_clause().unwrap(); let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); @@ -139,7 +172,6 @@ where // Add GAT where clauses from the trait's definition // FIXME: We don't need these, since these are the type's own WF obligations. - let cx = ecx.cx(); ecx.add_goals( GoalSource::AliasWellFormed, cx.own_predicates_of(goal.predicate.def_id()) @@ -147,7 +179,7 @@ where .map(|pred| goal.with(cx, pred)), ); - Ok(()) + then(ecx) } fn consider_additional_alias_assumptions( 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 e3addf8bf93f..966d5422fbb7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -17,7 +17,7 @@ use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidates use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, - NoSolution, ParamEnvSource, + NoSolution, ParamEnvSource, QueryResult, }; impl assembly::GoalKind for TraitPredicate @@ -150,13 +150,14 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, assumption: I::Clause, - ) -> Result<(), NoSolution> { + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult { let trait_clause = assumption.as_trait_clause().unwrap(); let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?; - Ok(()) + then(ecx) } fn consider_auto_trait_candidate( diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4ce37db42800..77c24adabe3a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1760,12 +1760,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if is_match { let generics = self.tcx().generics_of(obligation.predicate.def_id); - // FIXME(generic-associated-types): Addresses aggressive inference in #92917. + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. // If this type is a GAT, and of the GAT args resolve to something new, // that means that we must have newly inferred something about the GAT. // We should give up in that case. - // FIXME(generic-associated-types): This only detects one layer of inference, - // which is probably not what we actually want, but fixing it causes some ambiguity: + // + // This only detects one layer of inference, which is probably not what we actually + // want, but fixing it causes some ambiguity: // . if !generics.is_own_empty() && obligation.predicate.args[generics.parent_count..].iter().any(|&p| { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index e6e6466766be..ee4a8096462a 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -298,6 +298,14 @@ pub trait GenericArg>: + From + From { + fn as_term(&self) -> Option { + match self.kind() { + ty::GenericArgKind::Lifetime(_) => None, + ty::GenericArgKind::Type(ty) => Some(ty.into()), + ty::GenericArgKind::Const(ct) => Some(ct.into()), + } + } + fn as_type(&self) -> Option { if let ty::GenericArgKind::Type(ty) = self.kind() { Some(ty) } else { None } } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index b59495b93c83..f02d9c988c8d 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -682,6 +682,13 @@ impl AliasTerm { pub fn trait_ref(self, interner: I) -> TraitRef { self.trait_ref_and_own_args(interner).0 } + + /// Extract the own args from this projection. + /// For example, if this is a projection of `::Item<'a>`, + /// then this function would return the slice `['a]` as the own args. + pub fn own_args(self, interner: I) -> I::GenericArgsSlice { + self.trait_ref_and_own_args(interner).1 + } } /// The following methods work only with inherent associated term projections. diff --git a/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs b/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs index 96a0f2f40bf4..82ffa0221b99 100644 --- a/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs +++ b/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs @@ -1,5 +1,9 @@ -// Fix for . //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Fix for . trait Tr { type Gat; diff --git a/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs b/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs new file mode 100644 index 000000000000..0c25c64224b1 --- /dev/null +++ b/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs @@ -0,0 +1,33 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#202. We have +// to make sure we don't constrain ambiguous GAT args when normalizing +// via where bounds or item bounds. + +trait Trait { + type Assoc; +} + +fn ret(x: U) -> ::Assoc { + loop {} +} + +fn where_bound = u32>>() { + let inf = Default::default(); + let x = ret::(inf); + let _: i32 = inf; +} + +trait ItemBound { + type Bound: Trait = u32>; +} +fn item_bound() { + let inf = Default::default(); + let x = ret::(inf); + let _: i32 = inf; +} + +fn main() {} From 01854845bcf4aea41ddb5da889708ca53e025537 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 8 May 2025 14:49:52 +0000 Subject: [PATCH 093/245] refactor `probe_and_consider_param_env_candidate` --- .../src/solve/assembly/mod.rs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) 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 579be70fccf1..542e212e1bfb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -2,6 +2,7 @@ pub(super) mod structural_traits; +use std::cell::Cell; use std::ops::ControlFlow; use derive_where::derive_where; @@ -118,25 +119,23 @@ where Self::fast_reject_assumption(ecx, goal, assumption)?; // Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily - // check whether the candidate is global. - ecx.probe(|candidate: &Result, NoSolution>| match candidate { - Ok(candidate) => inspect::ProbeKind::TraitCandidate { - source: candidate.source, - result: Ok(candidate.result), - }, - Err(NoSolution) => inspect::ProbeKind::TraitCandidate { - source: CandidateSource::ParamEnv(ParamEnvSource::Global), - result: Err(NoSolution), - }, + // check whether the candidate is global while considering normalization. + // + // We need to write into `source` inside of `match_assumption`, but need to access it + // in `probe` even if the candidate does not apply before we get there. We handle this + // by using a `Cell` here. We only ever write into it inside of `match_assumption`. + let source = Cell::new(CandidateSource::ParamEnv(ParamEnvSource::Global)); + ecx.probe(|result: &QueryResult| inspect::ProbeKind::TraitCandidate { + source: source.get(), + result: *result, }) .enter(|ecx| { - let mut source = CandidateSource::ParamEnv(ParamEnvSource::Global); - let result = Self::match_assumption(ecx, goal, assumption, |ecx| { - source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?; + Self::match_assumption(ecx, goal, assumption, |ecx| { + source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - })?; - Ok(Candidate { source, result }) + }) }) + .map(|result| Candidate { source: source.get(), result }) } /// Try equating an assumption predicate against a goal's predicate. If it From 070fee2df3edadbf580bf4fc8dcbc0bafe20ac97 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 7 May 2025 01:52:09 +0200 Subject: [PATCH 094/245] Update miri, cargo-miri and miri-cript to edition 2024 Also update the format edition to 2024 --- src/tools/miri/Cargo.toml | 2 +- src/tools/miri/cargo-miri/Cargo.toml | 2 +- src/tools/miri/miri-script/Cargo.toml | 2 +- src/tools/miri/miri-script/src/util.rs | 2 +- src/tools/miri/src/bin/miri.rs | 2 +- .../miri/src/borrow_tracker/tree_borrows/diagnostics.rs | 2 +- src/tools/miri/src/diagnostics.rs | 3 +-- src/tools/miri/tests/pass/coroutine.rs | 8 ++++---- 8 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 7b7be97aa518..e4d7abdb0f73 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -6,7 +6,7 @@ name = "miri" repository = "https://github.com/rust-lang/miri" version = "0.1.0" default-run = "miri" -edition = "2021" +edition = "2024" [lib] test = true # we have unit tests diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index ed142b0e2114..23048914af18 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -5,7 +5,7 @@ license = "MIT OR Apache-2.0" name = "cargo-miri" repository = "https://github.com/rust-lang/miri" version = "0.1.0" -edition = "2021" +edition = "2024" [[bin]] name = "cargo-miri" diff --git a/src/tools/miri/miri-script/Cargo.toml b/src/tools/miri/miri-script/Cargo.toml index a04898de6aba..462a9cc62bff 100644 --- a/src/tools/miri/miri-script/Cargo.toml +++ b/src/tools/miri/miri-script/Cargo.toml @@ -6,7 +6,7 @@ name = "miri-script" repository = "https://github.com/rust-lang/miri" version = "0.1.0" default-run = "miri-script" -edition = "2021" +edition = "2024" [workspace] # We make this a workspace root so that cargo does not go looking in ../Cargo.toml for the workspace root. diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs index c039b4827ee3..5c2a055990fd 100644 --- a/src/tools/miri/miri-script/src/util.rs +++ b/src/tools/miri/miri-script/src/util.rs @@ -213,7 +213,7 @@ impl MiriEnv { let toolchain = &self.toolchain; let mut cmd = cmd!( self.sh, - "rustfmt +{toolchain} --edition=2021 --config-path {config_path} --unstable-features --skip-children {flags...}" + "rustfmt +{toolchain} --edition=2024 --config-path {config_path} --unstable-features --skip-children {flags...}" ); if first { // Log an abbreviating command, and only once. diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 69aa035fdc3d..eb5111774a00 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -459,7 +459,7 @@ fn jemalloc_magic() { // linking, so we need to explicitly depend on the function. #[cfg(target_os = "macos")] { - extern "C" { + unsafe extern "C" { fn _rjem_je_zone_register(); } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index b2fd9b2bf054..f5a0013047aa 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -179,7 +179,7 @@ impl NodeDebugInfo { /// Add a name to the tag. If a same tag is associated to several pointers, /// it can have several names which will be separated by commas. pub fn add_name(&mut self, name: &str) { - if let Some(ref mut prev_name) = &mut self.name { + if let Some(prev_name) = &mut self.name { prev_name.push_str(", "); prev_name.push_str(name); } else { diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 89768077d878..10570a37e5d8 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -648,8 +648,7 @@ impl<'tcx> MiriMachine<'tcx> { AccessedAlloc(AllocId(id), access_kind) => format!("{access_kind} to allocation with id {id}"), FreedAlloc(AllocId(id)) => format!("freed allocation with id {id}"), - RejectedIsolatedOp(ref op) => - format!("{op} was made to return an error due to isolation"), + RejectedIsolatedOp(op) => format!("{op} was made to return an error due to isolation"), ProgressReport { .. } => format!("progress report: current operation being executed is here"), Int2Ptr { .. } => format!("integer-to-pointer cast"), diff --git a/src/tools/miri/tests/pass/coroutine.rs b/src/tools/miri/tests/pass/coroutine.rs index 9ec9b1fc5bc4..96b60b515cb6 100644 --- a/src/tools/miri/tests/pass/coroutine.rs +++ b/src/tools/miri/tests/pass/coroutine.rs @@ -183,18 +183,18 @@ fn basic() { fn smoke_resume_arg() { fn drain + Unpin, R, Y>( - gen: &mut G, + gen_: &mut G, inout: Vec<(R, CoroutineState)>, ) where Y: Debug + PartialEq, G::Return: Debug + PartialEq, { - let mut gen = Pin::new(gen); + let mut gen_ = Pin::new(gen_); for (input, out) in inout { - assert_eq!(gen.as_mut().resume(input), out); + assert_eq!(gen_.as_mut().resume(input), out); // Test if the coroutine is valid (according to type invariants). - let _ = unsafe { ManuallyDrop::new(ptr::read(gen.as_mut().get_unchecked_mut())) }; + let _ = unsafe { ManuallyDrop::new(ptr::read(gen_.as_mut().get_unchecked_mut())) }; } } From 292ecd52429c337156601449fc67daccdd82ae9f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 8 May 2025 16:18:40 +0000 Subject: [PATCH 095/245] Remove some unnecessary erases --- compiler/rustc_hir_typeck/src/cast.rs | 4 ---- compiler/rustc_middle/src/ty/print/pretty.rs | 2 -- compiler/rustc_mir_transform/src/add_subtyping_projections.rs | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 5bfc3e810d9f..c044c4f7c37d 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -490,11 +490,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { && let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::From) { let ty = fcx.resolve_vars_if_possible(self.cast_ty); - // Erase regions to avoid panic in `prove_value` when calling - // `type_implements_trait`. - let ty = fcx.tcx.erase_regions(ty); let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); - let expr_ty = fcx.tcx.erase_regions(expr_ty); if fcx .infcx .type_implements_trait(from_trait, [ty, expr_ty], fcx.param_env) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 0250c777faf7..6fd6aff0e2b2 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1453,9 +1453,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // contain named regions. So we erase and anonymize everything // here to compare the types modulo regions below. let proj = cx.tcx().erase_regions(proj); - let proj = cx.tcx().anonymize_bound_vars(proj); let super_proj = cx.tcx().erase_regions(super_proj); - let super_proj = cx.tcx().anonymize_bound_vars(super_proj); proj == super_proj }); diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index b5cd41334598..92ee80eaa353 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -32,7 +32,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for SubTypeChecker<'a, 'tcx> { let mut rval_ty = rvalue.ty(self.local_decls, self.tcx); // Not erasing this causes `Free Regions` errors in validator, // when rval is `ReStatic`. - rval_ty = self.tcx.erase_regions_ty(rval_ty); + rval_ty = self.tcx.erase_regions(rval_ty); place_ty = self.tcx.erase_regions(place_ty); if place_ty != rval_ty { let temp = self From a1234d3b5edd156a77f2747587b838c740004e2a Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 8 May 2025 19:08:31 +0200 Subject: [PATCH 096/245] Remove let_chains feature as it is stable --- src/tools/miri/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 58b93ae82a1e..0b7a067058b9 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -9,7 +9,6 @@ #![feature(variant_count)] #![feature(yeet_expr)] #![feature(nonzero_ops)] -#![feature(let_chains)] #![feature(strict_overflow_ops)] #![feature(pointer_is_aligned_to)] #![feature(unqualified_local_imports)] From b73d3b4384e65ab3d0d7af0e2d2f6c07ef1620aa Mon Sep 17 00:00:00 2001 From: relaxcn Date: Fri, 9 May 2025 01:21:14 +0800 Subject: [PATCH 097/245] fix issue-14725 --- clippy_lints/src/unwrap.rs | 6 ++- .../ui/checked_unwrap/simple_conditionals.rs | 52 +++++++++++++++++++ .../checked_unwrap/simple_conditionals.stderr | 37 ++++++++++++- 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index ba140788bb54..f152d62bf495 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -224,8 +224,10 @@ impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { } } - fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) { - self.is_mutated = true; + fn mutate(&mut self, cat: &PlaceWithHirId<'tcx>, _: HirId) { + if is_potentially_local_place(self.local_id, &cat.place) { + self.is_mutated = true; + } } fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 4101897d3800..5589d8cc429c 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -188,6 +188,58 @@ fn issue11371() { } } +fn gen_option() -> Option<()> { + Some(()) + // Or None +} + +fn gen_result() -> Result<(), ()> { + Ok(()) + // Or Err(()) +} + +fn issue14725() { + let option = Some(()); + + if option.is_some() { + let _ = option.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + let _ = option.as_ref().unwrap(); + //~^ panicking_unwrap + } + + let result = Ok::<(), ()>(()); + + if result.is_ok() { + let _y = 1; + result.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + let _y = 1; + result.as_ref().unwrap(); + //~^ panicking_unwrap + } + + let mut option = Some(()); + if option.is_some() { + option = gen_option(); + option.as_mut().unwrap(); + } else { + option = gen_option(); + option.as_mut().unwrap(); + } + + let mut result = Ok::<(), ()>(()); + if result.is_ok() { + result = gen_result(); + result.as_mut().unwrap(); + } else { + result = gen_result(); + result.as_mut().unwrap(); + } +} + fn check_expect() { let x = Some(()); if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index ad3c420270c1..82a36aa5029f 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -236,6 +236,41 @@ LL | if result.is_ok() { LL | result.as_mut().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ +error: called `unwrap` on `option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:205:17 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some() = &option` +LL | let _ = option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:208:17 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | let _ = option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:216:9 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok() = &result` +LL | let _y = 1; +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:220:9 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + error: creating a shared reference to mutable static --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12 | @@ -246,5 +281,5 @@ LL | if X.is_some() { = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: `#[deny(static_mut_refs)]` on by default -error: aborting due to 26 previous errors +error: aborting due to 30 previous errors From 668a292ca33b340590631c426553265fd19339b5 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 8 May 2025 20:42:32 +0200 Subject: [PATCH 098/245] `unwrap.rs`: replace some strings usage by interned symbols --- clippy_lints/src/unwrap.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index ba140788bb54..85330c20d38e 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -4,15 +4,15 @@ use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{higher, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; -use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::declare_lint_pass; -use rustc_span::Span; use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -111,7 +111,7 @@ struct UnwrapInfo<'tcx> { /// The check, like `x.is_ok()` check: &'tcx Expr<'tcx>, /// The check's name, like `is_ok` - check_name: &'tcx PathSegment<'tcx>, + check_name: Symbol, /// The branch where the check takes place, like `if x.is_ok() { .. }` branch: &'tcx Expr<'tcx>, /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`). @@ -133,12 +133,12 @@ fn collect_unwrap_info<'tcx>( invert: bool, is_entire_condition: bool, ) -> Vec> { - fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym::Option) && ["is_some", "is_none"].contains(&method_name) + fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { + is_type_diagnostic_item(cx, ty, sym::Option) && matches!(method_name, sym::is_none | sym::is_some) } - fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym::Result) && ["is_ok", "is_err"].contains(&method_name) + fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { + is_type_diagnostic_item(cx, ty, sym::Result) && matches!(method_name, sym::is_err | sym::is_ok) } if let ExprKind::Binary(op, left, right) = &expr.kind { @@ -155,14 +155,10 @@ fn collect_unwrap_info<'tcx>( } else if let ExprKind::MethodCall(method_name, receiver, [], _) = &expr.kind && let Some(local_id) = path_to_local(receiver) && let ty = cx.typeck_results().expr_ty(receiver) - && let name = method_name.ident.as_str() + && let name = method_name.ident.name && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) { - let unwrappable = match name { - "is_some" | "is_ok" => true, - "is_err" | "is_none" => false, - _ => unreachable!(), - }; + let unwrappable = matches!(name, sym::is_some | sym::is_ok); let safe_to_unwrap = unwrappable != invert; let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { UnwrappableKind::Option @@ -174,7 +170,7 @@ fn collect_unwrap_info<'tcx>( local_id, if_expr, check: expr, - check_name: method_name, + check_name: name, branch, safe_to_unwrap, kind, @@ -332,8 +328,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { expr.span, format!( "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", - method_name.ident.name, - unwrappable.check_name.ident.as_str(), + method_name.ident.name, unwrappable.check_name, ), |diag| { if is_entire_condition { From 4cbd116460c795f77956f5a98891551a34ff06a6 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 8 May 2025 20:50:51 +0200 Subject: [PATCH 099/245] `unwrap.rs`: internal naming cleanup Since its conception, the lint has been extended from `Option` only to `Option` and `Result`, but some function names and comment still references only `Option`. Also, one cascaded `if` was replaced by a shorter `match`. --- clippy_lints/src/unwrap.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 85330c20d38e..44ca28957734 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -180,12 +180,12 @@ fn collect_unwrap_info<'tcx>( Vec::new() } -/// A HIR visitor delegate that checks if a local variable of type `Option<_>` is mutated, -/// *except* for if `Option::as_mut` is called. +/// A HIR visitor delegate that checks if a local variable of type `Option` or `Result` is mutated, +/// *except* for if `.as_mut()` is called. /// The reason for why we allow that one specifically is that `.as_mut()` cannot change -/// the option to `None`, and that is important because this lint relies on the fact that +/// the variant, and that is important because this lint relies on the fact that /// `is_some` + `unwrap` is equivalent to `if let Some(..) = ..`, which it would not be if -/// the option is changed to None between `is_some` and `unwrap`. +/// the option is changed to None between `is_some` and `unwrap`, ditto for `Result`. /// (And also `.as_mut()` is a somewhat common method that is still worth linting on.) struct MutationVisitor<'tcx> { is_mutated: bool, @@ -194,13 +194,13 @@ struct MutationVisitor<'tcx> { } /// Checks if the parent of the expression pointed at by the given `HirId` is a call to -/// `Option::as_mut`. +/// `.as_mut()`. /// /// Used by the mutation visitor to specifically allow `.as_mut()` calls. /// In particular, the `HirId` that the visitor receives is the id of the local expression /// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent /// expression: that will be where the actual method call is. -fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { +fn is_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id) && let ExprKind::MethodCall(path, _, [], _) = mutating_expr.kind { @@ -214,7 +214,7 @@ impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { fn borrow(&mut self, cat: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { if let ty::BorrowKind::Mutable = bk && is_potentially_local_place(self.local_id, &cat.place) - && !is_option_as_mut_use(self.tcx, diag_expr_id) + && !is_as_mut_use(self.tcx, diag_expr_id) { self.is_mutated = true; } @@ -272,12 +272,10 @@ enum AsRefKind { /// If it isn't, the expression itself is returned. fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option) { if let ExprKind::MethodCall(path, recv, [], _) = expr.kind { - if path.ident.name == sym::as_ref { - (recv, Some(AsRefKind::AsRef)) - } else if path.ident.name == sym::as_mut { - (recv, Some(AsRefKind::AsMut)) - } else { - (expr, None) + match path.ident.name { + sym::as_ref => (recv, Some(AsRefKind::AsRef)), + sym::as_mut => (recv, Some(AsRefKind::AsMut)), + _ => (expr, None), } } else { (expr, None) From 1044e5bbcc73fb581ec1801e66ad8deb9aebf237 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 8 May 2025 21:39:35 +0200 Subject: [PATCH 100/245] Set Manish in vacation mode --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index fe558e0ef8f3..e3d4507202ab 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -42,6 +42,7 @@ new_pr = true contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", + "Manishearth", ] [assign.owners] From 3a6fcd2170aef2f8096f8521870d7a655690bf80 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 9 May 2025 01:06:00 +0200 Subject: [PATCH 101/245] Allow any rust-lang team member to close an issue --- triagebot.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index e3d4507202ab..eb2f9f9dd61f 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -13,6 +13,8 @@ allow-unauthenticated = [ [note] +[close] + [issue-links] # Prevents mentions in commits to avoid users being spammed From 33f81c08aaa5701e66cad9aa3d527a683c929adc Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 9 May 2025 01:36:46 +0000 Subject: [PATCH 102/245] Use `cargo dev setup toolchain` in install from source docs --- book/src/development/basics.md | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/book/src/development/basics.md b/book/src/development/basics.md index 4219724ed5df..cdbbe76bdb08 100644 --- a/book/src/development/basics.md +++ b/book/src/development/basics.md @@ -145,42 +145,32 @@ unclear to you. If you are hacking on Clippy and want to install it from source, do the following: -First, take note of the toolchain -[override](https://rust-lang.github.io/rustup/overrides.html) in -`/rust-toolchain.toml`. We will use this override to install Clippy into the right -toolchain. - -> Tip: You can view the active toolchain for the current directory with `rustup -> show active-toolchain`. - From the Clippy project root, run the following command to build the Clippy -binaries and copy them into the toolchain directory. This will override the -currently installed Clippy component. +binaries and copy them into the toolchain directory. This will create a new +toolchain called `clippy` by default, see `cargo dev setup toolchain --help` +for other options. ```terminal -cargo build --release --bin cargo-clippy --bin clippy-driver -Zunstable-options --out-dir "$(rustc --print=sysroot)/bin" +cargo dev setup toolcahin ``` -Now you may run `cargo clippy` in any project, using the toolchain where you -just installed Clippy. +Now you may run `cargo +clippy clippy` in any project using the new toolchain. ```terminal cd my-project -cargo +nightly-2021-07-01 clippy +cargo +clippy clippy ``` ...or `clippy-driver` ```terminal -clippy-driver +nightly-2021-07-01 +clippy-driver +clippy ``` -If you need to restore the default Clippy installation, run the following (from -the Clippy project root). +If you no longer need the toolchain it can be uninstalled using `rustup`: ```terminal -rustup component remove clippy -rustup component add clippy +rustup toolchain uninstall clippy ``` > **DO NOT** install using `cargo install --path . --force` since this will From 9eac7a3e04a727bcbdb14826c6520210b3ca1d2e Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 9 May 2025 02:35:18 +0000 Subject: [PATCH 103/245] move (and remove) impl Trait tests --- src/tools/tidy/src/issues.txt | 60 +++++++++---------- .../arg-position-impl-trait-too-long.rs | 0 .../arg-position-impl-trait-too-long.stderr | 0 .../{ => apit}/impl-generic-mismatch-ab.rs | 0 .../impl-generic-mismatch-ab.stderr | 0 .../can-return-unconstrained-closure.rs | 23 ------- .../impl-trait/{ => issues}/issue-100075-2.rs | 0 .../{ => issues}/issue-100075-2.stderr | 0 .../impl-trait/{ => issues}/issue-100075.rs | 0 .../{ => issues}/issue-100075.stderr | 0 .../impl-trait/{ => issues}/issue-100187.rs | 0 .../impl-trait/{ => issues}/issue-102605.rs | 0 .../{ => issues}/issue-102605.stderr | 0 .../issue-103181-1.current.stderr | 0 .../{ => issues}/issue-103181-1.next.stderr | 0 .../impl-trait/{ => issues}/issue-103181-1.rs | 0 .../impl-trait/{ => issues}/issue-103181-2.rs | 0 .../{ => issues}/issue-103181-2.stderr | 0 .../impl-trait/{ => issues}/issue-103599.rs | 0 .../{ => issues}/issue-103599.stderr | 0 .../impl-trait/{ => issues}/issue-108591.rs | 0 .../impl-trait/{ => issues}/issue-108592.rs | 0 .../ui/impl-trait/{ => issues}/issue-35668.rs | 0 .../{ => issues}/issue-35668.stderr | 0 .../ui/impl-trait/{ => issues}/issue-36792.rs | 0 .../ui/impl-trait/{ => issues}/issue-46959.rs | 0 .../ui/impl-trait/{ => issues}/issue-49556.rs | 0 .../ui/impl-trait/{ => issues}/issue-49579.rs | 0 .../ui/impl-trait/{ => issues}/issue-49685.rs | 0 .../ui/impl-trait/{ => issues}/issue-51185.rs | 0 .../ui/impl-trait/{ => issues}/issue-54966.rs | 0 .../{ => issues}/issue-54966.stderr | 0 .../impl-trait/{ => issues}/issue-55872-1.rs | 0 .../{ => issues}/issue-55872-1.stderr | 0 .../impl-trait/{ => issues}/issue-55872-2.rs | 0 .../{ => issues}/issue-55872-2.stderr | 0 .../impl-trait/{ => issues}/issue-55872-3.rs | 0 .../{ => issues}/issue-55872-3.stderr | 0 .../ui/impl-trait/{ => issues}/issue-55872.rs | 0 .../{ => issues}/issue-55872.stderr | 0 .../ui/impl-trait/{ => issues}/issue-56445.rs | 0 .../ui/impl-trait/{ => issues}/issue-68532.rs | 0 .../ui/impl-trait/{ => issues}/issue-72911.rs | 0 .../{ => issues}/issue-72911.stderr | 0 .../ui/impl-trait/{ => issues}/issue-87450.rs | 0 .../{ => issues}/issue-87450.stderr | 0 .../impl-trait/{ => issues}/issue-99073-2.rs | 0 .../{ => issues}/issue-99073-2.stderr | 0 .../ui/impl-trait/{ => issues}/issue-99073.rs | 0 .../{ => issues}/issue-99073.stderr | 0 .../impl-trait/{ => issues}/issue-99642-2.rs | 0 .../ui/impl-trait/{ => issues}/issue-99642.rs | 0 .../ui/impl-trait/{ => issues}/issue-99914.rs | 0 .../{ => issues}/issue-99914.stderr | 0 tests/ui/impl-trait/lifetimes2.rs | 10 ---- .../coherence}/coherence-treats-tait-ambig.rs | 0 .../coherence-treats-tait-ambig.stderr | 0 .../method-suggestion-no-duplication.rs | 0 .../method-suggestion-no-duplication.stderr | 0 59 files changed, 30 insertions(+), 63 deletions(-) rename tests/ui/impl-trait/{ => apit}/arg-position-impl-trait-too-long.rs (100%) rename tests/ui/impl-trait/{ => apit}/arg-position-impl-trait-too-long.stderr (100%) rename tests/ui/impl-trait/{ => apit}/impl-generic-mismatch-ab.rs (100%) rename tests/ui/impl-trait/{ => apit}/impl-generic-mismatch-ab.stderr (100%) delete mode 100644 tests/ui/impl-trait/can-return-unconstrained-closure.rs rename tests/ui/impl-trait/{ => issues}/issue-100075-2.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-100075-2.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-100075.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-100075.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-100187.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-102605.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-102605.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-103181-1.current.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-103181-1.next.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-103181-1.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-103181-2.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-103181-2.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-103599.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-103599.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-108591.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-108592.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-35668.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-35668.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-36792.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-46959.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-49556.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-49579.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-49685.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-51185.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-54966.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-54966.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-55872-1.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-55872-1.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-55872-2.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-55872-2.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-55872-3.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-55872-3.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-55872.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-55872.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-56445.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-68532.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-72911.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-72911.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-87450.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-87450.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-99073-2.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-99073-2.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-99073.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-99073.stderr (100%) rename tests/ui/impl-trait/{ => issues}/issue-99642-2.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-99642.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-99914.rs (100%) rename tests/ui/impl-trait/{ => issues}/issue-99914.stderr (100%) delete mode 100644 tests/ui/impl-trait/lifetimes2.rs rename tests/ui/{impl-trait => type-alias-impl-trait/coherence}/coherence-treats-tait-ambig.rs (100%) rename tests/ui/{impl-trait => type-alias-impl-trait/coherence}/coherence-treats-tait-ambig.stderr (100%) rename tests/ui/{impl-trait => where-clauses}/method-suggestion-no-duplication.rs (100%) rename tests/ui/{impl-trait => where-clauses}/method-suggestion-no-duplication.stderr (100%) diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 2f0158609e08..760826145304 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -1183,47 +1183,39 @@ ui/impl-trait/explicit-generic-args-with-impl-trait/issue-87718.rs ui/impl-trait/in-trait/issue-102140.rs ui/impl-trait/in-trait/issue-102301.rs ui/impl-trait/in-trait/issue-102571.rs -ui/impl-trait/issue-100075-2.rs -ui/impl-trait/issue-100075.rs -ui/impl-trait/issue-100187.rs -ui/impl-trait/issue-102605.rs -ui/impl-trait/issue-103181-1.rs -ui/impl-trait/issue-103181-2.rs -ui/impl-trait/issue-103599.rs -ui/impl-trait/issue-108591.rs -ui/impl-trait/issue-108592.rs -ui/impl-trait/issue-35668.rs -ui/impl-trait/issue-36792.rs -ui/impl-trait/issue-46959.rs -ui/impl-trait/issue-49556.rs -ui/impl-trait/issue-49579.rs -ui/impl-trait/issue-49685.rs -ui/impl-trait/issue-51185.rs -ui/impl-trait/issue-54966.rs -ui/impl-trait/issue-55872-1.rs -ui/impl-trait/issue-55872-2.rs -ui/impl-trait/issue-55872-3.rs -ui/impl-trait/issue-55872.rs -ui/impl-trait/issue-56445.rs -ui/impl-trait/issue-68532.rs -ui/impl-trait/issue-72911.rs -ui/impl-trait/issue-87450.rs -ui/impl-trait/issue-99073-2.rs -ui/impl-trait/issue-99073.rs -ui/impl-trait/issue-99642-2.rs -ui/impl-trait/issue-99642.rs -ui/impl-trait/issue-99914.rs +ui/impl-trait/issues/issue-100075-2.rs +ui/impl-trait/issues/issue-100075.rs +ui/impl-trait/issues/issue-100187.rs +ui/impl-trait/issues/issue-102605.rs +ui/impl-trait/issues/issue-103181-1.rs +ui/impl-trait/issues/issue-103181-2.rs +ui/impl-trait/issues/issue-103599.rs ui/impl-trait/issues/issue-104815.rs ui/impl-trait/issues/issue-105826.rs +ui/impl-trait/issues/issue-108591.rs +ui/impl-trait/issues/issue-108592.rs ui/impl-trait/issues/issue-21659-show-relevant-trait-impls-3.rs +ui/impl-trait/issues/issue-35668.rs +ui/impl-trait/issues/issue-36792.rs ui/impl-trait/issues/issue-42479.rs +ui/impl-trait/issues/issue-46959.rs ui/impl-trait/issues/issue-49376.rs +ui/impl-trait/issues/issue-49556.rs +ui/impl-trait/issues/issue-49579.rs +ui/impl-trait/issues/issue-49685.rs +ui/impl-trait/issues/issue-51185.rs ui/impl-trait/issues/issue-52128.rs ui/impl-trait/issues/issue-53457.rs ui/impl-trait/issues/issue-54600.rs ui/impl-trait/issues/issue-54840.rs ui/impl-trait/issues/issue-54895.rs +ui/impl-trait/issues/issue-54966.rs ui/impl-trait/issues/issue-55608-captures-empty-region.rs +ui/impl-trait/issues/issue-55872-1.rs +ui/impl-trait/issues/issue-55872-2.rs +ui/impl-trait/issues/issue-55872-3.rs +ui/impl-trait/issues/issue-55872.rs +ui/impl-trait/issues/issue-56445.rs ui/impl-trait/issues/issue-57464-unexpected-regions.rs ui/impl-trait/issues/issue-57979-deeply-nested-impl-trait-in-assoc-proj.rs ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs @@ -1233,8 +1225,10 @@ ui/impl-trait/issues/issue-58956.rs ui/impl-trait/issues/issue-62742.rs ui/impl-trait/issues/issue-65581.rs ui/impl-trait/issues/issue-67830.rs +ui/impl-trait/issues/issue-68532.rs ui/impl-trait/issues/issue-70877.rs ui/impl-trait/issues/issue-70971.rs +ui/impl-trait/issues/issue-72911.rs ui/impl-trait/issues/issue-74282.rs ui/impl-trait/issues/issue-77987.rs ui/impl-trait/issues/issue-78722-2.rs @@ -1251,12 +1245,18 @@ ui/impl-trait/issues/issue-86719.rs ui/impl-trait/issues/issue-86800.rs ui/impl-trait/issues/issue-87295.rs ui/impl-trait/issues/issue-87340.rs +ui/impl-trait/issues/issue-87450.rs ui/impl-trait/issues/issue-88236-2.rs ui/impl-trait/issues/issue-88236.rs ui/impl-trait/issues/issue-89312.rs ui/impl-trait/issues/issue-92305.rs ui/impl-trait/issues/issue-93788.rs +ui/impl-trait/issues/issue-99073-2.rs +ui/impl-trait/issues/issue-99073.rs ui/impl-trait/issues/issue-99348-impl-compatibility.rs +ui/impl-trait/issues/issue-99642-2.rs +ui/impl-trait/issues/issue-99642.rs +ui/impl-trait/issues/issue-99914.rs ui/implied-bounds/issue-100690.rs ui/implied-bounds/issue-101951.rs ui/implied-bounds/issue-110161.rs diff --git a/tests/ui/impl-trait/arg-position-impl-trait-too-long.rs b/tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.rs similarity index 100% rename from tests/ui/impl-trait/arg-position-impl-trait-too-long.rs rename to tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.rs diff --git a/tests/ui/impl-trait/arg-position-impl-trait-too-long.stderr b/tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.stderr similarity index 100% rename from tests/ui/impl-trait/arg-position-impl-trait-too-long.stderr rename to tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.stderr diff --git a/tests/ui/impl-trait/impl-generic-mismatch-ab.rs b/tests/ui/impl-trait/apit/impl-generic-mismatch-ab.rs similarity index 100% rename from tests/ui/impl-trait/impl-generic-mismatch-ab.rs rename to tests/ui/impl-trait/apit/impl-generic-mismatch-ab.rs diff --git a/tests/ui/impl-trait/impl-generic-mismatch-ab.stderr b/tests/ui/impl-trait/apit/impl-generic-mismatch-ab.stderr similarity index 100% rename from tests/ui/impl-trait/impl-generic-mismatch-ab.stderr rename to tests/ui/impl-trait/apit/impl-generic-mismatch-ab.stderr diff --git a/tests/ui/impl-trait/can-return-unconstrained-closure.rs b/tests/ui/impl-trait/can-return-unconstrained-closure.rs deleted file mode 100644 index 1f8bdbc5054d..000000000000 --- a/tests/ui/impl-trait/can-return-unconstrained-closure.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Test that we are special casing "outlives" for opaque types. -// -// The return type of a closure is not required to outlive the closure. As such -// the following code would not compile if we used a standard outlives check -// when checking the return type, because the return type of the closure would -// be `&ReEmpty i32`, and we don't allow `ReEmpty` to occur in the concrete -// type used for an opaque type. -// -// However, opaque types are special cased to include check all regions in the -// concrete type against the bound, which forces the return type to be -// `&'static i32` here. - -//@ build-pass (FIXME(62277): could be check-pass?) - -fn make_identity() -> impl Sized { - |x: &'static i32| x -} - -fn make_identity_static() -> impl Sized + 'static { - |x: &'static i32| x -} - -fn main() {} diff --git a/tests/ui/impl-trait/issue-100075-2.rs b/tests/ui/impl-trait/issues/issue-100075-2.rs similarity index 100% rename from tests/ui/impl-trait/issue-100075-2.rs rename to tests/ui/impl-trait/issues/issue-100075-2.rs diff --git a/tests/ui/impl-trait/issue-100075-2.stderr b/tests/ui/impl-trait/issues/issue-100075-2.stderr similarity index 100% rename from tests/ui/impl-trait/issue-100075-2.stderr rename to tests/ui/impl-trait/issues/issue-100075-2.stderr diff --git a/tests/ui/impl-trait/issue-100075.rs b/tests/ui/impl-trait/issues/issue-100075.rs similarity index 100% rename from tests/ui/impl-trait/issue-100075.rs rename to tests/ui/impl-trait/issues/issue-100075.rs diff --git a/tests/ui/impl-trait/issue-100075.stderr b/tests/ui/impl-trait/issues/issue-100075.stderr similarity index 100% rename from tests/ui/impl-trait/issue-100075.stderr rename to tests/ui/impl-trait/issues/issue-100075.stderr diff --git a/tests/ui/impl-trait/issue-100187.rs b/tests/ui/impl-trait/issues/issue-100187.rs similarity index 100% rename from tests/ui/impl-trait/issue-100187.rs rename to tests/ui/impl-trait/issues/issue-100187.rs diff --git a/tests/ui/impl-trait/issue-102605.rs b/tests/ui/impl-trait/issues/issue-102605.rs similarity index 100% rename from tests/ui/impl-trait/issue-102605.rs rename to tests/ui/impl-trait/issues/issue-102605.rs diff --git a/tests/ui/impl-trait/issue-102605.stderr b/tests/ui/impl-trait/issues/issue-102605.stderr similarity index 100% rename from tests/ui/impl-trait/issue-102605.stderr rename to tests/ui/impl-trait/issues/issue-102605.stderr diff --git a/tests/ui/impl-trait/issue-103181-1.current.stderr b/tests/ui/impl-trait/issues/issue-103181-1.current.stderr similarity index 100% rename from tests/ui/impl-trait/issue-103181-1.current.stderr rename to tests/ui/impl-trait/issues/issue-103181-1.current.stderr diff --git a/tests/ui/impl-trait/issue-103181-1.next.stderr b/tests/ui/impl-trait/issues/issue-103181-1.next.stderr similarity index 100% rename from tests/ui/impl-trait/issue-103181-1.next.stderr rename to tests/ui/impl-trait/issues/issue-103181-1.next.stderr diff --git a/tests/ui/impl-trait/issue-103181-1.rs b/tests/ui/impl-trait/issues/issue-103181-1.rs similarity index 100% rename from tests/ui/impl-trait/issue-103181-1.rs rename to tests/ui/impl-trait/issues/issue-103181-1.rs diff --git a/tests/ui/impl-trait/issue-103181-2.rs b/tests/ui/impl-trait/issues/issue-103181-2.rs similarity index 100% rename from tests/ui/impl-trait/issue-103181-2.rs rename to tests/ui/impl-trait/issues/issue-103181-2.rs diff --git a/tests/ui/impl-trait/issue-103181-2.stderr b/tests/ui/impl-trait/issues/issue-103181-2.stderr similarity index 100% rename from tests/ui/impl-trait/issue-103181-2.stderr rename to tests/ui/impl-trait/issues/issue-103181-2.stderr diff --git a/tests/ui/impl-trait/issue-103599.rs b/tests/ui/impl-trait/issues/issue-103599.rs similarity index 100% rename from tests/ui/impl-trait/issue-103599.rs rename to tests/ui/impl-trait/issues/issue-103599.rs diff --git a/tests/ui/impl-trait/issue-103599.stderr b/tests/ui/impl-trait/issues/issue-103599.stderr similarity index 100% rename from tests/ui/impl-trait/issue-103599.stderr rename to tests/ui/impl-trait/issues/issue-103599.stderr diff --git a/tests/ui/impl-trait/issue-108591.rs b/tests/ui/impl-trait/issues/issue-108591.rs similarity index 100% rename from tests/ui/impl-trait/issue-108591.rs rename to tests/ui/impl-trait/issues/issue-108591.rs diff --git a/tests/ui/impl-trait/issue-108592.rs b/tests/ui/impl-trait/issues/issue-108592.rs similarity index 100% rename from tests/ui/impl-trait/issue-108592.rs rename to tests/ui/impl-trait/issues/issue-108592.rs diff --git a/tests/ui/impl-trait/issue-35668.rs b/tests/ui/impl-trait/issues/issue-35668.rs similarity index 100% rename from tests/ui/impl-trait/issue-35668.rs rename to tests/ui/impl-trait/issues/issue-35668.rs diff --git a/tests/ui/impl-trait/issue-35668.stderr b/tests/ui/impl-trait/issues/issue-35668.stderr similarity index 100% rename from tests/ui/impl-trait/issue-35668.stderr rename to tests/ui/impl-trait/issues/issue-35668.stderr diff --git a/tests/ui/impl-trait/issue-36792.rs b/tests/ui/impl-trait/issues/issue-36792.rs similarity index 100% rename from tests/ui/impl-trait/issue-36792.rs rename to tests/ui/impl-trait/issues/issue-36792.rs diff --git a/tests/ui/impl-trait/issue-46959.rs b/tests/ui/impl-trait/issues/issue-46959.rs similarity index 100% rename from tests/ui/impl-trait/issue-46959.rs rename to tests/ui/impl-trait/issues/issue-46959.rs diff --git a/tests/ui/impl-trait/issue-49556.rs b/tests/ui/impl-trait/issues/issue-49556.rs similarity index 100% rename from tests/ui/impl-trait/issue-49556.rs rename to tests/ui/impl-trait/issues/issue-49556.rs diff --git a/tests/ui/impl-trait/issue-49579.rs b/tests/ui/impl-trait/issues/issue-49579.rs similarity index 100% rename from tests/ui/impl-trait/issue-49579.rs rename to tests/ui/impl-trait/issues/issue-49579.rs diff --git a/tests/ui/impl-trait/issue-49685.rs b/tests/ui/impl-trait/issues/issue-49685.rs similarity index 100% rename from tests/ui/impl-trait/issue-49685.rs rename to tests/ui/impl-trait/issues/issue-49685.rs diff --git a/tests/ui/impl-trait/issue-51185.rs b/tests/ui/impl-trait/issues/issue-51185.rs similarity index 100% rename from tests/ui/impl-trait/issue-51185.rs rename to tests/ui/impl-trait/issues/issue-51185.rs diff --git a/tests/ui/impl-trait/issue-54966.rs b/tests/ui/impl-trait/issues/issue-54966.rs similarity index 100% rename from tests/ui/impl-trait/issue-54966.rs rename to tests/ui/impl-trait/issues/issue-54966.rs diff --git a/tests/ui/impl-trait/issue-54966.stderr b/tests/ui/impl-trait/issues/issue-54966.stderr similarity index 100% rename from tests/ui/impl-trait/issue-54966.stderr rename to tests/ui/impl-trait/issues/issue-54966.stderr diff --git a/tests/ui/impl-trait/issue-55872-1.rs b/tests/ui/impl-trait/issues/issue-55872-1.rs similarity index 100% rename from tests/ui/impl-trait/issue-55872-1.rs rename to tests/ui/impl-trait/issues/issue-55872-1.rs diff --git a/tests/ui/impl-trait/issue-55872-1.stderr b/tests/ui/impl-trait/issues/issue-55872-1.stderr similarity index 100% rename from tests/ui/impl-trait/issue-55872-1.stderr rename to tests/ui/impl-trait/issues/issue-55872-1.stderr diff --git a/tests/ui/impl-trait/issue-55872-2.rs b/tests/ui/impl-trait/issues/issue-55872-2.rs similarity index 100% rename from tests/ui/impl-trait/issue-55872-2.rs rename to tests/ui/impl-trait/issues/issue-55872-2.rs diff --git a/tests/ui/impl-trait/issue-55872-2.stderr b/tests/ui/impl-trait/issues/issue-55872-2.stderr similarity index 100% rename from tests/ui/impl-trait/issue-55872-2.stderr rename to tests/ui/impl-trait/issues/issue-55872-2.stderr diff --git a/tests/ui/impl-trait/issue-55872-3.rs b/tests/ui/impl-trait/issues/issue-55872-3.rs similarity index 100% rename from tests/ui/impl-trait/issue-55872-3.rs rename to tests/ui/impl-trait/issues/issue-55872-3.rs diff --git a/tests/ui/impl-trait/issue-55872-3.stderr b/tests/ui/impl-trait/issues/issue-55872-3.stderr similarity index 100% rename from tests/ui/impl-trait/issue-55872-3.stderr rename to tests/ui/impl-trait/issues/issue-55872-3.stderr diff --git a/tests/ui/impl-trait/issue-55872.rs b/tests/ui/impl-trait/issues/issue-55872.rs similarity index 100% rename from tests/ui/impl-trait/issue-55872.rs rename to tests/ui/impl-trait/issues/issue-55872.rs diff --git a/tests/ui/impl-trait/issue-55872.stderr b/tests/ui/impl-trait/issues/issue-55872.stderr similarity index 100% rename from tests/ui/impl-trait/issue-55872.stderr rename to tests/ui/impl-trait/issues/issue-55872.stderr diff --git a/tests/ui/impl-trait/issue-56445.rs b/tests/ui/impl-trait/issues/issue-56445.rs similarity index 100% rename from tests/ui/impl-trait/issue-56445.rs rename to tests/ui/impl-trait/issues/issue-56445.rs diff --git a/tests/ui/impl-trait/issue-68532.rs b/tests/ui/impl-trait/issues/issue-68532.rs similarity index 100% rename from tests/ui/impl-trait/issue-68532.rs rename to tests/ui/impl-trait/issues/issue-68532.rs diff --git a/tests/ui/impl-trait/issue-72911.rs b/tests/ui/impl-trait/issues/issue-72911.rs similarity index 100% rename from tests/ui/impl-trait/issue-72911.rs rename to tests/ui/impl-trait/issues/issue-72911.rs diff --git a/tests/ui/impl-trait/issue-72911.stderr b/tests/ui/impl-trait/issues/issue-72911.stderr similarity index 100% rename from tests/ui/impl-trait/issue-72911.stderr rename to tests/ui/impl-trait/issues/issue-72911.stderr diff --git a/tests/ui/impl-trait/issue-87450.rs b/tests/ui/impl-trait/issues/issue-87450.rs similarity index 100% rename from tests/ui/impl-trait/issue-87450.rs rename to tests/ui/impl-trait/issues/issue-87450.rs diff --git a/tests/ui/impl-trait/issue-87450.stderr b/tests/ui/impl-trait/issues/issue-87450.stderr similarity index 100% rename from tests/ui/impl-trait/issue-87450.stderr rename to tests/ui/impl-trait/issues/issue-87450.stderr diff --git a/tests/ui/impl-trait/issue-99073-2.rs b/tests/ui/impl-trait/issues/issue-99073-2.rs similarity index 100% rename from tests/ui/impl-trait/issue-99073-2.rs rename to tests/ui/impl-trait/issues/issue-99073-2.rs diff --git a/tests/ui/impl-trait/issue-99073-2.stderr b/tests/ui/impl-trait/issues/issue-99073-2.stderr similarity index 100% rename from tests/ui/impl-trait/issue-99073-2.stderr rename to tests/ui/impl-trait/issues/issue-99073-2.stderr diff --git a/tests/ui/impl-trait/issue-99073.rs b/tests/ui/impl-trait/issues/issue-99073.rs similarity index 100% rename from tests/ui/impl-trait/issue-99073.rs rename to tests/ui/impl-trait/issues/issue-99073.rs diff --git a/tests/ui/impl-trait/issue-99073.stderr b/tests/ui/impl-trait/issues/issue-99073.stderr similarity index 100% rename from tests/ui/impl-trait/issue-99073.stderr rename to tests/ui/impl-trait/issues/issue-99073.stderr diff --git a/tests/ui/impl-trait/issue-99642-2.rs b/tests/ui/impl-trait/issues/issue-99642-2.rs similarity index 100% rename from tests/ui/impl-trait/issue-99642-2.rs rename to tests/ui/impl-trait/issues/issue-99642-2.rs diff --git a/tests/ui/impl-trait/issue-99642.rs b/tests/ui/impl-trait/issues/issue-99642.rs similarity index 100% rename from tests/ui/impl-trait/issue-99642.rs rename to tests/ui/impl-trait/issues/issue-99642.rs diff --git a/tests/ui/impl-trait/issue-99914.rs b/tests/ui/impl-trait/issues/issue-99914.rs similarity index 100% rename from tests/ui/impl-trait/issue-99914.rs rename to tests/ui/impl-trait/issues/issue-99914.rs diff --git a/tests/ui/impl-trait/issue-99914.stderr b/tests/ui/impl-trait/issues/issue-99914.stderr similarity index 100% rename from tests/ui/impl-trait/issue-99914.stderr rename to tests/ui/impl-trait/issues/issue-99914.stderr diff --git a/tests/ui/impl-trait/lifetimes2.rs b/tests/ui/impl-trait/lifetimes2.rs deleted file mode 100644 index facf2f75bc4d..000000000000 --- a/tests/ui/impl-trait/lifetimes2.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ check-pass - -pub fn keys<'a>(x: &'a Result) -> impl std::fmt::Debug + 'a { - match x { - Ok(map) => Ok(map), - Err(map) => Err(map), - } -} - -fn main() {} diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.rs b/tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.rs similarity index 100% rename from tests/ui/impl-trait/coherence-treats-tait-ambig.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.rs diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.stderr similarity index 100% rename from tests/ui/impl-trait/coherence-treats-tait-ambig.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.stderr diff --git a/tests/ui/impl-trait/method-suggestion-no-duplication.rs b/tests/ui/where-clauses/method-suggestion-no-duplication.rs similarity index 100% rename from tests/ui/impl-trait/method-suggestion-no-duplication.rs rename to tests/ui/where-clauses/method-suggestion-no-duplication.rs diff --git a/tests/ui/impl-trait/method-suggestion-no-duplication.stderr b/tests/ui/where-clauses/method-suggestion-no-duplication.stderr similarity index 100% rename from tests/ui/impl-trait/method-suggestion-no-duplication.stderr rename to tests/ui/where-clauses/method-suggestion-no-duplication.stderr From 35bdd25e328554a466fd2d7286122208369e5eba Mon Sep 17 00:00:00 2001 From: bendn Date: Mon, 28 Apr 2025 23:03:35 +0700 Subject: [PATCH 104/245] remove superseded lints --- clippy_lints/src/declared_lints.rs | 4 - clippy_lints/src/deprecated_lints.rs | 8 + clippy_lints/src/transmute/mod.rs | 115 --------- .../src/transmute/transmute_float_to_int.rs | 66 ----- .../src/transmute/transmute_int_to_char.rs | 47 ---- .../src/transmute/transmute_int_to_float.rs | 50 ---- .../src/transmute/transmute_num_to_bytes.rs | 50 ---- tests/ui/rename.fixed | 5 + tests/ui/rename.rs | 5 + tests/ui/rename.stderr | 164 +++++++------ tests/ui/transmute.rs | 132 ---------- tests/ui/transmute.stderr | 230 +----------------- tests/ui/transmute_float_to_int.fixed | 60 ----- tests/ui/transmute_float_to_int.rs | 60 ----- tests/ui/transmute_float_to_int.stderr | 89 ------- tests/ui/transmute_int_to_char.fixed | 16 -- tests/ui/transmute_int_to_char.rs | 16 -- tests/ui/transmute_int_to_char.stderr | 17 -- tests/ui/transmute_int_to_char_no_std.fixed | 28 --- tests/ui/transmute_int_to_char_no_std.rs | 28 --- tests/ui/transmute_int_to_char_no_std.stderr | 17 -- 21 files changed, 116 insertions(+), 1091 deletions(-) delete mode 100644 clippy_lints/src/transmute/transmute_float_to_int.rs delete mode 100644 clippy_lints/src/transmute/transmute_int_to_char.rs delete mode 100644 clippy_lints/src/transmute/transmute_int_to_float.rs delete mode 100644 clippy_lints/src/transmute/transmute_num_to_bytes.rs delete mode 100644 tests/ui/transmute_float_to_int.fixed delete mode 100644 tests/ui/transmute_float_to_int.rs delete mode 100644 tests/ui/transmute_float_to_int.stderr delete mode 100644 tests/ui/transmute_int_to_char.fixed delete mode 100644 tests/ui/transmute_int_to_char.rs delete mode 100644 tests/ui/transmute_int_to_char.stderr delete mode 100644 tests/ui/transmute_int_to_char_no_std.fixed delete mode 100644 tests/ui/transmute_int_to_char_no_std.rs delete mode 100644 tests/ui/transmute_int_to_char_no_std.stderr diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2cccd6ba2702..e046ab0b42bb 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -705,13 +705,9 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS_INFO, crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, - crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO, crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, - crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO, - crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO, crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO, crate::transmute::TRANSMUTE_NULL_TO_FN_INFO, - crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO, crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index b60c11d79d48..a1909c5363ff 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -187,5 +187,13 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"), #[clippy::version = ""] ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_float", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_char", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_float_to_int", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), // end renamed lints. used by `cargo dev rename_lint` ]} diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index f2da8d35cb4f..d5112e2c3f97 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -1,13 +1,9 @@ mod crosspointer_transmute; mod eager_transmute; mod missing_transmute_annotations; -mod transmute_float_to_int; mod transmute_int_to_bool; -mod transmute_int_to_char; -mod transmute_int_to_float; mod transmute_int_to_non_zero; mod transmute_null_to_fn; -mod transmute_num_to_bytes; mod transmute_ptr_to_ptr; mod transmute_ptr_to_ref; mod transmute_ref_to_ref; @@ -141,40 +137,6 @@ declare_clippy_lint! { "transmutes from a pointer to a reference type" } -declare_clippy_lint! { - /// ### What it does - /// Checks for transmutes from an integer to a `char`. - /// - /// ### Why is this bad? - /// Not every integer is a Unicode scalar value. - /// - /// ### Known problems - /// - [`from_u32`] which this lint suggests using is slower than `transmute` - /// as it needs to validate the input. - /// If you are certain that the input is always a valid Unicode scalar value, - /// use [`from_u32_unchecked`] which is as fast as `transmute` - /// but has a semantically meaningful name. - /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`. - /// - /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html - /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html - /// - /// ### Example - /// ```no_run - /// let x = 1_u32; - /// unsafe { - /// let _: char = std::mem::transmute(x); // where x: u32 - /// } - /// - /// // should be: - /// let _ = std::char::from_u32(x).unwrap(); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub TRANSMUTE_INT_TO_CHAR, - complexity, - "transmutes from an integer to a `char`" -} - declare_clippy_lint! { /// ### What it does /// Checks for transmutes from a `&[u8]` to a `&str`. @@ -232,29 +194,6 @@ declare_clippy_lint! { "transmutes from an integer to a `bool`" } -declare_clippy_lint! { - /// ### What it does - /// Checks for transmutes from an integer to a float. - /// - /// ### Why is this bad? - /// Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive - /// and safe. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let _: f32 = std::mem::transmute(1_u32); // where x: u32 - /// } - /// - /// // should be: - /// let _: f32 = f32::from_bits(1_u32); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub TRANSMUTE_INT_TO_FLOAT, - complexity, - "transmutes from an integer to a float" -} - declare_clippy_lint! { /// ### What it does /// Checks for transmutes from `T` to `NonZero`, and suggests the `new_unchecked` @@ -280,52 +219,6 @@ declare_clippy_lint! { "transmutes from an integer to a non-zero wrapper" } -declare_clippy_lint! { - /// ### What it does - /// Checks for transmutes from a float to an integer. - /// - /// ### Why is this bad? - /// Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive - /// and safe. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let _: u32 = std::mem::transmute(1f32); - /// } - /// - /// // should be: - /// let _: u32 = 1f32.to_bits(); - /// ``` - #[clippy::version = "1.41.0"] - pub TRANSMUTE_FLOAT_TO_INT, - complexity, - "transmutes from a float to an integer" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for transmutes from a number to an array of `u8` - /// - /// ### Why this is bad? - /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes` - /// is intuitive and safe. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let x: [u8; 8] = std::mem::transmute(1i64); - /// } - /// - /// // should be - /// let x: [u8; 8] = 0i64.to_ne_bytes(); - /// ``` - #[clippy::version = "1.58.0"] - pub TRANSMUTE_NUM_TO_BYTES, - complexity, - "transmutes from a number to an array of `u8`" -} - declare_clippy_lint! { /// ### What it does /// Checks for transmutes from a pointer to a pointer, or @@ -581,13 +474,9 @@ impl_lint_pass!(Transmute => [ TRANSMUTE_PTR_TO_PTR, USELESS_TRANSMUTE, WRONG_TRANSMUTE, - TRANSMUTE_INT_TO_CHAR, TRANSMUTE_BYTES_TO_STR, TRANSMUTE_INT_TO_BOOL, - TRANSMUTE_INT_TO_FLOAT, TRANSMUTE_INT_TO_NON_ZERO, - TRANSMUTE_FLOAT_TO_INT, - TRANSMUTE_NUM_TO_BYTES, UNSOUND_COLLECTION_TRANSMUTE, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_UNDEFINED_REPR, @@ -632,14 +521,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | transmute_null_to_fn::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) | missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id) - | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) - | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) - | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) - | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) | (unsound_collection_transmute::check(cx, e, from_ty, to_ty) || transmute_undefined_repr::check(cx, e, from_ty, to_ty)) | (eager_transmute::check(cx, e, arg, from_ty, to_ty)); diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs deleted file mode 100644 index df2f681a1629..000000000000 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ /dev/null @@ -1,66 +0,0 @@ -use super::TRANSMUTE_FLOAT_TO_INT; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::sugg; -use rustc_ast as ast; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, UnOp}; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; - -/// Checks for `transmute_float_to_int` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - mut arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) - if !const_context || msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) => - { - span_lint_and_then( - cx, - TRANSMUTE_FLOAT_TO_INT, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let mut sugg = sugg::Sugg::hir(cx, arg, ".."); - - if let ExprKind::Unary(UnOp::Neg, inner_expr) = &arg.kind { - arg = inner_expr; - } - - if let ExprKind::Lit(lit) = &arg.kind - // if the expression is a float literal and it is unsuffixed then - // add a suffix so the suggestion is valid and unambiguous - && let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node - { - let op = format!("{sugg}{}", float_ty.name_str()).into(); - match sugg { - sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op), - _ => sugg = sugg::Sugg::NonParen(op), - } - } - - sugg = sugg::Sugg::NonParen(format!("{}.to_bits()", sugg.maybe_paren()).into()); - - // cast the result of `to_bits` if `to_ty` is signed - sugg = if let ty::Int(int_ty) = to_ty.kind() { - sugg.as_ty(int_ty.name_str().to_string()) - } else { - sugg - }; - - diag.span_suggestion(e.span, "consider using", sugg, Applicability::Unspecified); - }, - ); - true - }, - _ => false, - } -} diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs deleted file mode 100644 index 81d10a7d5bde..000000000000 --- a/clippy_lints/src/transmute/transmute_int_to_char.rs +++ /dev/null @@ -1,47 +0,0 @@ -use super::TRANSMUTE_INT_TO_CHAR; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{std_or_core, sugg}; -use rustc_ast as ast; -use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; - -/// Checks for `transmute_int_to_char` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - arg: &'tcx Expr<'_>, - const_context: bool, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) if !const_context => { - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_CHAR, - e.span, - format!("transmute from a `{from_ty}` to a `char`"), - |diag| { - let Some(top_crate) = std_or_core(cx) else { return }; - let arg = sugg::Sugg::hir(cx, arg, ".."); - let arg = if let ty::Int(_) = from_ty.kind() { - arg.as_ty(ast::UintTy::U32.name_str()) - } else { - arg - }; - diag.span_suggestion( - e.span, - "consider using", - format!("{top_crate}::char::from_u32({arg}).unwrap()"), - Applicability::Unspecified, - ); - }, - ); - true - }, - _ => false, - } -} diff --git a/clippy_lints/src/transmute/transmute_int_to_float.rs b/clippy_lints/src/transmute/transmute_int_to_float.rs deleted file mode 100644 index aaa95396b4b4..000000000000 --- a/clippy_lints/src/transmute/transmute_int_to_float.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::TRANSMUTE_INT_TO_FLOAT; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::sugg; -use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; - -/// Checks for `transmute_int_to_float` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context || msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) => { - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_FLOAT, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); - let arg = if let ty::Int(int_ty) = from_ty.kind() { - arg.as_ty(format!( - "u{}", - int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string()) - )) - } else { - arg - }; - diag.span_suggestion( - e.span, - "consider using", - format!("{to_ty}::from_bits({arg})"), - Applicability::Unspecified, - ); - }, - ); - true - }, - _ => false, - } -} diff --git a/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/clippy_lints/src/transmute/transmute_num_to_bytes.rs deleted file mode 100644 index d72be270b731..000000000000 --- a/clippy_lints/src/transmute/transmute_num_to_bytes.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::TRANSMUTE_NUM_TO_BYTES; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::sugg; -use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty, UintTy}; - -/// Checks for `transmute_int_to_float` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(_) | ty::Uint(_) | ty::Float(_), ty::Array(arr_ty, _)) => { - if !matches!(arr_ty.kind(), ty::Uint(UintTy::U8)) { - return false; - } - if matches!(from_ty.kind(), ty::Float(_)) && const_context && !msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) - { - return false; - } - - span_lint_and_then( - cx, - TRANSMUTE_NUM_TO_BYTES, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); - diag.span_suggestion( - e.span, - "consider using `to_ne_bytes()`", - format!("{arg}.to_ne_bytes()"), - Applicability::Unspecified, - ); - }, - ); - true - }, - _ => false, - } -} diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index acf7914d2536..55e287b91596 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -63,6 +63,7 @@ #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] #![allow(clippy::reversed_empty_ranges)] +#![allow(unnecessary_transmutes)] #![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_expr` @@ -132,5 +133,9 @@ #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` #![warn(ambiguous_wide_pointer_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` #![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` fn main() {} diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 32641a684a44..31dcd2cea081 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -63,6 +63,7 @@ #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] #![allow(clippy::reversed_empty_ranges)] +#![allow(unnecessary_transmutes)] #![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::block_in_if_condition_expr)] //~ ERROR: lint `clippy::block_in_if_condition_expr` @@ -132,5 +133,9 @@ #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` #![warn(clippy::vtable_address_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` #![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` +#![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(clippy::transmute_float_to_int)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` fn main() {} diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index e9d2debff91a..a8d5c96acc3a 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:66:9 + --> tests/ui/rename.rs:67:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,412 +8,436 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::fn_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::invalid_null_ptr_usage)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` error: lint `clippy::double_neg` has been renamed to `double_negations` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::double_neg)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:115:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:115:9 + --> tests/ui/rename.rs:116:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:116:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:118:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:118:9 + --> tests/ui/rename.rs:119:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:119:9 + --> tests/ui/rename.rs:120:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:120:9 + --> tests/ui/rename.rs:121:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:121:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:122:9 + --> tests/ui/rename.rs:123:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:123:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:124:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::maybe_misused_cfg)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:128:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:130:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:133:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:133:9 + --> tests/ui/rename.rs:134:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:134:9 + --> tests/ui/rename.rs:135:9 | LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` -error: aborting due to 69 previous errors +error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:136:9 + | +LL | #![warn(clippy::transmute_int_to_float)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:137:9 + | +LL | #![warn(clippy::transmute_int_to_char)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:138:9 + | +LL | #![warn(clippy::transmute_float_to_int)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:139:9 + | +LL | #![warn(clippy::transmute_num_to_bytes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: aborting due to 73 previous errors diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 2b8b6c539ad3..e968e7a59244 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -116,138 +116,6 @@ fn int_to_bool() { //~^ transmute_int_to_bool } -#[warn(clippy::transmute_int_to_float)] -mod int_to_float { - fn test() { - let _: f16 = unsafe { std::mem::transmute(0_u16) }; - //~^ transmute_int_to_float - - let _: f16 = unsafe { std::mem::transmute(0_i16) }; - //~^ transmute_int_to_float - - let _: f32 = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_float - - let _: f32 = unsafe { std::mem::transmute(0_i32) }; - //~^ transmute_int_to_float - - let _: f64 = unsafe { std::mem::transmute(0_u64) }; - //~^ transmute_int_to_float - - let _: f64 = unsafe { std::mem::transmute(0_i64) }; - //~^ transmute_int_to_float - - let _: f128 = unsafe { std::mem::transmute(0_u128) }; - //~^ transmute_int_to_float - - let _: f128 = unsafe { std::mem::transmute(0_i128) }; - //~^ transmute_int_to_float - } - - mod issue_5747 { - const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; - //~^ transmute_int_to_float - - const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_float - - const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; - //~^ transmute_int_to_float - - const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; - //~^ transmute_int_to_float - - const fn from_bits_16(v: i16) -> f16 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_32(v: i32) -> f32 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_64(v: u64) -> f64 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_128(v: u128) -> f128 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - } -} - -mod num_to_bytes { - fn test() { - unsafe { - let _: [u8; 1] = std::mem::transmute(0u8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0u32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0u128); - //~^ transmute_num_to_bytes - - let _: [u8; 1] = std::mem::transmute(0i8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0i32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0i128); - //~^ transmute_num_to_bytes - - let _: [u8; 2] = std::mem::transmute(0.0f16); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0.0f32); - //~^ transmute_num_to_bytes - - let _: [u8; 8] = std::mem::transmute(0.0f64); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0.0f128); - //~^ transmute_num_to_bytes - } - } - const fn test_const() { - unsafe { - let _: [u8; 1] = std::mem::transmute(0u8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0u32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0u128); - //~^ transmute_num_to_bytes - - let _: [u8; 1] = std::mem::transmute(0i8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0i32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0i128); - //~^ transmute_num_to_bytes - - let _: [u8; 2] = std::mem::transmute(0.0f16); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0.0f32); - //~^ transmute_num_to_bytes - - let _: [u8; 8] = std::mem::transmute(0.0f64); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0.0f128); - //~^ transmute_num_to_bytes - } - } -} - fn bytes_to_str(mb: &mut [u8]) { const B: &[u8] = b""; diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 1bb70151965c..79528ec06f1b 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -97,230 +97,8 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]` -error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:122:31 - | -LL | let _: f16 = unsafe { std::mem::transmute(0_u16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` - | - = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_float)]` - -error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:125:31 - | -LL | let _: f16 = unsafe { std::mem::transmute(0_i16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_i16 as u16)` - -error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:128:31 - | -LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` - -error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:131:31 - | -LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` - -error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:134:31 - | -LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` - -error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:137:31 - | -LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` - -error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:140:32 - | -LL | let _: f128 = unsafe { std::mem::transmute(0_u128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_u128)` - -error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:143:32 - | -LL | let _: f128 = unsafe { std::mem::transmute(0_i128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` - -error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:148:39 - | -LL | const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` - -error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:151:39 - | -LL | const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` - -error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:154:39 - | -LL | const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` - -error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:157:41 - | -LL | const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` - -error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:161:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(v as u16)` - -error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:166:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(v as u32)` - -error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:171:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(v)` - -error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:176:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(v)` - -error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:185:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0u8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` - | - = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_num_to_bytes)]` - -error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:188:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` - -error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:191:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0u128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` - -error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:194:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0i8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` - -error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:197:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` - -error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:200:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0i128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` - -error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:203:30 - | -LL | let _: [u8; 2] = std::mem::transmute(0.0f16); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` - -error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:206:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` - -error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:209:30 - | -LL | let _: [u8; 8] = std::mem::transmute(0.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` - -error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:212:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0.0f128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` - -error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:218:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0u8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` - -error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:221:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` - -error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:224:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0u128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` - -error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:227:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0i8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` - -error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:230:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` - -error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:233:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0i128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` - -error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:236:30 - | -LL | let _: [u8; 2] = std::mem::transmute(0.0f16); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` - -error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:239:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` - -error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:242:30 - | -LL | let _: [u8; 8] = std::mem::transmute(0.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` - -error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:245:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0.0f128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` - error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:254:28 + --> tests/ui/transmute.rs:122:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -329,16 +107,16 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> tests/ui/transmute.rs:257:32 + --> tests/ui/transmute.rs:125:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:260:30 + --> tests/ui/transmute.rs:128:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 54 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/transmute_float_to_int.fixed b/tests/ui/transmute_float_to_int.fixed deleted file mode 100644 index 844445907d7c..000000000000 --- a/tests/ui/transmute_float_to_int.fixed +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] -#![feature(f128)] -#![feature(f16)] - -fn float_to_int() { - let _: u32 = unsafe { 1f32.to_bits() }; - //~^ transmute_float_to_int - - let _: i32 = unsafe { 1f32.to_bits() as i32 }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { 1f64.to_bits() }; - //~^ transmute_float_to_int - - let _: i64 = unsafe { 1f64.to_bits() as i64 }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { 1.0f64.to_bits() }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { (-1.0f64).to_bits() }; - //~^ transmute_float_to_int -} - -mod issue_5747 { - const VALUE16: i16 = unsafe { 1f16.to_bits() as i16 }; - //~^ transmute_float_to_int - - const VALUE32: i32 = unsafe { 1f32.to_bits() as i32 }; - //~^ transmute_float_to_int - - const VALUE64: u64 = unsafe { 1f64.to_bits() }; - //~^ transmute_float_to_int - - const VALUE128: u128 = unsafe { 1f128.to_bits() }; - //~^ transmute_float_to_int - - const fn to_bits_16(v: f16) -> u16 { - unsafe { v.to_bits() } - //~^ transmute_float_to_int - } - - const fn to_bits_32(v: f32) -> u32 { - unsafe { v.to_bits() } - //~^ transmute_float_to_int - } - - const fn to_bits_64(v: f64) -> i64 { - unsafe { v.to_bits() as i64 } - //~^ transmute_float_to_int - } - - const fn to_bits_128(v: f128) -> i128 { - unsafe { v.to_bits() as i128 } - //~^ transmute_float_to_int - } -} - -fn main() {} diff --git a/tests/ui/transmute_float_to_int.rs b/tests/ui/transmute_float_to_int.rs deleted file mode 100644 index a1f3b15bbfee..000000000000 --- a/tests/ui/transmute_float_to_int.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] -#![feature(f128)] -#![feature(f16)] - -fn float_to_int() { - let _: u32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - let _: i32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - let _: i64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(1.0) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(-1.0) }; - //~^ transmute_float_to_int -} - -mod issue_5747 { - const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; - //~^ transmute_float_to_int - - const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; - //~^ transmute_float_to_int - - const fn to_bits_16(v: f16) -> u16 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_32(v: f32) -> u32 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_64(v: f64) -> i64 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_128(v: f128) -> i128 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } -} - -fn main() {} diff --git a/tests/ui/transmute_float_to_int.stderr b/tests/ui/transmute_float_to_int.stderr deleted file mode 100644 index 223cbc4e90c0..000000000000 --- a/tests/ui/transmute_float_to_int.stderr +++ /dev/null @@ -1,89 +0,0 @@ -error: transmute from a `f32` to a `u32` - --> tests/ui/transmute_float_to_int.rs:7:27 - | -LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` - | - = note: `-D clippy::transmute-float-to-int` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_float_to_int)]` - -error: transmute from a `f32` to a `i32` - --> tests/ui/transmute_float_to_int.rs:10:27 - | -LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:13:27 - | -LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` - -error: transmute from a `f64` to a `i64` - --> tests/ui/transmute_float_to_int.rs:16:27 - | -LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:19:27 - | -LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:22:27 - | -LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` - -error: transmute from a `f16` to a `i16` - --> tests/ui/transmute_float_to_int.rs:27:35 - | -LL | const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f16.to_bits() as i16` - -error: transmute from a `f32` to a `i32` - --> tests/ui/transmute_float_to_int.rs:30:35 - | -LL | const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:33:35 - | -LL | const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` - -error: transmute from a `f128` to a `u128` - --> tests/ui/transmute_float_to_int.rs:36:37 - | -LL | const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f128.to_bits()` - -error: transmute from a `f16` to a `u16` - --> tests/ui/transmute_float_to_int.rs:40:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits()` - -error: transmute from a `f32` to a `u32` - --> tests/ui/transmute_float_to_int.rs:45:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits()` - -error: transmute from a `f64` to a `i64` - --> tests/ui/transmute_float_to_int.rs:50:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits() as i64` - -error: transmute from a `f128` to a `i128` - --> tests/ui/transmute_float_to_int.rs:55:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits() as i128` - -error: aborting due to 14 previous errors - diff --git a/tests/ui/transmute_int_to_char.fixed b/tests/ui/transmute_int_to_char.fixed deleted file mode 100644 index 28644aa9ebbb..000000000000 --- a/tests/ui/transmute_int_to_char.fixed +++ /dev/null @@ -1,16 +0,0 @@ -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] - -fn int_to_char() { - let _: char = unsafe { std::char::from_u32(0_u32).unwrap() }; - //~^ transmute_int_to_char - - let _: char = unsafe { std::char::from_u32(0_i32 as u32).unwrap() }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { std::mem::transmute(0_u32) }; - const _: char = unsafe { std::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/tests/ui/transmute_int_to_char.rs b/tests/ui/transmute_int_to_char.rs deleted file mode 100644 index 8c83ecc8914b..000000000000 --- a/tests/ui/transmute_int_to_char.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] - -fn int_to_char() { - let _: char = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_char - - let _: char = unsafe { std::mem::transmute(0_i32) }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { std::mem::transmute(0_u32) }; - const _: char = unsafe { std::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/tests/ui/transmute_int_to_char.stderr b/tests/ui/transmute_int_to_char.stderr deleted file mode 100644 index e3a3620f28b7..000000000000 --- a/tests/ui/transmute_int_to_char.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: transmute from a `u32` to a `char` - --> tests/ui/transmute_int_to_char.rs:5:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` - | - = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` - -error: transmute from a `i32` to a `char` - --> tests/ui/transmute_int_to_char.rs:8:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` - -error: aborting due to 2 previous errors - diff --git a/tests/ui/transmute_int_to_char_no_std.fixed b/tests/ui/transmute_int_to_char_no_std.fixed deleted file mode 100644 index e6e09a2be4bf..000000000000 --- a/tests/ui/transmute_int_to_char_no_std.fixed +++ /dev/null @@ -1,28 +0,0 @@ -#![no_std] -#![feature(lang_items)] -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn int_to_char() { - let _: char = unsafe { core::char::from_u32(0_u32).unwrap() }; - //~^ transmute_int_to_char - - let _: char = unsafe { core::char::from_u32(0_i32 as u32).unwrap() }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { core::mem::transmute(0_u32) }; - const _: char = unsafe { core::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/tests/ui/transmute_int_to_char_no_std.rs b/tests/ui/transmute_int_to_char_no_std.rs deleted file mode 100644 index 0f2106df00e6..000000000000 --- a/tests/ui/transmute_int_to_char_no_std.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![no_std] -#![feature(lang_items)] -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn int_to_char() { - let _: char = unsafe { core::mem::transmute(0_u32) }; - //~^ transmute_int_to_char - - let _: char = unsafe { core::mem::transmute(0_i32) }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { core::mem::transmute(0_u32) }; - const _: char = unsafe { core::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/tests/ui/transmute_int_to_char_no_std.stderr b/tests/ui/transmute_int_to_char_no_std.stderr deleted file mode 100644 index d94580a84d7a..000000000000 --- a/tests/ui/transmute_int_to_char_no_std.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: transmute from a `u32` to a `char` - --> tests/ui/transmute_int_to_char_no_std.rs:17:28 - | -LL | let _: char = unsafe { core::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_u32).unwrap()` - | - = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` - -error: transmute from a `i32` to a `char` - --> tests/ui/transmute_int_to_char_no_std.rs:20:28 - | -LL | let _: char = unsafe { core::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_i32 as u32).unwrap()` - -error: aborting due to 2 previous errors - From a4707a4f3b38bd6aae7a452300ef5c65da19e524 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 8 May 2025 19:43:33 +0000 Subject: [PATCH 105/245] Do not ICE when reassigning in GatherLocalsVisitor on the bad path --- .../rustc_hir_typeck/src/gather_locals.rs | 14 +++++++++++-- tests/ui/typeck/gather-locals-twice.rs | 7 +++++++ tests/ui/typeck/gather-locals-twice.stderr | 20 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/ui/typeck/gather-locals-twice.rs create mode 100644 tests/ui/typeck/gather-locals-twice.stderr diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index a8bbc89dbded..956671fc66ed 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -105,16 +105,26 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { } fn assign(&mut self, span: Span, nid: HirId, ty_opt: Option>) -> Ty<'tcx> { + // We evaluate expressions twice occasionally in diagnostics for better + // type information or because it needs type information out-of-order. + // In order to not ICE and not lead to knock-on ambiguity errors, if we + // try to re-assign a type to a local, then just take out the previous + // type and delay a bug. + if let Some(&local) = self.fcx.locals.borrow_mut().get(&nid) { + self.fcx.dcx().span_delayed_bug(span, "evaluated expression more than once"); + return local; + } + match ty_opt { None => { // Infer the variable's type. let var_ty = self.fcx.next_ty_var(span); - assert_eq!(self.fcx.locals.borrow_mut().insert(nid, var_ty), None); + self.fcx.locals.borrow_mut().insert(nid, var_ty); var_ty } Some(typ) => { // Take type that the user specified. - assert_eq!(self.fcx.locals.borrow_mut().insert(nid, typ), None); + self.fcx.locals.borrow_mut().insert(nid, typ); typ } } diff --git a/tests/ui/typeck/gather-locals-twice.rs b/tests/ui/typeck/gather-locals-twice.rs new file mode 100644 index 000000000000..e9351146f456 --- /dev/null +++ b/tests/ui/typeck/gather-locals-twice.rs @@ -0,0 +1,7 @@ +// Regression test for . + +fn main() { + () += { let x; }; + //~^ ERROR binary assignment operation `+=` cannot be applied to type `()` + //~| ERROR invalid left-hand side of assignment +} diff --git a/tests/ui/typeck/gather-locals-twice.stderr b/tests/ui/typeck/gather-locals-twice.stderr new file mode 100644 index 000000000000..7598ef8e7ea4 --- /dev/null +++ b/tests/ui/typeck/gather-locals-twice.stderr @@ -0,0 +1,20 @@ +error[E0368]: binary assignment operation `+=` cannot be applied to type `()` + --> $DIR/gather-locals-twice.rs:4:5 + | +LL | () += { let x; }; + | --^^^^^^^^^^^^^^ + | | + | cannot use `+=` on type `()` + +error[E0067]: invalid left-hand side of assignment + --> $DIR/gather-locals-twice.rs:4:8 + | +LL | () += { let x; }; + | -- ^^ + | | + | cannot assign to this expression + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0067, E0368. +For more information about an error, try `rustc --explain E0067`. From 0533ff7d4169692e05d11d219c287a477a5471bb Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 9 May 2025 15:05:11 +0000 Subject: [PATCH 106/245] more moved tests --- .../{ => coherence}/auxiliary/coherence_cross_crate_trait_decl.rs | 0 .../{ => coherence}/auxiliary/foreign-crate.rs | 0 .../{ => coherence}/coherence.classic.stderr | 0 .../type-alias-impl-trait/{ => coherence}/coherence.next.stderr | 0 tests/ui/type-alias-impl-trait/{ => coherence}/coherence.rs | 0 .../{ => coherence}/coherence_cross_crate.rs | 0 .../{ => coherence}/coherence_cross_crate.stderr | 0 .../{ => coherence}/coherence_different_hidden_ty.rs | 0 .../{ => coherence}/coherence_different_hidden_ty.stderr | 0 .../{ => coherence}/coherence_generalization.rs | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/type-alias-impl-trait/{ => coherence}/auxiliary/coherence_cross_crate_trait_decl.rs (100%) rename tests/ui/type-alias-impl-trait/{ => coherence}/auxiliary/foreign-crate.rs (100%) rename tests/ui/type-alias-impl-trait/{ => coherence}/coherence.classic.stderr (100%) rename tests/ui/type-alias-impl-trait/{ => coherence}/coherence.next.stderr (100%) rename tests/ui/type-alias-impl-trait/{ => coherence}/coherence.rs (100%) rename tests/ui/type-alias-impl-trait/{ => coherence}/coherence_cross_crate.rs (100%) rename tests/ui/type-alias-impl-trait/{ => coherence}/coherence_cross_crate.stderr (100%) rename tests/ui/type-alias-impl-trait/{ => coherence}/coherence_different_hidden_ty.rs (100%) rename tests/ui/type-alias-impl-trait/{ => coherence}/coherence_different_hidden_ty.stderr (100%) rename tests/ui/type-alias-impl-trait/{ => coherence}/coherence_generalization.rs (100%) diff --git a/tests/ui/type-alias-impl-trait/auxiliary/coherence_cross_crate_trait_decl.rs b/tests/ui/type-alias-impl-trait/coherence/auxiliary/coherence_cross_crate_trait_decl.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/auxiliary/coherence_cross_crate_trait_decl.rs rename to tests/ui/type-alias-impl-trait/coherence/auxiliary/coherence_cross_crate_trait_decl.rs diff --git a/tests/ui/type-alias-impl-trait/auxiliary/foreign-crate.rs b/tests/ui/type-alias-impl-trait/coherence/auxiliary/foreign-crate.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/auxiliary/foreign-crate.rs rename to tests/ui/type-alias-impl-trait/coherence/auxiliary/foreign-crate.rs diff --git a/tests/ui/type-alias-impl-trait/coherence.classic.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence.classic.stderr similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence.classic.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence.classic.stderr diff --git a/tests/ui/type-alias-impl-trait/coherence.next.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence.next.stderr similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence.next.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence.next.stderr diff --git a/tests/ui/type-alias-impl-trait/coherence.rs b/tests/ui/type-alias-impl-trait/coherence/coherence.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence.rs diff --git a/tests/ui/type-alias-impl-trait/coherence_cross_crate.rs b/tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence_cross_crate.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.rs diff --git a/tests/ui/type-alias-impl-trait/coherence_cross_crate.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.stderr similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence_cross_crate.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.stderr diff --git a/tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.rs b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs diff --git a/tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr diff --git a/tests/ui/type-alias-impl-trait/coherence_generalization.rs b/tests/ui/type-alias-impl-trait/coherence/coherence_generalization.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence_generalization.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence_generalization.rs From 1ac228397d5ea1d84ad20c3d5e2ecca957af6146 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 8 May 2025 12:43:10 +0200 Subject: [PATCH 107/245] Use intrinsics for `{f16,f32,f64,f128}::{minimum,maximum}` operations --- src/intrinsic/mod.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs index 2ed5ec4381ed..9caceca92957 100644 --- a/src/intrinsic/mod.rs +++ b/src/intrinsic/mod.rs @@ -74,8 +74,44 @@ fn get_simple_intrinsic<'gcc, 'tcx>( sym::fabsf64 => "fabs", sym::minnumf32 => "fminf", sym::minnumf64 => "fmin", + sym::minimumf32 => "fminimumf", + sym::minimumf64 => "fminimum", + sym::minimumf128 => { + // GCC doesn't have the intrinsic we want so we use the compiler-builtins one + // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html + let f128_type = cx.type_f128(); + return Some(cx.context.new_function( + None, + FunctionType::Extern, + f128_type, + &[ + cx.context.new_parameter(None, f128_type, "a"), + cx.context.new_parameter(None, f128_type, "b"), + ], + "fminimumf128", + false, + )); + } sym::maxnumf32 => "fmaxf", sym::maxnumf64 => "fmax", + sym::maximumf32 => "fmaximumf", + sym::maximumf64 => "fmaximum", + sym::maximumf128 => { + // GCC doesn't have the intrinsic we want so we use the compiler-builtins one + // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html + let f128_type = cx.type_f128(); + return Some(cx.context.new_function( + None, + FunctionType::Extern, + f128_type, + &[ + cx.context.new_parameter(None, f128_type, "a"), + cx.context.new_parameter(None, f128_type, "b"), + ], + "fmaximumf128", + false, + )); + } sym::copysignf32 => "copysignf", sym::copysignf64 => "copysign", sym::copysignf128 => "copysignl", From 7e5fa896a210b205c48a2a8922d21fd680afd39c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 5 May 2025 15:26:06 +0200 Subject: [PATCH 108/245] remove 'unordered' atomic intrinsics --- src/builder.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index 6720f6186d16..9e5ebf3a9a40 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -2454,7 +2454,6 @@ impl ToGccOrdering for AtomicOrdering { use MemOrdering::*; let ordering = match self { - AtomicOrdering::Unordered => __ATOMIC_RELAXED, AtomicOrdering::Relaxed => __ATOMIC_RELAXED, // TODO(antoyo): check if that's the same. AtomicOrdering::Acquire => __ATOMIC_ACQUIRE, AtomicOrdering::Release => __ATOMIC_RELEASE, From e4d82aefd95ee2cf035b02f6dcf15b0ea49b0171 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 9 May 2025 22:03:49 +0000 Subject: [PATCH 109/245] Resolve through local re-exports in `lookup_path` --- clippy_utils/src/paths.rs | 19 +++++++-- .../toml_disallowed_methods/clippy.toml | 3 ++ .../conf_disallowed_methods.rs | 8 ++++ .../conf_disallowed_methods.stderr | 42 ++++++++++++------- 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 795fb502c9cc..e5179e479ccd 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -8,9 +8,9 @@ use crate::{MaybePath, path_def_id, sym}; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; -use rustc_hir::def::{DefKind, Namespace}; +use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; -use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, TraitItemRef}; +use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, TraitItemRef, UseKind}; use rustc_lint::LateContext; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy}; @@ -302,8 +302,19 @@ fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, n match item_kind { ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| { - let ident = tcx.hir_item(item_id).kind.ident()?; - res(ident, item_id.owner_id) + let item = tcx.hir_item(item_id); + if let ItemKind::Use(path, UseKind::Single(ident)) = item.kind { + if ident.name == name { + path.res + .iter() + .find(|res| ns.matches(res.ns())) + .and_then(Res::opt_def_id) + } else { + None + } + } else { + res(item.kind.ident()?, item_id.owner_id) + } }), ItemKind::Impl(r#impl) => r#impl .items diff --git a/tests/ui-toml/toml_disallowed_methods/clippy.toml b/tests/ui-toml/toml_disallowed_methods/clippy.toml index 41dbd5068479..c7a326f28295 100644 --- a/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -14,4 +14,7 @@ disallowed-methods = [ "conf_disallowed_methods::Struct::method", "conf_disallowed_methods::Trait::provided_method", "conf_disallowed_methods::Trait::implemented_method", + # re-exports + "conf_disallowed_methods::identity", + "conf_disallowed_methods::renamed", ] diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index dd170d6baf85..2dac01649a0f 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -8,6 +8,9 @@ extern crate regex; use futures::stream::{empty, select_all}; use regex::Regex; +use std::convert::identity; +use std::hint::black_box as renamed; + fn local_fn() {} struct Struct; @@ -71,4 +74,9 @@ fn main() { //~^ disallowed_methods s.implemented_method(); //~^ disallowed_methods + + identity(()); + //~^ disallowed_methods + renamed(1); + //~^ disallowed_methods } diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index f7dda81eb936..20474ad6e927 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:33:14 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:36:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); | ^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap(); = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` error: use of a disallowed method `regex::Regex::is_match` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:35:8 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:38:8 | LL | re.is_match("abc"); | ^^^^^^^^ @@ -16,76 +16,88 @@ LL | re.is_match("abc"); = note: no matching allowed error: use of a disallowed method `std::iter::Iterator::sum` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:39:14 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:42:14 | LL | a.iter().sum::(); | ^^^ error: use of a disallowed method `slice::sort_unstable` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:42:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:45:7 | LL | a.sort_unstable(); | ^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:46:20 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:49:20 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:50:61 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:53:61 | LL | let indirect: fn(&str) -> Result = Regex::new; | ^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:54:28 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:57:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:56:53 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:59:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ error: use of a disallowed method `futures::stream::select_all` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:60:31 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:63:31 | LL | let same_name_as_module = select_all(vec![empty::<()>()]); | ^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_fn` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:63:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:66:5 | LL | local_fn(); | ^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_mod::f` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:65:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:68:5 | LL | local_mod::f(); | ^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Struct::method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:68:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:71:7 | LL | s.method(); | ^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:70:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:73:7 | LL | s.provided_method(); | ^^^^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:72:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:75:7 | LL | s.implemented_method(); | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: use of a disallowed method `conf_disallowed_methods::identity` + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:78:5 + | +LL | identity(()); + | ^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::renamed` + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:80:5 + | +LL | renamed(1); + | ^^^^^^^ + +error: aborting due to 16 previous errors From bde939058b21dabcbd56b2fc55dacd36d1f97d49 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 9 May 2025 23:48:17 +0200 Subject: [PATCH 110/245] `char::is_digit()` is const-stable only since Rust 1.87 The `to_digit_is_some()` lint suggests using `char::is_digit()`. It should not trigger in const contexts before Rust 1.87. --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/lib.rs | 2 +- clippy_lints/src/to_digit_is_some.rs | 22 ++++++++++++++++++---- clippy_utils/src/msrvs.rs | 2 +- tests/ui/to_digit_is_some.fixed | 17 +++++++++++++++++ tests/ui/to_digit_is_some.rs | 17 +++++++++++++++++ tests/ui/to_digit_is_some.stderr | 14 +++++++++++++- 8 files changed, 69 insertions(+), 7 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 58c79c119ccf..282d892951cf 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -849,6 +849,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) * [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) +* [`to_digit_is_some`](https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) * [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index aef0516b75be..8a1d38ed600e 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -759,6 +759,7 @@ define_Conf! { same_item_push, seek_from_current, seek_rewind, + to_digit_is_some, transmute_ptr_to_ref, tuple_array_conversions, type_repetition_in_bounds, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ad8b223b3aad..64fbc0252e2a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -746,7 +746,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(conf))); store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(exit::Exit)); - store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome)); + store.register_late_pass(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf))); store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))); store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))); store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index c8a6a41d6d8d..7d7d74f27b3c 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -1,10 +1,12 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{paths, sym}; +use clippy_utils::{is_in_const_context, paths, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -32,7 +34,17 @@ declare_clippy_lint! { "`char.is_digit()` is clearer" } -declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); +impl_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); + +pub(crate) struct ToDigitIsSome { + msrv: Msrv, +} + +impl ToDigitIsSome { + pub(crate) fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { @@ -59,7 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { _ => None, }; - if let Some((is_method_call, char_arg, radix_arg)) = match_result { + if let Some((is_method_call, char_arg, radix_arg)) = match_result + && (!is_in_const_context(cx) || self.msrv.meets(cx, msrvs::CONST_CHAR_IS_DIGIT)) + { let mut applicability = Applicability::MachineApplicable; let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability); let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 71985cb4b091..434e43167d6b 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -23,7 +23,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,88,0 { LET_CHAINS } - 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT } + 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT } 1,85,0 { UINT_FLOAT_MIDPOINT } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } diff --git a/tests/ui/to_digit_is_some.fixed b/tests/ui/to_digit_is_some.fixed index 627d54c5f738..ff6b32e6bd18 100644 --- a/tests/ui/to_digit_is_some.fixed +++ b/tests/ui/to_digit_is_some.fixed @@ -9,3 +9,20 @@ fn main() { let _ = char::is_digit(c, 8); //~^ to_digit_is_some } + +#[clippy::msrv = "1.86"] +mod cannot_lint_in_const_context { + fn without_const(c: char) -> bool { + c.is_digit(8) + //~^ to_digit_is_some + } + const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + } +} + +#[clippy::msrv = "1.87"] +const fn with_const(c: char) -> bool { + c.is_digit(8) + //~^ to_digit_is_some +} diff --git a/tests/ui/to_digit_is_some.rs b/tests/ui/to_digit_is_some.rs index d4eccc9931f1..5ba086174331 100644 --- a/tests/ui/to_digit_is_some.rs +++ b/tests/ui/to_digit_is_some.rs @@ -9,3 +9,20 @@ fn main() { let _ = char::to_digit(c, 8).is_some(); //~^ to_digit_is_some } + +#[clippy::msrv = "1.86"] +mod cannot_lint_in_const_context { + fn without_const(c: char) -> bool { + c.to_digit(8).is_some() + //~^ to_digit_is_some + } + const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + } +} + +#[clippy::msrv = "1.87"] +const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + //~^ to_digit_is_some +} diff --git a/tests/ui/to_digit_is_some.stderr b/tests/ui/to_digit_is_some.stderr index f41382a60d53..5ffedb4683f1 100644 --- a/tests/ui/to_digit_is_some.stderr +++ b/tests/ui/to_digit_is_some.stderr @@ -13,5 +13,17 @@ error: use of `.to_digit(..).is_some()` LL | let _ = char::to_digit(c, 8).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `char::is_digit(c, 8)` -error: aborting due to 2 previous errors +error: use of `.to_digit(..).is_some()` + --> tests/ui/to_digit_is_some.rs:16:9 + | +LL | c.to_digit(8).is_some() + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_digit(8)` + +error: use of `.to_digit(..).is_some()` + --> tests/ui/to_digit_is_some.rs:26:5 + | +LL | c.to_digit(8).is_some() + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_digit(8)` + +error: aborting due to 4 previous errors From 33966ccbb6570c83b9fc1dd0942f83ae2d56f47c Mon Sep 17 00:00:00 2001 From: michal kostrubiec Date: Fri, 9 May 2025 21:22:33 +0200 Subject: [PATCH 111/245] Add a workaround for 128 bit switches --- example/mini_core.rs | 54 ++++++++++++++++++++++++----------- src/builder.rs | 25 +++++++++++++--- tests/run/switchint_128bit.rs | 37 ++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 20 deletions(-) create mode 100644 tests/run/switchint_128bit.rs diff --git a/example/mini_core.rs b/example/mini_core.rs index c554a87b8256..d1d8e8fd5bc4 100644 --- a/example/mini_core.rs +++ b/example/mini_core.rs @@ -1,6 +1,14 @@ #![feature( - no_core, lang_items, intrinsics, unboxed_closures, extern_types, - decl_macro, rustc_attrs, transparent_unions, auto_traits, freeze_impls, + no_core, + lang_items, + intrinsics, + unboxed_closures, + extern_types, + decl_macro, + rustc_attrs, + transparent_unions, + auto_traits, + freeze_impls, thread_local )] #![no_core] @@ -35,13 +43,13 @@ impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} pub trait DispatchFromDyn {} // &T -> &U -impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} // &mut T -> &mut U -impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} // *const T -> *const U -impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} +impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} // *mut T -> *mut U -impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} +impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} impl, U: ?Sized> DispatchFromDyn> for Box {} #[lang = "legacy_receiver"] @@ -52,8 +60,7 @@ impl LegacyReceiver for &mut T {} impl LegacyReceiver for Box {} #[lang = "receiver"] -trait Receiver { -} +trait Receiver {} #[lang = "copy"] pub trait Copy {} @@ -67,10 +74,13 @@ impl Copy for u16 {} impl Copy for u32 {} impl Copy for u64 {} impl Copy for usize {} +impl Copy for u128 {} impl Copy for i8 {} impl Copy for i16 {} impl Copy for i32 {} +impl Copy for i64 {} impl Copy for isize {} +impl Copy for i128 {} impl Copy for f32 {} impl Copy for f64 {} impl Copy for char {} @@ -336,7 +346,6 @@ impl PartialEq for u32 { } } - impl PartialEq for u64 { fn eq(&self, other: &u64) -> bool { (*self) == (*other) @@ -523,7 +532,11 @@ fn panic_in_cleanup() -> ! { #[track_caller] fn panic_bounds_check(index: usize, len: usize) -> ! { unsafe { - libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index); + libc::printf( + "index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, + len, + index, + ); intrinsics::abort(); } } @@ -551,8 +564,7 @@ pub trait Deref { fn deref(&self) -> &Self::Target; } -pub trait Allocator { -} +pub trait Allocator {} impl Allocator for () {} @@ -634,6 +646,8 @@ pub union MaybeUninit { } pub mod intrinsics { + #[rustc_intrinsic] + pub const fn black_box(_dummy: T) -> T; #[rustc_intrinsic] pub fn abort() -> !; #[rustc_intrinsic] @@ -711,19 +725,27 @@ pub struct VaList<'a>(&'a mut VaListImpl); #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro stringify($($t:tt)*) { /* compiler built-in */ } +pub macro stringify($($t:tt)*) { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro file() { /* compiler built-in */ } +pub macro file() { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro line() { /* compiler built-in */ } +pub macro line() { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro cfg() { /* compiler built-in */ } +pub macro cfg() { + /* compiler built-in */ +} pub static A_STATIC: u8 = 42; diff --git a/src/builder.rs b/src/builder.rs index 5c70f4a7df93..557f7da0db84 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -568,11 +568,28 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { ) { let mut gcc_cases = vec![]; let typ = self.val_ty(value); - for (on_val, dest) in cases { - let on_val = self.const_uint_big(typ, on_val); - gcc_cases.push(self.context.new_case(on_val, on_val, dest)); + // FIXME(FractalFir): This is a workaround for a libgccjit limitation. + // Currently, libgccjit can't directly create 128 bit integers. + // Since switch cases must be values, and casts are not constant, we can't use 128 bit switch cases. + // In such a case, we will simply fall back to an if-ladder. + // This *may* be slower than a native switch, but a slow working solution is better than none at all. + if typ.is_i128(self) || typ.is_u128(self) { + for (on_val, dest) in cases { + let on_val = self.const_uint_big(typ, on_val); + let is_case = + self.context.new_comparison(self.location, ComparisonOp::Equals, value, on_val); + let next_block = self.current_func().new_block("case"); + self.block.end_with_conditional(self.location, is_case, dest, next_block); + self.block = next_block; + } + self.block.end_with_jump(self.location, default_block); + } else { + for (on_val, dest) in cases { + let on_val = self.const_uint_big(typ, on_val); + gcc_cases.push(self.context.new_case(on_val, on_val, dest)); + } + self.block.end_with_switch(self.location, value, default_block, &gcc_cases); } - self.block.end_with_switch(self.location, value, default_block, &gcc_cases); } #[cfg(feature = "master")] diff --git a/tests/run/switchint_128bit.rs b/tests/run/switchint_128bit.rs new file mode 100644 index 000000000000..decae5bfcd78 --- /dev/null +++ b/tests/run/switchint_128bit.rs @@ -0,0 +1,37 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use intrinsics::black_box; +use mini_core::*; + +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { + // 1st. Check that small 128 bit values work. + let val = black_box(64_u128); + match val { + 0 => return 1, + 1 => return 2, + 64 => (), + _ => return 3, + } + // 2nd check that *large* values work. + const BIG: u128 = 0xDEAD_C0FE_BEEF_DECAF_BADD_DECAF_BEEF_u128; + let val = black_box(BIG); + match val { + 0 => return 4, + 1 => return 5, + // Check that we will not match on the lower u64, if the upper qword is different! + 0xcafbadddecafbeef => return 6, + 0xDEAD_C0FE_BEEF_DECAF_BADD_DECAF_BEEF_u128 => (), + _ => return 7, + } + 0 +} From e61886a6d9e510079582cbd11cb00a1c88941cca Mon Sep 17 00:00:00 2001 From: relaxcn Date: Sat, 10 May 2025 01:49:22 +0800 Subject: [PATCH 112/245] fix `unnecessary_unwrap` emitted twice in closure --- clippy_lints/src/unwrap.rs | 4 ++ .../ui/checked_unwrap/simple_conditionals.rs | 33 ++++++++++++ .../checked_unwrap/simple_conditionals.stderr | 53 ++++++++++++++++++- 3 files changed, 89 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index a6a918763468..c641d4e55b94 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -292,6 +292,10 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { if expr.span.in_external_macro(self.cx.tcx.sess.source_map()) { return; } + // Skip checking inside closures since they are visited through `Unwrap::check_fn()` already. + if matches!(expr.kind, ExprKind::Closure(_)) { + return; + } if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) { walk_expr(self, cond); self.visit_branch(expr, cond, then, false); diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 5589d8cc429c..ba0d36d85fe5 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -240,6 +240,39 @@ fn issue14725() { } } +fn issue14763(x: Option, r: Result<(), ()>) { + _ = || { + if x.is_some() { + _ = x.unwrap(); + //~^ unnecessary_unwrap + } else { + _ = x.unwrap(); + //~^ panicking_unwrap + } + }; + _ = || { + if r.is_ok() { + _ = r.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + _ = r.as_ref().unwrap(); + //~^ panicking_unwrap + } + }; +} + +const ISSUE14763: fn(Option) = |x| { + _ = || { + if x.is_some() { + _ = x.unwrap(); + //~^ unnecessary_unwrap + } else { + _ = x.unwrap(); + //~^ panicking_unwrap + } + } +}; + fn check_expect() { let x = Some(()); if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index 82a36aa5029f..a4bf00992445 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -271,6 +271,57 @@ LL | if result.is_ok() { LL | result.as_ref().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ +error: called `unwrap` on `x` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:246:17 + | +LL | if x.is_some() { + | -------------- help: try: `if let Some() = x` +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:249:17 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: called `unwrap` on `r` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:255:17 + | +LL | if r.is_ok() { + | ------------ help: try: `if let Ok() = &r` +LL | _ = r.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:258:17 + | +LL | if r.is_ok() { + | --------- because of this check +... +LL | _ = r.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `x` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:267:17 + | +LL | if x.is_some() { + | -------------- help: try: `if let Some() = x` +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:270:17 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + error: creating a shared reference to mutable static --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12 | @@ -281,5 +332,5 @@ LL | if X.is_some() { = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: `#[deny(static_mut_refs)]` on by default -error: aborting due to 30 previous errors +error: aborting due to 36 previous errors From 6e5d6473aaa21d6fae3eb7afdacdfaf93391bfc7 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sat, 10 May 2025 04:53:58 +0000 Subject: [PATCH 113/245] Preparing for merge from rustc --- 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 97bc826b57a4..9afbd3aa6357 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -2ad5f8607d0e192b60b130e5cc416b477b351c18 +fd9fad6dbcc1bae3cba2a8634339ffa620a49f28 From 25dd45f7bdbc13b2c3d9fea8f1cdb65187840879 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 10 May 2025 18:02:47 +0200 Subject: [PATCH 114/245] Correctly handle branches when updating repository --- tools/generate_intrinsics.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/generate_intrinsics.py b/tools/generate_intrinsics.py index 8efed3e43af8..181f1e501a40 100644 --- a/tools/generate_intrinsics.py +++ b/tools/generate_intrinsics.py @@ -12,7 +12,7 @@ def run_command(command, cwd=None): sys.exit(1) -def clone_repository(repo_name, path, repo_url, sub_paths=None): +def clone_repository(repo_name, path, repo_url, branch="master", sub_paths=None): if os.path.exists(path): while True: choice = input("There is already a `{}` folder, do you want to update it? [y/N]".format(path)) @@ -21,7 +21,7 @@ def clone_repository(repo_name, path, repo_url, sub_paths=None): return elif choice.lower() == "y": print("Updating repository...") - run_command(["git", "pull", "origin"], cwd=path) + run_command(["git", "pull", "origin", branch], cwd=path) return else: print("Didn't understand answer...") @@ -209,6 +209,7 @@ def main(): "llvm-project", llvm_path, "https://github.com/llvm/llvm-project", + branch="main", sub_paths=["llvm/include/llvm/IR", "llvm/include/llvm/CodeGen/"], ) clone_repository( From 43b95881f0ef12e61713fa2c8ed57d4e8ac59b71 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 10 May 2025 18:02:54 +0200 Subject: [PATCH 115/245] Regenerate intrinsics --- src/intrinsic/archs.rs | 554 +++++++++++++++++++++++++++++++---------- 1 file changed, 418 insertions(+), 136 deletions(-) diff --git a/src/intrinsic/archs.rs b/src/intrinsic/archs.rs index b8d1cde1d5dd..5ada535aa41d 100644 --- a/src/intrinsic/archs.rs +++ b/src/intrinsic/archs.rs @@ -38,6 +38,7 @@ match name { "llvm.aarch64.gcsss" => "__builtin_arm_gcsss", "llvm.aarch64.isb" => "__builtin_arm_isb", "llvm.aarch64.prefetch" => "__builtin_arm_prefetch", + "llvm.aarch64.sme.in.streaming.mode" => "__builtin_arm_in_streaming_mode", "llvm.aarch64.sve.aesd" => "__builtin_sve_svaesd_u8", "llvm.aarch64.sve.aese" => "__builtin_sve_svaese_u8", "llvm.aarch64.sve.aesimc" => "__builtin_sve_svaesimc_u8", @@ -55,6 +56,8 @@ match name { "llvm.aarch64.ttest" => "__builtin_arm_ttest", // amdgcn "llvm.amdgcn.alignbyte" => "__builtin_amdgcn_alignbyte", + "llvm.amdgcn.ashr.pk.i8.i32" => "__builtin_amdgcn_ashr_pk_i8_i32", + "llvm.amdgcn.ashr.pk.u8.i32" => "__builtin_amdgcn_ashr_pk_u8_i32", "llvm.amdgcn.buffer.wbinvl1" => "__builtin_amdgcn_buffer_wbinvl1", "llvm.amdgcn.buffer.wbinvl1.sc" => "__builtin_amdgcn_buffer_wbinvl1_sc", "llvm.amdgcn.buffer.wbinvl1.vol" => "__builtin_amdgcn_buffer_wbinvl1_vol", @@ -64,6 +67,7 @@ match name { "llvm.amdgcn.cubetc" => "__builtin_amdgcn_cubetc", "llvm.amdgcn.cvt.f32.bf8" => "__builtin_amdgcn_cvt_f32_bf8", "llvm.amdgcn.cvt.f32.fp8" => "__builtin_amdgcn_cvt_f32_fp8", + "llvm.amdgcn.cvt.off.f32.i4" => "__builtin_amdgcn_cvt_off_f32_i4", "llvm.amdgcn.cvt.pk.bf8.f32" => "__builtin_amdgcn_cvt_pk_bf8_f32", "llvm.amdgcn.cvt.pk.f32.bf8" => "__builtin_amdgcn_cvt_pk_f32_bf8", "llvm.amdgcn.cvt.pk.f32.fp8" => "__builtin_amdgcn_cvt_pk_f32_fp8", @@ -74,7 +78,58 @@ match name { "llvm.amdgcn.cvt.pknorm.i16" => "__builtin_amdgcn_cvt_pknorm_i16", "llvm.amdgcn.cvt.pknorm.u16" => "__builtin_amdgcn_cvt_pknorm_u16", "llvm.amdgcn.cvt.pkrtz" => "__builtin_amdgcn_cvt_pkrtz", + "llvm.amdgcn.cvt.scalef32.2xpk16.bf6.f32" => "__builtin_amdgcn_cvt_scalef32_2xpk16_bf6_f32", + "llvm.amdgcn.cvt.scalef32.2xpk16.fp6.f32" => "__builtin_amdgcn_cvt_scalef32_2xpk16_fp6_f32", + "llvm.amdgcn.cvt.scalef32.f16.bf8" => "__builtin_amdgcn_cvt_scalef32_f16_bf8", + "llvm.amdgcn.cvt.scalef32.f16.fp8" => "__builtin_amdgcn_cvt_scalef32_f16_fp8", + "llvm.amdgcn.cvt.scalef32.f32.bf8" => "__builtin_amdgcn_cvt_scalef32_f32_bf8", + "llvm.amdgcn.cvt.scalef32.f32.fp8" => "__builtin_amdgcn_cvt_scalef32_f32_fp8", + "llvm.amdgcn.cvt.scalef32.pk.bf16.bf8" => "__builtin_amdgcn_cvt_scalef32_pk_bf16_bf8", + "llvm.amdgcn.cvt.scalef32.pk.bf16.fp4" => "__builtin_amdgcn_cvt_scalef32_pk_bf16_fp4", + "llvm.amdgcn.cvt.scalef32.pk.bf16.fp8" => "__builtin_amdgcn_cvt_scalef32_pk_bf16_fp8", + "llvm.amdgcn.cvt.scalef32.pk.bf8.bf16" => "__builtin_amdgcn_cvt_scalef32_pk_bf8_bf16", + "llvm.amdgcn.cvt.scalef32.pk.bf8.f16" => "__builtin_amdgcn_cvt_scalef32_pk_bf8_f16", + "llvm.amdgcn.cvt.scalef32.pk.bf8.f32" => "__builtin_amdgcn_cvt_scalef32_pk_bf8_f32", + "llvm.amdgcn.cvt.scalef32.pk.f16.bf8" => "__builtin_amdgcn_cvt_scalef32_pk_f16_bf8", + "llvm.amdgcn.cvt.scalef32.pk.f16.fp4" => "__builtin_amdgcn_cvt_scalef32_pk_f16_fp4", + "llvm.amdgcn.cvt.scalef32.pk.f16.fp8" => "__builtin_amdgcn_cvt_scalef32_pk_f16_fp8", + "llvm.amdgcn.cvt.scalef32.pk.f32.bf8" => "__builtin_amdgcn_cvt_scalef32_pk_f32_bf8", + "llvm.amdgcn.cvt.scalef32.pk.f32.fp4" => "__builtin_amdgcn_cvt_scalef32_pk_f32_fp4", + "llvm.amdgcn.cvt.scalef32.pk.f32.fp8" => "__builtin_amdgcn_cvt_scalef32_pk_f32_fp8", + "llvm.amdgcn.cvt.scalef32.pk.fp4.bf16" => "__builtin_amdgcn_cvt_scalef32_pk_fp4_bf16", + "llvm.amdgcn.cvt.scalef32.pk.fp4.f16" => "__builtin_amdgcn_cvt_scalef32_pk_fp4_f16", + "llvm.amdgcn.cvt.scalef32.pk.fp4.f32" => "__builtin_amdgcn_cvt_scalef32_pk_fp4_f32", + "llvm.amdgcn.cvt.scalef32.pk.fp8.bf16" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_bf16", + "llvm.amdgcn.cvt.scalef32.pk.fp8.f16" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_f16", + "llvm.amdgcn.cvt.scalef32.pk.fp8.f32" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_f32", + "llvm.amdgcn.cvt.scalef32.pk32.bf16.bf6" => "__builtin_amdgcn_cvt_scalef32_pk32_bf16_bf6", + "llvm.amdgcn.cvt.scalef32.pk32.bf16.fp6" => "__builtin_amdgcn_cvt_scalef32_pk32_bf16_fp6", + "llvm.amdgcn.cvt.scalef32.pk32.bf6.bf16" => "__builtin_amdgcn_cvt_scalef32_pk32_bf6_bf16", + "llvm.amdgcn.cvt.scalef32.pk32.bf6.f16" => "__builtin_amdgcn_cvt_scalef32_pk32_bf6_f16", + "llvm.amdgcn.cvt.scalef32.pk32.f16.bf6" => "__builtin_amdgcn_cvt_scalef32_pk32_f16_bf6", + "llvm.amdgcn.cvt.scalef32.pk32.f16.fp6" => "__builtin_amdgcn_cvt_scalef32_pk32_f16_fp6", + "llvm.amdgcn.cvt.scalef32.pk32.f32.bf6" => "__builtin_amdgcn_cvt_scalef32_pk32_f32_bf6", + "llvm.amdgcn.cvt.scalef32.pk32.f32.fp6" => "__builtin_amdgcn_cvt_scalef32_pk32_f32_fp6", + "llvm.amdgcn.cvt.scalef32.pk32.fp6.bf16" => "__builtin_amdgcn_cvt_scalef32_pk32_fp6_bf16", + "llvm.amdgcn.cvt.scalef32.pk32.fp6.f16" => "__builtin_amdgcn_cvt_scalef32_pk32_fp6_f16", + "llvm.amdgcn.cvt.scalef32.sr.bf8.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_bf16", + "llvm.amdgcn.cvt.scalef32.sr.bf8.f16" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_f16", + "llvm.amdgcn.cvt.scalef32.sr.bf8.f32" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_f32", + "llvm.amdgcn.cvt.scalef32.sr.fp8.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_fp8_bf16", + "llvm.amdgcn.cvt.scalef32.sr.fp8.f16" => "__builtin_amdgcn_cvt_scalef32_sr_fp8_f16", + "llvm.amdgcn.cvt.scalef32.sr.fp8.f32" => "__builtin_amdgcn_cvt_scalef32_sr_fp8_f32", + "llvm.amdgcn.cvt.scalef32.sr.pk.fp4.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_bf16", + "llvm.amdgcn.cvt.scalef32.sr.pk.fp4.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_f16", + "llvm.amdgcn.cvt.scalef32.sr.pk.fp4.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_f32", + "llvm.amdgcn.cvt.scalef32.sr.pk32.bf6.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_bf6_bf16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.bf6.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_bf6_f16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.bf6.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_bf6_f32", + "llvm.amdgcn.cvt.scalef32.sr.pk32.fp6.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_fp6_bf16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.fp6.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_fp6_f16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.fp6.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_fp6_f32", + "llvm.amdgcn.cvt.sr.bf16.f32" => "__builtin_amdgcn_cvt_sr_bf16_f32", "llvm.amdgcn.cvt.sr.bf8.f32" => "__builtin_amdgcn_cvt_sr_bf8_f32", + "llvm.amdgcn.cvt.sr.f16.f32" => "__builtin_amdgcn_cvt_sr_f16_f32", "llvm.amdgcn.cvt.sr.fp8.f32" => "__builtin_amdgcn_cvt_sr_fp8_f32", "llvm.amdgcn.dispatch.id" => "__builtin_amdgcn_dispatch_id", "llvm.amdgcn.dot4.f32.bf8.bf8" => "__builtin_amdgcn_dot4_f32_bf8_bf8", @@ -83,6 +138,7 @@ match name { "llvm.amdgcn.dot4.f32.fp8.fp8" => "__builtin_amdgcn_dot4_f32_fp8_fp8", "llvm.amdgcn.ds.add.gs.reg.rtn" => "__builtin_amdgcn_ds_add_gs_reg_rtn", "llvm.amdgcn.ds.bpermute" => "__builtin_amdgcn_ds_bpermute", + "llvm.amdgcn.ds.bpermute.fi.b32" => "__builtin_amdgcn_ds_bpermute_fi_b32", "llvm.amdgcn.ds.gws.barrier" => "__builtin_amdgcn_ds_gws_barrier", "llvm.amdgcn.ds.gws.init" => "__builtin_amdgcn_ds_gws_init", "llvm.amdgcn.ds.gws.sema.br" => "__builtin_amdgcn_ds_gws_sema_br", @@ -97,6 +153,7 @@ match name { "llvm.amdgcn.fdot2.bf16.bf16" => "__builtin_amdgcn_fdot2_bf16_bf16", "llvm.amdgcn.fdot2.f16.f16" => "__builtin_amdgcn_fdot2_f16_f16", "llvm.amdgcn.fdot2.f32.bf16" => "__builtin_amdgcn_fdot2_f32_bf16", + "llvm.amdgcn.fdot2c.f32.bf16" => "__builtin_amdgcn_fdot2c_f32_bf16", "llvm.amdgcn.fmul.legacy" => "__builtin_amdgcn_fmul_legacy", "llvm.amdgcn.global.load.lds" => "__builtin_amdgcn_global_load_lds", "llvm.amdgcn.groupstaticsize" => "__builtin_amdgcn_groupstaticsize", @@ -118,8 +175,10 @@ match name { "llvm.amdgcn.mfma.f32.16x16x16f16" => "__builtin_amdgcn_mfma_f32_16x16x16f16", "llvm.amdgcn.mfma.f32.16x16x1f32" => "__builtin_amdgcn_mfma_f32_16x16x1f32", "llvm.amdgcn.mfma.f32.16x16x2bf16" => "__builtin_amdgcn_mfma_f32_16x16x2bf16", + "llvm.amdgcn.mfma.f32.16x16x32.bf16" => "__builtin_amdgcn_mfma_f32_16x16x32_bf16", "llvm.amdgcn.mfma.f32.16x16x32.bf8.bf8" => "__builtin_amdgcn_mfma_f32_16x16x32_bf8_bf8", "llvm.amdgcn.mfma.f32.16x16x32.bf8.fp8" => "__builtin_amdgcn_mfma_f32_16x16x32_bf8_fp8", + "llvm.amdgcn.mfma.f32.16x16x32.f16" => "__builtin_amdgcn_mfma_f32_16x16x32_f16", "llvm.amdgcn.mfma.f32.16x16x32.fp8.bf8" => "__builtin_amdgcn_mfma_f32_16x16x32_fp8_bf8", "llvm.amdgcn.mfma.f32.16x16x32.fp8.fp8" => "__builtin_amdgcn_mfma_f32_16x16x32_fp8_fp8", "llvm.amdgcn.mfma.f32.16x16x4bf16.1k" => "__builtin_amdgcn_mfma_f32_16x16x4bf16_1k", @@ -127,8 +186,10 @@ match name { "llvm.amdgcn.mfma.f32.16x16x4f32" => "__builtin_amdgcn_mfma_f32_16x16x4f32", "llvm.amdgcn.mfma.f32.16x16x8.xf32" => "__builtin_amdgcn_mfma_f32_16x16x8_xf32", "llvm.amdgcn.mfma.f32.16x16x8bf16" => "__builtin_amdgcn_mfma_f32_16x16x8bf16", + "llvm.amdgcn.mfma.f32.32x32x16.bf16" => "__builtin_amdgcn_mfma_f32_32x32x16_bf16", "llvm.amdgcn.mfma.f32.32x32x16.bf8.bf8" => "__builtin_amdgcn_mfma_f32_32x32x16_bf8_bf8", "llvm.amdgcn.mfma.f32.32x32x16.bf8.fp8" => "__builtin_amdgcn_mfma_f32_32x32x16_bf8_fp8", + "llvm.amdgcn.mfma.f32.32x32x16.f16" => "__builtin_amdgcn_mfma_f32_32x32x16_f16", "llvm.amdgcn.mfma.f32.32x32x16.fp8.bf8" => "__builtin_amdgcn_mfma_f32_32x32x16_fp8_bf8", "llvm.amdgcn.mfma.f32.32x32x16.fp8.fp8" => "__builtin_amdgcn_mfma_f32_32x32x16_fp8_fp8", "llvm.amdgcn.mfma.f32.32x32x1f32" => "__builtin_amdgcn_mfma_f32_32x32x1f32", @@ -149,7 +210,9 @@ match name { "llvm.amdgcn.mfma.i32.16x16x16i8" => "__builtin_amdgcn_mfma_i32_16x16x16i8", "llvm.amdgcn.mfma.i32.16x16x32.i8" => "__builtin_amdgcn_mfma_i32_16x16x32_i8", "llvm.amdgcn.mfma.i32.16x16x4i8" => "__builtin_amdgcn_mfma_i32_16x16x4i8", + "llvm.amdgcn.mfma.i32.16x16x64.i8" => "__builtin_amdgcn_mfma_i32_16x16x64_i8", "llvm.amdgcn.mfma.i32.32x32x16.i8" => "__builtin_amdgcn_mfma_i32_32x32x16_i8", + "llvm.amdgcn.mfma.i32.32x32x32.i8" => "__builtin_amdgcn_mfma_i32_32x32x32_i8", "llvm.amdgcn.mfma.i32.32x32x4i8" => "__builtin_amdgcn_mfma_i32_32x32x4i8", "llvm.amdgcn.mfma.i32.32x32x8i8" => "__builtin_amdgcn_mfma_i32_32x32x8i8", "llvm.amdgcn.mfma.i32.4x4x4i8" => "__builtin_amdgcn_mfma_i32_4x4x4i8", @@ -159,25 +222,25 @@ match name { "llvm.amdgcn.perm" => "__builtin_amdgcn_perm", "llvm.amdgcn.permlane16.var" => "__builtin_amdgcn_permlane16_var", "llvm.amdgcn.permlanex16.var" => "__builtin_amdgcn_permlanex16_var", + "llvm.amdgcn.prng.b32" => "__builtin_amdgcn_prng_b32", "llvm.amdgcn.qsad.pk.u16.u8" => "__builtin_amdgcn_qsad_pk_u16_u8", "llvm.amdgcn.queue.ptr" => "__builtin_amdgcn_queue_ptr", + "llvm.amdgcn.raw.ptr.buffer.load.lds" => "__builtin_amdgcn_raw_ptr_buffer_load_lds", "llvm.amdgcn.rcp.legacy" => "__builtin_amdgcn_rcp_legacy", "llvm.amdgcn.rsq.legacy" => "__builtin_amdgcn_rsq_legacy", "llvm.amdgcn.s.barrier" => "__builtin_amdgcn_s_barrier", - "llvm.amdgcn.s.barrier.init" => "__builtin_amdgcn_s_barrier_init", - "llvm.amdgcn.s.barrier.join" => "__builtin_amdgcn_s_barrier_join", - "llvm.amdgcn.s.barrier.leave" => "__builtin_amdgcn_s_barrier_leave", "llvm.amdgcn.s.barrier.signal" => "__builtin_amdgcn_s_barrier_signal", "llvm.amdgcn.s.barrier.signal.isfirst" => "__builtin_amdgcn_s_barrier_signal_isfirst", - "llvm.amdgcn.s.barrier.signal.isfirst.var" => "__builtin_amdgcn_s_barrier_signal_isfirst_var", "llvm.amdgcn.s.barrier.signal.var" => "__builtin_amdgcn_s_barrier_signal_var", "llvm.amdgcn.s.barrier.wait" => "__builtin_amdgcn_s_barrier_wait", + "llvm.amdgcn.s.buffer.prefetch.data" => "__builtin_amdgcn_s_buffer_prefetch_data", "llvm.amdgcn.s.dcache.inv" => "__builtin_amdgcn_s_dcache_inv", "llvm.amdgcn.s.dcache.inv.vol" => "__builtin_amdgcn_s_dcache_inv_vol", "llvm.amdgcn.s.dcache.wb" => "__builtin_amdgcn_s_dcache_wb", "llvm.amdgcn.s.dcache.wb.vol" => "__builtin_amdgcn_s_dcache_wb_vol", "llvm.amdgcn.s.decperflevel" => "__builtin_amdgcn_s_decperflevel", "llvm.amdgcn.s.get.barrier.state" => "__builtin_amdgcn_s_get_barrier_state", + "llvm.amdgcn.s.get.named.barrier.state" => "__builtin_amdgcn_s_get_named_barrier_state", "llvm.amdgcn.s.get.waveid.in.workgroup" => "__builtin_amdgcn_s_get_waveid_in_workgroup", "llvm.amdgcn.s.getpc" => "__builtin_amdgcn_s_getpc", "llvm.amdgcn.s.getreg" => "__builtin_amdgcn_s_getreg", @@ -194,7 +257,6 @@ match name { "llvm.amdgcn.s.ttracedata.imm" => "__builtin_amdgcn_s_ttracedata_imm", "llvm.amdgcn.s.wait.event.export.ready" => "__builtin_amdgcn_s_wait_event_export_ready", "llvm.amdgcn.s.waitcnt" => "__builtin_amdgcn_s_waitcnt", - "llvm.amdgcn.s.wakeup.barrier" => "__builtin_amdgcn_s_wakeup_barrier", "llvm.amdgcn.sad.hi.u8" => "__builtin_amdgcn_sad_hi_u8", "llvm.amdgcn.sad.u16" => "__builtin_amdgcn_sad_u16", "llvm.amdgcn.sad.u8" => "__builtin_amdgcn_sad_u8", @@ -203,20 +265,34 @@ match name { "llvm.amdgcn.sdot2" => "__builtin_amdgcn_sdot2", "llvm.amdgcn.sdot4" => "__builtin_amdgcn_sdot4", "llvm.amdgcn.sdot8" => "__builtin_amdgcn_sdot8", + "llvm.amdgcn.smfmac.f32.16x16x128.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x128_bf8_bf8", + "llvm.amdgcn.smfmac.f32.16x16x128.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x128_bf8_fp8", + "llvm.amdgcn.smfmac.f32.16x16x128.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x128_fp8_bf8", + "llvm.amdgcn.smfmac.f32.16x16x128.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x128_fp8_fp8", "llvm.amdgcn.smfmac.f32.16x16x32.bf16" => "__builtin_amdgcn_smfmac_f32_16x16x32_bf16", "llvm.amdgcn.smfmac.f32.16x16x32.f16" => "__builtin_amdgcn_smfmac_f32_16x16x32_f16", + "llvm.amdgcn.smfmac.f32.16x16x64.bf16" => "__builtin_amdgcn_smfmac_f32_16x16x64_bf16", "llvm.amdgcn.smfmac.f32.16x16x64.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x64_bf8_bf8", "llvm.amdgcn.smfmac.f32.16x16x64.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x64_bf8_fp8", + "llvm.amdgcn.smfmac.f32.16x16x64.f16" => "__builtin_amdgcn_smfmac_f32_16x16x64_f16", "llvm.amdgcn.smfmac.f32.16x16x64.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x64_fp8_bf8", "llvm.amdgcn.smfmac.f32.16x16x64.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x64_fp8_fp8", "llvm.amdgcn.smfmac.f32.32x32x16.bf16" => "__builtin_amdgcn_smfmac_f32_32x32x16_bf16", "llvm.amdgcn.smfmac.f32.32x32x16.f16" => "__builtin_amdgcn_smfmac_f32_32x32x16_f16", + "llvm.amdgcn.smfmac.f32.32x32x32.bf16" => "__builtin_amdgcn_smfmac_f32_32x32x32_bf16", "llvm.amdgcn.smfmac.f32.32x32x32.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x32_bf8_bf8", "llvm.amdgcn.smfmac.f32.32x32x32.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x32_bf8_fp8", + "llvm.amdgcn.smfmac.f32.32x32x32.f16" => "__builtin_amdgcn_smfmac_f32_32x32x32_f16", "llvm.amdgcn.smfmac.f32.32x32x32.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x32_fp8_bf8", "llvm.amdgcn.smfmac.f32.32x32x32.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x32_fp8_fp8", + "llvm.amdgcn.smfmac.f32.32x32x64.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x64_bf8_bf8", + "llvm.amdgcn.smfmac.f32.32x32x64.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x64_bf8_fp8", + "llvm.amdgcn.smfmac.f32.32x32x64.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x64_fp8_bf8", + "llvm.amdgcn.smfmac.f32.32x32x64.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x64_fp8_fp8", + "llvm.amdgcn.smfmac.i32.16x16x128.i8" => "__builtin_amdgcn_smfmac_i32_16x16x128_i8", "llvm.amdgcn.smfmac.i32.16x16x64.i8" => "__builtin_amdgcn_smfmac_i32_16x16x64_i8", "llvm.amdgcn.smfmac.i32.32x32x32.i8" => "__builtin_amdgcn_smfmac_i32_32x32x32_i8", + "llvm.amdgcn.smfmac.i32.32x32x64.i8" => "__builtin_amdgcn_smfmac_i32_32x32x64_i8", "llvm.amdgcn.sudot4" => "__builtin_amdgcn_sudot4", "llvm.amdgcn.sudot8" => "__builtin_amdgcn_sudot8", "llvm.amdgcn.udot2" => "__builtin_amdgcn_udot2", @@ -227,6 +303,9 @@ match name { "llvm.amdgcn.workgroup.id.x" => "__builtin_amdgcn_workgroup_id_x", "llvm.amdgcn.workgroup.id.y" => "__builtin_amdgcn_workgroup_id_y", "llvm.amdgcn.workgroup.id.z" => "__builtin_amdgcn_workgroup_id_z", + "llvm.amdgcn.workitem.id.x" => "__builtin_amdgcn_workitem_id_x", + "llvm.amdgcn.workitem.id.y" => "__builtin_amdgcn_workitem_id_y", + "llvm.amdgcn.workitem.id.z" => "__builtin_amdgcn_workitem_id_z", // arm "llvm.arm.cdp" => "__builtin_arm_cdp", "llvm.arm.cdp2" => "__builtin_arm_cdp2", @@ -342,8 +421,6 @@ match name { "llvm.bpf.pseudo" => "__builtin_bpf_pseudo", // cuda "llvm.cuda.syncthreads" => "__syncthreads", - // dx - "llvm.dx.create.handle" => "__builtin_hlsl_create_handle", // hexagon "llvm.hexagon.A2.abs" => "__builtin_HEXAGON_A2_abs", "llvm.hexagon.A2.absp" => "__builtin_HEXAGON_A2_absp", @@ -1255,6 +1332,10 @@ match name { "llvm.hexagon.SI.to.SXTHI.asrh" => "__builtin_SI_to_SXTHI_asrh", "llvm.hexagon.V6.extractw" => "__builtin_HEXAGON_V6_extractw", "llvm.hexagon.V6.extractw.128B" => "__builtin_HEXAGON_V6_extractw_128B", + "llvm.hexagon.V6.get.qfext" => "__builtin_HEXAGON_V6_get_qfext", + "llvm.hexagon.V6.get.qfext.128B" => "__builtin_HEXAGON_V6_get_qfext_128B", + "llvm.hexagon.V6.get.qfext.oracc" => "__builtin_HEXAGON_V6_get_qfext_oracc", + "llvm.hexagon.V6.get.qfext.oracc.128B" => "__builtin_HEXAGON_V6_get_qfext_oracc_128B", "llvm.hexagon.V6.hi" => "__builtin_HEXAGON_V6_hi", "llvm.hexagon.V6.hi.128B" => "__builtin_HEXAGON_V6_hi_128B", "llvm.hexagon.V6.lo" => "__builtin_HEXAGON_V6_lo", @@ -1281,6 +1362,8 @@ match name { "llvm.hexagon.V6.pred.scalar2v2.128B" => "__builtin_HEXAGON_V6_pred_scalar2v2_128B", "llvm.hexagon.V6.pred.xor" => "__builtin_HEXAGON_V6_pred_xor", "llvm.hexagon.V6.pred.xor.128B" => "__builtin_HEXAGON_V6_pred_xor_128B", + "llvm.hexagon.V6.set.qfext" => "__builtin_HEXAGON_V6_set_qfext", + "llvm.hexagon.V6.set.qfext.128B" => "__builtin_HEXAGON_V6_set_qfext_128B", "llvm.hexagon.V6.shuffeqh" => "__builtin_HEXAGON_V6_shuffeqh", "llvm.hexagon.V6.shuffeqh.128B" => "__builtin_HEXAGON_V6_shuffeqh_128B", "llvm.hexagon.V6.shuffeqw" => "__builtin_HEXAGON_V6_shuffeqw", @@ -1301,6 +1384,8 @@ match name { "llvm.hexagon.V6.vS32b.nt.qpred.ai.128B" => "__builtin_HEXAGON_V6_vS32b_nt_qpred_ai_128B", "llvm.hexagon.V6.vS32b.qpred.ai" => "__builtin_HEXAGON_V6_vS32b_qpred_ai", "llvm.hexagon.V6.vS32b.qpred.ai.128B" => "__builtin_HEXAGON_V6_vS32b_qpred_ai_128B", + "llvm.hexagon.V6.vabs.f8" => "__builtin_HEXAGON_V6_vabs_f8", + "llvm.hexagon.V6.vabs.f8.128B" => "__builtin_HEXAGON_V6_vabs_f8_128B", "llvm.hexagon.V6.vabs.hf" => "__builtin_HEXAGON_V6_vabs_hf", "llvm.hexagon.V6.vabs.hf.128B" => "__builtin_HEXAGON_V6_vabs_hf_128B", "llvm.hexagon.V6.vabs.sf" => "__builtin_HEXAGON_V6_vabs_sf", @@ -1327,6 +1412,8 @@ match name { "llvm.hexagon.V6.vabsw.sat.128B" => "__builtin_HEXAGON_V6_vabsw_sat_128B", "llvm.hexagon.V6.vadd.hf" => "__builtin_HEXAGON_V6_vadd_hf", "llvm.hexagon.V6.vadd.hf.128B" => "__builtin_HEXAGON_V6_vadd_hf_128B", + "llvm.hexagon.V6.vadd.hf.f8" => "__builtin_HEXAGON_V6_vadd_hf_f8", + "llvm.hexagon.V6.vadd.hf.f8.128B" => "__builtin_HEXAGON_V6_vadd_hf_f8_128B", "llvm.hexagon.V6.vadd.hf.hf" => "__builtin_HEXAGON_V6_vadd_hf_hf", "llvm.hexagon.V6.vadd.hf.hf.128B" => "__builtin_HEXAGON_V6_vadd_hf_hf_128B", "llvm.hexagon.V6.vadd.qf16" => "__builtin_HEXAGON_V6_vadd_qf16", @@ -1549,10 +1636,14 @@ match name { "llvm.hexagon.V6.vcvt.b.hf.128B" => "__builtin_HEXAGON_V6_vcvt_b_hf_128B", "llvm.hexagon.V6.vcvt.bf.sf" => "__builtin_HEXAGON_V6_vcvt_bf_sf", "llvm.hexagon.V6.vcvt.bf.sf.128B" => "__builtin_HEXAGON_V6_vcvt_bf_sf_128B", + "llvm.hexagon.V6.vcvt.f8.hf" => "__builtin_HEXAGON_V6_vcvt_f8_hf", + "llvm.hexagon.V6.vcvt.f8.hf.128B" => "__builtin_HEXAGON_V6_vcvt_f8_hf_128B", "llvm.hexagon.V6.vcvt.h.hf" => "__builtin_HEXAGON_V6_vcvt_h_hf", "llvm.hexagon.V6.vcvt.h.hf.128B" => "__builtin_HEXAGON_V6_vcvt_h_hf_128B", "llvm.hexagon.V6.vcvt.hf.b" => "__builtin_HEXAGON_V6_vcvt_hf_b", "llvm.hexagon.V6.vcvt.hf.b.128B" => "__builtin_HEXAGON_V6_vcvt_hf_b_128B", + "llvm.hexagon.V6.vcvt.hf.f8" => "__builtin_HEXAGON_V6_vcvt_hf_f8", + "llvm.hexagon.V6.vcvt.hf.f8.128B" => "__builtin_HEXAGON_V6_vcvt_hf_f8_128B", "llvm.hexagon.V6.vcvt.hf.h" => "__builtin_HEXAGON_V6_vcvt_hf_h", "llvm.hexagon.V6.vcvt.hf.h.128B" => "__builtin_HEXAGON_V6_vcvt_hf_h_128B", "llvm.hexagon.V6.vcvt.hf.sf" => "__builtin_HEXAGON_V6_vcvt_hf_sf", @@ -1567,6 +1658,14 @@ match name { "llvm.hexagon.V6.vcvt.ub.hf.128B" => "__builtin_HEXAGON_V6_vcvt_ub_hf_128B", "llvm.hexagon.V6.vcvt.uh.hf" => "__builtin_HEXAGON_V6_vcvt_uh_hf", "llvm.hexagon.V6.vcvt.uh.hf.128B" => "__builtin_HEXAGON_V6_vcvt_uh_hf_128B", + "llvm.hexagon.V6.vcvt2.b.hf" => "__builtin_HEXAGON_V6_vcvt2_b_hf", + "llvm.hexagon.V6.vcvt2.b.hf.128B" => "__builtin_HEXAGON_V6_vcvt2_b_hf_128B", + "llvm.hexagon.V6.vcvt2.hf.b" => "__builtin_HEXAGON_V6_vcvt2_hf_b", + "llvm.hexagon.V6.vcvt2.hf.b.128B" => "__builtin_HEXAGON_V6_vcvt2_hf_b_128B", + "llvm.hexagon.V6.vcvt2.hf.ub" => "__builtin_HEXAGON_V6_vcvt2_hf_ub", + "llvm.hexagon.V6.vcvt2.hf.ub.128B" => "__builtin_HEXAGON_V6_vcvt2_hf_ub_128B", + "llvm.hexagon.V6.vcvt2.ub.hf" => "__builtin_HEXAGON_V6_vcvt2_ub_hf", + "llvm.hexagon.V6.vcvt2.ub.hf.128B" => "__builtin_HEXAGON_V6_vcvt2_ub_hf_128B", "llvm.hexagon.V6.vd0" => "__builtin_HEXAGON_V6_vd0", "llvm.hexagon.V6.vd0.128B" => "__builtin_HEXAGON_V6_vd0_128B", "llvm.hexagon.V6.vdd0" => "__builtin_HEXAGON_V6_vdd0", @@ -1649,14 +1748,20 @@ match name { "llvm.hexagon.V6.veqw.or.128B" => "__builtin_HEXAGON_V6_veqw_or_128B", "llvm.hexagon.V6.veqw.xor" => "__builtin_HEXAGON_V6_veqw_xor", "llvm.hexagon.V6.veqw.xor.128B" => "__builtin_HEXAGON_V6_veqw_xor_128B", + "llvm.hexagon.V6.vfmax.f8" => "__builtin_HEXAGON_V6_vfmax_f8", + "llvm.hexagon.V6.vfmax.f8.128B" => "__builtin_HEXAGON_V6_vfmax_f8_128B", "llvm.hexagon.V6.vfmax.hf" => "__builtin_HEXAGON_V6_vfmax_hf", "llvm.hexagon.V6.vfmax.hf.128B" => "__builtin_HEXAGON_V6_vfmax_hf_128B", "llvm.hexagon.V6.vfmax.sf" => "__builtin_HEXAGON_V6_vfmax_sf", "llvm.hexagon.V6.vfmax.sf.128B" => "__builtin_HEXAGON_V6_vfmax_sf_128B", + "llvm.hexagon.V6.vfmin.f8" => "__builtin_HEXAGON_V6_vfmin_f8", + "llvm.hexagon.V6.vfmin.f8.128B" => "__builtin_HEXAGON_V6_vfmin_f8_128B", "llvm.hexagon.V6.vfmin.hf" => "__builtin_HEXAGON_V6_vfmin_hf", "llvm.hexagon.V6.vfmin.hf.128B" => "__builtin_HEXAGON_V6_vfmin_hf_128B", "llvm.hexagon.V6.vfmin.sf" => "__builtin_HEXAGON_V6_vfmin_sf", "llvm.hexagon.V6.vfmin.sf.128B" => "__builtin_HEXAGON_V6_vfmin_sf_128B", + "llvm.hexagon.V6.vfneg.f8" => "__builtin_HEXAGON_V6_vfneg_f8", + "llvm.hexagon.V6.vfneg.f8.128B" => "__builtin_HEXAGON_V6_vfneg_f8_128B", "llvm.hexagon.V6.vfneg.hf" => "__builtin_HEXAGON_V6_vfneg_hf", "llvm.hexagon.V6.vfneg.hf.128B" => "__builtin_HEXAGON_V6_vfneg_hf_128B", "llvm.hexagon.V6.vfneg.sf" => "__builtin_HEXAGON_V6_vfneg_sf", @@ -1807,6 +1912,8 @@ match name { "llvm.hexagon.V6.vmaxuh.128B" => "__builtin_HEXAGON_V6_vmaxuh_128B", "llvm.hexagon.V6.vmaxw" => "__builtin_HEXAGON_V6_vmaxw", "llvm.hexagon.V6.vmaxw.128B" => "__builtin_HEXAGON_V6_vmaxw_128B", + "llvm.hexagon.V6.vmerge.qf" => "__builtin_HEXAGON_V6_vmerge_qf", + "llvm.hexagon.V6.vmerge.qf.128B" => "__builtin_HEXAGON_V6_vmerge_qf_128B", "llvm.hexagon.V6.vmin.bf" => "__builtin_HEXAGON_V6_vmin_bf", "llvm.hexagon.V6.vmin.bf.128B" => "__builtin_HEXAGON_V6_vmin_bf_128B", "llvm.hexagon.V6.vmin.hf" => "__builtin_HEXAGON_V6_vmin_hf", @@ -1849,6 +1956,10 @@ match name { "llvm.hexagon.V6.vmpauhuhsat.128B" => "__builtin_HEXAGON_V6_vmpauhuhsat_128B", "llvm.hexagon.V6.vmpsuhuhsat" => "__builtin_HEXAGON_V6_vmpsuhuhsat", "llvm.hexagon.V6.vmpsuhuhsat.128B" => "__builtin_HEXAGON_V6_vmpsuhuhsat_128B", + "llvm.hexagon.V6.vmpy.hf.f8" => "__builtin_HEXAGON_V6_vmpy_hf_f8", + "llvm.hexagon.V6.vmpy.hf.f8.128B" => "__builtin_HEXAGON_V6_vmpy_hf_f8_128B", + "llvm.hexagon.V6.vmpy.hf.f8.acc" => "__builtin_HEXAGON_V6_vmpy_hf_f8_acc", + "llvm.hexagon.V6.vmpy.hf.f8.acc.128B" => "__builtin_HEXAGON_V6_vmpy_hf_f8_acc_128B", "llvm.hexagon.V6.vmpy.hf.hf" => "__builtin_HEXAGON_V6_vmpy_hf_hf", "llvm.hexagon.V6.vmpy.hf.hf.128B" => "__builtin_HEXAGON_V6_vmpy_hf_hf_128B", "llvm.hexagon.V6.vmpy.hf.hf.acc" => "__builtin_HEXAGON_V6_vmpy_hf_hf_acc", @@ -1869,6 +1980,12 @@ match name { "llvm.hexagon.V6.vmpy.qf32.qf16.128B" => "__builtin_HEXAGON_V6_vmpy_qf32_qf16_128B", "llvm.hexagon.V6.vmpy.qf32.sf" => "__builtin_HEXAGON_V6_vmpy_qf32_sf", "llvm.hexagon.V6.vmpy.qf32.sf.128B" => "__builtin_HEXAGON_V6_vmpy_qf32_sf_128B", + "llvm.hexagon.V6.vmpy.rt.hf" => "__builtin_HEXAGON_V6_vmpy_rt_hf", + "llvm.hexagon.V6.vmpy.rt.hf.128B" => "__builtin_HEXAGON_V6_vmpy_rt_hf_128B", + "llvm.hexagon.V6.vmpy.rt.qf16" => "__builtin_HEXAGON_V6_vmpy_rt_qf16", + "llvm.hexagon.V6.vmpy.rt.qf16.128B" => "__builtin_HEXAGON_V6_vmpy_rt_qf16_128B", + "llvm.hexagon.V6.vmpy.rt.sf" => "__builtin_HEXAGON_V6_vmpy_rt_sf", + "llvm.hexagon.V6.vmpy.rt.sf.128B" => "__builtin_HEXAGON_V6_vmpy_rt_sf_128B", "llvm.hexagon.V6.vmpy.sf.bf" => "__builtin_HEXAGON_V6_vmpy_sf_bf", "llvm.hexagon.V6.vmpy.sf.bf.128B" => "__builtin_HEXAGON_V6_vmpy_sf_bf_128B", "llvm.hexagon.V6.vmpy.sf.bf.acc" => "__builtin_HEXAGON_V6_vmpy_sf_bf_acc", @@ -2127,6 +2244,8 @@ match name { "llvm.hexagon.V6.vshufoh.128B" => "__builtin_HEXAGON_V6_vshufoh_128B", "llvm.hexagon.V6.vsub.hf" => "__builtin_HEXAGON_V6_vsub_hf", "llvm.hexagon.V6.vsub.hf.128B" => "__builtin_HEXAGON_V6_vsub_hf_128B", + "llvm.hexagon.V6.vsub.hf.f8" => "__builtin_HEXAGON_V6_vsub_hf_f8", + "llvm.hexagon.V6.vsub.hf.f8.128B" => "__builtin_HEXAGON_V6_vsub_hf_f8_128B", "llvm.hexagon.V6.vsub.hf.hf" => "__builtin_HEXAGON_V6_vsub_hf_hf", "llvm.hexagon.V6.vsub.hf.hf.128B" => "__builtin_HEXAGON_V6_vsub_hf_hf_128B", "llvm.hexagon.V6.vsub.qf16" => "__builtin_HEXAGON_V6_vsub_qf16", @@ -4445,8 +4564,6 @@ match name { "llvm.mips.xor.v" => "__builtin_msa_xor_v", "llvm.mips.xori.b" => "__builtin_msa_xori_b", // nvvm - "llvm.nvvm.abs.bf16" => "__nvvm_abs_bf16", - "llvm.nvvm.abs.bf16x2" => "__nvvm_abs_bf16x2", "llvm.nvvm.abs.i" => "__nvvm_abs_i", "llvm.nvvm.abs.ll" => "__nvvm_abs_ll", "llvm.nvvm.activemask" => "__nvvm_activemask", @@ -4473,6 +4590,10 @@ match name { "llvm.nvvm.barrier0.and" => "__nvvm_bar0_and", "llvm.nvvm.barrier0.or" => "__nvvm_bar0_or", "llvm.nvvm.barrier0.popc" => "__nvvm_bar0_popc", + "llvm.nvvm.bf16x2.to.ue8m0x2.rp" => "__nvvm_bf16x2_to_ue8m0x2_rp", + "llvm.nvvm.bf16x2.to.ue8m0x2.rp.satfinite" => "__nvvm_bf16x2_to_ue8m0x2_rp_satfinite", + "llvm.nvvm.bf16x2.to.ue8m0x2.rz" => "__nvvm_bf16x2_to_ue8m0x2_rz", + "llvm.nvvm.bf16x2.to.ue8m0x2.rz.satfinite" => "__nvvm_bf16x2_to_ue8m0x2_rz_satfinite", "llvm.nvvm.bf2h.rn" => "__nvvm_bf2h_rn", "llvm.nvvm.bf2h.rn.ftz" => "__nvvm_bf2h_rn_ftz", "llvm.nvvm.bitcast.d2ll" => "__nvvm_bitcast_d2ll", @@ -4523,6 +4644,8 @@ match name { "llvm.nvvm.d2ull.rz" => "__nvvm_d2ull_rz", "llvm.nvvm.div.approx.f" => "__nvvm_div_approx_f", "llvm.nvvm.div.approx.ftz.f" => "__nvvm_div_approx_ftz_f", + "llvm.nvvm.div.full" => "__nvvm_div_full", + "llvm.nvvm.div.full.ftz" => "__nvvm_div_full_ftz", "llvm.nvvm.div.rm.d" => "__nvvm_div_rm_d", "llvm.nvvm.div.rm.f" => "__nvvm_div_rm_f", "llvm.nvvm.div.rm.ftz.f" => "__nvvm_div_rm_ftz_f", @@ -4535,6 +4658,10 @@ match name { "llvm.nvvm.div.rz.d" => "__nvvm_div_rz_d", "llvm.nvvm.div.rz.f" => "__nvvm_div_rz_f", "llvm.nvvm.div.rz.ftz.f" => "__nvvm_div_rz_ftz_f", + "llvm.nvvm.e2m3x2.to.f16x2.rn" => "__nvvm_e2m3x2_to_f16x2_rn", + "llvm.nvvm.e2m3x2.to.f16x2.rn.relu" => "__nvvm_e2m3x2_to_f16x2_rn_relu", + "llvm.nvvm.e3m2x2.to.f16x2.rn" => "__nvvm_e3m2x2_to_f16x2_rn", + "llvm.nvvm.e3m2x2.to.f16x2.rn.relu" => "__nvvm_e3m2x2_to_f16x2_rn_relu", "llvm.nvvm.e4m3x2.to.f16x2.rn" => "__nvvm_e4m3x2_to_f16x2_rn", "llvm.nvvm.e4m3x2.to.f16x2.rn.relu" => "__nvvm_e4m3x2_to_f16x2_rn_relu", "llvm.nvvm.e5m2x2.to.f16x2.rn" => "__nvvm_e5m2x2_to_f16x2_rn", @@ -4569,7 +4696,16 @@ match name { "llvm.nvvm.f2ll.rp.ftz" => "__nvvm_f2ll_rp_ftz", "llvm.nvvm.f2ll.rz" => "__nvvm_f2ll_rz", "llvm.nvvm.f2ll.rz.ftz" => "__nvvm_f2ll_rz_ftz", + "llvm.nvvm.f2tf32.rn" => "__nvvm_f2tf32_rn", + "llvm.nvvm.f2tf32.rn.relu" => "__nvvm_f2tf32_rn_relu", + "llvm.nvvm.f2tf32.rn.relu.satfinite" => "__nvvm_f2tf32_rn_relu_satfinite", + "llvm.nvvm.f2tf32.rn.satfinite" => "__nvvm_f2tf32_rn_satfinite", "llvm.nvvm.f2tf32.rna" => "__nvvm_f2tf32_rna", + "llvm.nvvm.f2tf32.rna.satfinite" => "__nvvm_f2tf32_rna_satfinite", + "llvm.nvvm.f2tf32.rz" => "__nvvm_f2tf32_rz", + "llvm.nvvm.f2tf32.rz.relu" => "__nvvm_f2tf32_rz_relu", + "llvm.nvvm.f2tf32.rz.relu.satfinite" => "__nvvm_f2tf32_rz_relu_satfinite", + "llvm.nvvm.f2tf32.rz.satfinite" => "__nvvm_f2tf32_rz_satfinite", "llvm.nvvm.f2ui.rm" => "__nvvm_f2ui_rm", "llvm.nvvm.f2ui.rm.ftz" => "__nvvm_f2ui_rm_ftz", "llvm.nvvm.f2ui.rn" => "__nvvm_f2ui_rn", @@ -4589,10 +4725,18 @@ match name { "llvm.nvvm.fabs.d" => "__nvvm_fabs_d", "llvm.nvvm.fabs.f" => "__nvvm_fabs_f", "llvm.nvvm.fabs.ftz.f" => "__nvvm_fabs_ftz_f", + "llvm.nvvm.ff.to.e2m3x2.rn.relu.satfinite" => "__nvvm_ff_to_e2m3x2_rn_relu_satfinite", + "llvm.nvvm.ff.to.e2m3x2.rn.satfinite" => "__nvvm_ff_to_e2m3x2_rn_satfinite", + "llvm.nvvm.ff.to.e3m2x2.rn.relu.satfinite" => "__nvvm_ff_to_e3m2x2_rn_relu_satfinite", + "llvm.nvvm.ff.to.e3m2x2.rn.satfinite" => "__nvvm_ff_to_e3m2x2_rn_satfinite", "llvm.nvvm.ff.to.e4m3x2.rn" => "__nvvm_ff_to_e4m3x2_rn", "llvm.nvvm.ff.to.e4m3x2.rn.relu" => "__nvvm_ff_to_e4m3x2_rn_relu", "llvm.nvvm.ff.to.e5m2x2.rn" => "__nvvm_ff_to_e5m2x2_rn", "llvm.nvvm.ff.to.e5m2x2.rn.relu" => "__nvvm_ff_to_e5m2x2_rn_relu", + "llvm.nvvm.ff.to.ue8m0x2.rp" => "__nvvm_ff_to_ue8m0x2_rp", + "llvm.nvvm.ff.to.ue8m0x2.rp.satfinite" => "__nvvm_ff_to_ue8m0x2_rp_satfinite", + "llvm.nvvm.ff.to.ue8m0x2.rz" => "__nvvm_ff_to_ue8m0x2_rz", + "llvm.nvvm.ff.to.ue8m0x2.rz.satfinite" => "__nvvm_ff_to_ue8m0x2_rz_satfinite", "llvm.nvvm.ff2bf16x2.rn" => "__nvvm_ff2bf16x2_rn", "llvm.nvvm.ff2bf16x2.rn.relu" => "__nvvm_ff2bf16x2_rn_relu", "llvm.nvvm.ff2bf16x2.rz" => "__nvvm_ff2bf16x2_rz", @@ -4862,6 +5006,14 @@ match name { // [DUPLICATE]: "llvm.nvvm.read.ptx.sreg.warpsize" => "__nvvm_read_ptx_sreg_", "llvm.nvvm.redux.sync.add" => "__nvvm_redux_sync_add", "llvm.nvvm.redux.sync.and" => "__nvvm_redux_sync_and", + "llvm.nvvm.redux.sync.fmax" => "__nvvm_redux_sync_fmax", + "llvm.nvvm.redux.sync.fmax.NaN" => "__nvvm_redux_sync_fmax_NaN", + "llvm.nvvm.redux.sync.fmax.abs" => "__nvvm_redux_sync_fmax_abs", + "llvm.nvvm.redux.sync.fmax.abs.NaN" => "__nvvm_redux_sync_fmax_abs_NaN", + "llvm.nvvm.redux.sync.fmin" => "__nvvm_redux_sync_fmin", + "llvm.nvvm.redux.sync.fmin.NaN" => "__nvvm_redux_sync_fmin_NaN", + "llvm.nvvm.redux.sync.fmin.abs" => "__nvvm_redux_sync_fmin_abs", + "llvm.nvvm.redux.sync.fmin.abs.NaN" => "__nvvm_redux_sync_fmin_abs_NaN", "llvm.nvvm.redux.sync.max" => "__nvvm_redux_sync_max", "llvm.nvvm.redux.sync.min" => "__nvvm_redux_sync_min", "llvm.nvvm.redux.sync.or" => "__nvvm_redux_sync_or", @@ -5149,6 +5301,7 @@ match name { "llvm.nvvm.txq.num.mipmap.levels" => "__nvvm_txq_num_mipmap_levels", "llvm.nvvm.txq.num.samples" => "__nvvm_txq_num_samples", "llvm.nvvm.txq.width" => "__nvvm_txq_width", + "llvm.nvvm.ue8m0x2.to.bf16x2" => "__nvvm_ue8m0x2_to_bf16x2", "llvm.nvvm.ui2d.rm" => "__nvvm_ui2d_rm", "llvm.nvvm.ui2d.rn" => "__nvvm_ui2d_rn", "llvm.nvvm.ui2d.rp" => "__nvvm_ui2d_rp", @@ -5783,6 +5936,9 @@ match name { "llvm.r600.read.tgid.x" => "__builtin_r600_read_tgid_x", "llvm.r600.read.tgid.y" => "__builtin_r600_read_tgid_y", "llvm.r600.read.tgid.z" => "__builtin_r600_read_tgid_z", + "llvm.r600.read.tidig.x" => "__builtin_r600_read_tidig_x", + "llvm.r600.read.tidig.y" => "__builtin_r600_read_tidig_y", + "llvm.r600.read.tidig.z" => "__builtin_r600_read_tidig_z", // riscv "llvm.riscv.aes32dsi" => "__builtin_riscv_aes32dsi", "llvm.riscv.aes32dsmi" => "__builtin_riscv_aes32dsmi", @@ -5806,6 +5962,8 @@ match name { "llvm.riscv.sha512sum1" => "__builtin_riscv_sha512sum1", "llvm.riscv.sha512sum1r" => "__builtin_riscv_sha512sum1r", // s390 + "llvm.s390.bdepg" => "__builtin_s390_bdepg", + "llvm.s390.bextg" => "__builtin_s390_bextg", "llvm.s390.efpc" => "__builtin_s390_efpc", "llvm.s390.etnd" => "__builtin_tx_nesting_depth", "llvm.s390.lcbb" => "__builtin_s390_lcbb", @@ -5828,6 +5986,8 @@ match name { "llvm.s390.vavglf" => "__builtin_s390_vavglf", "llvm.s390.vavglg" => "__builtin_s390_vavglg", "llvm.s390.vavglh" => "__builtin_s390_vavglh", + "llvm.s390.vavglq" => "__builtin_s390_vavglq", + "llvm.s390.vavgq" => "__builtin_s390_vavgq", "llvm.s390.vbperm" => "__builtin_s390_vbperm", "llvm.s390.vcfn" => "__builtin_s390_vcfn", "llvm.s390.vcksm" => "__builtin_s390_vcksm", @@ -5839,6 +5999,7 @@ match name { "llvm.s390.verimf" => "__builtin_s390_verimf", "llvm.s390.verimg" => "__builtin_s390_verimg", "llvm.s390.verimh" => "__builtin_s390_verimh", + "llvm.s390.veval" => "__builtin_s390_veval", "llvm.s390.vfaeb" => "__builtin_s390_vfaeb", "llvm.s390.vfaef" => "__builtin_s390_vfaef", "llvm.s390.vfaeh" => "__builtin_s390_vfaeh", @@ -5857,6 +6018,11 @@ match name { "llvm.s390.vfenezb" => "__builtin_s390_vfenezb", "llvm.s390.vfenezf" => "__builtin_s390_vfenezf", "llvm.s390.vfenezh" => "__builtin_s390_vfenezh", + "llvm.s390.vgemb" => "__builtin_s390_vgemb", + "llvm.s390.vgemf" => "__builtin_s390_vgemf", + "llvm.s390.vgemg" => "__builtin_s390_vgemg", + "llvm.s390.vgemh" => "__builtin_s390_vgemh", + "llvm.s390.vgemq" => "__builtin_s390_vgemq", "llvm.s390.vgfmab" => "__builtin_s390_vgfmab", "llvm.s390.vgfmaf" => "__builtin_s390_vgfmaf", "llvm.s390.vgfmag" => "__builtin_s390_vgfmag", @@ -5873,39 +6039,55 @@ match name { "llvm.s390.vlrl" => "__builtin_s390_vlrlr", "llvm.s390.vmaeb" => "__builtin_s390_vmaeb", "llvm.s390.vmaef" => "__builtin_s390_vmaef", + "llvm.s390.vmaeg" => "__builtin_s390_vmaeg", "llvm.s390.vmaeh" => "__builtin_s390_vmaeh", "llvm.s390.vmahb" => "__builtin_s390_vmahb", "llvm.s390.vmahf" => "__builtin_s390_vmahf", + "llvm.s390.vmahg" => "__builtin_s390_vmahg", "llvm.s390.vmahh" => "__builtin_s390_vmahh", + "llvm.s390.vmahq" => "__builtin_s390_vmahq", "llvm.s390.vmaleb" => "__builtin_s390_vmaleb", "llvm.s390.vmalef" => "__builtin_s390_vmalef", + "llvm.s390.vmaleg" => "__builtin_s390_vmaleg", "llvm.s390.vmaleh" => "__builtin_s390_vmaleh", "llvm.s390.vmalhb" => "__builtin_s390_vmalhb", "llvm.s390.vmalhf" => "__builtin_s390_vmalhf", + "llvm.s390.vmalhg" => "__builtin_s390_vmalhg", "llvm.s390.vmalhh" => "__builtin_s390_vmalhh", + "llvm.s390.vmalhq" => "__builtin_s390_vmalhq", "llvm.s390.vmalob" => "__builtin_s390_vmalob", "llvm.s390.vmalof" => "__builtin_s390_vmalof", + "llvm.s390.vmalog" => "__builtin_s390_vmalog", "llvm.s390.vmaloh" => "__builtin_s390_vmaloh", "llvm.s390.vmaob" => "__builtin_s390_vmaob", "llvm.s390.vmaof" => "__builtin_s390_vmaof", + "llvm.s390.vmaog" => "__builtin_s390_vmaog", "llvm.s390.vmaoh" => "__builtin_s390_vmaoh", "llvm.s390.vmeb" => "__builtin_s390_vmeb", "llvm.s390.vmef" => "__builtin_s390_vmef", + "llvm.s390.vmeg" => "__builtin_s390_vmeg", "llvm.s390.vmeh" => "__builtin_s390_vmeh", "llvm.s390.vmhb" => "__builtin_s390_vmhb", "llvm.s390.vmhf" => "__builtin_s390_vmhf", + "llvm.s390.vmhg" => "__builtin_s390_vmhg", "llvm.s390.vmhh" => "__builtin_s390_vmhh", + "llvm.s390.vmhq" => "__builtin_s390_vmhq", "llvm.s390.vmleb" => "__builtin_s390_vmleb", "llvm.s390.vmlef" => "__builtin_s390_vmlef", + "llvm.s390.vmleg" => "__builtin_s390_vmleg", "llvm.s390.vmleh" => "__builtin_s390_vmleh", "llvm.s390.vmlhb" => "__builtin_s390_vmlhb", "llvm.s390.vmlhf" => "__builtin_s390_vmlhf", + "llvm.s390.vmlhg" => "__builtin_s390_vmlhg", "llvm.s390.vmlhh" => "__builtin_s390_vmlhh", + "llvm.s390.vmlhq" => "__builtin_s390_vmlhq", "llvm.s390.vmlob" => "__builtin_s390_vmlob", "llvm.s390.vmlof" => "__builtin_s390_vmlof", + "llvm.s390.vmlog" => "__builtin_s390_vmlog", "llvm.s390.vmloh" => "__builtin_s390_vmloh", "llvm.s390.vmob" => "__builtin_s390_vmob", "llvm.s390.vmof" => "__builtin_s390_vmof", + "llvm.s390.vmog" => "__builtin_s390_vmog", "llvm.s390.vmoh" => "__builtin_s390_vmoh", "llvm.s390.vmslg" => "__builtin_s390_vmslg", "llvm.s390.vpdi" => "__builtin_s390_vpdi", @@ -5950,18 +6132,20 @@ match name { "llvm.s390.vtm" => "__builtin_s390_vtm", "llvm.s390.vuphb" => "__builtin_s390_vuphb", "llvm.s390.vuphf" => "__builtin_s390_vuphf", + "llvm.s390.vuphg" => "__builtin_s390_vuphg", "llvm.s390.vuphh" => "__builtin_s390_vuphh", "llvm.s390.vuplb" => "__builtin_s390_vuplb", "llvm.s390.vuplf" => "__builtin_s390_vuplf", + "llvm.s390.vuplg" => "__builtin_s390_vuplg", "llvm.s390.vuplhb" => "__builtin_s390_vuplhb", "llvm.s390.vuplhf" => "__builtin_s390_vuplhf", + "llvm.s390.vuplhg" => "__builtin_s390_vuplhg", "llvm.s390.vuplhh" => "__builtin_s390_vuplhh", "llvm.s390.vuplhw" => "__builtin_s390_vuplhw", "llvm.s390.vupllb" => "__builtin_s390_vupllb", "llvm.s390.vupllf" => "__builtin_s390_vupllf", + "llvm.s390.vupllg" => "__builtin_s390_vupllg", "llvm.s390.vupllh" => "__builtin_s390_vupllh", - // spv - "llvm.spv.create.handle" => "__builtin_hlsl_create_handle", // ve "llvm.ve.vl.andm.MMM" => "__builtin_ve_vl_andm_MMM", "llvm.ve.vl.andm.mmm" => "__builtin_ve_vl_andm_mmm", @@ -7328,6 +7512,27 @@ match name { "llvm.x86.avx.vtestz.ps.256" => "__builtin_ia32_vtestzps256", "llvm.x86.avx.vzeroall" => "__builtin_ia32_vzeroall", "llvm.x86.avx.vzeroupper" => "__builtin_ia32_vzeroupper", + "llvm.x86.avx10.mask.getexp.bf16.128" => "__builtin_ia32_vgetexpbf16128_mask", + "llvm.x86.avx10.mask.getexp.bf16.256" => "__builtin_ia32_vgetexpbf16256_mask", + "llvm.x86.avx10.mask.getexp.bf16.512" => "__builtin_ia32_vgetexpbf16512_mask", + "llvm.x86.avx10.mask.getmant.bf16.128" => "__builtin_ia32_vgetmantbf16128_mask", + "llvm.x86.avx10.mask.getmant.bf16.256" => "__builtin_ia32_vgetmantbf16256_mask", + "llvm.x86.avx10.mask.getmant.bf16.512" => "__builtin_ia32_vgetmantbf16512_mask", + "llvm.x86.avx10.mask.rcp.bf16.128" => "__builtin_ia32_vrcpbf16128_mask", + "llvm.x86.avx10.mask.rcp.bf16.256" => "__builtin_ia32_vrcpbf16256_mask", + "llvm.x86.avx10.mask.rcp.bf16.512" => "__builtin_ia32_vrcpbf16512_mask", + "llvm.x86.avx10.mask.reduce.bf16.128" => "__builtin_ia32_vreducebf16128_mask", + "llvm.x86.avx10.mask.reduce.bf16.256" => "__builtin_ia32_vreducebf16256_mask", + "llvm.x86.avx10.mask.reduce.bf16.512" => "__builtin_ia32_vreducebf16512_mask", + "llvm.x86.avx10.mask.rndscale.bf16.128" => "__builtin_ia32_vrndscalebf16_128_mask", + "llvm.x86.avx10.mask.rndscale.bf16.256" => "__builtin_ia32_vrndscalebf16_256_mask", + "llvm.x86.avx10.mask.rndscale.bf16.512" => "__builtin_ia32_vrndscalebf16_mask", + "llvm.x86.avx10.mask.rsqrt.bf16.128" => "__builtin_ia32_vrsqrtbf16128_mask", + "llvm.x86.avx10.mask.rsqrt.bf16.256" => "__builtin_ia32_vrsqrtbf16256_mask", + "llvm.x86.avx10.mask.rsqrt.bf16.512" => "__builtin_ia32_vrsqrtbf16512_mask", + "llvm.x86.avx10.mask.scalef.bf16.128" => "__builtin_ia32_vscalefbf16128_mask", + "llvm.x86.avx10.mask.scalef.bf16.256" => "__builtin_ia32_vscalefbf16256_mask", + "llvm.x86.avx10.mask.scalef.bf16.512" => "__builtin_ia32_vscalefbf16512_mask", "llvm.x86.avx10.mask.vcvt2ps2phx.128" => "__builtin_ia32_vcvt2ps2phx128_mask", "llvm.x86.avx10.mask.vcvt2ps2phx.256" => "__builtin_ia32_vcvt2ps2phx256_mask", "llvm.x86.avx10.mask.vcvt2ps2phx.512" => "__builtin_ia32_vcvt2ps2phx512_mask", @@ -7346,171 +7551,194 @@ match name { "llvm.x86.avx10.mask.vcvthf82ph128" => "__builtin_ia32_vcvthf8_2ph128_mask", "llvm.x86.avx10.mask.vcvthf82ph256" => "__builtin_ia32_vcvthf8_2ph256_mask", "llvm.x86.avx10.mask.vcvthf82ph512" => "__builtin_ia32_vcvthf8_2ph512_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8128" => "__builtin_ia32_vcvtneph2bf8_128_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8256" => "__builtin_ia32_vcvtneph2bf8_256_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8512" => "__builtin_ia32_vcvtneph2bf8_512_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8s128" => "__builtin_ia32_vcvtneph2bf8s_128_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8s256" => "__builtin_ia32_vcvtneph2bf8s_256_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8s512" => "__builtin_ia32_vcvtneph2bf8s_512_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8128" => "__builtin_ia32_vcvtneph2hf8_128_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8256" => "__builtin_ia32_vcvtneph2hf8_256_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8512" => "__builtin_ia32_vcvtneph2hf8_512_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8s128" => "__builtin_ia32_vcvtneph2hf8s_128_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8s256" => "__builtin_ia32_vcvtneph2hf8s_256_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8s512" => "__builtin_ia32_vcvtneph2hf8s_512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2dq256" => "__builtin_ia32_vcvtpd2dq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2ph256" => "__builtin_ia32_vcvtpd2ph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2ps256" => "__builtin_ia32_vcvtpd2ps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2qq256" => "__builtin_ia32_vcvtpd2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2udq256" => "__builtin_ia32_vcvtpd2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2uqq256" => "__builtin_ia32_vcvtpd2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2dq256" => "__builtin_ia32_vcvtph2dq256_round_mask", + "llvm.x86.avx10.mask.vcvtph2bf8128" => "__builtin_ia32_vcvtph2bf8_128_mask", + "llvm.x86.avx10.mask.vcvtph2bf8256" => "__builtin_ia32_vcvtph2bf8_256_mask", + "llvm.x86.avx10.mask.vcvtph2bf8512" => "__builtin_ia32_vcvtph2bf8_512_mask", + "llvm.x86.avx10.mask.vcvtph2bf8s128" => "__builtin_ia32_vcvtph2bf8s_128_mask", + "llvm.x86.avx10.mask.vcvtph2bf8s256" => "__builtin_ia32_vcvtph2bf8s_256_mask", + "llvm.x86.avx10.mask.vcvtph2bf8s512" => "__builtin_ia32_vcvtph2bf8s_512_mask", + "llvm.x86.avx10.mask.vcvtph2hf8128" => "__builtin_ia32_vcvtph2hf8_128_mask", + "llvm.x86.avx10.mask.vcvtph2hf8256" => "__builtin_ia32_vcvtph2hf8_256_mask", + "llvm.x86.avx10.mask.vcvtph2hf8512" => "__builtin_ia32_vcvtph2hf8_512_mask", + "llvm.x86.avx10.mask.vcvtph2hf8s128" => "__builtin_ia32_vcvtph2hf8s_128_mask", + "llvm.x86.avx10.mask.vcvtph2hf8s256" => "__builtin_ia32_vcvtph2hf8s_256_mask", + "llvm.x86.avx10.mask.vcvtph2hf8s512" => "__builtin_ia32_vcvtph2hf8s_512_mask", "llvm.x86.avx10.mask.vcvtph2ibs128" => "__builtin_ia32_vcvtph2ibs128_mask", "llvm.x86.avx10.mask.vcvtph2ibs256" => "__builtin_ia32_vcvtph2ibs256_mask", "llvm.x86.avx10.mask.vcvtph2ibs512" => "__builtin_ia32_vcvtph2ibs512_mask", "llvm.x86.avx10.mask.vcvtph2iubs128" => "__builtin_ia32_vcvtph2iubs128_mask", "llvm.x86.avx10.mask.vcvtph2iubs256" => "__builtin_ia32_vcvtph2iubs256_mask", "llvm.x86.avx10.mask.vcvtph2iubs512" => "__builtin_ia32_vcvtph2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2pd256" => "__builtin_ia32_vcvtph2pd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2psx256" => "__builtin_ia32_vcvtph2psx256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2qq256" => "__builtin_ia32_vcvtph2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2udq256" => "__builtin_ia32_vcvtph2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2uqq256" => "__builtin_ia32_vcvtph2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2uw256" => "__builtin_ia32_vcvtph2uw256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2w256" => "__builtin_ia32_vcvtph2w256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2dq256" => "__builtin_ia32_vcvtps2dq256_round_mask", "llvm.x86.avx10.mask.vcvtps2ibs128" => "__builtin_ia32_vcvtps2ibs128_mask", "llvm.x86.avx10.mask.vcvtps2ibs256" => "__builtin_ia32_vcvtps2ibs256_mask", "llvm.x86.avx10.mask.vcvtps2ibs512" => "__builtin_ia32_vcvtps2ibs512_mask", "llvm.x86.avx10.mask.vcvtps2iubs128" => "__builtin_ia32_vcvtps2iubs128_mask", "llvm.x86.avx10.mask.vcvtps2iubs256" => "__builtin_ia32_vcvtps2iubs256_mask", "llvm.x86.avx10.mask.vcvtps2iubs512" => "__builtin_ia32_vcvtps2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2pd256" => "__builtin_ia32_vcvtps2pd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2ph256" => "__builtin_ia32_vcvtps2ph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2phx256" => "__builtin_ia32_vcvtps2phx256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2qq256" => "__builtin_ia32_vcvtps2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2udq256" => "__builtin_ia32_vcvtps2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2uqq256" => "__builtin_ia32_vcvtps2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2dq256" => "__builtin_ia32_vcvttpd2dq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2qq256" => "__builtin_ia32_vcvttpd2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2udq256" => "__builtin_ia32_vcvttpd2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2uqq256" => "__builtin_ia32_vcvttpd2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2dq256" => "__builtin_ia32_vcvttph2dq256_round_mask", + "llvm.x86.avx10.mask.vcvttpd2dqs.128" => "__builtin_ia32_vcvttpd2dqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2dqs.256" => "__builtin_ia32_vcvttpd2dqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2dqs.round.512" => "__builtin_ia32_vcvttpd2dqs512_round_mask", + "llvm.x86.avx10.mask.vcvttpd2qqs.128" => "__builtin_ia32_vcvttpd2qqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2qqs.256" => "__builtin_ia32_vcvttpd2qqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2qqs.round.512" => "__builtin_ia32_vcvttpd2qqs512_round_mask", + "llvm.x86.avx10.mask.vcvttpd2udqs.128" => "__builtin_ia32_vcvttpd2udqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2udqs.256" => "__builtin_ia32_vcvttpd2udqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2udqs.round.512" => "__builtin_ia32_vcvttpd2udqs512_round_mask", + "llvm.x86.avx10.mask.vcvttpd2uqqs.128" => "__builtin_ia32_vcvttpd2uqqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2uqqs.256" => "__builtin_ia32_vcvttpd2uqqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2uqqs.round.512" => "__builtin_ia32_vcvttpd2uqqs512_round_mask", "llvm.x86.avx10.mask.vcvttph2ibs128" => "__builtin_ia32_vcvttph2ibs128_mask", "llvm.x86.avx10.mask.vcvttph2ibs256" => "__builtin_ia32_vcvttph2ibs256_mask", "llvm.x86.avx10.mask.vcvttph2ibs512" => "__builtin_ia32_vcvttph2ibs512_mask", "llvm.x86.avx10.mask.vcvttph2iubs128" => "__builtin_ia32_vcvttph2iubs128_mask", "llvm.x86.avx10.mask.vcvttph2iubs256" => "__builtin_ia32_vcvttph2iubs256_mask", "llvm.x86.avx10.mask.vcvttph2iubs512" => "__builtin_ia32_vcvttph2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2qq256" => "__builtin_ia32_vcvttph2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2udq256" => "__builtin_ia32_vcvttph2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2uqq256" => "__builtin_ia32_vcvttph2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2uw256" => "__builtin_ia32_vcvttph2uw256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2w256" => "__builtin_ia32_vcvttph2w256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2dq256" => "__builtin_ia32_vcvttps2dq256_round_mask", + "llvm.x86.avx10.mask.vcvttps2dqs.128" => "__builtin_ia32_vcvttps2dqs128_mask", + "llvm.x86.avx10.mask.vcvttps2dqs.256" => "__builtin_ia32_vcvttps2dqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2dqs.round.512" => "__builtin_ia32_vcvttps2dqs512_round_mask", "llvm.x86.avx10.mask.vcvttps2ibs128" => "__builtin_ia32_vcvttps2ibs128_mask", "llvm.x86.avx10.mask.vcvttps2ibs256" => "__builtin_ia32_vcvttps2ibs256_mask", "llvm.x86.avx10.mask.vcvttps2ibs512" => "__builtin_ia32_vcvttps2ibs512_mask", "llvm.x86.avx10.mask.vcvttps2iubs128" => "__builtin_ia32_vcvttps2iubs128_mask", "llvm.x86.avx10.mask.vcvttps2iubs256" => "__builtin_ia32_vcvttps2iubs256_mask", "llvm.x86.avx10.mask.vcvttps2iubs512" => "__builtin_ia32_vcvttps2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2qq256" => "__builtin_ia32_vcvttps2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2udq256" => "__builtin_ia32_vcvttps2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2uqq256" => "__builtin_ia32_vcvttps2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfcmaddcph256" => "__builtin_ia32_vfcmaddcph256_round_mask3", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfcmulcph256" => "__builtin_ia32_vfcmulcph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfixupimmpd256" => "__builtin_ia32_vfixupimmpd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfixupimmps256" => "__builtin_ia32_vfixupimmps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfmaddcph256" => "__builtin_ia32_vfmaddcph256_round_mask3", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfmulcph256" => "__builtin_ia32_vfmulcph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexppd256" => "__builtin_ia32_vgetexppd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexpph256" => "__builtin_ia32_vgetexpph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexpps256" => "__builtin_ia32_vgetexpps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantpd256" => "__builtin_ia32_vgetmantpd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantph256" => "__builtin_ia32_vgetmantph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantps256" => "__builtin_ia32_vgetmantps256_round_mask", + "llvm.x86.avx10.mask.vcvttps2qqs.128" => "__builtin_ia32_vcvttps2qqs128_mask", + "llvm.x86.avx10.mask.vcvttps2qqs.256" => "__builtin_ia32_vcvttps2qqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2qqs.round.512" => "__builtin_ia32_vcvttps2qqs512_round_mask", + "llvm.x86.avx10.mask.vcvttps2udqs.128" => "__builtin_ia32_vcvttps2udqs128_mask", + "llvm.x86.avx10.mask.vcvttps2udqs.256" => "__builtin_ia32_vcvttps2udqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2udqs.round.512" => "__builtin_ia32_vcvttps2udqs512_round_mask", + "llvm.x86.avx10.mask.vcvttps2uqqs.128" => "__builtin_ia32_vcvttps2uqqs128_mask", + "llvm.x86.avx10.mask.vcvttps2uqqs.256" => "__builtin_ia32_vcvttps2uqqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2uqqs.round.512" => "__builtin_ia32_vcvttps2uqqs512_round_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxpd.round" => "__builtin_ia32_vminmaxpd512_round_mask", "llvm.x86.avx10.mask.vminmaxpd128" => "__builtin_ia32_vminmaxpd128_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxpd256.round" => "__builtin_ia32_vminmaxpd256_round_mask", + "llvm.x86.avx10.mask.vminmaxpd256" => "__builtin_ia32_vminmaxpd256_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxph.round" => "__builtin_ia32_vminmaxph512_round_mask", "llvm.x86.avx10.mask.vminmaxph128" => "__builtin_ia32_vminmaxph128_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxph256.round" => "__builtin_ia32_vminmaxph256_round_mask", + "llvm.x86.avx10.mask.vminmaxph256" => "__builtin_ia32_vminmaxph256_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxps.round" => "__builtin_ia32_vminmaxps512_round_mask", "llvm.x86.avx10.mask.vminmaxps128" => "__builtin_ia32_vminmaxps128_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxps256.round" => "__builtin_ia32_vminmaxps256_round_mask", + "llvm.x86.avx10.mask.vminmaxps256" => "__builtin_ia32_vminmaxps256_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxsd.round" => "__builtin_ia32_vminmaxsd_round_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxsh.round" => "__builtin_ia32_vminmaxsh_round_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxss.round" => "__builtin_ia32_vminmaxss_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrangepd256" => "__builtin_ia32_vrangepd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrangeps256" => "__builtin_ia32_vrangeps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreducepd256" => "__builtin_ia32_vreducepd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreduceph256" => "__builtin_ia32_vreduceph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreduceps256" => "__builtin_ia32_vreduceps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscalepd256" => "__builtin_ia32_vrndscalepd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscaleph256" => "__builtin_ia32_vrndscaleph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscaleps256" => "__builtin_ia32_vrndscaleps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefpd256" => "__builtin_ia32_vscalefpd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefph256" => "__builtin_ia32_vscalefph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefps256" => "__builtin_ia32_vscalefps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfcmaddcph256" => "__builtin_ia32_vfcmaddcph256_round_maskz", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfixupimmpd256" => "__builtin_ia32_vfixupimmpd256_round_maskz", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfixupimmps256" => "__builtin_ia32_vfixupimmps256_round_maskz", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfmaddcph256" => "__builtin_ia32_vfmaddcph256_round_maskz", + "llvm.x86.avx10.vaddbf16128" => "__builtin_ia32_vaddbf16128", + "llvm.x86.avx10.vaddbf16256" => "__builtin_ia32_vaddbf16256", + "llvm.x86.avx10.vaddbf16512" => "__builtin_ia32_vaddbf16512", "llvm.x86.avx10.vaddpd256" => "__builtin_ia32_vaddpd256_round", "llvm.x86.avx10.vaddph256" => "__builtin_ia32_vaddph256_round", "llvm.x86.avx10.vaddps256" => "__builtin_ia32_vaddps256_round", - "llvm.x86.avx10.vcvtne2ph2bf8128" => "__builtin_ia32_vcvtne2ph2bf8_128", - "llvm.x86.avx10.vcvtne2ph2bf8256" => "__builtin_ia32_vcvtne2ph2bf8_256", - "llvm.x86.avx10.vcvtne2ph2bf8512" => "__builtin_ia32_vcvtne2ph2bf8_512", - "llvm.x86.avx10.vcvtne2ph2bf8s128" => "__builtin_ia32_vcvtne2ph2bf8s_128", - "llvm.x86.avx10.vcvtne2ph2bf8s256" => "__builtin_ia32_vcvtne2ph2bf8s_256", - "llvm.x86.avx10.vcvtne2ph2bf8s512" => "__builtin_ia32_vcvtne2ph2bf8s_512", - "llvm.x86.avx10.vcvtne2ph2hf8128" => "__builtin_ia32_vcvtne2ph2hf8_128", - "llvm.x86.avx10.vcvtne2ph2hf8256" => "__builtin_ia32_vcvtne2ph2hf8_256", - "llvm.x86.avx10.vcvtne2ph2hf8512" => "__builtin_ia32_vcvtne2ph2hf8_512", - "llvm.x86.avx10.vcvtne2ph2hf8s128" => "__builtin_ia32_vcvtne2ph2hf8s_128", - "llvm.x86.avx10.vcvtne2ph2hf8s256" => "__builtin_ia32_vcvtne2ph2hf8s_256", - "llvm.x86.avx10.vcvtne2ph2hf8s512" => "__builtin_ia32_vcvtne2ph2hf8s_512", - "llvm.x86.avx10.vcvtnebf162ibs128" => "__builtin_ia32_vcvtnebf162ibs128", - "llvm.x86.avx10.vcvtnebf162ibs256" => "__builtin_ia32_vcvtnebf162ibs256", - "llvm.x86.avx10.vcvtnebf162ibs512" => "__builtin_ia32_vcvtnebf162ibs512", - "llvm.x86.avx10.vcvtnebf162iubs128" => "__builtin_ia32_vcvtnebf162iubs128", - "llvm.x86.avx10.vcvtnebf162iubs256" => "__builtin_ia32_vcvtnebf162iubs256", - "llvm.x86.avx10.vcvtnebf162iubs512" => "__builtin_ia32_vcvtnebf162iubs512", - "llvm.x86.avx10.vcvttnebf162ibs128" => "__builtin_ia32_vcvttnebf162ibs128", - "llvm.x86.avx10.vcvttnebf162ibs256" => "__builtin_ia32_vcvttnebf162ibs256", - "llvm.x86.avx10.vcvttnebf162ibs512" => "__builtin_ia32_vcvttnebf162ibs512", - "llvm.x86.avx10.vcvttnebf162iubs128" => "__builtin_ia32_vcvttnebf162iubs128", - "llvm.x86.avx10.vcvttnebf162iubs256" => "__builtin_ia32_vcvttnebf162iubs256", - "llvm.x86.avx10.vcvttnebf162iubs512" => "__builtin_ia32_vcvttnebf162iubs512", - "llvm.x86.avx10.vdivpd256" => "__builtin_ia32_vdivpd256_round", - "llvm.x86.avx10.vdivph256" => "__builtin_ia32_vdivph256_round", - "llvm.x86.avx10.vdivps256" => "__builtin_ia32_vdivps256_round", + "llvm.x86.avx10.vcomisbf16eq" => "__builtin_ia32_vcomisbf16eq", + "llvm.x86.avx10.vcomisbf16ge" => "__builtin_ia32_vcomisbf16ge", + "llvm.x86.avx10.vcomisbf16gt" => "__builtin_ia32_vcomisbf16gt", + "llvm.x86.avx10.vcomisbf16le" => "__builtin_ia32_vcomisbf16le", + "llvm.x86.avx10.vcomisbf16lt" => "__builtin_ia32_vcomisbf16lt", + "llvm.x86.avx10.vcomisbf16neq" => "__builtin_ia32_vcomisbf16neq", + "llvm.x86.avx10.vcvt2ph2bf8128" => "__builtin_ia32_vcvt2ph2bf8_128", + "llvm.x86.avx10.vcvt2ph2bf8256" => "__builtin_ia32_vcvt2ph2bf8_256", + "llvm.x86.avx10.vcvt2ph2bf8512" => "__builtin_ia32_vcvt2ph2bf8_512", + "llvm.x86.avx10.vcvt2ph2bf8s128" => "__builtin_ia32_vcvt2ph2bf8s_128", + "llvm.x86.avx10.vcvt2ph2bf8s256" => "__builtin_ia32_vcvt2ph2bf8s_256", + "llvm.x86.avx10.vcvt2ph2bf8s512" => "__builtin_ia32_vcvt2ph2bf8s_512", + "llvm.x86.avx10.vcvt2ph2hf8128" => "__builtin_ia32_vcvt2ph2hf8_128", + "llvm.x86.avx10.vcvt2ph2hf8256" => "__builtin_ia32_vcvt2ph2hf8_256", + "llvm.x86.avx10.vcvt2ph2hf8512" => "__builtin_ia32_vcvt2ph2hf8_512", + "llvm.x86.avx10.vcvt2ph2hf8s128" => "__builtin_ia32_vcvt2ph2hf8s_128", + "llvm.x86.avx10.vcvt2ph2hf8s256" => "__builtin_ia32_vcvt2ph2hf8s_256", + "llvm.x86.avx10.vcvt2ph2hf8s512" => "__builtin_ia32_vcvt2ph2hf8s_512", + "llvm.x86.avx10.vcvtbf162ibs128" => "__builtin_ia32_vcvtbf162ibs128", + "llvm.x86.avx10.vcvtbf162ibs256" => "__builtin_ia32_vcvtbf162ibs256", + "llvm.x86.avx10.vcvtbf162ibs512" => "__builtin_ia32_vcvtbf162ibs512", + "llvm.x86.avx10.vcvtbf162iubs128" => "__builtin_ia32_vcvtbf162iubs128", + "llvm.x86.avx10.vcvtbf162iubs256" => "__builtin_ia32_vcvtbf162iubs256", + "llvm.x86.avx10.vcvtbf162iubs512" => "__builtin_ia32_vcvtbf162iubs512", + "llvm.x86.avx10.vcvttbf162ibs128" => "__builtin_ia32_vcvttbf162ibs128", + "llvm.x86.avx10.vcvttbf162ibs256" => "__builtin_ia32_vcvttbf162ibs256", + "llvm.x86.avx10.vcvttbf162ibs512" => "__builtin_ia32_vcvttbf162ibs512", + "llvm.x86.avx10.vcvttbf162iubs128" => "__builtin_ia32_vcvttbf162iubs128", + "llvm.x86.avx10.vcvttbf162iubs256" => "__builtin_ia32_vcvttbf162iubs256", + "llvm.x86.avx10.vcvttbf162iubs512" => "__builtin_ia32_vcvttbf162iubs512", + "llvm.x86.avx10.vcvttsd2sis" => "__builtin_ia32_vcvttsd2sis32", + "llvm.x86.avx10.vcvttsd2sis64" => "__builtin_ia32_vcvttsd2sis64", + "llvm.x86.avx10.vcvttsd2usis" => "__builtin_ia32_vcvttsd2usis32", + "llvm.x86.avx10.vcvttsd2usis64" => "__builtin_ia32_vcvttsd2usis64", + "llvm.x86.avx10.vcvttss2sis" => "__builtin_ia32_vcvttss2sis32", + "llvm.x86.avx10.vcvttss2sis64" => "__builtin_ia32_vcvttss2sis64", + "llvm.x86.avx10.vcvttss2usis" => "__builtin_ia32_vcvttss2usis32", + "llvm.x86.avx10.vcvttss2usis64" => "__builtin_ia32_vcvttss2usis64", + "llvm.x86.avx10.vdivbf16128" => "__builtin_ia32_vdivbf16128", + "llvm.x86.avx10.vdivbf16256" => "__builtin_ia32_vdivbf16256", + "llvm.x86.avx10.vdivbf16512" => "__builtin_ia32_vdivbf16512", "llvm.x86.avx10.vdpphps.128" => "__builtin_ia32_vdpphps128", "llvm.x86.avx10.vdpphps.256" => "__builtin_ia32_vdpphps256", "llvm.x86.avx10.vdpphps.512" => "__builtin_ia32_vdpphps512", - "llvm.x86.avx10.vfmaddsubpd256" => "__builtin_ia32_vfmaddsubpd256_round", - "llvm.x86.avx10.vfmaddsubph256" => "__builtin_ia32_vfmaddsubph256_round", - "llvm.x86.avx10.vfmaddsubps256" => "__builtin_ia32_vfmaddsubps256_round", - "llvm.x86.avx10.vmaxpd256" => "__builtin_ia32_vmaxpd256_round", - "llvm.x86.avx10.vmaxph256" => "__builtin_ia32_vmaxph256_round", - "llvm.x86.avx10.vmaxps256" => "__builtin_ia32_vmaxps256_round", - "llvm.x86.avx10.vminmaxnepbf16128" => "__builtin_ia32_vminmaxnepbf16128", - "llvm.x86.avx10.vminmaxnepbf16256" => "__builtin_ia32_vminmaxnepbf16256", - "llvm.x86.avx10.vminmaxnepbf16512" => "__builtin_ia32_vminmaxnepbf16512", + "llvm.x86.avx10.vfmadd132bf16128" => "__builtin_ia32_vfmadd132bf16128", + "llvm.x86.avx10.vfmadd132bf16256" => "__builtin_ia32_vfmadd132bf16256", + "llvm.x86.avx10.vfmadd132bf16512" => "__builtin_ia32_vfmadd132bf16512", + "llvm.x86.avx10.vfmadd213bf16128" => "__builtin_ia32_vfmadd213bf16128", + "llvm.x86.avx10.vfmadd213bf16256" => "__builtin_ia32_vfmadd213bf16256", + "llvm.x86.avx10.vfmadd231bf16128" => "__builtin_ia32_vfmadd231bf16128", + "llvm.x86.avx10.vfmadd231bf16256" => "__builtin_ia32_vfmadd231bf16256", + "llvm.x86.avx10.vfmadd231bf16512" => "__builtin_ia32_vfmadd231bf16512", + "llvm.x86.avx10.vfmsub132bf16128" => "__builtin_ia32_vfmsub132bf16128", + "llvm.x86.avx10.vfmsub132bf16256" => "__builtin_ia32_vfmsub132bf16256", + "llvm.x86.avx10.vfmsub132bf16512" => "__builtin_ia32_vfmsub132bf16512", + "llvm.x86.avx10.vfmsub213bf16128" => "__builtin_ia32_vfmsub213bf16128", + "llvm.x86.avx10.vfmsub213bf16256" => "__builtin_ia32_vfmsub213bf16256", + "llvm.x86.avx10.vfmsub213bf16512" => "__builtin_ia32_vfmsub213bf16512", + "llvm.x86.avx10.vfmsub231bf16128" => "__builtin_ia32_vfmsub231bf16128", + "llvm.x86.avx10.vfmsub231bf16256" => "__builtin_ia32_vfmsub231bf16256", + "llvm.x86.avx10.vfmsub231bf16512" => "__builtin_ia32_vfmsub231bf16512", + "llvm.x86.avx10.vfnmadd132bf16128" => "__builtin_ia32_vfnmadd132bf16128", + "llvm.x86.avx10.vfnmadd132bf16256" => "__builtin_ia32_vfnmadd132bf16256", + "llvm.x86.avx10.vfnmadd132bf16512" => "__builtin_ia32_vfnmadd132bf16512", + "llvm.x86.avx10.vfnmadd213bf16128" => "__builtin_ia32_vfnmadd213bf16128", + "llvm.x86.avx10.vfnmadd213bf16256" => "__builtin_ia32_vfnmadd213bf16256", + "llvm.x86.avx10.vfnmadd213bf16512" => "__builtin_ia32_vfnmadd213bf16512", + "llvm.x86.avx10.vfnmadd231bf16128" => "__builtin_ia32_vfnmadd231bf16128", + "llvm.x86.avx10.vfnmadd231bf16256" => "__builtin_ia32_vfnmadd231bf16256", + "llvm.x86.avx10.vfnmadd231bf16512" => "__builtin_ia32_vfnmadd231bf16512", + "llvm.x86.avx10.vfnmsub132bf16128" => "__builtin_ia32_vfnmsub132bf16128", + "llvm.x86.avx10.vfnmsub132bf16256" => "__builtin_ia32_vfnmsub132bf16256", + "llvm.x86.avx10.vfnmsub132bf16512" => "__builtin_ia32_vfnmsub132bf16512", + "llvm.x86.avx10.vfnmsub213bf16128" => "__builtin_ia32_vfnmsub213bf16128", + "llvm.x86.avx10.vfnmsub213bf16256" => "__builtin_ia32_vfnmsub213bf16256", + "llvm.x86.avx10.vfnmsub213bf16512" => "__builtin_ia32_vfnmsub213bf16512", + "llvm.x86.avx10.vfnmsub231bf16128" => "__builtin_ia32_vfnmsub231bf16128", + "llvm.x86.avx10.vfnmsub231bf16256" => "__builtin_ia32_vfnmsub231bf16256", + "llvm.x86.avx10.vfnmsub231bf16512" => "__builtin_ia32_vfnmsub231bf16512", + "llvm.x86.avx10.vmaxbf16128" => "__builtin_ia32_vmaxbf16128", + "llvm.x86.avx10.vmaxbf16256" => "__builtin_ia32_vmaxbf16256", + "llvm.x86.avx10.vmaxbf16512" => "__builtin_ia32_vmaxbf16512", + "llvm.x86.avx10.vminbf16128" => "__builtin_ia32_vminbf16128", + "llvm.x86.avx10.vminbf16256" => "__builtin_ia32_vminbf16256", + "llvm.x86.avx10.vminbf16512" => "__builtin_ia32_vminbf16512", + "llvm.x86.avx10.vminmaxbf16128" => "__builtin_ia32_vminmaxbf16128", + "llvm.x86.avx10.vminmaxbf16256" => "__builtin_ia32_vminmaxbf16256", + "llvm.x86.avx10.vminmaxbf16512" => "__builtin_ia32_vminmaxbf16512", "llvm.x86.avx10.vminmaxpd128" => "__builtin_ia32_vminmaxpd128", "llvm.x86.avx10.vminmaxpd256" => "__builtin_ia32_vminmaxpd256", "llvm.x86.avx10.vminmaxph128" => "__builtin_ia32_vminmaxph128", "llvm.x86.avx10.vminmaxph256" => "__builtin_ia32_vminmaxph256", "llvm.x86.avx10.vminmaxps128" => "__builtin_ia32_vminmaxps128", "llvm.x86.avx10.vminmaxps256" => "__builtin_ia32_vminmaxps256", - "llvm.x86.avx10.vminpd256" => "__builtin_ia32_vminpd256_round", - "llvm.x86.avx10.vminph256" => "__builtin_ia32_vminph256_round", - "llvm.x86.avx10.vminps256" => "__builtin_ia32_vminps256_round", + "llvm.x86.avx10.vmovrsb128" => "__builtin_ia32_vmovrsb128", + "llvm.x86.avx10.vmovrsb256" => "__builtin_ia32_vmovrsb256", + "llvm.x86.avx10.vmovrsb512" => "__builtin_ia32_vmovrsb512", + "llvm.x86.avx10.vmovrsd128" => "__builtin_ia32_vmovrsd128", + "llvm.x86.avx10.vmovrsd256" => "__builtin_ia32_vmovrsd256", + "llvm.x86.avx10.vmovrsd512" => "__builtin_ia32_vmovrsd512", + "llvm.x86.avx10.vmovrsq128" => "__builtin_ia32_vmovrsq128", + "llvm.x86.avx10.vmovrsq256" => "__builtin_ia32_vmovrsq256", + "llvm.x86.avx10.vmovrsq512" => "__builtin_ia32_vmovrsq512", + "llvm.x86.avx10.vmovrsw128" => "__builtin_ia32_vmovrsw128", + "llvm.x86.avx10.vmovrsw256" => "__builtin_ia32_vmovrsw256", + "llvm.x86.avx10.vmovrsw512" => "__builtin_ia32_vmovrsw512", "llvm.x86.avx10.vmpsadbw.512" => "__builtin_ia32_mpsadbw512", - "llvm.x86.avx10.vmulpd256" => "__builtin_ia32_vmulpd256_round", - "llvm.x86.avx10.vmulph256" => "__builtin_ia32_vmulph256_round", - "llvm.x86.avx10.vmulps256" => "__builtin_ia32_vmulps256_round", + "llvm.x86.avx10.vmulbf16128" => "__builtin_ia32_vmulbf16128", + "llvm.x86.avx10.vmulbf16256" => "__builtin_ia32_vmulbf16256", + "llvm.x86.avx10.vmulbf16512" => "__builtin_ia32_vmulbf16512", "llvm.x86.avx10.vpdpbssd.512" => "__builtin_ia32_vpdpbssd512", "llvm.x86.avx10.vpdpbssds.512" => "__builtin_ia32_vpdpbssds512", "llvm.x86.avx10.vpdpbsud.512" => "__builtin_ia32_vpdpbsud512", @@ -7523,12 +7751,9 @@ match name { "llvm.x86.avx10.vpdpwusds.512" => "__builtin_ia32_vpdpwusds512", "llvm.x86.avx10.vpdpwuud.512" => "__builtin_ia32_vpdpwuud512", "llvm.x86.avx10.vpdpwuuds.512" => "__builtin_ia32_vpdpwuuds512", - "llvm.x86.avx10.vsqrtpd256" => "__builtin_ia32_vsqrtpd256_round", - "llvm.x86.avx10.vsqrtph256" => "__builtin_ia32_vsqrtph256_round", - "llvm.x86.avx10.vsqrtps256" => "__builtin_ia32_vsqrtps256_round", - "llvm.x86.avx10.vsubpd256" => "__builtin_ia32_vsubpd256_round", - "llvm.x86.avx10.vsubph256" => "__builtin_ia32_vsubph256_round", - "llvm.x86.avx10.vsubps256" => "__builtin_ia32_vsubps256_round", + "llvm.x86.avx10.vsubbf16128" => "__builtin_ia32_vsubbf16128", + "llvm.x86.avx10.vsubbf16256" => "__builtin_ia32_vsubbf16256", + "llvm.x86.avx10.vsubbf16512" => "__builtin_ia32_vsubbf16512", "llvm.x86.avx2.gather.d.d" => "__builtin_ia32_gatherd_d", "llvm.x86.avx2.gather.d.d.256" => "__builtin_ia32_gatherd_d256", "llvm.x86.avx2.gather.d.pd" => "__builtin_ia32_gatherd_pd", @@ -9279,10 +9504,15 @@ match name { "llvm.x86.mmx.femms" => "__builtin_ia32_femms", "llvm.x86.monitorx" => "__builtin_ia32_monitorx", "llvm.x86.movdir64b" => "__builtin_ia32_movdir64b", + "llvm.x86.movrsdi" => "__builtin_ia32_movrsdi", + "llvm.x86.movrshi" => "__builtin_ia32_movrshi", + "llvm.x86.movrsqi" => "__builtin_ia32_movrsqi", + "llvm.x86.movrssi" => "__builtin_ia32_movrssi", "llvm.x86.mwaitx" => "__builtin_ia32_mwaitx", "llvm.x86.pclmulqdq" => "__builtin_ia32_pclmulqdq128", "llvm.x86.pclmulqdq.256" => "__builtin_ia32_pclmulqdq256", "llvm.x86.pclmulqdq.512" => "__builtin_ia32_pclmulqdq512", + "llvm.x86.prefetchrs" => "__builtin_ia32_prefetchrs", "llvm.x86.ptwrite32" => "__builtin_ia32_ptwrite32", "llvm.x86.ptwrite64" => "__builtin_ia32_ptwrite64", "llvm.x86.rdfsbase.32" => "__builtin_ia32_rdfsbase32", @@ -9536,14 +9766,40 @@ match name { "llvm.x86.stui" => "__builtin_ia32_stui", "llvm.x86.subborrow.u32" => "__builtin_ia32_subborrow_u32", "llvm.x86.subborrow.u64" => "__builtin_ia32_subborrow_u64", + "llvm.x86.t2rpntlvwz0" => "__builtin_ia32_t2rpntlvwz0", + "llvm.x86.t2rpntlvwz0rs" => "__builtin_ia32_t2rpntlvwz0rs", + "llvm.x86.t2rpntlvwz0rst1" => "__builtin_ia32_t2rpntlvwz0rst1", + "llvm.x86.t2rpntlvwz0t1" => "__builtin_ia32_t2rpntlvwz0t1", + "llvm.x86.t2rpntlvwz1" => "__builtin_ia32_t2rpntlvwz1", + "llvm.x86.t2rpntlvwz1rs" => "__builtin_ia32_t2rpntlvwz1rs", + "llvm.x86.t2rpntlvwz1rst1" => "__builtin_ia32_t2rpntlvwz1rst1", + "llvm.x86.t2rpntlvwz1t1" => "__builtin_ia32_t2rpntlvwz1t1", "llvm.x86.tbm.bextri.u32" => "__builtin_ia32_bextri_u32", "llvm.x86.tbm.bextri.u64" => "__builtin_ia32_bextri_u64", "llvm.x86.tcmmimfp16ps" => "__builtin_ia32_tcmmimfp16ps", "llvm.x86.tcmmimfp16ps.internal" => "__builtin_ia32_tcmmimfp16ps_internal", "llvm.x86.tcmmrlfp16ps" => "__builtin_ia32_tcmmrlfp16ps", "llvm.x86.tcmmrlfp16ps.internal" => "__builtin_ia32_tcmmrlfp16ps_internal", + "llvm.x86.tconjtcmmimfp16ps" => "__builtin_ia32_tconjtcmmimfp16ps", + "llvm.x86.tconjtcmmimfp16ps.internal" => "__builtin_ia32_tconjtcmmimfp16ps_internal", + "llvm.x86.tconjtfp16" => "__builtin_ia32_tconjtfp16", + "llvm.x86.tconjtfp16.internal" => "__builtin_ia32_tconjtfp16_internal", + "llvm.x86.tcvtrowd2ps" => "__builtin_ia32_tcvtrowd2ps", + "llvm.x86.tcvtrowd2ps.internal" => "__builtin_ia32_tcvtrowd2ps_internal", + "llvm.x86.tcvtrowps2bf16h" => "__builtin_ia32_tcvtrowps2bf16h", + "llvm.x86.tcvtrowps2bf16h.internal" => "__builtin_ia32_tcvtrowps2bf16h_internal", + "llvm.x86.tcvtrowps2bf16l" => "__builtin_ia32_tcvtrowps2bf16l", + "llvm.x86.tcvtrowps2bf16l.internal" => "__builtin_ia32_tcvtrowps2bf16l_internal", + "llvm.x86.tcvtrowps2phh" => "__builtin_ia32_tcvtrowps2phh", + "llvm.x86.tcvtrowps2phh.internal" => "__builtin_ia32_tcvtrowps2phh_internal", + "llvm.x86.tcvtrowps2phl" => "__builtin_ia32_tcvtrowps2phl", + "llvm.x86.tcvtrowps2phl.internal" => "__builtin_ia32_tcvtrowps2phl_internal", "llvm.x86.tdpbf16ps" => "__builtin_ia32_tdpbf16ps", "llvm.x86.tdpbf16ps.internal" => "__builtin_ia32_tdpbf16ps_internal", + "llvm.x86.tdpbf8ps" => "__builtin_ia32_tdpbf8ps", + "llvm.x86.tdpbf8ps.internal" => "__builtin_ia32_tdpbf8ps_internal", + "llvm.x86.tdpbhf8ps" => "__builtin_ia32_tdpbhf8ps", + "llvm.x86.tdpbhf8ps.internal" => "__builtin_ia32_tdpbhf8ps_internal", "llvm.x86.tdpbssd" => "__builtin_ia32_tdpbssd", "llvm.x86.tdpbssd.internal" => "__builtin_ia32_tdpbssd_internal", "llvm.x86.tdpbsud" => "__builtin_ia32_tdpbsud", @@ -9554,17 +9810,41 @@ match name { "llvm.x86.tdpbuud.internal" => "__builtin_ia32_tdpbuud_internal", "llvm.x86.tdpfp16ps" => "__builtin_ia32_tdpfp16ps", "llvm.x86.tdpfp16ps.internal" => "__builtin_ia32_tdpfp16ps_internal", + "llvm.x86.tdphbf8ps" => "__builtin_ia32_tdphbf8ps", + "llvm.x86.tdphbf8ps.internal" => "__builtin_ia32_tdphbf8ps_internal", + "llvm.x86.tdphf8ps" => "__builtin_ia32_tdphf8ps", + "llvm.x86.tdphf8ps.internal" => "__builtin_ia32_tdphf8ps_internal", "llvm.x86.testui" => "__builtin_ia32_testui", "llvm.x86.tileloadd64" => "__builtin_ia32_tileloadd64", "llvm.x86.tileloadd64.internal" => "__builtin_ia32_tileloadd64_internal", + "llvm.x86.tileloaddrs64" => "__builtin_ia32_tileloaddrs64", + "llvm.x86.tileloaddrs64.internal" => "__builtin_ia32_tileloaddrs64_internal", + "llvm.x86.tileloaddrst164" => "__builtin_ia32_tileloaddrst164", + "llvm.x86.tileloaddrst164.internal" => "__builtin_ia32_tileloaddrst164_internal", "llvm.x86.tileloaddt164" => "__builtin_ia32_tileloaddt164", "llvm.x86.tileloaddt164.internal" => "__builtin_ia32_tileloaddt164_internal", + "llvm.x86.tilemovrow" => "__builtin_ia32_tilemovrow", + "llvm.x86.tilemovrow.internal" => "__builtin_ia32_tilemovrow_internal", "llvm.x86.tilerelease" => "__builtin_ia32_tilerelease", "llvm.x86.tilestored64" => "__builtin_ia32_tilestored64", "llvm.x86.tilestored64.internal" => "__builtin_ia32_tilestored64_internal", "llvm.x86.tilezero" => "__builtin_ia32_tilezero", "llvm.x86.tilezero.internal" => "__builtin_ia32_tilezero_internal", + "llvm.x86.tmmultf32ps" => "__builtin_ia32_tmmultf32ps", + "llvm.x86.tmmultf32ps.internal" => "__builtin_ia32_tmmultf32ps_internal", "llvm.x86.tpause" => "__builtin_ia32_tpause", + "llvm.x86.ttcmmimfp16ps" => "__builtin_ia32_ttcmmimfp16ps", + "llvm.x86.ttcmmimfp16ps.internal" => "__builtin_ia32_ttcmmimfp16ps_internal", + "llvm.x86.ttcmmrlfp16ps" => "__builtin_ia32_ttcmmrlfp16ps", + "llvm.x86.ttcmmrlfp16ps.internal" => "__builtin_ia32_ttcmmrlfp16ps_internal", + "llvm.x86.ttdpbf16ps" => "__builtin_ia32_ttdpbf16ps", + "llvm.x86.ttdpbf16ps.internal" => "__builtin_ia32_ttdpbf16ps_internal", + "llvm.x86.ttdpfp16ps" => "__builtin_ia32_ttdpfp16ps", + "llvm.x86.ttdpfp16ps.internal" => "__builtin_ia32_ttdpfp16ps_internal", + "llvm.x86.ttmmultf32ps" => "__builtin_ia32_ttmmultf32ps", + "llvm.x86.ttmmultf32ps.internal" => "__builtin_ia32_ttmmultf32ps_internal", + "llvm.x86.ttransposed" => "__builtin_ia32_ttransposed", + "llvm.x86.ttransposed.internal" => "__builtin_ia32_ttransposed_internal", "llvm.x86.umonitor" => "__builtin_ia32_umonitor", "llvm.x86.umwait" => "__builtin_ia32_umwait", "llvm.x86.urdmsr" => "__builtin_ia32_urdmsr", @@ -9604,8 +9884,10 @@ match name { "llvm.x86.vsm3rnds2" => "__builtin_ia32_vsm3rnds2", "llvm.x86.vsm4key4128" => "__builtin_ia32_vsm4key4128", "llvm.x86.vsm4key4256" => "__builtin_ia32_vsm4key4256", + "llvm.x86.vsm4key4512" => "__builtin_ia32_vsm4key4512", "llvm.x86.vsm4rnds4128" => "__builtin_ia32_vsm4rnds4128", "llvm.x86.vsm4rnds4256" => "__builtin_ia32_vsm4rnds4256", + "llvm.x86.vsm4rnds4512" => "__builtin_ia32_vsm4rnds4512", "llvm.x86.wbinvd" => "__builtin_ia32_wbinvd", "llvm.x86.wbnoinvd" => "__builtin_ia32_wbnoinvd", "llvm.x86.wrfsbase.32" => "__builtin_ia32_wrfsbase32", From 9ed53b85b7ebb52badbc8676e40725a55331c214 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 10 May 2025 21:53:56 +0200 Subject: [PATCH 116/245] Fix ICE in `missing_const_for_fn` The `mir_drops_elaborated_and_const_checked` query result has been stolen already and cannot be borrowed again. Use the `optimized_mir` query result instead. --- clippy_lints/src/missing_const_for_fn.rs | 4 ++-- .../ui/crashes/missing_const_for_fn_14774.fixed | 13 +++++++++++++ tests/ui/crashes/missing_const_for_fn_14774.rs | 13 +++++++++++++ .../crashes/missing_const_for_fn_14774.stderr | 17 +++++++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 tests/ui/crashes/missing_const_for_fn_14774.fixed create mode 100644 tests/ui/crashes/missing_const_for_fn_14774.rs create mode 100644 tests/ui/crashes/missing_const_for_fn_14774.stderr diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 1f142bc3ba63..f3e24044fb6c 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -155,9 +155,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { return; } - let mir = cx.tcx.mir_drops_elaborated_and_const_checked(def_id); + let mir = cx.tcx.optimized_mir(def_id); - if let Ok(()) = is_min_const_fn(cx, &mir.borrow(), self.msrv) + if let Ok(()) = is_min_const_fn(cx, mir, self.msrv) && let hir::Node::Item(hir::Item { vis_span, .. }) | hir::Node::ImplItem(hir::ImplItem { vis_span, .. }) = cx.tcx.hir_node_by_def_id(def_id) { diff --git a/tests/ui/crashes/missing_const_for_fn_14774.fixed b/tests/ui/crashes/missing_const_for_fn_14774.fixed new file mode 100644 index 000000000000..9c85c4b84648 --- /dev/null +++ b/tests/ui/crashes/missing_const_for_fn_14774.fixed @@ -0,0 +1,13 @@ +//@compile-flags: -Z validate-mir +#![warn(clippy::missing_const_for_fn)] + +static BLOCK_FN_DEF: fn(usize) -> usize = { + //~v missing_const_for_fn + const fn foo(a: usize) -> usize { + a + 10 + } + foo +}; +struct X; + +fn main() {} diff --git a/tests/ui/crashes/missing_const_for_fn_14774.rs b/tests/ui/crashes/missing_const_for_fn_14774.rs new file mode 100644 index 000000000000..6519be61256e --- /dev/null +++ b/tests/ui/crashes/missing_const_for_fn_14774.rs @@ -0,0 +1,13 @@ +//@compile-flags: -Z validate-mir +#![warn(clippy::missing_const_for_fn)] + +static BLOCK_FN_DEF: fn(usize) -> usize = { + //~v missing_const_for_fn + fn foo(a: usize) -> usize { + a + 10 + } + foo +}; +struct X; + +fn main() {} diff --git a/tests/ui/crashes/missing_const_for_fn_14774.stderr b/tests/ui/crashes/missing_const_for_fn_14774.stderr new file mode 100644 index 000000000000..a407376d0b9d --- /dev/null +++ b/tests/ui/crashes/missing_const_for_fn_14774.stderr @@ -0,0 +1,17 @@ +error: this could be a `const fn` + --> tests/ui/crashes/missing_const_for_fn_14774.rs:6:5 + | +LL | / fn foo(a: usize) -> usize { +LL | | a + 10 +LL | | } + | |_____^ + | + = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_const_for_fn)]` +help: make the function `const` + | +LL | const fn foo(a: usize) -> usize { + | +++++ + +error: aborting due to 1 previous error + From f111416e43a36a1ee062a2194eae37c39d0f0be1 Mon Sep 17 00:00:00 2001 From: michal kostrubiec Date: Fri, 9 May 2025 16:41:46 +0200 Subject: [PATCH 117/245] Fixed a recursive inling bug, added a test for it --- src/attributes.rs | 53 +++++++++++++++++++++++++++++++++++--- tests/run/always_inline.rs | 53 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 tests/run/always_inline.rs diff --git a/src/attributes.rs b/src/attributes.rs index 8bc1b7702430..f933119d0ba0 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -6,21 +6,68 @@ use rustc_attr_parsing::InlineAttr; use rustc_attr_parsing::InstructionSetAttr; #[cfg(feature = "master")] use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::TerminatorKind; use rustc_middle::ty; use crate::context::CodegenCx; use crate::gcc_util::to_gcc_features; -/// Get GCC attribute for the provided inline heuristic. +/// Checks if the function `instance` is recursively inline. +/// Returns `false` if a functions is guaranteed to be non-recursive, and `true` if it *might* be recursive. +#[cfg(feature = "master")] +fn resursively_inline<'gcc, 'tcx>( + cx: &CodegenCx<'gcc, 'tcx>, + instance: ty::Instance<'tcx>, +) -> bool { + // No body, so we can't check if this is recursively inline, so we assume it is. + if !cx.tcx.is_mir_available(instance.def_id()) { + return true; + } + // `expect_local` ought to never fail: we should be checking a function within this codegen unit. + let body = cx.tcx.optimized_mir(instance.def_id()); + for block in body.basic_blocks.iter() { + let Some(ref terminator) = block.terminator else { continue }; + // I assume that the recursive-inline issue applies only to functions, and not to drops. + // In principle, a recursive, `#[inline(always)]` drop could(?) exist, but I don't think it does. + let TerminatorKind::Call { ref func, .. } = terminator.kind else { continue }; + let Some((def, _args)) = func.const_fn_def() else { continue }; + // Check if the called function is recursively inline. + if matches!( + cx.tcx.codegen_fn_attrs(def).inline, + InlineAttr::Always | InlineAttr::Force { .. } + ) { + return true; + } + } + false +} + +/// Get GCC attribute for the provided inline heuristic, attached to `instance`. #[cfg(feature = "master")] #[inline] fn inline_attr<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, inline: InlineAttr, + instance: ty::Instance<'tcx>, ) -> Option> { match inline { + InlineAttr::Always => { + // We can't simply always return `always_inline` unconditionally. + // It is *NOT A HINT* and does not work for recursive functions. + // + // So, it can only be applied *if*: + // The current function does not call any functions marked `#[inline(always)]`. + // + // That prevents issues steming from recursive `#[inline(always)]` at a *relatively* small cost. + // We *only* need to check all the terminators of a function marked with this attribute. + if resursively_inline(cx, instance) { + Some(FnAttribute::Inline) + } else { + Some(FnAttribute::AlwaysInline) + } + } InlineAttr::Hint => Some(FnAttribute::Inline), - InlineAttr::Always | InlineAttr::Force { .. } => Some(FnAttribute::AlwaysInline), + InlineAttr::Force { .. } => Some(FnAttribute::AlwaysInline), InlineAttr::Never => { if cx.sess().target.arch != "amdgpu" { Some(FnAttribute::NoInline) @@ -52,7 +99,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( } else { codegen_fn_attrs.inline }; - if let Some(attr) = inline_attr(cx, inline) { + if let Some(attr) = inline_attr(cx, inline, instance) { if let FnAttribute::AlwaysInline = attr { func.add_attribute(FnAttribute::Inline); } diff --git a/tests/run/always_inline.rs b/tests/run/always_inline.rs new file mode 100644 index 000000000000..ebd741ee090c --- /dev/null +++ b/tests/run/always_inline.rs @@ -0,0 +1,53 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +#[inline(always)] +fn fib(n: u8) -> u8 { + if n == 0 { + return 1; + } + if n == 1 { + return 1; + } + fib(n - 1) + fib(n - 2) +} + +#[inline(always)] +fn fib_b(n: u8) -> u8 { + if n == 0 { + return 1; + } + if n == 1 { + return 1; + } + fib_a(n - 1) + fib_a(n - 2) +} + +#[inline(always)] +fn fib_a(n: u8) -> u8 { + if n == 0 { + return 1; + } + if n == 1 { + return 1; + } + fib_b(n - 1) + fib_b(n - 2) +} + +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { + if fib(2) != fib_a(2) { + intrinsics::abort(); + } + 0 +} From aeea4727a4a00030fa888eccb10bcc372c4b7d2d Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Sat, 10 May 2025 23:46:43 +0000 Subject: [PATCH 118/245] Remove `stable` attribute from wasi fs (read_exact|write_all)_at --- library/std/src/os/wasi/fs.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 34f0e89f2f1e..5ea91dd6521a 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -72,7 +72,6 @@ pub trait FileExt { /// If this function returns an error, it is unspecified how many bytes it /// has read, but it will never read more than would be necessary to /// completely fill the buffer. - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { while !buf.is_empty() { match self.read_at(buf, offset) { @@ -144,7 +143,6 @@ pub trait FileExt { /// non-[`io::ErrorKind::Interrupted`] kind that [`write_at`] returns. /// /// [`write_at`]: FileExt::write_at - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { while !buf.is_empty() { match self.write_at(buf, offset) { From 18eb2934357d253da68503716ea0bd21dcb61310 Mon Sep 17 00:00:00 2001 From: Kazuki Obata Date: Sun, 11 May 2025 16:05:31 +0900 Subject: [PATCH 119/245] update dev doc: update FnKind::Fn matching Ident was moved to the struct Fn in https://github.com/rust-lang/rust/pull/138740 --- book/src/development/adding_lints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index e5e82ede4fdf..2b89e94cf8f4 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -416,7 +416,7 @@ In our example, `is_foo_fn` looks like: fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { match fn_kind { - FnKind::Fn(_, ident, ..) => { + FnKind::Fn(_, _, Fn { ident, .. }) => { // check if `fn` name is `foo` ident.name.as_str() == "foo" } From f96fb61e4b87f9b2a90df2ee58c8a99d0dc0f83a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 11 May 2025 09:28:47 +0200 Subject: [PATCH 120/245] checktools.sh: fix bashism --- src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index 9ed5b519b6ea..9222710b8437 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -47,7 +47,7 @@ fi # we only ensure that all assertions still pass. MIRIFLAGS="-Zmiri-force-intrinsic-fallback --cfg force_intrinsic_fallback -O -Zmir-opt-level=4 -Cdebug-assertions=yes" \ MIRI_SKIP_UI_CHECKS=1 \ - python3 "$X_PY" test --stage 2 src/tools/miri -- tests/{pass,panic} + python3 "$X_PY" test --stage 2 src/tools/miri -- tests/pass tests/panic # We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. # Also cover some other targets via cross-testing, in particular all tier 1 targets. case $HOST_TARGET in From 9ccabd5390375972f3e4d0d2c5299cd7bb77a421 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 11 May 2025 14:00:16 +0200 Subject: [PATCH 121/245] Make some `f32`/`f64` tests also run in const-context --- library/coretests/tests/num/mod.rs | 332 ++++++++++++++++------------- 1 file changed, 189 insertions(+), 143 deletions(-) diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 0add9a01e682..a6b75f702660 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -732,157 +732,157 @@ assume_usize_width! { } macro_rules! test_float { - ($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => { + ($modname: ident, $fassert: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => { mod $modname { #[test] fn min() { - assert_eq!((0.0 as $fty).min(0.0), 0.0); - assert!((0.0 as $fty).min(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).min(-0.0), -0.0); - assert!((-0.0 as $fty).min(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).min(9.0), 9.0); - assert_eq!((-9.0 as $fty).min(0.0), -9.0); - assert_eq!((0.0 as $fty).min(9.0), 0.0); - assert!((0.0 as $fty).min(9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).min(9.0), -0.0); - assert!((-0.0 as $fty).min(9.0).is_sign_negative()); - assert_eq!((-0.0 as $fty).min(-9.0), -9.0); - assert_eq!(($inf as $fty).min(9.0), 9.0); - assert_eq!((9.0 as $fty).min($inf), 9.0); - assert_eq!(($inf as $fty).min(-9.0), -9.0); - assert_eq!((-9.0 as $fty).min($inf), -9.0); - assert_eq!(($neginf as $fty).min(9.0), $neginf); - assert_eq!((9.0 as $fty).min($neginf), $neginf); - assert_eq!(($neginf as $fty).min(-9.0), $neginf); - assert_eq!((-9.0 as $fty).min($neginf), $neginf); - assert_eq!(($nan as $fty).min(9.0), 9.0); - assert_eq!(($nan as $fty).min(-9.0), -9.0); - assert_eq!((9.0 as $fty).min($nan), 9.0); - assert_eq!((-9.0 as $fty).min($nan), -9.0); - assert!(($nan as $fty).min($nan).is_nan()); + $fassert!((0.0 as $fty).min(0.0), 0.0); + $fassert!((0.0 as $fty).min(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).min(-0.0), -0.0); + $fassert!((-0.0 as $fty).min(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).min(9.0), 9.0); + $fassert!((-9.0 as $fty).min(0.0), -9.0); + $fassert!((0.0 as $fty).min(9.0), 0.0); + $fassert!((0.0 as $fty).min(9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).min(9.0), -0.0); + $fassert!((-0.0 as $fty).min(9.0).is_sign_negative()); + $fassert!((-0.0 as $fty).min(-9.0), -9.0); + $fassert!(($inf as $fty).min(9.0), 9.0); + $fassert!((9.0 as $fty).min($inf), 9.0); + $fassert!(($inf as $fty).min(-9.0), -9.0); + $fassert!((-9.0 as $fty).min($inf), -9.0); + $fassert!(($neginf as $fty).min(9.0), $neginf); + $fassert!((9.0 as $fty).min($neginf), $neginf); + $fassert!(($neginf as $fty).min(-9.0), $neginf); + $fassert!((-9.0 as $fty).min($neginf), $neginf); + $fassert!(($nan as $fty).min(9.0), 9.0); + $fassert!(($nan as $fty).min(-9.0), -9.0); + $fassert!((9.0 as $fty).min($nan), 9.0); + $fassert!((-9.0 as $fty).min($nan), -9.0); + $fassert!(($nan as $fty).min($nan).is_nan()); } #[test] fn max() { - assert_eq!((0.0 as $fty).max(0.0), 0.0); - assert!((0.0 as $fty).max(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).max(-0.0), -0.0); - assert!((-0.0 as $fty).max(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).max(9.0), 9.0); - assert_eq!((-9.0 as $fty).max(0.0), 0.0); - assert!((-9.0 as $fty).max(0.0).is_sign_positive()); - assert_eq!((-9.0 as $fty).max(-0.0), -0.0); - assert!((-9.0 as $fty).max(-0.0).is_sign_negative()); - assert_eq!((0.0 as $fty).max(9.0), 9.0); - assert_eq!((0.0 as $fty).max(-9.0), 0.0); - assert!((0.0 as $fty).max(-9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).max(-9.0), -0.0); - assert!((-0.0 as $fty).max(-9.0).is_sign_negative()); - assert_eq!(($inf as $fty).max(9.0), $inf); - assert_eq!((9.0 as $fty).max($inf), $inf); - assert_eq!(($inf as $fty).max(-9.0), $inf); - assert_eq!((-9.0 as $fty).max($inf), $inf); - assert_eq!(($neginf as $fty).max(9.0), 9.0); - assert_eq!((9.0 as $fty).max($neginf), 9.0); - assert_eq!(($neginf as $fty).max(-9.0), -9.0); - assert_eq!((-9.0 as $fty).max($neginf), -9.0); - assert_eq!(($nan as $fty).max(9.0), 9.0); - assert_eq!(($nan as $fty).max(-9.0), -9.0); - assert_eq!((9.0 as $fty).max($nan), 9.0); - assert_eq!((-9.0 as $fty).max($nan), -9.0); - assert!(($nan as $fty).max($nan).is_nan()); + $fassert!((0.0 as $fty).max(0.0), 0.0); + $fassert!((0.0 as $fty).max(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).max(-0.0), -0.0); + $fassert!((-0.0 as $fty).max(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).max(9.0), 9.0); + $fassert!((-9.0 as $fty).max(0.0), 0.0); + $fassert!((-9.0 as $fty).max(0.0).is_sign_positive()); + $fassert!((-9.0 as $fty).max(-0.0), -0.0); + $fassert!((-9.0 as $fty).max(-0.0).is_sign_negative()); + $fassert!((0.0 as $fty).max(9.0), 9.0); + $fassert!((0.0 as $fty).max(-9.0), 0.0); + $fassert!((0.0 as $fty).max(-9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).max(-9.0), -0.0); + $fassert!((-0.0 as $fty).max(-9.0).is_sign_negative()); + $fassert!(($inf as $fty).max(9.0), $inf); + $fassert!((9.0 as $fty).max($inf), $inf); + $fassert!(($inf as $fty).max(-9.0), $inf); + $fassert!((-9.0 as $fty).max($inf), $inf); + $fassert!(($neginf as $fty).max(9.0), 9.0); + $fassert!((9.0 as $fty).max($neginf), 9.0); + $fassert!(($neginf as $fty).max(-9.0), -9.0); + $fassert!((-9.0 as $fty).max($neginf), -9.0); + $fassert!(($nan as $fty).max(9.0), 9.0); + $fassert!(($nan as $fty).max(-9.0), -9.0); + $fassert!((9.0 as $fty).max($nan), 9.0); + $fassert!((-9.0 as $fty).max($nan), -9.0); + $fassert!(($nan as $fty).max($nan).is_nan()); } #[test] fn minimum() { - assert_eq!((0.0 as $fty).minimum(0.0), 0.0); - assert!((0.0 as $fty).minimum(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).minimum(0.0), -0.0); - assert!((-0.0 as $fty).minimum(0.0).is_sign_negative()); - assert_eq!((-0.0 as $fty).minimum(-0.0), -0.0); - assert!((-0.0 as $fty).minimum(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).minimum(9.0), 9.0); - assert_eq!((-9.0 as $fty).minimum(0.0), -9.0); - assert_eq!((0.0 as $fty).minimum(9.0), 0.0); - assert!((0.0 as $fty).minimum(9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).minimum(9.0), -0.0); - assert!((-0.0 as $fty).minimum(9.0).is_sign_negative()); - assert_eq!((-0.0 as $fty).minimum(-9.0), -9.0); - assert_eq!(($inf as $fty).minimum(9.0), 9.0); - assert_eq!((9.0 as $fty).minimum($inf), 9.0); - assert_eq!(($inf as $fty).minimum(-9.0), -9.0); - assert_eq!((-9.0 as $fty).minimum($inf), -9.0); - assert_eq!(($neginf as $fty).minimum(9.0), $neginf); - assert_eq!((9.0 as $fty).minimum($neginf), $neginf); - assert_eq!(($neginf as $fty).minimum(-9.0), $neginf); - assert_eq!((-9.0 as $fty).minimum($neginf), $neginf); - assert!(($nan as $fty).minimum(9.0).is_nan()); - assert!(($nan as $fty).minimum(-9.0).is_nan()); - assert!((9.0 as $fty).minimum($nan).is_nan()); - assert!((-9.0 as $fty).minimum($nan).is_nan()); - assert!(($nan as $fty).minimum($nan).is_nan()); + $fassert!((0.0 as $fty).minimum(0.0), 0.0); + $fassert!((0.0 as $fty).minimum(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).minimum(0.0), -0.0); + $fassert!((-0.0 as $fty).minimum(0.0).is_sign_negative()); + $fassert!((-0.0 as $fty).minimum(-0.0), -0.0); + $fassert!((-0.0 as $fty).minimum(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).minimum(9.0), 9.0); + $fassert!((-9.0 as $fty).minimum(0.0), -9.0); + $fassert!((0.0 as $fty).minimum(9.0), 0.0); + $fassert!((0.0 as $fty).minimum(9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).minimum(9.0), -0.0); + $fassert!((-0.0 as $fty).minimum(9.0).is_sign_negative()); + $fassert!((-0.0 as $fty).minimum(-9.0), -9.0); + $fassert!(($inf as $fty).minimum(9.0), 9.0); + $fassert!((9.0 as $fty).minimum($inf), 9.0); + $fassert!(($inf as $fty).minimum(-9.0), -9.0); + $fassert!((-9.0 as $fty).minimum($inf), -9.0); + $fassert!(($neginf as $fty).minimum(9.0), $neginf); + $fassert!((9.0 as $fty).minimum($neginf), $neginf); + $fassert!(($neginf as $fty).minimum(-9.0), $neginf); + $fassert!((-9.0 as $fty).minimum($neginf), $neginf); + $fassert!(($nan as $fty).minimum(9.0).is_nan()); + $fassert!(($nan as $fty).minimum(-9.0).is_nan()); + $fassert!((9.0 as $fty).minimum($nan).is_nan()); + $fassert!((-9.0 as $fty).minimum($nan).is_nan()); + $fassert!(($nan as $fty).minimum($nan).is_nan()); } #[test] fn maximum() { - assert_eq!((0.0 as $fty).maximum(0.0), 0.0); - assert!((0.0 as $fty).maximum(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).maximum(0.0), 0.0); - assert!((-0.0 as $fty).maximum(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).maximum(-0.0), -0.0); - assert!((-0.0 as $fty).maximum(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).maximum(9.0), 9.0); - assert_eq!((-9.0 as $fty).maximum(0.0), 0.0); - assert!((-9.0 as $fty).maximum(0.0).is_sign_positive()); - assert_eq!((-9.0 as $fty).maximum(-0.0), -0.0); - assert!((-9.0 as $fty).maximum(-0.0).is_sign_negative()); - assert_eq!((0.0 as $fty).maximum(9.0), 9.0); - assert_eq!((0.0 as $fty).maximum(-9.0), 0.0); - assert!((0.0 as $fty).maximum(-9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).maximum(-9.0), -0.0); - assert!((-0.0 as $fty).maximum(-9.0).is_sign_negative()); - assert_eq!(($inf as $fty).maximum(9.0), $inf); - assert_eq!((9.0 as $fty).maximum($inf), $inf); - assert_eq!(($inf as $fty).maximum(-9.0), $inf); - assert_eq!((-9.0 as $fty).maximum($inf), $inf); - assert_eq!(($neginf as $fty).maximum(9.0), 9.0); - assert_eq!((9.0 as $fty).maximum($neginf), 9.0); - assert_eq!(($neginf as $fty).maximum(-9.0), -9.0); - assert_eq!((-9.0 as $fty).maximum($neginf), -9.0); - assert!(($nan as $fty).maximum(9.0).is_nan()); - assert!(($nan as $fty).maximum(-9.0).is_nan()); - assert!((9.0 as $fty).maximum($nan).is_nan()); - assert!((-9.0 as $fty).maximum($nan).is_nan()); - assert!(($nan as $fty).maximum($nan).is_nan()); + $fassert!((0.0 as $fty).maximum(0.0), 0.0); + $fassert!((0.0 as $fty).maximum(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).maximum(0.0), 0.0); + $fassert!((-0.0 as $fty).maximum(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).maximum(-0.0), -0.0); + $fassert!((-0.0 as $fty).maximum(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).maximum(9.0), 9.0); + $fassert!((-9.0 as $fty).maximum(0.0), 0.0); + $fassert!((-9.0 as $fty).maximum(0.0).is_sign_positive()); + $fassert!((-9.0 as $fty).maximum(-0.0), -0.0); + $fassert!((-9.0 as $fty).maximum(-0.0).is_sign_negative()); + $fassert!((0.0 as $fty).maximum(9.0), 9.0); + $fassert!((0.0 as $fty).maximum(-9.0), 0.0); + $fassert!((0.0 as $fty).maximum(-9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).maximum(-9.0), -0.0); + $fassert!((-0.0 as $fty).maximum(-9.0).is_sign_negative()); + $fassert!(($inf as $fty).maximum(9.0), $inf); + $fassert!((9.0 as $fty).maximum($inf), $inf); + $fassert!(($inf as $fty).maximum(-9.0), $inf); + $fassert!((-9.0 as $fty).maximum($inf), $inf); + $fassert!(($neginf as $fty).maximum(9.0), 9.0); + $fassert!((9.0 as $fty).maximum($neginf), 9.0); + $fassert!(($neginf as $fty).maximum(-9.0), -9.0); + $fassert!((-9.0 as $fty).maximum($neginf), -9.0); + $fassert!(($nan as $fty).maximum(9.0).is_nan()); + $fassert!(($nan as $fty).maximum(-9.0).is_nan()); + $fassert!((9.0 as $fty).maximum($nan).is_nan()); + $fassert!((-9.0 as $fty).maximum($nan).is_nan()); + $fassert!(($nan as $fty).maximum($nan).is_nan()); } #[test] fn midpoint() { - assert_eq!((0.5 as $fty).midpoint(0.5), 0.5); - assert_eq!((0.5 as $fty).midpoint(2.5), 1.5); - assert_eq!((3.0 as $fty).midpoint(4.0), 3.5); - assert_eq!((-3.0 as $fty).midpoint(4.0), 0.5); - assert_eq!((3.0 as $fty).midpoint(-4.0), -0.5); - assert_eq!((-3.0 as $fty).midpoint(-4.0), -3.5); - assert_eq!((0.0 as $fty).midpoint(0.0), 0.0); - assert_eq!((-0.0 as $fty).midpoint(-0.0), -0.0); - assert_eq!((-5.0 as $fty).midpoint(5.0), 0.0); - assert_eq!(($max as $fty).midpoint($min), 0.0); - assert_eq!(($min as $fty).midpoint($max), -0.0); - assert_eq!(($max as $fty).midpoint($min_pos), $max / 2.); - assert_eq!((-$max as $fty).midpoint($min_pos), -$max / 2.); - assert_eq!(($max as $fty).midpoint(-$min_pos), $max / 2.); - assert_eq!((-$max as $fty).midpoint(-$min_pos), -$max / 2.); - assert_eq!(($min_pos as $fty).midpoint($max), $max / 2.); - assert_eq!(($min_pos as $fty).midpoint(-$max), -$max / 2.); - assert_eq!((-$min_pos as $fty).midpoint($max), $max / 2.); - assert_eq!((-$min_pos as $fty).midpoint(-$max), -$max / 2.); - assert_eq!(($max as $fty).midpoint($max), $max); - assert_eq!(($min_pos as $fty).midpoint($min_pos), $min_pos); - assert_eq!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos); - assert_eq!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5); - assert_eq!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5); - assert_eq!(($inf as $fty).midpoint($inf), $inf); - assert_eq!(($neginf as $fty).midpoint($neginf), $neginf); - assert!(($nan as $fty).midpoint(1.0).is_nan()); - assert!((1.0 as $fty).midpoint($nan).is_nan()); - assert!(($nan as $fty).midpoint($nan).is_nan()); + $fassert!((0.5 as $fty).midpoint(0.5), 0.5); + $fassert!((0.5 as $fty).midpoint(2.5), 1.5); + $fassert!((3.0 as $fty).midpoint(4.0), 3.5); + $fassert!((-3.0 as $fty).midpoint(4.0), 0.5); + $fassert!((3.0 as $fty).midpoint(-4.0), -0.5); + $fassert!((-3.0 as $fty).midpoint(-4.0), -3.5); + $fassert!((0.0 as $fty).midpoint(0.0), 0.0); + $fassert!((-0.0 as $fty).midpoint(-0.0), -0.0); + $fassert!((-5.0 as $fty).midpoint(5.0), 0.0); + $fassert!(($max as $fty).midpoint($min), 0.0); + $fassert!(($min as $fty).midpoint($max), -0.0); + $fassert!(($max as $fty).midpoint($min_pos), $max / 2.); + $fassert!((-$max as $fty).midpoint($min_pos), -$max / 2.); + $fassert!(($max as $fty).midpoint(-$min_pos), $max / 2.); + $fassert!((-$max as $fty).midpoint(-$min_pos), -$max / 2.); + $fassert!(($min_pos as $fty).midpoint($max), $max / 2.); + $fassert!(($min_pos as $fty).midpoint(-$max), -$max / 2.); + $fassert!((-$min_pos as $fty).midpoint($max), $max / 2.); + $fassert!((-$min_pos as $fty).midpoint(-$max), -$max / 2.); + $fassert!(($max as $fty).midpoint($max), $max); + $fassert!(($min_pos as $fty).midpoint($min_pos), $min_pos); + $fassert!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos); + $fassert!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5); + $fassert!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5); + $fassert!(($inf as $fty).midpoint($inf), $inf); + $fassert!(($neginf as $fty).midpoint($neginf), $neginf); + $fassert!(($nan as $fty).midpoint(1.0).is_nan()); + $fassert!((1.0 as $fty).midpoint($nan).is_nan()); + $fassert!(($nan as $fty).midpoint($nan).is_nan()); // test if large differences in magnitude are still correctly computed. // NOTE: that because of how small x and y are, x + y can never overflow @@ -907,19 +907,19 @@ macro_rules! test_float { } #[test] fn rem_euclid() { - let a: $fty = 42.0; - assert!($inf.rem_euclid(a).is_nan()); - assert_eq!(a.rem_euclid($inf), a); - assert!(a.rem_euclid($nan).is_nan()); + // FIXME: Use $fassert when rem_euclid becomes const + assert!($inf.rem_euclid((42.0 as $fty)).is_nan()); + assert_eq!((42.0 as $fty).rem_euclid($inf), (42.0 as $fty)); + assert!((42.0 as $fty).rem_euclid($nan).is_nan()); assert!($inf.rem_euclid($inf).is_nan()); assert!($inf.rem_euclid($nan).is_nan()); assert!($nan.rem_euclid($inf).is_nan()); } #[test] fn div_euclid() { - let a: $fty = 42.0; - assert_eq!(a.div_euclid($inf), 0.0); - assert!(a.div_euclid($nan).is_nan()); + // FIXME: Use $fassert when div_euclid becomes const + assert_eq!((42.0 as $fty).div_euclid($inf), 0.0); + assert!((42.0 as $fty).div_euclid($nan).is_nan()); assert!($inf.div_euclid($inf).is_nan()); assert!($inf.div_euclid($nan).is_nan()); assert!($nan.div_euclid($inf).is_nan()); @@ -928,8 +928,41 @@ macro_rules! test_float { }; } +// Custom assert macro that distribute between assert! and assert_eq! in a non-const context +macro_rules! float_assert { + ($b:expr) => { + assert!($b); + }; + ($left:expr, $right:expr) => { + assert_eq!($left, $right); + }; +} + +// Custom assert macro that only uses assert! in a const context +macro_rules! float_const_assert { + ($b:expr) => { + assert!(const { $b }); + }; + ($left:expr, $right:expr) => { + assert!(const { $left == $right }); + }; +} + test_float!( f32, + float_assert, + f32, + f32::INFINITY, + f32::NEG_INFINITY, + f32::NAN, + f32::MIN, + f32::MAX, + f32::MIN_POSITIVE, + f32::MAX_EXP +); +test_float!( + f32_const, + float_const_assert, f32, f32::INFINITY, f32::NEG_INFINITY, @@ -941,6 +974,19 @@ test_float!( ); test_float!( f64, + float_assert, + f64, + f64::INFINITY, + f64::NEG_INFINITY, + f64::NAN, + f64::MIN, + f64::MAX, + f64::MIN_POSITIVE, + f64::MAX_EXP +); +test_float!( + f64_const, + float_const_assert, f64, f64::INFINITY, f64::NEG_INFINITY, From f6aeef3d9b4962fbcab5546704dfc094ff156b07 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 10 May 2025 12:42:21 -0700 Subject: [PATCH 122/245] Add an issue template for future-incompatible lints --- .../ISSUE_TEMPLATE/tracking_issue_future.md | 54 +++++++++++++++++++ .../rustc-dev-guide/src/bug-fix-procedure.md | 36 ++----------- 2 files changed, 57 insertions(+), 33 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/tracking_issue_future.md diff --git a/.github/ISSUE_TEMPLATE/tracking_issue_future.md b/.github/ISSUE_TEMPLATE/tracking_issue_future.md new file mode 100644 index 000000000000..f04a458d8a5a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tracking_issue_future.md @@ -0,0 +1,54 @@ +--- +name: Future Incompatibility Tracking Issue +about: A tracking issue for a future-incompatible lint +title: Tracking Issue for future-incompatibility lint XXX +labels: C-tracking-issue C-future-incompatibility T-compiler A-lints +--- + + +This is the **tracking issue** for the `YOUR_LINT_NAME_HERE` future-compatibility warning and other related errors. The goal of this page is describe why this change was made and how you can fix code that is affected by it. It also provides a place to ask questions or register a complaint if you feel the change should not be made. For more information on the policy around future-compatibility warnings, see our [breaking change policy guidelines][guidelines]. + +[guidelines]: https://rustc-dev-guide.rust-lang.org/bug-fix-procedure.html + +### What is the warning for? + +*Describe the conditions that trigger the warning.* + +### Why was this change made? + +*Explain why this change was made. If there is additional context, like an MCP, link it here.* + +### Example + +```rust +// Include an example here. +``` + +### Recommendations + +*Give some recommendations on how a user can avoid the lint.* + +### When will this warning become a hard error? + +*If known, describe the future plans. For example, how long you anticipate this being a warning, or if there are other factors that will influence the anticipated closure.* + +### Steps + +- [ ] Implement the lint +- [ ] Raise lint level to deny +- [ ] Make lint report in dependencies +- [ ] Switch to a hard error + +### Implementation history + + diff --git a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md index 8e6725c54efa..55436261fdef 100644 --- a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md +++ b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md @@ -80,41 +80,11 @@ approachable and practical; it may make sense to direct users to an RFC or some other issue for the full details. The issue also serves as a place where users can comment with questions or other concerns. -A template for these breaking-change tracking issues can be found below. An -example of how such an issue should look can be [found +A template for these breaking-change tracking issues can be found +[here][template]. An example of how such an issue should look can be [found here][breaking-change-issue]. -The issue should be tagged with (at least) `B-unstable` and `T-compiler`. - -### Tracking issue template - -This is a template to use for tracking issues: - -``` -This is the **summary issue** for the `YOUR_LINT_NAME_HERE` -future-compatibility warning and other related errors. The goal of -this page is describe why this change was made and how you can fix -code that is affected by it. It also provides a place to ask questions -or register a complaint if you feel the change should not be made. For -more information on the policy around future-compatibility warnings, -see our [breaking change policy guidelines][guidelines]. - -[guidelines]: LINK_TO_THIS_RFC - -#### What is the warning for? - -*Describe the conditions that trigger the warning and how they can be -fixed. Also explain why the change was made.** - -#### When will this warning become a hard error? - -At the beginning of each 6-week release cycle, the Rust compiler team -will review the set of outstanding future compatibility warnings and -nominate some of them for **Final Comment Period**. Toward the end of -the cycle, we will review any comments and make a final determination -whether to convert the warning into a hard error or remove it -entirely. -``` +[template]: https://github.com/rust-lang/rust/issues/new?template=tracking_issue_future.md ### Issuing future compatibility warnings From c040e9f6fc51f9198cab65f021cd80a14fae5f52 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 11 May 2025 17:33:20 +0200 Subject: [PATCH 123/245] `return_and_then`: only lint returning expressions If an expression is not going to return from the current function of closure, it should not get linted. This also allows `return` expression to be linted, in addition to the final expression. Those require blockification and proper indentation. --- clippy_lints/src/methods/return_and_then.rs | 28 +++++++---- tests/ui/return_and_then.fixed | 52 +++++++++++++++++++++ tests/ui/return_and_then.rs | 47 +++++++++++++++++++ tests/ui/return_and_then.stderr | 47 ++++++++++++++++++- 4 files changed, 165 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/methods/return_and_then.rs b/clippy_lints/src/methods/return_and_then.rs index 91643b0dfefd..df8544f92203 100644 --- a/clippy_lints/src/methods/return_and_then.rs +++ b/clippy_lints/src/methods/return_and_then.rs @@ -9,7 +9,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::for_each_unconsumed_temporary; -use clippy_utils::{is_expr_final_block_expr, peel_blocks}; +use clippy_utils::{get_parent_expr, peel_blocks}; use super::RETURN_AND_THEN; @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'_>, ) { - if !is_expr_final_block_expr(cx.tcx, expr) { + if cx.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_none() { return; } @@ -55,12 +55,24 @@ pub(super) fn check<'tcx>( None => &body_snip, }; - let sugg = format!( - "let {} = {}?;\n{}", - arg_snip, - recv_snip, - reindent_multiline(inner, false, indent_of(cx, expr.span)) - ); + // If suggestion is going to get inserted as part of a `return` expression, it must be blockified. + let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { + let base_indent = indent_of(cx, parent_expr.span); + let inner_indent = base_indent.map(|i| i + 4); + format!( + "{}\n{}\n{}", + reindent_multiline(&format!("{{\nlet {arg_snip} = {recv_snip}?;"), true, inner_indent), + reindent_multiline(inner, false, inner_indent), + reindent_multiline("}", false, base_indent), + ) + } else { + format!( + "let {} = {}?;\n{}", + arg_snip, + recv_snip, + reindent_multiline(inner, false, indent_of(cx, expr.span)) + ) + }; span_lint_and_sugg( cx, diff --git a/tests/ui/return_and_then.fixed b/tests/ui/return_and_then.fixed index 74efa14eeec8..8d9481d15951 100644 --- a/tests/ui/return_and_then.fixed +++ b/tests/ui/return_and_then.fixed @@ -67,8 +67,60 @@ fn main() { .first() // creates temporary reference .and_then(|x| test_opt_block(Some(*x))) } + + fn in_closure() -> bool { + let _ = || { + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + //~^ return_and_then + }; + true + } + + fn with_return(shortcut: bool) -> Option { + if shortcut { + return { + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + }; + //~^ return_and_then + }; + None + } + + fn with_return_multiline(shortcut: bool) -> Option { + if shortcut { + return { + let mut x = Some("")?; + let x = format!("{x}."); + if x.len() > 2 { Some(3) } else { None } + }; + //~^^^^ return_and_then + }; + None + } } fn gen_option(n: i32) -> Option { Some(n) } + +mod issue14781 { + fn foo(_: &str, _: (u32, u32)) -> Result<(u32, u32), ()> { + Ok((1, 1)) + } + + fn bug(_: Option<&str>) -> Result<(), ()> { + let year: Option<&str> = None; + let month: Option<&str> = None; + let day: Option<&str> = None; + + let _day = if let (Some(year), Some(month)) = (year, month) { + day.and_then(|day| foo(day, (1, 31)).ok()) + } else { + None + }; + + Ok(()) + } +} diff --git a/tests/ui/return_and_then.rs b/tests/ui/return_and_then.rs index 188dc57e588c..beada921a918 100644 --- a/tests/ui/return_and_then.rs +++ b/tests/ui/return_and_then.rs @@ -63,8 +63,55 @@ fn main() { .first() // creates temporary reference .and_then(|x| test_opt_block(Some(*x))) } + + fn in_closure() -> bool { + let _ = || { + Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + //~^ return_and_then + }; + true + } + + fn with_return(shortcut: bool) -> Option { + if shortcut { + return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }); + //~^ return_and_then + }; + None + } + + fn with_return_multiline(shortcut: bool) -> Option { + if shortcut { + return Some("").and_then(|mut x| { + let x = format!("{x}."); + if x.len() > 2 { Some(3) } else { None } + }); + //~^^^^ return_and_then + }; + None + } } fn gen_option(n: i32) -> Option { Some(n) } + +mod issue14781 { + fn foo(_: &str, _: (u32, u32)) -> Result<(u32, u32), ()> { + Ok((1, 1)) + } + + fn bug(_: Option<&str>) -> Result<(), ()> { + let year: Option<&str> = None; + let month: Option<&str> = None; + let day: Option<&str> = None; + + let _day = if let (Some(year), Some(month)) = (year, month) { + day.and_then(|day| foo(day, (1, 31)).ok()) + } else { + None + }; + + Ok(()) + } +} diff --git a/tests/ui/return_and_then.stderr b/tests/ui/return_and_then.stderr index a7acbe7b3401..5feca8828605 100644 --- a/tests/ui/return_and_then.stderr +++ b/tests/ui/return_and_then.stderr @@ -101,5 +101,50 @@ LL + })?; LL + if x.len() > 2 { Some(3) } else { None } | -error: aborting due to 7 previous errors +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:69:13 + | +LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:77:20 + | +LL | return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ return { +LL + let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } +LL ~ }; + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:85:20 + | +LL | return Some("").and_then(|mut x| { + | ____________________^ +LL | | let x = format!("{x}."); +LL | | if x.len() > 2 { Some(3) } else { None } +LL | | }); + | |______________^ + | +help: try + | +LL ~ return { +LL + let mut x = Some("")?; +LL + let x = format!("{x}."); +LL + if x.len() > 2 { Some(3) } else { None } +LL ~ }; + | + +error: aborting due to 10 previous errors From ba80d820e5b4fb975f12eabac19e2a9c264a3afb Mon Sep 17 00:00:00 2001 From: Andrew Zhogin Date: Sun, 11 May 2025 20:58:52 +0700 Subject: [PATCH 124/245] Return value of coroutine_layout fn changed to Result with LayoutError --- compiler/rustc_middle/src/ty/mod.rs | 31 ++++++++++++++------ compiler/rustc_mir_transform/src/validate.rs | 4 +-- compiler/rustc_ty_utils/src/layout.rs | 4 +-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index dda0faa3afed..4ba5d8f3ce33 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -120,6 +120,7 @@ use crate::ty; use crate::ty::codec::{TyDecoder, TyEncoder}; pub use crate::ty::diagnostics::*; use crate::ty::fast_reject::SimplifiedType; +use crate::ty::layout::LayoutError; use crate::ty::util::Discr; use crate::ty::walk::TypeWalker; @@ -1877,6 +1878,11 @@ impl<'tcx> TyCtxt<'tcx> { self.def_kind(trait_def_id) == DefKind::TraitAlias } + /// Arena-alloc of LayoutError for coroutine layout + fn layout_error(self, err: LayoutError<'tcx>) -> &'tcx LayoutError<'tcx> { + self.arena.alloc(err) + } + /// Returns layout of a non-async-drop coroutine. Layout might be unavailable if the /// coroutine is tainted by errors. /// @@ -1885,12 +1891,14 @@ impl<'tcx> TyCtxt<'tcx> { fn ordinary_coroutine_layout( self, def_id: DefId, - coroutine_kind_ty: Ty<'tcx>, - ) -> Option<&'tcx CoroutineLayout<'tcx>> { + args: GenericArgsRef<'tcx>, + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { + let coroutine_kind_ty = args.as_coroutine().kind_ty(); let mir = self.optimized_mir(def_id); + let ty = || Ty::new_coroutine(self, def_id, args); // Regular coroutine if coroutine_kind_ty.is_unit() { - mir.coroutine_layout_raw() + mir.coroutine_layout_raw().ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } else { // If we have a `Coroutine` that comes from an coroutine-closure, // then it may be a by-move or by-ref body. @@ -1904,6 +1912,7 @@ impl<'tcx> TyCtxt<'tcx> { // a by-ref coroutine. if identity_kind_ty == coroutine_kind_ty { mir.coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } else { assert_matches!(coroutine_kind_ty.to_opt_closure_kind(), Some(ClosureKind::FnOnce)); assert_matches!( @@ -1912,6 +1921,7 @@ impl<'tcx> TyCtxt<'tcx> { ); self.optimized_mir(self.coroutine_by_move_body_def_id(def_id)) .coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } } } @@ -1923,12 +1933,15 @@ impl<'tcx> TyCtxt<'tcx> { self, def_id: DefId, args: GenericArgsRef<'tcx>, - ) -> Option<&'tcx CoroutineLayout<'tcx>> { + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { + let ty = || Ty::new_coroutine(self, def_id, args); if args[0].has_placeholders() || args[0].has_non_region_param() { - return None; + return Err(self.layout_error(LayoutError::TooGeneric(ty()))); } let instance = InstanceKind::AsyncDropGlue(def_id, Ty::new_coroutine(self, def_id, args)); - self.mir_shims(instance).coroutine_layout_raw() + self.mir_shims(instance) + .coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } /// Returns layout of a coroutine. Layout might be unavailable if the @@ -1937,7 +1950,7 @@ impl<'tcx> TyCtxt<'tcx> { self, def_id: DefId, args: GenericArgsRef<'tcx>, - ) -> Option<&'tcx CoroutineLayout<'tcx>> { + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { if self.is_async_drop_in_place_coroutine(def_id) { // layout of `async_drop_in_place::{closure}` in case, // when T is a coroutine, contains this internal coroutine's ptr in upvars @@ -1959,12 +1972,12 @@ impl<'tcx> TyCtxt<'tcx> { variant_source_info, storage_conflicts: BitMatrix::new(0, 0), }; - return Some(self.arena.alloc(proxy_layout)); + return Ok(self.arena.alloc(proxy_layout)); } else { self.async_drop_coroutine_layout(def_id, args) } } else { - self.ordinary_coroutine_layout(def_id, args.as_coroutine().kind_ty()) + self.ordinary_coroutine_layout(def_id, args) } } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index f541a32cd264..f8d1629b0e26 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -752,7 +752,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let layout = if def_id == self.caller_body.source.def_id() { self.caller_body .coroutine_layout_raw() - .or_else(|| self.tcx.coroutine_layout(def_id, args)) + .or_else(|| self.tcx.coroutine_layout(def_id, args).ok()) } else if self.tcx.needs_coroutine_by_move_body_def_id(def_id) && let ty::ClosureKind::FnOnce = args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap() @@ -762,7 +762,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // Same if this is the by-move body of a coroutine-closure. self.caller_body.coroutine_layout_raw() } else { - self.tcx.coroutine_layout(def_id, args) + self.tcx.coroutine_layout(def_id, args).ok() }; let Some(layout) = layout else { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 908fcb14cb2f..ad57555bd24d 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -492,9 +492,7 @@ fn layout_of_uncached<'tcx>( ty::Coroutine(def_id, args) => { use rustc_middle::ty::layout::PrimitiveExt as _; - let Some(info) = tcx.coroutine_layout(def_id, args) else { - return Err(error(cx, LayoutError::Unknown(ty))); - }; + let info = tcx.coroutine_layout(def_id, args)?; let local_layouts = info .field_tys From acbc79c8da33d7e38c52e48c83eb967af63ac8f3 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 11 May 2025 19:29:18 +0000 Subject: [PATCH 125/245] cargo update compiler & tools dependencies: Locking 40 packages to latest compatible versions Updating clap v4.5.37 -> v4.5.38 Updating clap_builder v4.5.37 -> v4.5.38 Updating ctrlc v3.4.6 -> v3.4.7 Updating derive_setters v0.1.6 -> v0.1.7 Updating getrandom v0.3.2 -> v0.3.3 Updating icu_collections v1.5.0 -> v2.0.0 Adding icu_locale_core v2.0.0 Updating icu_normalizer v1.5.0 -> v2.0.0 Updating icu_normalizer_data v1.5.1 -> v2.0.0 Updating icu_properties v1.5.1 -> v2.0.0 Updating icu_properties_data v1.5.1 -> v2.0.0 Adding icu_provider v2.0.0 Updating idna_adapter v1.2.0 -> v1.2.1 Updating jiff v0.2.12 -> v0.2.13 Updating jiff-static v0.2.12 -> v0.2.13 Updating libffi v4.0.0 -> v4.1.0 Updating libffi-sys v3.2.0 -> v3.3.1 Updating libloading v0.8.6 -> v0.8.7 Updating libm v0.2.13 -> v0.2.15 Adding litemap v0.8.0 Updating nix v0.29.0 -> v0.30.1 Adding potential_utf v0.1.2 Updating rustc-build-sysroot v0.5.4 -> v0.5.5 Adding tinystr v0.8.1 Updating tokio v1.44.2 -> v1.45.0 Updating unic-langid v0.9.5 -> v0.9.6 Updating unic-langid-impl v0.9.5 -> v0.9.6 Updating unic-langid-macros v0.9.5 -> v0.9.6 Updating unic-langid-macros-impl v0.9.5 -> v0.9.6 Removing utf16_iter v1.0.5 Adding wasm-encoder v0.230.0 Adding wasmparser v0.230.0 Updating wast v229.0.0 -> v230.0.0 Updating wat v1.229.0 -> v1.230.0 Updating winnow v0.7.9 -> v0.7.10 Removing write16 v1.0.0 Adding writeable v0.6.1 Adding yoke v0.8.0 Adding yoke-derive v0.8.0 Adding zerotrie v0.2.2 Adding zerovec v0.11.2 Adding zerovec-derive v0.11.1 note: pass `--verbose` to see 36 unchanged dependencies behind latest library dependencies: Locking 0 packages to latest compatible versions note: pass `--verbose` to see 3 unchanged dependencies behind latest rustbook dependencies: Locking 26 packages to latest compatible versions Updating cc v1.2.21 -> v1.2.22 Updating clap v4.5.37 -> v4.5.38 Updating clap_builder v4.5.37 -> v4.5.38 Updating clap_complete v4.5.48 -> v4.5.50 Updating getrandom v0.3.2 -> v0.3.3 Updating icu_collections v1.5.0 -> v2.0.0 Adding icu_locale_core v2.0.0 Removing icu_locid v1.5.0 Removing icu_locid_transform v1.5.0 Removing icu_locid_transform_data v1.5.1 Updating icu_normalizer v1.5.0 -> v2.0.0 Updating icu_normalizer_data v1.5.1 -> v2.0.0 Updating icu_properties v1.5.1 -> v2.0.0 Updating icu_properties_data v1.5.1 -> v2.0.0 Updating icu_provider v1.5.0 -> v2.0.0 Removing icu_provider_macros v1.5.0 Updating idna_adapter v1.2.0 -> v1.2.1 Updating jiff v0.2.12 -> v0.2.13 Updating jiff-static v0.2.12 -> v0.2.13 Updating litemap v0.7.5 -> v0.8.0 Adding potential_utf v0.1.2 Updating tinystr v0.7.6 -> v0.8.1 Removing utf16_iter v1.0.5 Updating web_atoms v0.1.1 -> v0.1.2 Updating winnow v0.7.9 -> v0.7.10 Removing write16 v1.0.0 Updating writeable v0.5.5 -> v0.6.1 Updating yoke v0.7.5 -> v0.8.0 Updating yoke-derive v0.7.5 -> v0.8.0 Adding zerotrie v0.2.2 Updating zerovec v0.10.4 -> v0.11.2 Updating zerovec-derive v0.10.3 -> v0.11.1 --- Cargo.lock | 346 +++++++++++++++++++++++----------- src/tools/rustbook/Cargo.lock | 176 ++++++++--------- 2 files changed, 312 insertions(+), 210 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd6fa99881b9..cbea8c6f08ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,7 +222,7 @@ dependencies = [ "memchr", "serde", "serde_derive", - "winnow 0.7.9", + "winnow 0.7.10", ] [[package]] @@ -493,9 +493,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -513,9 +513,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -849,9 +849,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.6" +version = "3.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" dependencies = [ "nix", "windows-sys 0.59.0", @@ -983,9 +983,9 @@ dependencies = [ [[package]] name = "derive_setters" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" +checksum = "d9c848e86c87e5cc305313041c5677d4d95d60baa71cf95e5f6ea2554bb629ff" dependencies = [ "darling", "proc-macro2", @@ -1443,9 +1443,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -1607,14 +1607,15 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", - "yoke", + "potential_utf", + "yoke 0.8.0", "zerofrom", - "zerovec", + "zerovec 0.11.2", ] [[package]] @@ -1626,9 +1627,9 @@ dependencies = [ "displaydoc", "icu_list_data", "icu_locid_transform", - "icu_provider", + "icu_provider 1.5.0", "regex-automata 0.2.0", - "writeable", + "writeable 0.5.5", ] [[package]] @@ -1637,6 +1638,19 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52b1a7fbdbf3958f1be8354cb59ac73f165b7b7082d447ff2090355c9a069120" +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap 0.8.0", + "tinystr 0.8.1", + "writeable 0.6.1", + "zerovec 0.11.2", +] + [[package]] name = "icu_locid" version = "1.5.0" @@ -1644,10 +1658,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", + "litemap 0.7.5", + "tinystr 0.7.6", + "writeable 0.5.5", + "zerovec 0.10.4", ] [[package]] @@ -1659,9 +1673,9 @@ dependencies = [ "displaydoc", "icu_locid", "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", + "icu_provider 1.5.0", + "tinystr 0.7.6", + "zerovec 0.10.4", ] [[package]] @@ -1672,48 +1686,46 @@ checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", - "icu_provider", + "icu_provider 2.0.0", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", + "zerovec 0.11.2", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", + "icu_provider 2.0.0", + "potential_utf", + "zerotrie", + "zerovec 0.11.2", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" [[package]] name = "icu_provider" @@ -1725,11 +1737,28 @@ dependencies = [ "icu_locid", "icu_provider_macros", "stable_deref_trait", - "tinystr", - "writeable", - "yoke", + "tinystr 0.7.6", + "writeable 0.5.5", + "yoke 0.7.5", "zerofrom", - "zerovec", + "zerovec 0.10.4", +] + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr 0.8.1", + "writeable 0.6.1", + "yoke 0.8.0", + "zerofrom", + "zerotrie", + "zerovec 0.11.2", ] [[package]] @@ -1740,9 +1769,9 @@ checksum = "d6324dfd08348a8e0374a447ebd334044d766b1839bb8d5ccf2482a99a77c0bc" dependencies = [ "icu_locid", "icu_locid_transform", - "icu_provider", - "tinystr", - "zerovec", + "icu_provider 1.5.0", + "tinystr 0.7.6", + "zerovec 0.10.4", ] [[package]] @@ -1781,9 +1810,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -1911,9 +1940,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" dependencies = [ "jiff-static", "log", @@ -1924,9 +1953,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" dependencies = [ "proc-macro2", "quote", @@ -1939,7 +1968,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", ] @@ -2039,9 +2068,9 @@ dependencies = [ [[package]] name = "libffi" -version = "4.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9434b6fc77375fb624698d5f8c49d7e80b10d59eb1219afda27d1f824d4074" +checksum = "ebfd30a67b482a08116e753d0656cb626548cf4242543e5cc005be7639d99838" dependencies = [ "libc", "libffi-sys", @@ -2049,28 +2078,28 @@ dependencies = [ [[package]] name = "libffi-sys" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ead36a2496acfc8edd6cc32352110e9478ac5b9b5f5b9856ebd3d28019addb84" +checksum = "f003aa318c9f0ee69eb0ada7c78f5c9d2fedd2ceb274173b5c7ff475eee584a3" dependencies = [ "cc", ] [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.53.0", ] [[package]] name = "libm" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" @@ -2124,6 +2153,12 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "lld-wrapper" version = "0.1.0" @@ -2294,7 +2329,7 @@ dependencies = [ "chrono-tz", "colored", "directories", - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", "libffi", "libloading", @@ -2321,9 +2356,9 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags", "cfg-if", @@ -2784,6 +2819,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec 0.11.2", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2934,7 +2978,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] @@ -3089,9 +3133,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6d984a9db43148467059309bd1e5ad577085162f695d9fe2cf3543aeb25cd38" +checksum = "fb332121f7845c6bd016f9655cf22f03c2999df936694b624a88669a78667d98" dependencies = [ "anyhow", "rustc_version", @@ -3324,8 +3368,8 @@ dependencies = [ "icu_list", "icu_locid", "icu_locid_transform", - "icu_provider", - "zerovec", + "icu_provider 1.5.0", + "zerovec 0.10.4", ] [[package]] @@ -5104,7 +5148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", "rustix", "windows-sys 0.59.0", @@ -5279,7 +5323,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", - "zerovec", + "zerovec 0.10.4", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec 0.11.2", ] [[package]] @@ -5299,9 +5353,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -5505,9 +5559,9 @@ dependencies = [ [[package]] name = "unic-langid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" +checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05" dependencies = [ "unic-langid-impl", "unic-langid-macros", @@ -5515,30 +5569,30 @@ dependencies = [ [[package]] name = "unic-langid-impl" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" +checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" dependencies = [ - "tinystr", + "tinystr 0.8.1", ] [[package]] name = "unic-langid-macros" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da1cd2c042d3c7569a1008806b02039e7a4a2bdf8f8e96bd3c792434a0e275e" +checksum = "d5957eb82e346d7add14182a3315a7e298f04e1ba4baac36f7f0dbfedba5fc25" dependencies = [ "proc-macro-hack", - "tinystr", + "tinystr 0.8.1", "unic-langid-impl", "unic-langid-macros-impl", ] [[package]] name = "unic-langid-macros-impl" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" +checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5" dependencies = [ "proc-macro-hack", "quote", @@ -5654,12 +5708,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8-width" version = "0.1.7" @@ -5684,7 +5732,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] @@ -5841,6 +5889,16 @@ dependencies = [ "wasmparser 0.229.0", ] +[[package]] +name = "wasm-encoder" +version = "0.230.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4349d0943718e6e434b51b9639e876293093dca4b96384fb136ab5bd5ce6660" +dependencies = [ + "leb128fmt", + "wasmparser 0.230.0", +] + [[package]] name = "wasm-metadata" version = "0.229.0" @@ -5886,23 +5944,34 @@ dependencies = [ ] [[package]] -name = "wast" -version = "229.0.0" +name = "wasmparser" +version = "0.230.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63fcaff613c12225696bb163f79ca38ffb40e9300eff0ff4b8aa8b2f7eadf0d9" +checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + +[[package]] +name = "wast" +version = "230.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8edac03c5fa691551531533928443faf3dc61a44f814a235c7ec5d17b7b34f1" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.229.0", + "wasm-encoder 0.230.0", ] [[package]] name = "wat" -version = "1.229.0" +version = "1.230.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4189bad08b70455a9e9e67dc126d2dcf91fac143a80f1046747a5dde6d4c33e0" +checksum = "0d77d62229e38db83eac32bacb5f61ebb952366ab0dae90cf2b3c07a65eea894" dependencies = [ "wast", ] @@ -6326,9 +6395,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -6385,18 +6454,18 @@ dependencies = [ "wasmparser 0.229.0", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + [[package]] name = "x" version = "0.1.1" @@ -6437,7 +6506,19 @@ checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", - "yoke-derive", + "yoke-derive 0.7.5", + "zerofrom", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive 0.8.0", "zerofrom", ] @@ -6453,6 +6534,18 @@ dependencies = [ "synstructure", ] +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.25" @@ -6494,15 +6587,37 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke 0.8.0", + "zerofrom", +] + [[package]] name = "zerovec" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ - "yoke", + "yoke 0.7.5", "zerofrom", - "zerovec-derive", + "zerovec-derive 0.10.3", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke 0.8.0", + "zerofrom", + "zerovec-derive 0.11.1", ] [[package]] @@ -6515,3 +6630,14 @@ dependencies = [ "quote", "syn 2.0.101", ] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index 3b5b31bed14a..0b3893770111 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -156,9 +156,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "cc" -version = "1.2.21" +version = "1.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" +checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" dependencies = [ "shlex", ] @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -208,9 +208,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.48" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8c97f3a6f02b9e24cadc12aaba75201d18754b53ea0a9d99642f806ccdb4c9" +checksum = "c91d3baa3bcd889d60e6ef28874126a0b384fd225ab83aa6d8a801c519194ce1" dependencies = [ "clap", ] @@ -547,9 +547,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -644,21 +644,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -667,31 +668,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -699,67 +680,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -779,9 +747,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -811,9 +779,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" dependencies = [ "jiff-static", "log", @@ -824,9 +792,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" dependencies = [ "proc-macro2", "quote", @@ -882,9 +850,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" @@ -1274,6 +1242,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "precomputed-hash" version = "0.1.1" @@ -1710,9 +1687,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -1821,12 +1798,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -1924,9 +1895,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bcbdcad8fb2e316072ba6bbe09419afdb550285668ac2534f4230a6f2da0ee" +checksum = "0b9c5f0bc545ea3b20b423e33b9b457764de0b3730cd957f6c6aa6c301785f6e" dependencies = [ "phf", "phf_codegen", @@ -2099,9 +2070,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -2115,23 +2086,17 @@ dependencies = [ "bitflags 2.9.0", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -2141,9 +2106,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -2173,10 +2138,21 @@ dependencies = [ ] [[package]] -name = "zerovec" -version = "0.10.4" +name = "zerotrie" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -2185,9 +2161,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", From 30eca8ba9160bf18c4f0a3c9bb760d270f012240 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 12 May 2025 04:53:41 +0000 Subject: [PATCH 126/245] Preparing for merge from rustc --- 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 9afbd3aa6357..801f78ba85cb 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -fd9fad6dbcc1bae3cba2a8634339ffa620a49f28 +718ddf660e6a1802c39b4962cf7eaa4db57025ef From 8f5dc85f37fb1dc1830e11f387c6eed6b4713c23 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 12 May 2025 08:48:36 +0200 Subject: [PATCH 127/245] Use `tempfile::TempDir::keep()` instead of deprecated `into_path()` `tempfile` has deprecated `TempDir::into_path()` (replacing it by `TempDir::keep()`) between version `3.3` which Clippy required and version `3.20.0` which is the latest semver-compatible version. Since Clippy doesn't use a `Cargo.lock` file, the latest version of `tempfile` is used which leads to CI failure. --- Cargo.toml | 2 +- tests/integration.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 603169f72b61..92c4db845000 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ clippy_lints = { path = "clippy_lints" } clippy_utils = { path = "clippy_utils" } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } clippy_lints_internal = { path = "clippy_lints_internal", optional = true } -tempfile = { version = "3.3", optional = true } +tempfile = { version = "3.20", optional = true } termize = "0.1" color-print = "0.3.4" anstream = "0.6.18" diff --git a/tests/integration.rs b/tests/integration.rs index 13cf36823c5e..cb7d61eee24a 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -30,7 +30,7 @@ fn integration_test() { let repo_dir = tempfile::tempdir() .expect("couldn't create temp dir") - .into_path() + .keep() .join(crate_name); let st = Command::new("git") From 41a2260bc7d7388a6ef2b0dfa7a980c977e26081 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 12 May 2025 09:23:30 +0200 Subject: [PATCH 128/245] Use unreachable instead of panic Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- compiler/rustc_target/src/callconv/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 6aae8bc75560..a141accb7380 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -919,7 +919,7 @@ fn conv_to_externabi(conv: &Conv) -> ExternAbi { Conv::AvrNonBlockingInterrupt => ExternAbi::AvrNonBlockingInterrupt, Conv::RiscvInterrupt { kind: RiscvInterruptKind::Machine } => ExternAbi::RiscvInterruptM, Conv::RiscvInterrupt { kind: RiscvInterruptKind::Supervisor } => ExternAbi::RiscvInterruptS, - Conv::Cold | Conv::PreserveAll => panic!("This is deadcode"), + Conv::Cold | Conv::PreserveAll => unreachable!(), } } From 55b5c835c57ce88d8499b281ead008ff54908b00 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 7 May 2025 17:10:02 +0500 Subject: [PATCH 129/245] Changelog for Clippy 1.87 --- CHANGELOG.md | 100 +++++++++++++++++++++++++- clippy_lints/src/lifetimes.rs | 2 +- clippy_lints/src/mem_replace.rs | 2 +- clippy_lints/src/methods/mod.rs | 4 +- clippy_lints/src/single_option_map.rs | 2 +- clippy_lints/src/types/mod.rs | 2 +- 6 files changed, 105 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b62c9a59aa5..a3c6cba48763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,105 @@ document. ## Unreleased / Beta / In Rust Nightly -[3e3715c3...master](https://github.com/rust-lang/rust-clippy/compare/3e3715c3...master) +[1e5237f4...master](https://github.com/rust-lang/rust-clippy/compare/1e5237f4...master) + +## Rust 1.87 + +Current stable, released 2025-05-15 + +[View all 127 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-02-06T14%3A54%3A28Z..2025-03-20T20%3A07%3A53Z+base%3Amaster) + +### New Lints + +* Added [`doc_comment_double_space_linebreaks`] to `pedantic` [#12876](https://github.com/rust-lang/rust-clippy/pull/12876) +* Added [`manual_midpoint`] to `pedantic` [#13851](https://github.com/rust-lang/rust-clippy/pull/13851) +* Added [`io_other_error`] to `style` [#14022](https://github.com/rust-lang/rust-clippy/pull/14022) +* Added [`owned_cow`] to `pedantic` [#13948](https://github.com/rust-lang/rust-clippy/pull/13948) +* Added [`manual_contains`] to `perf` [#13817](https://github.com/rust-lang/rust-clippy/pull/13817) +* Added [`unnecessary_debug_formatting`] to `pedantic` [#13893](https://github.com/rust-lang/rust-clippy/pull/13893) +* Added [`elidable_lifetime_names`] to `pedantic` [#13960](https://github.com/rust-lang/rust-clippy/pull/13960) +* Added [`mem_replace_option_with_some`] to `style` [#14197](https://github.com/rust-lang/rust-clippy/pull/14197) +* Added [`unbuffered_bytes`] to `perf` [#14089](https://github.com/rust-lang/rust-clippy/pull/14089) +* Added [`single_option_map`] to `nursery` [#14033](https://github.com/rust-lang/rust-clippy/pull/14033) + +### Moves and Deprecations + +* Moved [`comparison_chain`] to `pedantic` (from `style`) + [#14219](https://github.com/rust-lang/rust-clippy/pull/14219) +* Moved [`manual_ok_or`] to `style` (from `pedantic`) + [#14027](https://github.com/rust-lang/rust-clippy/pull/14027) +* Deprecated [`option_map_or_err_ok`] in favor of [`manual_ok_or`] + [#14027](https://github.com/rust-lang/rust-clippy/pull/14027) + +### Enhancements + +* Add `allow_expect_in_consts` and `allow_unwrap_in_consts` configuration options to [`unwrap_used`], [`expect_used`] + [#14200](https://github.com/rust-lang/rust-clippy/pull/14200) +* Add `check-incompatible-msrv-in-tests` configuration option to [`incompatible_msrv`] + [#14279](https://github.com/rust-lang/rust-clippy/pull/14279) +* [`len_zero`] now also triggers if deref target implements `is_empty()` + [#13871](https://github.com/rust-lang/rust-clippy/pull/13871) +* [`ptr_eq`] now handles more cases, including `!=` in addition to `==` + [#14339](https://github.com/rust-lang/rust-clippy/pull/14339) +* [`struct_field_names`] now also checks private fields of public structs + [#14076](https://github.com/rust-lang/rust-clippy/pull/14076) +* [`needless_pass_by_value`] suggests using a reference on the innermost `Option` content + [#14392](https://github.com/rust-lang/rust-clippy/pull/14392) +* [`obfuscated_if_else`] now supports `then().unwrap_or_else()` and `then_some().unwrap_or_else()` + [#14165](https://github.com/rust-lang/rust-clippy/pull/14165) +* Format macros: all format-handling lints now validate `todo!` and `unimplemented!` macros + [#14266](https://github.com/rust-lang/rust-clippy/pull/14266) +* [`disallowed_methods`] now supports replacements + [#13669](https://github.com/rust-lang/rust-clippy/pull/13669) +* Added MSRV checks for several lints: + * [`question_mark`] [#14436](https://github.com/rust-lang/rust-clippy/pull/14436) + * [`repeat_vec_with_capacity`] [#14126](https://github.com/rust-lang/rust-clippy/pull/14126) + * [`manual_flatten`] [#14086](https://github.com/rust-lang/rust-clippy/pull/14086) + * [`lines_filter_map_ok`] [#14130](https://github.com/rust-lang/rust-clippy/pull/14130) + +### False Positive Fixes + +* [`missing_const_for_fn`] no longer triggers on unstable const traits [#14294](https://github.com/rust-lang/rust-clippy/pull/14294) +* [`unnecessary_to_owned`] now avoids suggesting to call `iter()` on a temporary object [#14243](https://github.com/rust-lang/rust-clippy/pull/14243) +* [`unnecessary_debug_formatting`] no longer triggers in tests [#14347](https://github.com/rust-lang/rust-clippy/pull/14347) +* [`option_if_let_else`] now handles cases when value is partially moved [#14209](https://github.com/rust-lang/rust-clippy/pull/14209) +* [`blocks_in_conditions`] no longer triggers when the condition contains a `return` [#14338](https://github.com/rust-lang/rust-clippy/pull/14338) +* [`undocumented_unsafe_blocks`] no longer triggers on trait/impl items [#13888](https://github.com/rust-lang/rust-clippy/pull/13888) +* [`manual_slice_fill`] no longer triggers due to missing index checks [#14193](https://github.com/rust-lang/rust-clippy/pull/14193) +* [`useless_asref`] no longer suggests using `.clone()` if the target type doesn't implement `Clone` [#14174](https://github.com/rust-lang/rust-clippy/pull/14174) +* [`unnecessary_safety_comment`] no longer triggers on desugared assign [#14371](https://github.com/rust-lang/rust-clippy/pull/14371) +* [`unnecessary_map_or`] no longer consumes the comparison value if it does not implement `Copy` [#14207](https://github.com/rust-lang/rust-clippy/pull/14207) +* [`let_and_return`] no longer triggers involving short-lived block temporary variables [#14180](https://github.com/rust-lang/rust-clippy/pull/14180) +* [`manual_async_fn`] no longer emits suggestions inside macros [#14142](https://github.com/rust-lang/rust-clippy/pull/14142) +* [`use_self`] skips analysis inside macro expansions of a `impl Self` block [#13128](https://github.com/rust-lang/rust-clippy/pull/13128) +* [`double_ended_iterator_last`] no longer triggers on non-reference immutable receiver [#14140](https://github.com/rust-lang/rust-clippy/pull/14140) + +### ICE Fixes + +* [`macro_use_imports`] Fix ICE when checking attributes + [#14317](https://github.com/rust-lang/rust-clippy/pull/14317) +* [`doc_nested_refdefs`] Fix ICE by avoiding invalid ranges + [#14308](https://github.com/rust-lang/rust-clippy/pull/14308) +* [`just_underscores_and_digits`] Fix ICE in error recovery scenario + [#14168](https://github.com/rust-lang/rust-clippy/pull/14168) +* [`declare_interior_mutable_const`], [`borrow_interior_mutable_const`] Fix ICE by properly resolving `::AssocT` projections + [#14125](https://github.com/rust-lang/rust-clippy/pull/14125) + +### Documentation Improvements + +* [`struct_excessive_bools`] Documentation improved with rationale + [#14351](https://github.com/rust-lang/rust-clippy/pull/14351) + +### Others + +* Use edition=2021 in `rustc_tools_util` + [#14211](https://github.com/rust-lang/rust-clippy/pull/14211) +* Fix rustc_tools_util's `version.host_compiler` release channel, expose the rustc version, and add tests + [#14123](https://github.com/rust-lang/rust-clippy/pull/14123) +* Make UI test annotations mandatory + [#11421](https://github.com/rust-lang/rust-clippy/pull/11421) + [#14388](https://github.com/rust-lang/rust-clippy/pull/14388) + [#14393](https://github.com/rust-lang/rust-clippy/pull/14393) ## Rust 1.86 diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 5ef5e3a44f85..9a64226b1ed9 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -88,7 +88,7 @@ declare_clippy_lint! { /// x.chars() /// } /// ``` - #[clippy::version = "1.84.0"] + #[clippy::version = "1.87.0"] pub ELIDABLE_LIFETIME_NAMES, pedantic, "lifetime name that can be replaced with the anonymous lifetime" diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index a54d835b538c..28efd2038b38 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -62,7 +62,7 @@ declare_clippy_lint! { /// let mut an_option = Some(0); /// let taken = an_option.replace(1); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub MEM_REPLACE_OPTION_WITH_SOME, style, "replacing an `Option` with `Some` instead of `replace()`" diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 10f4637d08f6..45c61c5cb328 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4428,7 +4428,7 @@ declare_clippy_lint! { /// let file = BufReader::new(std::fs::File::open("./bytes.txt").unwrap()); /// file.bytes(); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub UNBUFFERED_BYTES, perf, "calling .bytes() is very inefficient when data is not in memory" @@ -4453,7 +4453,7 @@ declare_clippy_lint! { /// values.contains(&10) /// } /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub MANUAL_CONTAINS, perf, "unnecessary `iter().any()` on slices that can be replaced with `contains()`" diff --git a/clippy_lints/src/single_option_map.rs b/clippy_lints/src/single_option_map.rs index 1fb54950612a..cc497c97a472 100644 --- a/clippy_lints/src/single_option_map.rs +++ b/clippy_lints/src/single_option_map.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// param * 2 /// } /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub SINGLE_OPTION_MAP, nursery, "Checks for functions with method calls to `.map(_)` on an arg of type `Option` as the outermost expression." diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 3147058b4cda..c1c7cc516565 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -385,7 +385,7 @@ declare_clippy_lint! { /// ```no_run /// let right: std::borrow::Cow<'_, [u8]>; /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.87.0"] pub OWNED_COW, style, "needlessly owned Cow type" From c12d12b1bb64abd3b694c7af703989ef3d8c1426 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Fri, 9 May 2025 09:28:42 -0400 Subject: [PATCH 130/245] Fix for renamed/removed UI tests --- tests/failing-ui-tests.txt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/failing-ui-tests.txt b/tests/failing-ui-tests.txt index 499c1a962311..0a01a661c357 100644 --- a/tests/failing-ui-tests.txt +++ b/tests/failing-ui-tests.txt @@ -10,7 +10,7 @@ tests/ui/sepcomp/sepcomp-fns-backwards.rs tests/ui/sepcomp/sepcomp-fns.rs tests/ui/sepcomp/sepcomp-statics.rs tests/ui/asm/x86_64/may_unwind.rs -tests/ui/catch-unwind-bang.rs +tests/ui/panics/catch-unwind-bang.rs tests/ui/drop/dynamic-drop-async.rs tests/ui/cfg/cfg-panic-abort.rs tests/ui/drop/repeat-drop.rs @@ -94,23 +94,14 @@ tests/ui/simd/intrinsic/generic-as.rs tests/ui/backtrace/backtrace.rs tests/ui/lifetimes/tail-expr-lock-poisoning.rs tests/ui/runtime/rt-explody-panic-payloads.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/function.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/basic.rs tests/ui/codegen/equal-pointers-unequal/as-cast/inline1.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/print.rs tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs tests/ui/codegen/equal-pointers-unequal/as-cast/segfault.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline1.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/segfault.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline1.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs From bef68d04d9be557d954dd89667da97c5565b21a1 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Mon, 12 May 2025 11:05:47 -0400 Subject: [PATCH 131/245] Update to nightly-2025-05-12 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index fbaa22190052..a8cda28688c8 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-04-26" +channel = "nightly-2025-05-12" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From 57011501d5c907b89d2854ebfb889d652024b4fc Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Mon, 12 May 2025 11:13:34 -0400 Subject: [PATCH 132/245] Fix for libgccjit 12 --- src/attributes.rs | 1 + src/intrinsic/mod.rs | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/attributes.rs b/src/attributes.rs index f933119d0ba0..e63091c6082a 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -6,6 +6,7 @@ use rustc_attr_parsing::InlineAttr; use rustc_attr_parsing::InstructionSetAttr; #[cfg(feature = "master")] use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +#[cfg(feature = "master")] use rustc_middle::mir::TerminatorKind; use rustc_middle::ty; diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs index 9caceca92957..f292c467418f 100644 --- a/src/intrinsic/mod.rs +++ b/src/intrinsic/mod.rs @@ -4,9 +4,7 @@ mod simd; #[cfg(feature = "master")] use std::iter; -#[cfg(feature = "master")] -use gccjit::FunctionType; -use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp}; +use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp}; #[cfg(feature = "master")] use rustc_abi::ExternAbi; use rustc_abi::{BackendRepr, HasDataLayout}; From 01e08a4c5ba414fe852467efeb90204477aa9d23 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 12 May 2025 18:23:44 +0200 Subject: [PATCH 133/245] Do not use `clippy_utils` from UI tests --- Cargo.toml | 1 - tests/ui/let_underscore_untyped.rs | 1 - tests/ui/let_underscore_untyped.stderr | 20 ++++++++++---------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 92c4db845000..0df9f2e55598 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,6 @@ pulldown-cmark = { version = "0.11", default-features = false, features = ["html askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] } # UI test dependencies -clippy_utils = { path = "clippy_utils" } if_chain = "1.0" quote = "1.0.25" syn = { version = "2.0", features = ["full"] } diff --git a/tests/ui/let_underscore_untyped.rs b/tests/ui/let_underscore_untyped.rs index 26ba8682dc2d..7f74ab035bda 100644 --- a/tests/ui/let_underscore_untyped.rs +++ b/tests/ui/let_underscore_untyped.rs @@ -6,7 +6,6 @@ extern crate proc_macros; use proc_macros::with_span; -use clippy_utils::is_from_proc_macro; use std::boxed::Box; use std::fmt::Display; use std::future::Future; diff --git a/tests/ui/let_underscore_untyped.stderr b/tests/ui/let_underscore_untyped.stderr index 86cdd5c662cc..54b955ac3a56 100644 --- a/tests/ui/let_underscore_untyped.stderr +++ b/tests/ui/let_underscore_untyped.stderr @@ -1,11 +1,11 @@ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:51:5 + --> tests/ui/let_underscore_untyped.rs:50:5 | LL | let _ = a(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:51:10 + --> tests/ui/let_underscore_untyped.rs:50:10 | LL | let _ = a(); | ^ @@ -13,49 +13,49 @@ LL | let _ = a(); = help: to override `-D warnings` add `#[allow(clippy::let_underscore_untyped)]` error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:53:5 + --> tests/ui/let_underscore_untyped.rs:52:5 | LL | let _ = b(1); | ^^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:53:10 + --> tests/ui/let_underscore_untyped.rs:52:10 | LL | let _ = b(1); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:56:5 + --> tests/ui/let_underscore_untyped.rs:55:5 | LL | let _ = d(&1); | ^^^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:56:10 + --> tests/ui/let_underscore_untyped.rs:55:10 | LL | let _ = d(&1); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:58:5 + --> tests/ui/let_underscore_untyped.rs:57:5 | LL | let _ = e(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:58:10 + --> tests/ui/let_underscore_untyped.rs:57:10 | LL | let _ = e(); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:60:5 + --> tests/ui/let_underscore_untyped.rs:59:5 | LL | let _ = f(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:60:10 + --> tests/ui/let_underscore_untyped.rs:59:10 | LL | let _ = f(); | ^ From c6a23f8fbdc2e356209eac58cad09fab700bfc8c Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 8 May 2025 00:17:13 +0200 Subject: [PATCH 134/245] Include all bounds in bounds consolidation Also, include the type name in the lint message, and simplify the span computation. --- clippy_lints/src/trait_bounds.rs | 18 ++++------- .../ui-toml/type_repetition_in_bounds/main.rs | 2 +- .../type_repetition_in_bounds/main.stderr | 2 +- tests/ui/type_repetition_in_bounds.rs | 27 +++++++++++++---- tests/ui/type_repetition_in_bounds.stderr | 30 ++++++++++++++----- 5 files changed, 52 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 8aac3a591029..45e54302e32c 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -15,7 +15,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{BytePos, Span}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -282,18 +282,18 @@ impl TraitBounds { .iter() .copied() .chain(p.bounds.iter()) - .filter_map(get_trait_info_from_bound) - .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability)) + .map(|bound| snippet_with_applicability(cx, bound.span(), "_", &mut applicability)) .join(" + "); let hint_string = format!( "consider combining the bounds: `{}: {trait_bounds}`", snippet(cx, p.bounded_ty.span, "_"), ); + let ty_name = snippet(cx, p.bounded_ty.span, "_"); span_lint_and_help( cx, TYPE_REPETITION_IN_BOUNDS, bound.span, - "this type has already been used as a bound predicate", + format!("type `{ty_name}` has already been used as a bound predicate"), None, hint_string, ); @@ -395,15 +395,7 @@ impl Hash for ComparableTraitRef<'_, '_> { fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> { if let GenericBound::Trait(t) = bound { let trait_path = t.trait_ref.path; - let trait_span = { - let path_span = trait_path.span; - if let BoundPolarity::Maybe(_) = t.modifiers.polarity { - path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?` - } else { - path_span - } - }; - Some((trait_path.res, trait_path.segments, trait_span)) + Some((trait_path.res, trait_path.segments, t.span)) } else { None } diff --git a/tests/ui-toml/type_repetition_in_bounds/main.rs b/tests/ui-toml/type_repetition_in_bounds/main.rs index 7f93d2071c9d..b60cb7632e20 100644 --- a/tests/ui-toml/type_repetition_in_bounds/main.rs +++ b/tests/ui-toml/type_repetition_in_bounds/main.rs @@ -12,7 +12,7 @@ fn f2() where T: Copy + Clone + Sync + Send + ?Sized, T: Unpin + PartialEq, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } diff --git a/tests/ui-toml/type_repetition_in_bounds/main.stderr b/tests/ui-toml/type_repetition_in_bounds/main.stderr index c5102c39d1cf..ba0f41167a00 100644 --- a/tests/ui-toml/type_repetition_in_bounds/main.stderr +++ b/tests/ui-toml/type_repetition_in_bounds/main.stderr @@ -1,4 +1,4 @@ -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui-toml/type_repetition_in_bounds/main.rs:14:5 | LL | T: Unpin + PartialEq, diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index d325887bfba3..e75678d5fd93 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -12,7 +12,7 @@ pub fn foo(_t: T) where T: Copy, T: Clone, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { unimplemented!(); } @@ -30,7 +30,7 @@ trait LintBounds where Self: Clone, Self: Copy + Default + Ord, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds Self: Add + AddAssign + Sub + SubAssign, Self: Mul + MulAssign + Div + DivAssign, { @@ -105,13 +105,13 @@ where pub fn f() where T: Clone, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } pub fn g() where T: ?Sized, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } @@ -137,10 +137,27 @@ mod issue8772_pass { pub fn f(arg: usize) where T: Trait, Box<[String]>, bool> + 'static, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds U: Clone + Sync + 'static, { } } +struct Issue14744<'a, K: 'a> +where + K: Clone, +{ + phantom: std::marker::PhantomData<&'a K>, +} +//~^^^^ type_repetition_in_bounds + +struct ComplexType +where + Vec: Clone, + Vec: Clone, +{ + t: T, +} +//~^^^^ type_repetition_in_bounds + fn main() {} diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 77944c950457..de1b14da1985 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -1,4 +1,4 @@ -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:14:5 | LL | T: Clone, @@ -11,7 +11,7 @@ note: the lint level is defined here LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this type has already been used as a bound predicate +error: type `Self` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:32:5 | LL | Self: Copy + Default + Ord, @@ -19,7 +19,7 @@ LL | Self: Copy + Default + Ord, | = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:107:5 | LL | T: Clone, @@ -27,7 +27,7 @@ LL | T: Clone, | = help: consider combining the bounds: `T: ?Sized + Clone` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:113:5 | LL | T: ?Sized, @@ -35,13 +35,29 @@ LL | T: ?Sized, | = help: consider combining the bounds: `T: Clone + ?Sized` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:139:9 | LL | T: Trait, Box<[String]>, bool> + 'static, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider combining the bounds: `T: ?Sized + Trait, Box<[String]>, bool>` + = help: consider combining the bounds: `T: ?Sized + Trait, Box<[String]>, bool> + 'static` -error: aborting due to 5 previous errors +error: type `K` has already been used as a bound predicate + --> tests/ui/type_repetition_in_bounds.rs:148:5 + | +LL | K: Clone, + | ^^^^^^^^ + | + = help: consider combining the bounds: `K: 'a + Clone` + +error: type `Vec` has already been used as a bound predicate + --> tests/ui/type_repetition_in_bounds.rs:157:5 + | +LL | Vec: Clone, + | ^^^^^^^^^^^^^ + | + = help: consider combining the bounds: `Vec: Clone + Clone` + +error: aborting due to 7 previous errors From 82fbbc0882e64847e60934dba64188c07207b345 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 12 May 2025 16:18:27 +0000 Subject: [PATCH 135/245] compiletest: fix "blessing" message It was showing compare mode instead of test name. --- src/tools/compiletest/src/runtest.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 3c87bcb205fc..75f24adb70fa 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2609,18 +2609,19 @@ impl<'test> TestCx<'test> { (expected, actual) }; - // Write the actual output to a file in build/ - let test_name = self.config.compare_mode.as_ref().map_or("", |m| m.to_str()); + // Write the actual output to a file in build directory. let actual_path = self .output_base_name() .with_extra_extension(self.revision.unwrap_or("")) - .with_extra_extension(test_name) + .with_extra_extension( + self.config.compare_mode.as_ref().map(|cm| cm.to_str()).unwrap_or(""), + ) .with_extra_extension(stream); if let Err(err) = fs::write(&actual_path, &actual) { - self.fatal(&format!("failed to write {stream} to `{actual_path:?}`: {err}",)); + self.fatal(&format!("failed to write {stream} to `{actual_path}`: {err}",)); } - println!("Saved the actual {stream} to {actual_path:?}"); + println!("Saved the actual {stream} to `{actual_path}`"); if !self.config.bless { if expected.is_empty() { @@ -2646,13 +2647,16 @@ impl<'test> TestCx<'test> { if !actual.is_empty() { if let Err(err) = fs::write(&expected_path, &actual) { - self.fatal(&format!("failed to write {stream} to `{expected_path:?}`: {err}")); + self.fatal(&format!("failed to write {stream} to `{expected_path}`: {err}")); } - println!("Blessing the {stream} of {test_name} in {expected_path:?}"); + println!( + "Blessing the {stream} of `{test_name}` as `{expected_path}`", + test_name = self.testpaths.file + ); } } - println!("\nThe actual {0} differed from the expected {0}.", stream); + println!("\nThe actual {stream} differed from the expected {stream}"); if self.config.bless { CompareOutcome::Blessed } else { CompareOutcome::Differed } } From 2215282fa40b2bf0e727633f9c7025b1ca1fc373 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Sun, 11 May 2025 18:54:05 +0500 Subject: [PATCH 136/245] Update Changelog update walkthrough docs section --- .../infrastructure/changelog_update.md | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/book/src/development/infrastructure/changelog_update.md b/book/src/development/infrastructure/changelog_update.md index 2b2c096b0496..eede6b78d921 100644 --- a/book/src/development/infrastructure/changelog_update.md +++ b/book/src/development/infrastructure/changelog_update.md @@ -51,7 +51,9 @@ Once you've got the correct commit range, run util/fetch_prs_between.sh commit1 commit2 > changes.txt ``` -and open that file in your editor of choice. +where `commit2` is the commit hash from the previous command and `commit1` +is the commit hash from the current CHANGELOG file. +Open `changes.txt` file in your editor of choice. When updating the changelog it's also a good idea to make sure that `commit1` is already correct in the current changelog. @@ -60,8 +62,8 @@ already correct in the current changelog. The above script should have dumped all the relevant PRs to the file you specified. It should have filtered out most of the irrelevant PRs already, but -it's a good idea to do a manual cleanup pass where you look for more irrelevant -PRs. If you're not sure about some PRs, just leave them in for the review and +it's a good idea to do a manual cleanup pass and choose valuable PRs. +If you're not sure about some PRs, just leave them in for the review and ask for feedback. With the PRs filtered, you can start to take each PR and move the `changelog: ` @@ -74,10 +76,9 @@ The order should roughly be: 2. Moves or deprecations of lints 3. Changes that expand what code existing lints cover 4. False positive fixes -5. Suggestion fixes/improvements -6. ICE fixes -7. Documentation improvements -8. Others +5. ICE fixes +6. Documentation improvements +7. Others As section headers, we use: @@ -91,7 +92,6 @@ As section headers, we use: ### Enhancements ### False Positive Fixes -### Suggestion Fixes/Improvements ### ICE Fixes ### Documentation Improvements ### Others @@ -112,7 +112,16 @@ that label in the changelog. If you can, remove the `beta-accepted` labels ### 4. Update `clippy::version` attributes Next, make sure to check that the `#[clippy::version]` attributes for the added -lints contain the correct version. +lints contain the correct version. +In order to find lints that need a version update, go through the lints in the +"New Lints" section and run the following command for each lint name: + +``` +grep -rB1 "pub $LINT_NAME" . +``` + +The version shown should match the version of the release the changelog is +written for. If not, update the version to the changelog version. [changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md [forge]: https://forge.rust-lang.org/ From df1da673f75937a1a8ebd4ad09b5ffb244df6927 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 12 May 2025 14:22:48 +0000 Subject: [PATCH 137/245] Flush errors before deep normalize in dropck_outlives --- .../src/traits/query/dropck_outlives.rs | 8 ++ tests/ui/drop/dropck-normalize-errors.rs | 31 ++++++++ tests/ui/drop/dropck-normalize-errors.stderr | 76 +++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 tests/ui/drop/dropck-normalize-errors.rs create mode 100644 tests/ui/drop/dropck-normalize-errors.stderr diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index f04a5feba301..c82d5ca59efc 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -196,6 +196,14 @@ where debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); ty } else { + // Flush errors b/c `deeply_normalize` doesn't expect pending + // obligations, and we may have pending obligations from the + // branch above (from other types). + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + return Err(errors); + } + ocx.deeply_normalize(&cause, param_env, ty)?; let errors = ocx.select_where_possible(); diff --git a/tests/ui/drop/dropck-normalize-errors.rs b/tests/ui/drop/dropck-normalize-errors.rs new file mode 100644 index 000000000000..793122bd33d4 --- /dev/null +++ b/tests/ui/drop/dropck-normalize-errors.rs @@ -0,0 +1,31 @@ +// Test that we don't ICE when computing the drop types for + +trait Decode<'a> { + type Decoder; +} + +trait NonImplementedTrait { + type Assoc; +} +struct NonImplementedStruct; + +pub struct ADecoder<'a> { + b: >::Decoder, +} +fn make_a_decoder<'a>() -> ADecoder<'a> { + //~^ ERROR the trait bound + //~| ERROR the trait bound + panic!() +} + +struct B; +impl<'a> Decode<'a> for B { + type Decoder = BDecoder; + //~^ ERROR the trait bound +} +pub struct BDecoder { + non_implemented: ::Assoc, + //~^ ERROR the trait bound +} + +fn main() {} diff --git a/tests/ui/drop/dropck-normalize-errors.stderr b/tests/ui/drop/dropck-normalize-errors.stderr new file mode 100644 index 000000000000..2bb5909c6b22 --- /dev/null +++ b/tests/ui/drop/dropck-normalize-errors.stderr @@ -0,0 +1,76 @@ +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `ADecoder<'a>` + --> $DIR/dropck-normalize-errors.rs:15:28 + | +LL | fn make_a_decoder<'a>() -> ADecoder<'a> { + | ^^^^^^^^^^^^ within `ADecoder<'a>`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `BDecoder` + --> $DIR/dropck-normalize-errors.rs:26:12 + | +LL | pub struct BDecoder { + | ^^^^^^^^ +note: required because it appears within the type `ADecoder<'a>` + --> $DIR/dropck-normalize-errors.rs:12:12 + | +LL | pub struct ADecoder<'a> { + | ^^^^^^^^ + = note: the return type of a function must have a statically known size + +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `BDecoder` + --> $DIR/dropck-normalize-errors.rs:23:20 + | +LL | type Decoder = BDecoder; + | ^^^^^^^^ within `BDecoder`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `BDecoder` + --> $DIR/dropck-normalize-errors.rs:26:12 + | +LL | pub struct BDecoder { + | ^^^^^^^^ +note: required by a bound in `Decode::Decoder` + --> $DIR/dropck-normalize-errors.rs:4:5 + | +LL | type Decoder; + | ^^^^^^^^^^^^^ required by this bound in `Decode::Decoder` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Decoder: ?Sized; + | ++++++++ + +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied + --> $DIR/dropck-normalize-errors.rs:27:22 + | +LL | non_implemented: ::Assoc, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied + --> $DIR/dropck-normalize-errors.rs:15:28 + | +LL | fn make_a_decoder<'a>() -> ADecoder<'a> { + | ^^^^^^^^^^^^ the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. From 2f39264d00eef5e5990438a5c23cb670500548d9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 13 Apr 2025 07:05:29 -0400 Subject: [PATCH 138/245] clippy_dev: Set the current directory to clippy's root path. --- clippy_dev/src/dogfood.rs | 5 +- clippy_dev/src/fmt.rs | 32 ++--- clippy_dev/src/lib.rs | 2 +- clippy_dev/src/main.rs | 18 ++- clippy_dev/src/new_lint.rs | 46 ++++--- clippy_dev/src/release.rs | 14 +-- clippy_dev/src/update_lints.rs | 67 +++++------ clippy_dev/src/utils.rs | 211 +++++++++++++++++++++++++++------ 8 files changed, 266 insertions(+), 129 deletions(-) diff --git a/clippy_dev/src/dogfood.rs b/clippy_dev/src/dogfood.rs index 05fa24d8d4ee..7e9d92458d05 100644 --- a/clippy_dev/src/dogfood.rs +++ b/clippy_dev/src/dogfood.rs @@ -1,4 +1,4 @@ -use crate::utils::{clippy_project_root, exit_if_err}; +use crate::utils::exit_if_err; use std::process::Command; /// # Panics @@ -8,8 +8,7 @@ use std::process::Command; pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { let mut cmd = Command::new("cargo"); - cmd.current_dir(clippy_project_root()) - .args(["test", "--test", "dogfood"]) + cmd.args(["test", "--test", "dogfood"]) .args(["--features", "internal"]) .args(["--", "dogfood_clippy", "--nocapture"]); diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index bdddf46a2cb1..b4c13213f552 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -1,4 +1,3 @@ -use crate::utils::clippy_project_root; use itertools::Itertools; use rustc_lexer::{TokenKind, tokenize}; use shell_escape::escape; @@ -104,15 +103,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { Field, } - let path: PathBuf = [ - clippy_project_root().as_path(), - "clippy_config".as_ref(), - "src".as_ref(), - "conf.rs".as_ref(), - ] - .into_iter() - .collect(); - let text = fs::read_to_string(&path)?; + let path = "clippy_config/src/conf.rs"; + let text = fs::read_to_string(path)?; let (pre, conf) = text .split_once("define_Conf! {\n") @@ -203,7 +195,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> { | (State::Lints, TokenKind::Comma | TokenKind::OpenParen | TokenKind::CloseParen) => {}, _ => { return Err(Error::Parse( - path, + PathBuf::from(path), offset_to_line(&text, conf_offset + i), format!("unexpected token `{}`", &conf[i..i + t.len as usize]), )); @@ -213,7 +205,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> { if !matches!(state, State::Field) { return Err(Error::Parse( - path, + PathBuf::from(path), offset_to_line(&text, conf_offset + conf.len()), "incomplete field".into(), )); @@ -260,18 +252,16 @@ fn fmt_conf(check: bool) -> Result<(), Error> { if check { return Err(Error::CheckFailed); } - fs::write(&path, new_text.as_bytes())?; + fs::write(path, new_text.as_bytes())?; } Ok(()) } fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { - let project_root = clippy_project_root(); - // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to // format because rustfmt would also format the entire rustc repo as it is a local // dependency - if fs::read_to_string(project_root.join("Cargo.toml")) + if fs::read_to_string("Cargo.toml") .expect("Failed to read clippy Cargo.toml") .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") { @@ -280,12 +270,12 @@ fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { check_for_rustfmt(context)?; - cargo_fmt(context, project_root.as_path())?; - cargo_fmt(context, &project_root.join("clippy_dev"))?; - cargo_fmt(context, &project_root.join("rustc_tools_util"))?; - cargo_fmt(context, &project_root.join("lintcheck"))?; + cargo_fmt(context, ".".as_ref())?; + cargo_fmt(context, "clippy_dev".as_ref())?; + cargo_fmt(context, "rustc_tools_util".as_ref())?; + cargo_fmt(context, "lintcheck".as_ref())?; - let chunks = WalkDir::new(project_root.join("tests")) + let chunks = WalkDir::new("tests") .into_iter() .filter_map(|entry| { let entry = entry.expect("failed to find tests"); diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index db4b4d07c156..70e57fcfc26e 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(rustc_private)] +#![feature(rustc_private, if_let_guard, let_chains)] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 83f8e66b3347..bd87e980c222 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -5,9 +5,14 @@ use clap::{Args, Parser, Subcommand}; use clippy_dev::{dogfood, fmt, lint, new_lint, release, serve, setup, sync, update_lints, utils}; use std::convert::Infallible; +use std::env; fn main() { let dev = Dev::parse(); + let clippy = utils::ClippyInfo::search_for_manifest(); + if let Err(e) = env::set_current_dir(&clippy.path) { + panic!("error setting current directory to `{}`: {e}", clippy.path.display()); + } match dev.command { DevCommand::Bless => { @@ -35,7 +40,7 @@ fn main() { category, r#type, msrv, - } => match new_lint::create(pass, &name, &category, r#type.as_deref(), msrv) { + } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) { Ok(()) => update_lints::update(utils::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {e}"), }, @@ -79,13 +84,18 @@ fn main() { old_name, new_name, uplift, - } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift), - DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason), + } => update_lints::rename( + clippy.version, + &old_name, + new_name.as_ref().unwrap_or(&old_name), + uplift, + ), + DevCommand::Deprecate { name, reason } => update_lints::deprecate(clippy.version, &name, &reason), DevCommand::Sync(SyncCommand { subcommand }) => match subcommand { SyncSubcommand::UpdateNightly => sync::update_nightly(), }, DevCommand::Release(ReleaseCommand { subcommand }) => match subcommand { - ReleaseSubcommand::BumpVersion => release::bump_version(), + ReleaseSubcommand::BumpVersion => release::bump_version(clippy.version), }, } } diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 771f48b3f8b1..a6e8c3ac3249 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,4 +1,4 @@ -use crate::utils::{clippy_project_root, clippy_version}; +use crate::utils::Version; use clap::ValueEnum; use indoc::{formatdoc, writedoc}; use std::fmt::{self, Write as _}; @@ -22,11 +22,11 @@ impl fmt::Display for Pass { } struct LintData<'a> { + clippy_version: Version, pass: Pass, name: &'a str, category: &'a str, ty: Option<&'a str>, - project_root: PathBuf, } trait Context { @@ -50,18 +50,25 @@ impl Context for io::Result { /// # Errors /// /// This function errors out if the files couldn't be created or written to. -pub fn create(pass: Pass, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> { +pub fn create( + clippy_version: Version, + pass: Pass, + name: &str, + category: &str, + mut ty: Option<&str>, + msrv: bool, +) -> io::Result<()> { if category == "cargo" && ty.is_none() { // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo` ty = Some("cargo"); } let lint = LintData { + clippy_version, pass, name, category, ty, - project_root: clippy_project_root(), }; create_lint(&lint, msrv).context("Unable to create lint implementation")?; @@ -88,7 +95,7 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { } else { let lint_contents = get_lint_file_contents(lint, enable_msrv); let lint_path = format!("clippy_lints/src/{}.rs", lint.name); - write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?; + write_file(&lint_path, lint_contents.as_bytes())?; println!("Generated lint file: `{lint_path}`"); Ok(()) @@ -115,8 +122,7 @@ fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> { } if lint.category == "cargo" { - let relative_test_dir = format!("tests/ui-cargo/{}", lint.name); - let test_dir = lint.project_root.join(&relative_test_dir); + let test_dir = format!("tests/ui-cargo/{}", lint.name); fs::create_dir(&test_dir)?; create_project_layout( @@ -134,11 +140,11 @@ fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> { false, )?; - println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`"); + println!("Generated test directories: `{test_dir}/pass`, `{test_dir}/fail`"); } else { let test_path = format!("tests/ui/{}.rs", lint.name); let test_contents = get_test_file_contents(lint.name, msrv); - write_file(lint.project_root.join(&test_path), test_contents)?; + write_file(&test_path, test_contents)?; println!("Generated test file: `{test_path}`"); } @@ -193,11 +199,6 @@ fn to_camel_case(name: &str) -> String { .collect() } -pub(crate) fn get_stabilization_version() -> String { - let (minor, patch) = clippy_version(); - format!("{minor}.{patch}.0") -} - fn get_test_file_contents(lint_name: &str, msrv: bool) -> String { let mut test = formatdoc!( r" @@ -292,7 +293,11 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ); } - let _: fmt::Result = writeln!(result, "{}", get_lint_declaration(&name_upper, category)); + let _: fmt::Result = writeln!( + result, + "{}", + get_lint_declaration(lint.clippy_version, &name_upper, category) + ); if enable_msrv { let _: fmt::Result = writedoc!( @@ -330,7 +335,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result } -fn get_lint_declaration(name_upper: &str, category: &str) -> String { +fn get_lint_declaration(version: Version, name_upper: &str, category: &str) -> String { let justification_heading = if category == "restriction" { "Why restrict this?" } else { @@ -357,7 +362,7 @@ fn get_lint_declaration(name_upper: &str, category: &str) -> String { "default lint description" }} "#, - get_stabilization_version(), + version.rust_display(), ) } @@ -371,7 +376,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R _ => {}, } - let ty_dir = lint.project_root.join(format!("clippy_lints/src/{ty}")); + let ty_dir = PathBuf::from(format!("clippy_lints/src/{ty}")); assert!( ty_dir.exists() && ty_dir.is_dir(), "Directory `{}` does not exist!", @@ -529,7 +534,10 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> file_contents.replace_range( // Remove the trailing newline, which should always be present last_decl_curly_offset..=last_decl_curly_offset, - &format!("\n\n{}", get_lint_declaration(&lint_name_upper, lint.category)), + &format!( + "\n\n{}", + get_lint_declaration(lint.clippy_version, &lint_name_upper, lint.category) + ), ); // Add the lint to `impl_lint_pass`/`declare_lint_pass` diff --git a/clippy_dev/src/release.rs b/clippy_dev/src/release.rs index ac7551687010..34f81e10a393 100644 --- a/clippy_dev/src/release.rs +++ b/clippy_dev/src/release.rs @@ -1,7 +1,6 @@ use std::fmt::Write; -use std::path::Path; -use crate::utils::{UpdateMode, clippy_version, replace_region_in_file}; +use crate::utils::{UpdateMode, Version, replace_region_in_file}; const CARGO_TOML_FILES: [&str; 4] = [ "clippy_config/Cargo.toml", @@ -10,17 +9,16 @@ const CARGO_TOML_FILES: [&str; 4] = [ "Cargo.toml", ]; -pub fn bump_version() { - let (minor, mut patch) = clippy_version(); - patch += 1; - for file in &CARGO_TOML_FILES { +pub fn bump_version(mut version: Version) { + version.minor += 1; + for &file in &CARGO_TOML_FILES { replace_region_in_file( UpdateMode::Change, - Path::new(file), + file.as_ref(), "# begin autogenerated version\n", "# end autogenerated version", |res| { - writeln!(res, "version = \"0.{minor}.{patch}\"").unwrap(); + writeln!(res, "version = \"{}\"", version.toml_display()).unwrap(); }, ); } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index d848a97f86d2..e53c454be7a2 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,4 +1,4 @@ -use crate::utils::{UpdateMode, clippy_project_root, exit_with_failure, replace_region_in_file}; +use crate::utils::{UpdateMode, Version, exit_with_failure, replace_region_in_file}; use aho_corasick::AhoCorasickBuilder; use itertools::Itertools; use rustc_lexer::{LiteralKind, TokenKind, tokenize}; @@ -139,7 +139,7 @@ pub fn print_lints() { /// * If `old_name` doesn't name an existing lint. /// * If `old_name` names a deprecated or renamed lint. #[allow(clippy::too_many_lines)] -pub fn rename(old_name: &str, new_name: &str, uplift: bool) { +pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: bool) { if let Some((prefix, _)) = old_name.split_once("::") { panic!("`{old_name}` should not contain the `{prefix}` prefix"); } @@ -180,31 +180,28 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { ); // Update all lint level attributes. (`clippy::lint_name`) - for file in WalkDir::new(clippy_project_root()) - .into_iter() - .map(Result::unwrap) - .filter(|f| { - let name = f.path().file_name(); - let ext = f.path().extension(); - (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) - && name != Some(OsStr::new("rename.rs")) - && name != Some(OsStr::new("deprecated_lints.rs")) - }) - { + for file in WalkDir::new(".").into_iter().map(Result::unwrap).filter(|f| { + let name = f.path().file_name(); + let ext = f.path().extension(); + (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) + && name != Some(OsStr::new("rename.rs")) + && name != Some(OsStr::new("deprecated_lints.rs")) + }) { rewrite_file(file.path(), |s| { replace_ident_like(s, &[(&lint.old_name, &lint.new_name)]) }); } - let version = crate::new_lint::get_stabilization_version(); rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { insert_at_marker( s, "// end renamed lints. used by `cargo dev rename_lint`", &format!( - "#[clippy::version = \"{version}\"]\n \ + "#[clippy::version = \"{}\"]\n \ (\"{}\", \"{}\"),\n ", - lint.old_name, lint.new_name, + clippy_version.rust_display(), + lint.old_name, + lint.new_name, ), ) }); @@ -284,9 +281,15 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being // renamed. - for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("deprecated_lints.rs")) - { - rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); + for file in clippy_lints_src_files() { + if file + .path() + .as_os_str() + .to_str() + .is_none_or(|x| x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") + { + rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); + } } generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); @@ -305,7 +308,7 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { /// # Panics /// /// If a file path could not read from or written to -pub fn deprecate(name: &str, reason: &str) { +pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { let prefixed_name = if name.starts_with("clippy::") { name.to_owned() } else { @@ -329,15 +332,15 @@ pub fn deprecate(name: &str, reason: &str) { mod_path }; - let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs"); - if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { - let version = crate::new_lint::get_stabilization_version(); - rewrite_file(deprecated_lints_path, |s| { + rewrite_file("clippy_lints/src/deprecated_lints.rs".as_ref(), |s| { insert_at_marker( s, "// end deprecated lints. used by `cargo dev deprecate_lint`", - &format!("#[clippy::version = \"{version}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ",), + &format!( + "#[clippy::version = \"{}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ", + clippy_version.rust_display(), + ), ) }); @@ -612,15 +615,11 @@ fn gather_all() -> (Vec, Vec, Vec) { let mut deprecated_lints = Vec::with_capacity(50); let mut renamed_lints = Vec::with_capacity(50); - for (rel_path, file) in clippy_lints_src_files() { + for file in clippy_lints_src_files() { let path = file.path(); let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); - let module = rel_path - .components() - .map(|c| c.as_os_str().to_str().unwrap()) - .collect::>() - .join("::"); + let module = path.as_os_str().to_str().unwrap()["clippy_lints/src/".len()..].replace(['/', '\\'], "::"); // If the lints are stored in mod.rs, we get the module name from // the containing directory: @@ -639,12 +638,10 @@ fn gather_all() -> (Vec, Vec, Vec) { (lints, deprecated_lints, renamed_lints) } -fn clippy_lints_src_files() -> impl Iterator { - let root_path = clippy_project_root().join("clippy_lints/src"); - let iter = WalkDir::new(&root_path).into_iter(); +fn clippy_lints_src_files() -> impl Iterator { + let iter = WalkDir::new("clippy_lints/src").into_iter(); iter.map(Result::unwrap) .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) - .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) } macro_rules! match_tokens { diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 206816398f50..2b199d96fcde 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -1,12 +1,90 @@ +use core::fmt::{self, Display}; +use core::str::FromStr; +use std::env; +use std::fs::{self, OpenOptions}; +use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::process::{self, ExitStatus}; -use std::{fs, io}; #[cfg(not(windows))] static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; #[cfg(windows)] static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; +#[cold] +#[track_caller] +fn panic_io(e: &io::Error, action: &str, path: &Path) -> ! { + panic!("error {action} `{}`: {}", path.display(), *e) +} + +/// Wrapper around `std::fs::File` which panics with a path on failure. +pub struct File<'a> { + pub inner: fs::File, + pub path: &'a Path, +} +impl<'a> File<'a> { + /// Opens a file panicking on failure. + #[track_caller] + pub fn open(path: &'a (impl AsRef + ?Sized), options: &mut OpenOptions) -> Self { + let path = path.as_ref(); + match options.open(path) { + Ok(inner) => Self { inner, path }, + Err(e) => panic_io(&e, "opening", path), + } + } + + /// Opens a file if it exists, panicking on any other failure. + #[track_caller] + pub fn open_if_exists(path: &'a (impl AsRef + ?Sized), options: &mut OpenOptions) -> Option { + let path = path.as_ref(); + match options.open(path) { + Ok(inner) => Some(Self { inner, path }), + Err(e) if e.kind() == io::ErrorKind::NotFound => None, + Err(e) => panic_io(&e, "opening", path), + } + } + + /// Opens and reads a file into a string, panicking of failure. + #[track_caller] + pub fn open_read_to_cleared_string<'dst>( + path: &'a (impl AsRef + ?Sized), + dst: &'dst mut String, + ) -> &'dst mut String { + Self::open(path, OpenOptions::new().read(true)).read_to_cleared_string(dst) + } + + /// Read the entire contents of a file to the given buffer. + #[track_caller] + pub fn read_append_to_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { + match self.inner.read_to_string(dst) { + Ok(_) => {}, + Err(e) => panic_io(&e, "reading", self.path), + } + dst + } + + #[track_caller] + pub fn read_to_cleared_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { + dst.clear(); + self.read_append_to_string(dst) + } + + /// Replaces the entire contents of a file. + #[track_caller] + pub fn replace_contents(&mut self, data: &[u8]) { + let res = match self.inner.seek(SeekFrom::Start(0)) { + Ok(_) => match self.inner.write_all(data) { + Ok(()) => self.inner.set_len(data.len() as u64), + Err(e) => Err(e), + }, + Err(e) => Err(e), + }; + if let Err(e) = res { + panic_io(&e, "writing", self.path); + } + } +} + /// Returns the path to the `cargo-clippy` binary /// /// # Panics @@ -14,34 +92,107 @@ static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; /// Panics if the path of current executable could not be retrieved. #[must_use] pub fn cargo_clippy_path() -> PathBuf { - let mut path = std::env::current_exe().expect("failed to get current executable name"); + let mut path = env::current_exe().expect("failed to get current executable name"); path.set_file_name(CARGO_CLIPPY_EXE); path } -/// Returns the path to the Clippy project directory -/// -/// # Panics -/// -/// Panics if the current directory could not be retrieved, there was an error reading any of the -/// Cargo.toml files or ancestor directory is the clippy root directory -#[must_use] -pub fn clippy_project_root() -> PathBuf { - let current_dir = std::env::current_dir().unwrap(); - for path in current_dir.ancestors() { - let result = fs::read_to_string(path.join("Cargo.toml")); - if let Err(err) = &result - && err.kind() == io::ErrorKind::NotFound +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + pub major: u16, + pub minor: u16, +} +impl FromStr for Version { + type Err = (); + fn from_str(s: &str) -> Result { + if let Some(s) = s.strip_prefix("0.") + && let Some((major, minor)) = s.split_once('.') + && let Ok(major) = major.parse() + && let Ok(minor) = minor.parse() { - continue; - } - - let content = result.unwrap(); - if content.contains("[package]\nname = \"clippy\"") { - return path.to_path_buf(); + Ok(Self { major, minor }) + } else { + Err(()) + } + } +} +impl Version { + /// Displays the version as a rust version. i.e. `x.y.0` + #[must_use] + pub fn rust_display(self) -> impl Display { + struct X(Version); + impl Display for X { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}.{}.0", self.0.major, self.0.minor) + } + } + X(self) + } + + /// Displays the version as it should appear in clippy's toml files. i.e. `0.x.y` + #[must_use] + pub fn toml_display(self) -> impl Display { + struct X(Version); + impl Display for X { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0.{}.{}", self.0.major, self.0.minor) + } + } + X(self) + } +} + +pub struct ClippyInfo { + pub path: PathBuf, + pub version: Version, +} +impl ClippyInfo { + #[must_use] + pub fn search_for_manifest() -> Self { + let mut path = env::current_dir().expect("error reading the working directory"); + let mut buf = String::new(); + loop { + path.push("Cargo.toml"); + if let Some(mut file) = File::open_if_exists(&path, OpenOptions::new().read(true)) { + let mut in_package = false; + let mut is_clippy = false; + let mut version: Option = None; + + // Ad-hoc parsing to avoid dependencies. We control all the file so this + // isn't actually a problem + for line in file.read_to_cleared_string(&mut buf).lines() { + if line.starts_with('[') { + in_package = line.starts_with("[package]"); + } else if in_package && let Some((name, value)) = line.split_once('=') { + match name.trim() { + "name" => is_clippy = value.trim() == "\"clippy\"", + "version" + if let Some(value) = value.trim().strip_prefix('"') + && let Some(value) = value.strip_suffix('"') => + { + version = value.parse().ok(); + }, + _ => {}, + } + } + } + + if is_clippy { + let Some(version) = version else { + panic!("error reading clippy version from {}", file.path.display()); + }; + path.pop(); + return ClippyInfo { path, version }; + } + } + + path.pop(); + assert!( + path.pop(), + "error finding project root, please run from inside the clippy directory" + ); } } - panic!("error: Can't determine root of project. Please run inside a Clippy working dir."); } /// # Panics @@ -57,22 +208,6 @@ pub fn exit_if_err(status: io::Result) { } } -pub(crate) fn clippy_version() -> (u32, u32) { - fn parse_manifest(contents: &str) -> Option<(u32, u32)> { - let version = contents - .lines() - .filter_map(|l| l.split_once('=')) - .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?; - let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else { - return None; - }; - let (minor, patch) = version.split_once('.')?; - Some((minor.parse().ok()?, patch.parse().ok()?)) - } - let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`"); - parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`") -} - #[derive(Clone, Copy, PartialEq, Eq)] pub enum UpdateMode { Check, From 3fe5fb296786e9396c1e91280360d9f029367b44 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 14 Apr 2025 08:09:24 -0400 Subject: [PATCH 139/245] clippy_dev: Split `rename` and `deprecate` out of `update_lints` --- clippy_dev/src/deprecate_lint.rs | 162 +++++++++++ clippy_dev/src/lib.rs | 2 + clippy_dev/src/main.rs | 8 +- clippy_dev/src/rename_lint.rs | 185 +++++++++++++ clippy_dev/src/update_lints.rs | 460 ++----------------------------- clippy_dev/src/utils.rs | 85 ++++++ 6 files changed, 460 insertions(+), 442 deletions(-) create mode 100644 clippy_dev/src/deprecate_lint.rs create mode 100644 clippy_dev/src/rename_lint.rs diff --git a/clippy_dev/src/deprecate_lint.rs b/clippy_dev/src/deprecate_lint.rs new file mode 100644 index 000000000000..2c4286b325db --- /dev/null +++ b/clippy_dev/src/deprecate_lint.rs @@ -0,0 +1,162 @@ +use crate::update_lints::{DeprecatedLint, Lint, gather_all, generate_lint_files}; +use crate::utils::{UpdateMode, Version, insert_at_marker, rewrite_file}; +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; +use std::{fs, io}; + +/// Runs the `deprecate` command +/// +/// This does the following: +/// * Adds an entry to `deprecated_lints.rs`. +/// * Removes the lint declaration (and the entire file if applicable) +/// +/// # Panics +/// +/// If a file path could not read from or written to +pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { + let prefixed_name = if name.starts_with("clippy::") { + name.to_owned() + } else { + format!("clippy::{name}") + }; + let stripped_name = &prefixed_name[8..]; + + let (mut lints, mut deprecated_lints, renamed_lints) = gather_all(); + let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { + eprintln!("error: failed to find lint `{name}`"); + return; + }; + + let mod_path = { + let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); + if mod_path.is_dir() { + mod_path = mod_path.join("mod"); + } + + mod_path.set_extension("rs"); + mod_path + }; + + if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { + rewrite_file("clippy_lints/src/deprecated_lints.rs".as_ref(), |s| { + insert_at_marker( + s, + "// end deprecated lints. used by `cargo dev deprecate_lint`", + &format!( + "#[clippy::version = \"{}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ", + clippy_version.rust_display(), + ), + ) + }); + + deprecated_lints.push(DeprecatedLint { + name: prefixed_name, + reason: reason.into(), + }); + + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{name}` has successfully been deprecated"); + println!("note: you must run `cargo uitest` to update the test results"); + } else { + eprintln!("error: lint not found"); + } +} + +fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io::Result { + fn remove_lint(name: &str, lints: &mut Vec) { + lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); + } + + fn remove_test_assets(name: &str) { + let test_file_stem = format!("tests/ui/{name}"); + let path = Path::new(&test_file_stem); + + // Some lints have their own directories, delete them + if path.is_dir() { + let _ = fs::remove_dir_all(path); + return; + } + + // Remove all related test files + let _ = fs::remove_file(path.with_extension("rs")); + let _ = fs::remove_file(path.with_extension("stderr")); + let _ = fs::remove_file(path.with_extension("fixed")); + } + + fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { + let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| { + content + .find("declare_lint_pass!") + .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`")) + }); + let mut impl_lint_pass_end = content[impl_lint_pass_start..] + .find(']') + .expect("failed to find `impl_lint_pass` terminator"); + + impl_lint_pass_end += impl_lint_pass_start; + if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { + let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); + for c in content[lint_name_end..impl_lint_pass_end].chars() { + // Remove trailing whitespace + if c == ',' || c.is_whitespace() { + lint_name_end += 1; + } else { + break; + } + } + + content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, ""); + } + } + + if path.exists() + && let Some(lint) = lints.iter().find(|l| l.name == name) + { + if lint.module == name { + // The lint name is the same as the file, we can just delete the entire file + fs::remove_file(path)?; + } else { + // We can't delete the entire file, just remove the declaration + + if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { + // Remove clippy_lints/src/some_mod/some_lint.rs + let mut lint_mod_path = path.to_path_buf(); + lint_mod_path.set_file_name(name); + lint_mod_path.set_extension("rs"); + + let _ = fs::remove_file(lint_mod_path); + } + + let mut content = + fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); + + eprintln!( + "warn: you will have to manually remove any code related to `{name}` from `{}`", + path.display() + ); + + assert!( + content[lint.declaration_range.clone()].contains(&name.to_uppercase()), + "error: `{}` does not contain lint `{}`'s declaration", + path.display(), + lint.name + ); + + // Remove lint declaration (declare_clippy_lint!) + content.replace_range(lint.declaration_range.clone(), ""); + + // Remove the module declaration (mod xyz;) + let mod_decl = format!("\nmod {name};"); + content = content.replacen(&mod_decl, "", 1); + + remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); + fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); + } + + remove_test_assets(name); + remove_lint(name, lints); + return Ok(true); + } + + Ok(false) +} diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 70e57fcfc26e..e237a05b2530 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -14,11 +14,13 @@ extern crate rustc_driver; extern crate rustc_lexer; extern crate rustc_literal_escaper; +pub mod deprecate_lint; pub mod dogfood; pub mod fmt; pub mod lint; pub mod new_lint; pub mod release; +pub mod rename_lint; pub mod serve; pub mod setup; pub mod sync; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index bd87e980c222..565416572a95 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -3,7 +3,9 @@ #![warn(rust_2018_idioms, unused_lifetimes)] use clap::{Args, Parser, Subcommand}; -use clippy_dev::{dogfood, fmt, lint, new_lint, release, serve, setup, sync, update_lints, utils}; +use clippy_dev::{ + deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, update_lints, utils, +}; use std::convert::Infallible; use std::env; @@ -84,13 +86,13 @@ fn main() { old_name, new_name, uplift, - } => update_lints::rename( + } => rename_lint::rename( clippy.version, &old_name, new_name.as_ref().unwrap_or(&old_name), uplift, ), - DevCommand::Deprecate { name, reason } => update_lints::deprecate(clippy.version, &name, &reason), + DevCommand::Deprecate { name, reason } => deprecate_lint::deprecate(clippy.version, &name, &reason), DevCommand::Sync(SyncCommand { subcommand }) => match subcommand { SyncSubcommand::UpdateNightly => sync::update_nightly(), }, diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs new file mode 100644 index 000000000000..045e1f83e13e --- /dev/null +++ b/clippy_dev/src/rename_lint.rs @@ -0,0 +1,185 @@ +use crate::update_lints::{ + RenamedLint, clippy_lints_src_files, gather_all, gen_renamed_lints_test, generate_lint_files, +}; +use crate::utils::{ + UpdateMode, Version, insert_at_marker, replace_ident_like, rewrite_file, try_rename_file, write_file, +}; +use std::ffi::OsStr; +use std::path::Path; +use walkdir::WalkDir; + +/// Runs the `rename_lint` command. +/// +/// This does the following: +/// * Adds an entry to `renamed_lints.rs`. +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). +/// * Renames the lint struct to the new name. +/// * Renames the module containing the lint struct to the new name if it shares a name with the +/// lint. +/// +/// # Panics +/// Panics for the following conditions: +/// * If a file path could not read from or then written to +/// * If either lint name has a prefix +/// * If `old_name` doesn't name an existing lint. +/// * If `old_name` names a deprecated or renamed lint. +#[allow(clippy::too_many_lines)] +pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: bool) { + if let Some((prefix, _)) = old_name.split_once("::") { + panic!("`{old_name}` should not contain the `{prefix}` prefix"); + } + if let Some((prefix, _)) = new_name.split_once("::") { + panic!("`{new_name}` should not contain the `{prefix}` prefix"); + } + + let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); + let mut old_lint_index = None; + let mut found_new_name = false; + for (i, lint) in lints.iter().enumerate() { + if lint.name == old_name { + old_lint_index = Some(i); + } else if lint.name == new_name { + found_new_name = true; + } + } + let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`")); + + let lint = RenamedLint { + old_name: format!("clippy::{old_name}"), + new_name: if uplift { + new_name.into() + } else { + format!("clippy::{new_name}") + }, + }; + + // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in + // case. + assert!( + !renamed_lints.iter().any(|l| lint.old_name == l.old_name), + "`{old_name}` has already been renamed" + ); + assert!( + !deprecated_lints.iter().any(|l| lint.old_name == l.name), + "`{old_name}` has already been deprecated" + ); + + // Update all lint level attributes. (`clippy::lint_name`) + for file in WalkDir::new(".").into_iter().map(Result::unwrap).filter(|f| { + let name = f.path().file_name(); + let ext = f.path().extension(); + (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) + && name != Some(OsStr::new("rename.rs")) + && name != Some(OsStr::new("deprecated_lints.rs")) + }) { + rewrite_file(file.path(), |s| { + replace_ident_like(s, &[(&lint.old_name, &lint.new_name)]) + }); + } + + rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { + insert_at_marker( + s, + "// end renamed lints. used by `cargo dev rename_lint`", + &format!( + "#[clippy::version = \"{}\"]\n \ + (\"{}\", \"{}\"),\n ", + clippy_version.rust_display(), + lint.old_name, + lint.new_name, + ), + ) + }); + + renamed_lints.push(lint); + renamed_lints.sort_by(|lhs, rhs| { + lhs.new_name + .starts_with("clippy::") + .cmp(&rhs.new_name.starts_with("clippy::")) + .reverse() + .then_with(|| lhs.old_name.cmp(&rhs.old_name)) + }); + + if uplift { + write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + println!( + "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." + ); + } else if found_new_name { + write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + println!( + "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." + ); + } else { + // Rename the lint struct and source files sharing a name with the lint. + let lint = &mut lints[old_lint_index]; + let old_name_upper = old_name.to_uppercase(); + let new_name_upper = new_name.to_uppercase(); + lint.name = new_name.into(); + + // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. + if try_rename_file( + Path::new(&format!("tests/ui/{old_name}.rs")), + Path::new(&format!("tests/ui/{new_name}.rs")), + ) { + try_rename_file( + Path::new(&format!("tests/ui/{old_name}.stderr")), + Path::new(&format!("tests/ui/{new_name}.stderr")), + ); + try_rename_file( + Path::new(&format!("tests/ui/{old_name}.fixed")), + Path::new(&format!("tests/ui/{new_name}.fixed")), + ); + } + + // Try to rename the file containing the lint if the file name matches the lint's name. + let replacements; + let replacements = if lint.module == old_name + && try_rename_file( + Path::new(&format!("clippy_lints/src/{old_name}.rs")), + Path::new(&format!("clippy_lints/src/{new_name}.rs")), + ) { + // Edit the module name in the lint list. Note there could be multiple lints. + for lint in lints.iter_mut().filter(|l| l.module == old_name) { + lint.module = new_name.into(); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else if !lint.module.contains("::") + // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` + && try_rename_file( + Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)), + Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)), + ) + { + // Edit the module name in the lint list. Note there could be multiple lints, or none. + let renamed_mod = format!("{}::{old_name}", lint.module); + for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { + lint.module = format!("{}::{new_name}", lint.module); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else { + replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; + &replacements[0..1] + }; + + // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being + // renamed. + for file in clippy_lints_src_files() { + if file + .path() + .as_os_str() + .to_str() + .is_none_or(|x| x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") + { + rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); + } + } + + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("{old_name} has been successfully renamed"); + } + + println!("note: `cargo uitest` still needs to be run to update the test results"); +} diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index e53c454be7a2..2f450b7d9ea6 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,15 +1,13 @@ -use crate::utils::{UpdateMode, Version, exit_with_failure, replace_region_in_file}; -use aho_corasick::AhoCorasickBuilder; +use crate::utils::{UpdateMode, exit_with_failure, replace_region_in_file}; use itertools::Itertools; use rustc_lexer::{LiteralKind, TokenKind, tokenize}; use rustc_literal_escaper::{Mode, unescape_unicode}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fmt::{self, Write}; -use std::fs::{self, OpenOptions}; -use std::io::{self, Read, Seek, Write as _}; +use std::fs; use std::ops::Range; -use std::path::{Path, PathBuf}; +use std::path::Path; use walkdir::{DirEntry, WalkDir}; const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\ @@ -32,7 +30,7 @@ pub fn update(update_mode: UpdateMode) { generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); } -fn generate_lint_files( +pub fn generate_lint_files( update_mode: UpdateMode, lints: &[Lint], deprecated_lints: &[DeprecatedLint], @@ -123,373 +121,6 @@ pub fn print_lints() { println!("there are {lint_count} lints"); } -/// Runs the `rename_lint` command. -/// -/// This does the following: -/// * Adds an entry to `renamed_lints.rs`. -/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). -/// * Renames the lint struct to the new name. -/// * Renames the module containing the lint struct to the new name if it shares a name with the -/// lint. -/// -/// # Panics -/// Panics for the following conditions: -/// * If a file path could not read from or then written to -/// * If either lint name has a prefix -/// * If `old_name` doesn't name an existing lint. -/// * If `old_name` names a deprecated or renamed lint. -#[allow(clippy::too_many_lines)] -pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: bool) { - if let Some((prefix, _)) = old_name.split_once("::") { - panic!("`{old_name}` should not contain the `{prefix}` prefix"); - } - if let Some((prefix, _)) = new_name.split_once("::") { - panic!("`{new_name}` should not contain the `{prefix}` prefix"); - } - - let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); - let mut old_lint_index = None; - let mut found_new_name = false; - for (i, lint) in lints.iter().enumerate() { - if lint.name == old_name { - old_lint_index = Some(i); - } else if lint.name == new_name { - found_new_name = true; - } - } - let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`")); - - let lint = RenamedLint { - old_name: format!("clippy::{old_name}"), - new_name: if uplift { - new_name.into() - } else { - format!("clippy::{new_name}") - }, - }; - - // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in - // case. - assert!( - !renamed_lints.iter().any(|l| lint.old_name == l.old_name), - "`{old_name}` has already been renamed" - ); - assert!( - !deprecated_lints.iter().any(|l| lint.old_name == l.name), - "`{old_name}` has already been deprecated" - ); - - // Update all lint level attributes. (`clippy::lint_name`) - for file in WalkDir::new(".").into_iter().map(Result::unwrap).filter(|f| { - let name = f.path().file_name(); - let ext = f.path().extension(); - (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) - && name != Some(OsStr::new("rename.rs")) - && name != Some(OsStr::new("deprecated_lints.rs")) - }) { - rewrite_file(file.path(), |s| { - replace_ident_like(s, &[(&lint.old_name, &lint.new_name)]) - }); - } - - rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { - insert_at_marker( - s, - "// end renamed lints. used by `cargo dev rename_lint`", - &format!( - "#[clippy::version = \"{}\"]\n \ - (\"{}\", \"{}\"),\n ", - clippy_version.rust_display(), - lint.old_name, - lint.new_name, - ), - ) - }); - - renamed_lints.push(lint); - renamed_lints.sort_by(|lhs, rhs| { - lhs.new_name - .starts_with("clippy::") - .cmp(&rhs.new_name.starts_with("clippy::")) - .reverse() - .then_with(|| lhs.old_name.cmp(&rhs.old_name)) - }); - - if uplift { - write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); - println!( - "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." - ); - } else if found_new_name { - write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); - println!( - "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." - ); - } else { - // Rename the lint struct and source files sharing a name with the lint. - let lint = &mut lints[old_lint_index]; - let old_name_upper = old_name.to_uppercase(); - let new_name_upper = new_name.to_uppercase(); - lint.name = new_name.into(); - - // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. - if try_rename_file( - Path::new(&format!("tests/ui/{old_name}.rs")), - Path::new(&format!("tests/ui/{new_name}.rs")), - ) { - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.stderr")), - Path::new(&format!("tests/ui/{new_name}.stderr")), - ); - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.fixed")), - Path::new(&format!("tests/ui/{new_name}.fixed")), - ); - } - - // Try to rename the file containing the lint if the file name matches the lint's name. - let replacements; - let replacements = if lint.module == old_name - && try_rename_file( - Path::new(&format!("clippy_lints/src/{old_name}.rs")), - Path::new(&format!("clippy_lints/src/{new_name}.rs")), - ) { - // Edit the module name in the lint list. Note there could be multiple lints. - for lint in lints.iter_mut().filter(|l| l.module == old_name) { - lint.module = new_name.into(); - } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() - } else if !lint.module.contains("::") - // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` - && try_rename_file( - Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)), - Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)), - ) - { - // Edit the module name in the lint list. Note there could be multiple lints, or none. - let renamed_mod = format!("{}::{old_name}", lint.module); - for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { - lint.module = format!("{}::{new_name}", lint.module); - } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() - } else { - replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; - &replacements[0..1] - }; - - // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being - // renamed. - for file in clippy_lints_src_files() { - if file - .path() - .as_os_str() - .to_str() - .is_none_or(|x| x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") - { - rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); - } - } - - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("{old_name} has been successfully renamed"); - } - - println!("note: `cargo uitest` still needs to be run to update the test results"); -} - -/// Runs the `deprecate` command -/// -/// This does the following: -/// * Adds an entry to `deprecated_lints.rs`. -/// * Removes the lint declaration (and the entire file if applicable) -/// -/// # Panics -/// -/// If a file path could not read from or written to -pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { - let prefixed_name = if name.starts_with("clippy::") { - name.to_owned() - } else { - format!("clippy::{name}") - }; - let stripped_name = &prefixed_name[8..]; - - let (mut lints, mut deprecated_lints, renamed_lints) = gather_all(); - let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { - eprintln!("error: failed to find lint `{name}`"); - return; - }; - - let mod_path = { - let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); - if mod_path.is_dir() { - mod_path = mod_path.join("mod"); - } - - mod_path.set_extension("rs"); - mod_path - }; - - if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { - rewrite_file("clippy_lints/src/deprecated_lints.rs".as_ref(), |s| { - insert_at_marker( - s, - "// end deprecated lints. used by `cargo dev deprecate_lint`", - &format!( - "#[clippy::version = \"{}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ", - clippy_version.rust_display(), - ), - ) - }); - - deprecated_lints.push(DeprecatedLint { - name: prefixed_name, - reason: reason.into(), - }); - - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{name}` has successfully been deprecated"); - println!("note: you must run `cargo uitest` to update the test results"); - } else { - eprintln!("error: lint not found"); - } -} - -fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io::Result { - fn remove_lint(name: &str, lints: &mut Vec) { - lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); - } - - fn remove_test_assets(name: &str) { - let test_file_stem = format!("tests/ui/{name}"); - let path = Path::new(&test_file_stem); - - // Some lints have their own directories, delete them - if path.is_dir() { - let _ = fs::remove_dir_all(path); - return; - } - - // Remove all related test files - let _ = fs::remove_file(path.with_extension("rs")); - let _ = fs::remove_file(path.with_extension("stderr")); - let _ = fs::remove_file(path.with_extension("fixed")); - } - - fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { - let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| { - content - .find("declare_lint_pass!") - .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`")) - }); - let mut impl_lint_pass_end = content[impl_lint_pass_start..] - .find(']') - .expect("failed to find `impl_lint_pass` terminator"); - - impl_lint_pass_end += impl_lint_pass_start; - if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { - let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); - for c in content[lint_name_end..impl_lint_pass_end].chars() { - // Remove trailing whitespace - if c == ',' || c.is_whitespace() { - lint_name_end += 1; - } else { - break; - } - } - - content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, ""); - } - } - - if path.exists() - && let Some(lint) = lints.iter().find(|l| l.name == name) - { - if lint.module == name { - // The lint name is the same as the file, we can just delete the entire file - fs::remove_file(path)?; - } else { - // We can't delete the entire file, just remove the declaration - - if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { - // Remove clippy_lints/src/some_mod/some_lint.rs - let mut lint_mod_path = path.to_path_buf(); - lint_mod_path.set_file_name(name); - lint_mod_path.set_extension("rs"); - - let _ = fs::remove_file(lint_mod_path); - } - - let mut content = - fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); - - eprintln!( - "warn: you will have to manually remove any code related to `{name}` from `{}`", - path.display() - ); - - assert!( - content[lint.declaration_range.clone()].contains(&name.to_uppercase()), - "error: `{}` does not contain lint `{}`'s declaration", - path.display(), - lint.name - ); - - // Remove lint declaration (declare_clippy_lint!) - content.replace_range(lint.declaration_range.clone(), ""); - - // Remove the module declaration (mod xyz;) - let mod_decl = format!("\nmod {name};"); - content = content.replacen(&mod_decl, "", 1); - - remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); - fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); - } - - remove_test_assets(name); - remove_lint(name, lints); - return Ok(true); - } - - Ok(false) -} - -/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there -/// were no replacements. -fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option { - fn is_ident_char(c: u8) -> bool { - matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') - } - - let searcher = AhoCorasickBuilder::new() - .match_kind(aho_corasick::MatchKind::LeftmostLongest) - .build(replacements.iter().map(|&(x, _)| x.as_bytes())) - .unwrap(); - - let mut result = String::with_capacity(contents.len() + 1024); - let mut pos = 0; - let mut edited = false; - for m in searcher.find_iter(contents) { - let (old, new) = replacements[m.pattern()]; - result.push_str(&contents[pos..m.start()]); - result.push_str( - if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) - && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0)) - { - edited = true; - new - } else { - old - }, - ); - pos = m.end(); - } - result.push_str(&contents[pos..]); - edited.then_some(result) -} - fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } @@ -509,12 +140,12 @@ fn process_file(path: impl AsRef, update_mode: UpdateMode, content: &str) /// Lint data parsed from the Clippy source code. #[derive(Clone, PartialEq, Eq, Debug)] -struct Lint { - name: String, - group: String, - desc: String, - module: String, - declaration_range: Range, +pub struct Lint { + pub name: String, + pub group: String, + pub desc: String, + pub module: String, + pub declaration_range: Range, } impl Lint { @@ -537,9 +168,9 @@ impl Lint { } #[derive(Clone, PartialEq, Eq, Debug)] -struct DeprecatedLint { - name: String, - reason: String, +pub struct DeprecatedLint { + pub name: String, + pub reason: String, } impl DeprecatedLint { fn new(name: &str, reason: &str) -> Self { @@ -550,9 +181,9 @@ impl DeprecatedLint { } } -struct RenamedLint { - old_name: String, - new_name: String, +pub struct RenamedLint { + pub old_name: String, + pub new_name: String, } impl RenamedLint { fn new(old_name: &str, new_name: &str) -> Self { @@ -589,7 +220,8 @@ fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { res } -fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { +#[must_use] +pub fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { let mut seen_lints = HashSet::new(); let mut res: String = GENERATED_FILE_COMMENT.into(); @@ -610,7 +242,8 @@ fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { } /// Gathers all lints defined in `clippy_lints/src` -fn gather_all() -> (Vec, Vec, Vec) { +#[must_use] +pub fn gather_all() -> (Vec, Vec, Vec) { let mut lints = Vec::with_capacity(1000); let mut deprecated_lints = Vec::with_capacity(50); let mut renamed_lints = Vec::with_capacity(50); @@ -638,7 +271,7 @@ fn gather_all() -> (Vec, Vec, Vec) { (lints, deprecated_lints, renamed_lints) } -fn clippy_lints_src_files() -> impl Iterator { +pub fn clippy_lints_src_files() -> impl Iterator { let iter = WalkDir::new("clippy_lints/src").into_iter(); iter.map(Result::unwrap) .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) @@ -808,57 +441,6 @@ fn remove_line_splices(s: &str) -> String { }); res } -fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { - match OpenOptions::new().create_new(true).write(true).open(new_name) { - Ok(file) => drop(file), - Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, - Err(e) => panic_file(e, new_name, "create"), - } - match fs::rename(old_name, new_name) { - Ok(()) => true, - Err(e) => { - drop(fs::remove_file(new_name)); - if e.kind() == io::ErrorKind::NotFound { - false - } else { - panic_file(e, old_name, "rename"); - } - }, - } -} - -#[allow(clippy::needless_pass_by_value)] -fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { - panic!("failed to {action} file `{}`: {error}", name.display()) -} - -fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option { - let i = text.find(marker)?; - let (pre, post) = text.split_at(i); - Some([pre, new_text, post].into_iter().collect()) -} - -fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { - let mut file = OpenOptions::new() - .write(true) - .read(true) - .open(path) - .unwrap_or_else(|e| panic_file(e, path, "open")); - let mut buf = String::new(); - file.read_to_string(&mut buf) - .unwrap_or_else(|e| panic_file(e, path, "read")); - if let Some(new_contents) = f(&buf) { - file.rewind().unwrap_or_else(|e| panic_file(e, path, "write")); - file.write_all(new_contents.as_bytes()) - .unwrap_or_else(|e| panic_file(e, path, "write")); - file.set_len(new_contents.len() as u64) - .unwrap_or_else(|e| panic_file(e, path, "write")); - } -} - -fn write_file(path: &Path, contents: &str) { - fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write")); -} #[cfg(test)] mod tests { diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 2b199d96fcde..38a839c16a75 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -1,3 +1,4 @@ +use aho_corasick::AhoCorasickBuilder; use core::fmt::{self, Display}; use core::str::FromStr; use std::env; @@ -275,3 +276,87 @@ pub(crate) fn replace_region_in_text<'a>( Ok(res) } + +/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there +/// were no replacements. +#[must_use] +pub fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option { + fn is_ident_char(c: u8) -> bool { + matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') + } + + let searcher = AhoCorasickBuilder::new() + .match_kind(aho_corasick::MatchKind::LeftmostLongest) + .build(replacements.iter().map(|&(x, _)| x.as_bytes())) + .unwrap(); + + let mut result = String::with_capacity(contents.len() + 1024); + let mut pos = 0; + let mut edited = false; + for m in searcher.find_iter(contents) { + let (old, new) = replacements[m.pattern()]; + result.push_str(&contents[pos..m.start()]); + result.push_str( + if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) + && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0)) + { + edited = true; + new + } else { + old + }, + ); + pos = m.end(); + } + result.push_str(&contents[pos..]); + edited.then_some(result) +} + +#[expect(clippy::must_use_candidate)] +pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { + match OpenOptions::new().create_new(true).write(true).open(new_name) { + Ok(file) => drop(file), + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(e) => panic_io(&e, "creating", new_name), + } + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(e) => { + drop(fs::remove_file(new_name)); + if e.kind() == io::ErrorKind::NotFound { + false + } else { + panic_io(&e, "renaming", old_name); + } + }, + } +} + +#[must_use] +pub fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option { + let i = text.find(marker)?; + let (pre, post) = text.split_at(i); + Some([pre, new_text, post].into_iter().collect()) +} + +pub fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { + let mut file = OpenOptions::new() + .write(true) + .read(true) + .open(path) + .unwrap_or_else(|e| panic_io(&e, "opening", path)); + let mut buf = String::new(); + file.read_to_string(&mut buf) + .unwrap_or_else(|e| panic_io(&e, "reading", path)); + if let Some(new_contents) = f(&buf) { + file.rewind().unwrap_or_else(|e| panic_io(&e, "writing", path)); + file.write_all(new_contents.as_bytes()) + .unwrap_or_else(|e| panic_io(&e, "writing", path)); + file.set_len(new_contents.len() as u64) + .unwrap_or_else(|e| panic_io(&e, "writing", path)); + } +} + +pub fn write_file(path: &Path, contents: &str) { + fs::write(path, contents).unwrap_or_else(|e| panic_io(&e, "writing", path)); +} From 98cb92f3235c4c963d1c17e8a833f37091b5b487 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 14 Apr 2025 13:40:05 -0400 Subject: [PATCH 140/245] clippy_dev: Reuse buffers when updating files and don't write unchanged files in `clippy_dev` --- clippy_dev/src/main.rs | 4 +- clippy_dev/src/release.rs | 26 +++-- clippy_dev/src/rename_lint.rs | 17 +-- clippy_dev/src/sync.rs | 31 ++--- clippy_dev/src/update_lints.rs | 206 ++++++++++++++------------------- clippy_dev/src/utils.rs | 205 ++++++++++++++++++++------------ 6 files changed, 251 insertions(+), 238 deletions(-) diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 565416572a95..73248d72d04a 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -30,10 +30,8 @@ fn main() { DevCommand::UpdateLints { print_only, check } => { if print_only { update_lints::print_lints(); - } else if check { - update_lints::update(utils::UpdateMode::Check); } else { - update_lints::update(utils::UpdateMode::Change); + update_lints::update(utils::UpdateMode::from_check(check)); } }, DevCommand::NewLint { diff --git a/clippy_dev/src/release.rs b/clippy_dev/src/release.rs index 34f81e10a393..d3b1a7ff3201 100644 --- a/clippy_dev/src/release.rs +++ b/clippy_dev/src/release.rs @@ -1,8 +1,7 @@ +use crate::utils::{FileUpdater, Version, update_text_region_fn}; use std::fmt::Write; -use crate::utils::{UpdateMode, Version, replace_region_in_file}; - -const CARGO_TOML_FILES: [&str; 4] = [ +static CARGO_TOML_FILES: &[&str] = &[ "clippy_config/Cargo.toml", "clippy_lints/Cargo.toml", "clippy_utils/Cargo.toml", @@ -11,15 +10,18 @@ const CARGO_TOML_FILES: [&str; 4] = [ pub fn bump_version(mut version: Version) { version.minor += 1; - for &file in &CARGO_TOML_FILES { - replace_region_in_file( - UpdateMode::Change, - file.as_ref(), - "# begin autogenerated version\n", - "# end autogenerated version", - |res| { - writeln!(res, "version = \"{}\"", version.toml_display()).unwrap(); - }, + + let mut updater = FileUpdater::default(); + for file in CARGO_TOML_FILES { + updater.update_file( + file, + &mut update_text_region_fn( + "# begin autogenerated version\n", + "# end autogenerated version", + |dst| { + writeln!(dst, "version = \"{}\"", version.toml_display()).unwrap(); + }, + ), ); } } diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index 045e1f83e13e..25db95a44893 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -1,8 +1,8 @@ use crate::update_lints::{ - RenamedLint, clippy_lints_src_files, gather_all, gen_renamed_lints_test, generate_lint_files, + RenamedLint, clippy_lints_src_files, gather_all, gen_renamed_lints_test_fn, generate_lint_files, }; use crate::utils::{ - UpdateMode, Version, insert_at_marker, replace_ident_like, rewrite_file, try_rename_file, write_file, + FileUpdater, UpdateMode, Version, insert_at_marker, replace_ident_like, rewrite_file, try_rename_file, }; use std::ffi::OsStr; use std::path::Path; @@ -32,6 +32,7 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b panic!("`{new_name}` should not contain the `{prefix}` prefix"); } + let mut updater = FileUpdater::default(); let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); let mut old_lint_index = None; let mut found_new_name = false; @@ -72,8 +73,8 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b && name != Some(OsStr::new("rename.rs")) && name != Some(OsStr::new("deprecated_lints.rs")) }) { - rewrite_file(file.path(), |s| { - replace_ident_like(s, &[(&lint.old_name, &lint.new_name)]) + updater.update_file(file.path(), &mut |_, src, dst| { + replace_ident_like(&[(&lint.old_name, &lint.new_name)], src, dst) }); } @@ -101,12 +102,12 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b }); if uplift { - write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + updater.update_file("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(&renamed_lints)); println!( "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." ); } else if found_new_name { - write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + updater.update_file("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(&renamed_lints)); println!( "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." ); @@ -173,7 +174,9 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b .to_str() .is_none_or(|x| x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") { - rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); + updater.update_file(file.path(), &mut |_, src, dst| { + replace_ident_like(replacements, src, dst) + }); } } diff --git a/clippy_dev/src/sync.rs b/clippy_dev/src/sync.rs index a6b65e561c22..c699b0d7b959 100644 --- a/clippy_dev/src/sync.rs +++ b/clippy_dev/src/sync.rs @@ -1,33 +1,18 @@ -use std::fmt::Write; -use std::path::Path; - +use crate::utils::{FileUpdater, update_text_region_fn}; use chrono::offset::Utc; - -use crate::utils::{UpdateMode, replace_region_in_file}; +use std::fmt::Write; pub fn update_nightly() { - // Update rust-toolchain nightly version let date = Utc::now().format("%Y-%m-%d").to_string(); - replace_region_in_file( - UpdateMode::Change, - Path::new("rust-toolchain.toml"), + let update = &mut update_text_region_fn( "# begin autogenerated nightly\n", "# end autogenerated nightly", - |res| { - writeln!(res, "channel = \"nightly-{date}\"").unwrap(); + |dst| { + writeln!(dst, "channel = \"nightly-{date}\"").unwrap(); }, ); - // Update clippy_utils nightly version - replace_region_in_file( - UpdateMode::Change, - Path::new("clippy_utils/README.md"), - "\n", - "", - |res| { - writeln!(res, "```").unwrap(); - writeln!(res, "nightly-{date}").unwrap(); - writeln!(res, "```").unwrap(); - }, - ); + let mut updater = FileUpdater::default(); + updater.update_file("rust-toolchain.toml", update); + updater.update_file("clippy_utils/README.md", update); } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 2f450b7d9ea6..ad995f5e4c2c 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,10 +1,10 @@ -use crate::utils::{UpdateMode, exit_with_failure, replace_region_in_file}; +use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; use itertools::Itertools; use rustc_lexer::{LiteralKind, TokenKind, tokenize}; use rustc_literal_escaper::{Mode, unescape_unicode}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; -use std::fmt::{self, Write}; +use std::fmt::Write; use std::fs; use std::ops::Range; use std::path::Path; @@ -33,74 +33,77 @@ pub fn update(update_mode: UpdateMode) { pub fn generate_lint_files( update_mode: UpdateMode, lints: &[Lint], - deprecated_lints: &[DeprecatedLint], - renamed_lints: &[RenamedLint], + deprecated: &[DeprecatedLint], + renamed: &[RenamedLint], ) { let mut lints = lints.to_owned(); - lints.sort_by_key(|lint| lint.name.clone()); - - replace_region_in_file( + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + FileUpdater::default().update_files_checked( + "cargo dev update_lints", update_mode, - Path::new("README.md"), - "[There are over ", - " lints included in this crate!]", - |res| { - write!(res, "{}", round_to_fifty(lints.len())).unwrap(); - }, + &mut [ + ( + "README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + }), + ), + ( + "book/src/README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + }), + ), + ( + "CHANGELOG.md", + &mut update_text_region_fn( + "\n", + "", + |dst| { + for lint in lints + .iter() + .map(|l| &*l.name) + .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) + .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) + .sorted() + { + writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); + } + }, + ), + ), + ( + "clippy_lints/src/lib.rs", + &mut update_text_region_fn( + "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", + "// end lints modules, do not remove this comment, it’s used in `update_lints`", + |dst| { + for lint_mod in lints.iter().map(|l| &l.module).sorted().dedup() { + writeln!(dst, "mod {lint_mod};").unwrap(); + } + }, + ), + ), + ("clippy_lints/src/declared_lints.rs", &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n"); + for (module_name, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() { + writeln!(dst, " crate::{module_name}::{lint_name}_INFO,").unwrap(); + } + dst.push_str("];\n"); + UpdateStatus::from_changed(src != dst) + }), + ("tests/ui/deprecated.rs", &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + for lint in deprecated { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }), + ("tests/ui/rename.rs", &mut gen_renamed_lints_test_fn(renamed)), + ], ); - - replace_region_in_file( - update_mode, - Path::new("book/src/README.md"), - "[There are over ", - " lints included in this crate!]", - |res| { - write!(res, "{}", round_to_fifty(lints.len())).unwrap(); - }, - ); - - replace_region_in_file( - update_mode, - Path::new("CHANGELOG.md"), - "\n", - "", - |res| { - for lint in lints - .iter() - .map(|l| &*l.name) - .chain(deprecated_lints.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) - .chain(renamed_lints.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) - .sorted() - { - writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); - } - }, - ); - - // This has to be in lib.rs, otherwise rustfmt doesn't work - replace_region_in_file( - update_mode, - Path::new("clippy_lints/src/lib.rs"), - "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", - "// end lints modules, do not remove this comment, it’s used in `update_lints`", - |res| { - for lint_mod in lints.iter().map(|l| &l.module).unique().sorted() { - writeln!(res, "mod {lint_mod};").unwrap(); - } - }, - ); - - process_file( - "clippy_lints/src/declared_lints.rs", - update_mode, - &gen_declared_lints(lints.iter()), - ); - - let content = gen_deprecated_lints_test(deprecated_lints); - process_file("tests/ui/deprecated.rs", update_mode, &content); - - let content = gen_renamed_lints_test(renamed_lints); - process_file("tests/ui/rename.rs", update_mode, &content); } pub fn print_lints() { @@ -125,19 +128,6 @@ fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } -fn process_file(path: impl AsRef, update_mode: UpdateMode, content: &str) { - if update_mode == UpdateMode::Check { - let old_content = - fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {e}", path.as_ref().display())); - if content != old_content { - exit_with_failure(); - } - } else { - fs::write(&path, content.as_bytes()) - .unwrap_or_else(|e| panic!("Cannot write to {}: {e}", path.as_ref().display())); - } -} - /// Lint data parsed from the Clippy source code. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Lint { @@ -194,51 +184,25 @@ impl RenamedLint { } } -/// Generates the code for registering lints -#[must_use] -fn gen_declared_lints<'a>(lints: impl Iterator) -> String { - let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); - details.sort_unstable(); - - let mut output = GENERATED_FILE_COMMENT.to_string(); - output.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n"); - - for (module_name, lint_name) in details { - let _: fmt::Result = writeln!(output, " crate::{module_name}::{lint_name}_INFO,"); - } - output.push_str("];\n"); - - output -} - -fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { - let mut res: String = GENERATED_FILE_COMMENT.into(); - for lint in lints { - writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); - } - res.push_str("\nfn main() {}\n"); - res -} - -#[must_use] -pub fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { - let mut seen_lints = HashSet::new(); - let mut res: String = GENERATED_FILE_COMMENT.into(); - - res.push_str("#![allow(clippy::duplicated_attributes)]\n"); - for lint in lints { - if seen_lints.insert(&lint.new_name) { - writeln!(res, "#![allow({})]", lint.new_name).unwrap(); +pub fn gen_renamed_lints_test_fn(lints: &[RenamedLint]) -> impl Fn(&Path, &str, &mut String) -> UpdateStatus { + move |_, src, dst| { + let mut seen_lints = HashSet::new(); + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); + for lint in lints { + if seen_lints.insert(&lint.new_name) { + writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); + } } - } - seen_lints.clear(); - for lint in lints { - if seen_lints.insert(&lint.old_name) { - writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); + seen_lints.clear(); + for lint in lints { + if seen_lints.insert(&lint.old_name) { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); + } } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) } - res.push_str("\nfn main() {}\n"); - res } /// Gathers all lints defined in `clippy_lints/src` diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 38a839c16a75..aaacdcf65c16 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -209,78 +209,145 @@ pub fn exit_if_err(status: io::Result) { } } -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy)] +pub enum UpdateStatus { + Unchanged, + Changed, +} +impl UpdateStatus { + #[must_use] + pub fn from_changed(value: bool) -> Self { + if value { Self::Changed } else { Self::Unchanged } + } + + #[must_use] + pub fn is_changed(self) -> bool { + matches!(self, Self::Changed) + } +} + +#[derive(Clone, Copy)] pub enum UpdateMode { - Check, Change, + Check, +} +impl UpdateMode { + #[must_use] + pub fn from_check(check: bool) -> Self { + if check { Self::Check } else { Self::Change } + } } -pub(crate) fn exit_with_failure() { - println!( - "Not all lints defined properly. \ - Please run `cargo dev update_lints` to make sure all lints are defined properly." - ); - process::exit(1); +#[derive(Default)] +pub struct FileUpdater { + src_buf: String, + dst_buf: String, } +impl FileUpdater { + fn update_file_checked_inner( + &mut self, + tool: &str, + mode: UpdateMode, + path: &Path, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + let mut file = File::open(path, OpenOptions::new().read(true).write(true)); + file.read_to_cleared_string(&mut self.src_buf); + self.dst_buf.clear(); + match (mode, update(path, &self.src_buf, &mut self.dst_buf)) { + (UpdateMode::Check, UpdateStatus::Changed) => { + eprintln!( + "the contents of `{}` are out of date\nplease run `{tool}` to update", + path.display() + ); + process::exit(1); + }, + (UpdateMode::Change, UpdateStatus::Changed) => file.replace_contents(self.dst_buf.as_bytes()), + (UpdateMode::Check | UpdateMode::Change, UpdateStatus::Unchanged) => {}, + } + } -/// Replaces a region in a file delimited by two lines matching regexes. -/// -/// `path` is the relative path to the file on which you want to perform the replacement. -/// -/// See `replace_region_in_text` for documentation of the other options. -/// -/// # Panics -/// -/// Panics if the path could not read or then written -pub(crate) fn replace_region_in_file( - update_mode: UpdateMode, - path: &Path, - start: &str, - end: &str, - write_replacement: impl FnMut(&mut String), -) { - let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); - let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) { - Ok(x) => x, - Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()), - }; + fn update_file_inner(&mut self, path: &Path, update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus) { + let mut file = File::open(path, OpenOptions::new().read(true).write(true)); + file.read_to_cleared_string(&mut self.src_buf); + self.dst_buf.clear(); + if update(path, &self.src_buf, &mut self.dst_buf).is_changed() { + file.replace_contents(self.dst_buf.as_bytes()); + } + } - match update_mode { - UpdateMode::Check if contents != new_contents => exit_with_failure(), - UpdateMode::Check => (), - UpdateMode::Change => { - if let Err(e) = fs::write(path, new_contents.as_bytes()) { - panic!("Cannot write to `{}`: {e}", path.display()); - } - }, + pub fn update_file_checked( + &mut self, + tool: &str, + mode: UpdateMode, + path: impl AsRef, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + self.update_file_checked_inner(tool, mode, path.as_ref(), update); + } + + #[expect(clippy::type_complexity)] + pub fn update_files_checked( + &mut self, + tool: &str, + mode: UpdateMode, + files: &mut [( + impl AsRef, + &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + )], + ) { + for (path, update) in files { + self.update_file_checked_inner(tool, mode, path.as_ref(), update); + } + } + + pub fn update_file( + &mut self, + path: impl AsRef, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + self.update_file_inner(path.as_ref(), update); } } /// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters /// were found, or the missing delimiter if not. -pub(crate) fn replace_region_in_text<'a>( - text: &str, - start: &'a str, - end: &'a str, - mut write_replacement: impl FnMut(&mut String), -) -> Result { - let (text_start, rest) = text.split_once(start).ok_or(start)?; - let (_, text_end) = rest.split_once(end).ok_or(end)?; +pub fn update_text_region( + path: &Path, + start: &str, + end: &str, + src: &str, + dst: &mut String, + insert: &mut impl FnMut(&mut String), +) -> UpdateStatus { + let Some((src_start, src_end)) = src.split_once(start) else { + panic!("`{}` does not contain `{start}`", path.display()); + }; + let Some((replaced_text, src_end)) = src_end.split_once(end) else { + panic!("`{}` does not contain `{end}`", path.display()); + }; + dst.push_str(src_start); + dst.push_str(start); + let new_start = dst.len(); + insert(dst); + let changed = dst[new_start..] != *replaced_text; + dst.push_str(end); + dst.push_str(src_end); + UpdateStatus::from_changed(changed) +} - let mut res = String::with_capacity(text.len() + 4096); - res.push_str(text_start); - res.push_str(start); - write_replacement(&mut res); - res.push_str(end); - res.push_str(text_end); - - Ok(res) +pub fn update_text_region_fn( + start: &str, + end: &str, + mut insert: impl FnMut(&mut String), +) -> impl FnMut(&Path, &str, &mut String) -> UpdateStatus { + move |path, src, dst| update_text_region(path, start, end, src, dst, &mut insert) } /// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there /// were no replacements. #[must_use] -pub fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option { +pub fn replace_ident_like(replacements: &[(&str, &str)], src: &str, dst: &mut String) -> UpdateStatus { fn is_ident_char(c: u8) -> bool { matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') } @@ -290,26 +357,20 @@ pub fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Opti .build(replacements.iter().map(|&(x, _)| x.as_bytes())) .unwrap(); - let mut result = String::with_capacity(contents.len() + 1024); let mut pos = 0; - let mut edited = false; - for m in searcher.find_iter(contents) { - let (old, new) = replacements[m.pattern()]; - result.push_str(&contents[pos..m.start()]); - result.push_str( - if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) - && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0)) - { - edited = true; - new - } else { - old - }, - ); - pos = m.end(); + let mut changed = false; + for m in searcher.find_iter(src) { + if !is_ident_char(src.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) + && !is_ident_char(src.as_bytes().get(m.end()).copied().unwrap_or(0)) + { + dst.push_str(&src[pos..m.start()]); + dst.push_str(replacements[m.pattern()].1); + pos = m.end(); + changed = true; + } } - result.push_str(&contents[pos..]); - edited.then_some(result) + dst.push_str(&src[pos..]); + UpdateStatus::from_changed(changed) } #[expect(clippy::must_use_candidate)] From 2c85cb0371ca0e8980a4225714ed3407f1670e12 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 14 Apr 2025 14:14:03 -0400 Subject: [PATCH 141/245] clippy_dev: Only build `AhoCorasick` searcher once for multiple files. --- clippy_dev/src/rename_lint.rs | 15 ++++----- clippy_dev/src/utils.rs | 61 +++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index 25db95a44893..c78ec98c1aa8 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -1,9 +1,7 @@ use crate::update_lints::{ RenamedLint, clippy_lints_src_files, gather_all, gen_renamed_lints_test_fn, generate_lint_files, }; -use crate::utils::{ - FileUpdater, UpdateMode, Version, insert_at_marker, replace_ident_like, rewrite_file, try_rename_file, -}; +use crate::utils::{FileUpdater, StringReplacer, UpdateMode, Version, insert_at_marker, rewrite_file, try_rename_file}; use std::ffi::OsStr; use std::path::Path; use walkdir::WalkDir; @@ -66,6 +64,8 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b ); // Update all lint level attributes. (`clippy::lint_name`) + let replacements = &[(&*lint.old_name, &*lint.new_name)]; + let replacer = StringReplacer::new(replacements); for file in WalkDir::new(".").into_iter().map(Result::unwrap).filter(|f| { let name = f.path().file_name(); let ext = f.path().extension(); @@ -73,9 +73,7 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b && name != Some(OsStr::new("rename.rs")) && name != Some(OsStr::new("deprecated_lints.rs")) }) { - updater.update_file(file.path(), &mut |_, src, dst| { - replace_ident_like(&[(&lint.old_name, &lint.new_name)], src, dst) - }); + updater.update_file(file.path(), &mut replacer.replace_ident_fn()); } rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { @@ -167,6 +165,7 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being // renamed. + let replacer = StringReplacer::new(replacements); for file in clippy_lints_src_files() { if file .path() @@ -174,9 +173,7 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b .to_str() .is_none_or(|x| x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") { - updater.update_file(file.path(), &mut |_, src, dst| { - replace_ident_like(replacements, src, dst) - }); + updater.update_file(file.path(), &mut replacer.replace_ident_fn()); } } diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index aaacdcf65c16..9f48832af006 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -1,4 +1,4 @@ -use aho_corasick::AhoCorasickBuilder; +use aho_corasick::{AhoCorasick, AhoCorasickBuilder}; use core::fmt::{self, Display}; use core::str::FromStr; use std::env; @@ -344,33 +344,46 @@ pub fn update_text_region_fn( move |path, src, dst| update_text_region(path, start, end, src, dst, &mut insert) } -/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there -/// were no replacements. #[must_use] -pub fn replace_ident_like(replacements: &[(&str, &str)], src: &str, dst: &mut String) -> UpdateStatus { - fn is_ident_char(c: u8) -> bool { - matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') - } +pub fn is_ident_char(c: u8) -> bool { + matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') +} - let searcher = AhoCorasickBuilder::new() - .match_kind(aho_corasick::MatchKind::LeftmostLongest) - .build(replacements.iter().map(|&(x, _)| x.as_bytes())) - .unwrap(); - - let mut pos = 0; - let mut changed = false; - for m in searcher.find_iter(src) { - if !is_ident_char(src.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) - && !is_ident_char(src.as_bytes().get(m.end()).copied().unwrap_or(0)) - { - dst.push_str(&src[pos..m.start()]); - dst.push_str(replacements[m.pattern()].1); - pos = m.end(); - changed = true; +pub struct StringReplacer<'a> { + searcher: AhoCorasick, + replacements: &'a [(&'a str, &'a str)], +} +impl<'a> StringReplacer<'a> { + #[must_use] + pub fn new(replacements: &'a [(&'a str, &'a str)]) -> Self { + Self { + searcher: AhoCorasickBuilder::new() + .match_kind(aho_corasick::MatchKind::LeftmostLongest) + .build(replacements.iter().map(|&(x, _)| x)) + .unwrap(), + replacements, + } + } + + /// Replace substrings if they aren't bordered by identifier characters. + pub fn replace_ident_fn(&self) -> impl Fn(&Path, &str, &mut String) -> UpdateStatus { + move |_, src, dst| { + let mut pos = 0; + let mut changed = false; + for m in self.searcher.find_iter(src) { + if !is_ident_char(src.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) + && !is_ident_char(src.as_bytes().get(m.end()).copied().unwrap_or(0)) + { + changed = true; + dst.push_str(&src[pos..m.start()]); + dst.push_str(self.replacements[m.pattern()].1); + pos = m.end(); + } + } + dst.push_str(&src[pos..]); + UpdateStatus::from_changed(changed) } } - dst.push_str(&src[pos..]); - UpdateStatus::from_changed(changed) } #[expect(clippy::must_use_candidate)] From 97abf33fa0f8eec35d162d8eab1673e29ecbba29 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 15 Apr 2025 00:27:55 -0400 Subject: [PATCH 142/245] clippy_dev: Split gathering lint decls from parsing deprecated lints. --- clippy_dev/src/deprecate_lint.rs | 38 +++++--- clippy_dev/src/rename_lint.rs | 45 +++++---- clippy_dev/src/update_lints.rs | 140 +++++++++++++++++---------- clippy_dev/src/utils.rs | 63 ++++++------ clippy_lints/src/deprecated_lints.rs | 2 - 5 files changed, 172 insertions(+), 116 deletions(-) diff --git a/clippy_dev/src/deprecate_lint.rs b/clippy_dev/src/deprecate_lint.rs index 2c4286b325db..bf0e77710469 100644 --- a/clippy_dev/src/deprecate_lint.rs +++ b/clippy_dev/src/deprecate_lint.rs @@ -1,5 +1,7 @@ -use crate::update_lints::{DeprecatedLint, Lint, gather_all, generate_lint_files}; -use crate::utils::{UpdateMode, Version, insert_at_marker, rewrite_file}; +use crate::update_lints::{ + DeprecatedLint, DeprecatedLints, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints, +}; +use crate::utils::{UpdateMode, Version}; use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::{fs, io}; @@ -21,7 +23,16 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { }; let stripped_name = &prefixed_name[8..]; - let (mut lints, mut deprecated_lints, renamed_lints) = gather_all(); + let mut lints = find_lint_decls(); + let DeprecatedLints { + renamed: renamed_lints, + deprecated: mut deprecated_lints, + file: mut deprecated_file, + contents: mut deprecated_contents, + deprecated_end, + .. + } = read_deprecated_lints(); + let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { eprintln!("error: failed to find lint `{name}`"); return; @@ -38,16 +49,17 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { }; if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { - rewrite_file("clippy_lints/src/deprecated_lints.rs".as_ref(), |s| { - insert_at_marker( - s, - "// end deprecated lints. used by `cargo dev deprecate_lint`", - &format!( - "#[clippy::version = \"{}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ", - clippy_version.rust_display(), - ), - ) - }); + deprecated_contents.insert_str( + deprecated_end as usize, + &format!( + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + clippy_version.rust_display(), + prefixed_name, + reason, + ), + ); + deprecated_file.replace_contents(deprecated_contents.as_bytes()); + drop(deprecated_file); deprecated_lints.push(DeprecatedLint { name: prefixed_name, diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index c78ec98c1aa8..9e7e5d97f021 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -1,7 +1,8 @@ use crate::update_lints::{ - RenamedLint, clippy_lints_src_files, gather_all, gen_renamed_lints_test_fn, generate_lint_files, + DeprecatedLints, RenamedLint, find_lint_decls, gen_renamed_lints_test_fn, generate_lint_files, + read_deprecated_lints, }; -use crate::utils::{FileUpdater, StringReplacer, UpdateMode, Version, insert_at_marker, rewrite_file, try_rename_file}; +use crate::utils::{FileUpdater, StringReplacer, UpdateMode, Version, try_rename_file}; use std::ffi::OsStr; use std::path::Path; use walkdir::WalkDir; @@ -31,7 +32,16 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b } let mut updater = FileUpdater::default(); - let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); + let mut lints = find_lint_decls(); + let DeprecatedLints { + renamed: mut renamed_lints, + deprecated: deprecated_lints, + file: mut deprecated_file, + contents: mut deprecated_contents, + renamed_end, + .. + } = read_deprecated_lints(); + let mut old_lint_index = None; let mut found_new_name = false; for (i, lint) in lints.iter().enumerate() { @@ -76,19 +86,17 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b updater.update_file(file.path(), &mut replacer.replace_ident_fn()); } - rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { - insert_at_marker( - s, - "// end renamed lints. used by `cargo dev rename_lint`", - &format!( - "#[clippy::version = \"{}\"]\n \ - (\"{}\", \"{}\"),\n ", - clippy_version.rust_display(), - lint.old_name, - lint.new_name, - ), - ) - }); + deprecated_contents.insert_str( + renamed_end as usize, + &format!( + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + clippy_version.rust_display(), + lint.old_name, + lint.new_name, + ), + ); + deprecated_file.replace_contents(deprecated_contents.as_bytes()); + drop(deprecated_file); renamed_lints.push(lint); renamed_lints.sort_by(|lhs, rhs| { @@ -166,12 +174,13 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being // renamed. let replacer = StringReplacer::new(replacements); - for file in clippy_lints_src_files() { + for file in WalkDir::new("clippy_lints/src") { + let file = file.expect("error reading `clippy_lints/src`"); if file .path() .as_os_str() .to_str() - .is_none_or(|x| x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") + .is_some_and(|x| x.ends_with("*.rs") && x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") { updater.update_file(file.path(), &mut replacer.replace_ident_fn()); } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index ad995f5e4c2c..28c988bc19ff 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,11 +1,11 @@ -use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; +use crate::utils::{File, FileAction, FileUpdater, UpdateMode, UpdateStatus, panic_file, update_text_region_fn}; +use core::str; use itertools::Itertools; use rustc_lexer::{LiteralKind, TokenKind, tokenize}; use rustc_literal_escaper::{Mode, unescape_unicode}; use std::collections::{HashMap, HashSet}; -use std::ffi::OsStr; use std::fmt::Write; -use std::fs; +use std::fs::OpenOptions; use std::ops::Range; use std::path::Path; use walkdir::{DirEntry, WalkDir}; @@ -26,8 +26,11 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht /// /// Panics if a file path could not read from or then written to pub fn update(update_mode: UpdateMode) { - let (lints, deprecated_lints, renamed_lints) = gather_all(); - generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); + let lints = find_lint_decls(); + let DeprecatedLints { + renamed, deprecated, .. + } = read_deprecated_lints(); + generate_lint_files(update_mode, &lints, &deprecated, &renamed); } pub fn generate_lint_files( @@ -36,8 +39,6 @@ pub fn generate_lint_files( deprecated: &[DeprecatedLint], renamed: &[RenamedLint], ) { - let mut lints = lints.to_owned(); - lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); FileUpdater::default().update_files_checked( "cargo dev update_lints", update_mode, @@ -107,7 +108,7 @@ pub fn generate_lint_files( } pub fn print_lints() { - let (lints, _, _) = gather_all(); + let lints = find_lint_decls(); let lint_count = lints.len(); let grouped_by_lint_group = Lint::by_lint_group(lints.into_iter()); @@ -205,40 +206,54 @@ pub fn gen_renamed_lints_test_fn(lints: &[RenamedLint]) -> impl Fn(&Path, &str, } } -/// Gathers all lints defined in `clippy_lints/src` +/// Finds all lint declarations (`declare_clippy_lint!`) #[must_use] -pub fn gather_all() -> (Vec, Vec, Vec) { +pub fn find_lint_decls() -> Vec { let mut lints = Vec::with_capacity(1000); - let mut deprecated_lints = Vec::with_capacity(50); - let mut renamed_lints = Vec::with_capacity(50); - - for file in clippy_lints_src_files() { - let path = file.path(); - let contents = - fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); - let module = path.as_os_str().to_str().unwrap()["clippy_lints/src/".len()..].replace(['/', '\\'], "::"); - - // If the lints are stored in mod.rs, we get the module name from - // the containing directory: - let module = if let Some(module) = module.strip_suffix("::mod.rs") { - module - } else { - module.strip_suffix(".rs").unwrap_or(&module) - }; - - if module == "deprecated_lints" { - parse_deprecated_contents(&contents, &mut deprecated_lints, &mut renamed_lints); - } else { - parse_contents(&contents, module, &mut lints); - } + let mut contents = String::new(); + for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) { + parse_clippy_lint_decls( + File::open_read_to_cleared_string(file.path(), &mut contents), + &module, + &mut lints, + ); } - (lints, deprecated_lints, renamed_lints) + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + lints } -pub fn clippy_lints_src_files() -> impl Iterator { - let iter = WalkDir::new("clippy_lints/src").into_iter(); - iter.map(Result::unwrap) - .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) +/// Reads the source files from the given root directory +fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator { + WalkDir::new(src_root).into_iter().filter_map(move |e| { + let e = match e { + Ok(e) => e, + Err(ref e) => panic_file(e, FileAction::Read, src_root), + }; + let path = e.path().as_os_str().as_encoded_bytes(); + if let Some(path) = path.strip_suffix(b".rs") + && let Some(path) = path.get("clippy_lints/src/".len()..) + { + if path == b"lib" { + Some((e, String::new())) + } else { + let path = if let Some(path) = path.strip_suffix(b"mod") + && let Some(path) = path.strip_suffix(b"/").or_else(|| path.strip_suffix(b"\\")) + { + path + } else { + path + }; + if let Ok(path) = str::from_utf8(path) { + let path = path.replace(['/', '\\'], "::"); + Some((e, path)) + } else { + None + } + } + } else { + None + } + }) } macro_rules! match_tokens { @@ -266,7 +281,7 @@ pub(crate) struct LintDeclSearchResult<'a> { } /// Parse a source file looking for `declare_clippy_lint` macro invocations. -fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { +fn parse_clippy_lint_decls(contents: &str, module: &str, lints: &mut Vec) { let mut offset = 0usize; let mut iter = tokenize(contents).map(|t| { let range = offset..offset + t.len as usize; @@ -333,15 +348,40 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { } } -/// Parse a source file looking for `declare_deprecated_lint` macro invocations. -fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec, renamed: &mut Vec) { - let Some((_, contents)) = contents.split_once("\ndeclare_with_version! { DEPRECATED") else { - return; - }; - let Some((deprecated_src, renamed_src)) = contents.split_once("\ndeclare_with_version! { RENAMED") else { - return; +pub struct DeprecatedLints { + pub file: File<'static>, + pub contents: String, + pub deprecated: Vec, + pub renamed: Vec, + pub deprecated_end: u32, + pub renamed_end: u32, +} + +#[must_use] +#[expect(clippy::cast_possible_truncation)] +pub fn read_deprecated_lints() -> DeprecatedLints { + let mut res = DeprecatedLints { + file: File::open( + "clippy_lints/src/deprecated_lints.rs", + OpenOptions::new().read(true).write(true), + ), + contents: String::new(), + deprecated: Vec::with_capacity(30), + renamed: Vec::with_capacity(80), + deprecated_end: 0, + renamed_end: 0, }; + res.file.read_append_to_string(&mut res.contents); + + let (_, contents) = res.contents.split_once("\ndeclare_with_version! { DEPRECATED").unwrap(); + let (deprecated_src, contents) = contents.split_once("\n]}").unwrap(); + res.deprecated_end = (res.contents.len() - contents.len() - 2) as u32; + + let (_, contents) = contents.split_once("\ndeclare_with_version! { RENAMED").unwrap(); + let (renamed_src, contents) = contents.split_once("\n]}").unwrap(); + res.renamed_end = (res.contents.len() - contents.len() - 2) as u32; + for line in deprecated_src.lines() { let mut offset = 0usize; let mut iter = tokenize(line).map(|t| { @@ -362,7 +402,7 @@ fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec &'static str { + match self { + Self::Open => "opening", + Self::Read => "reading", + Self::Write => "writing", + Self::Create => "creating", + Self::Rename => "renaming", + } + } +} + #[cold] #[track_caller] -fn panic_io(e: &io::Error, action: &str, path: &Path) -> ! { - panic!("error {action} `{}`: {}", path.display(), *e) +pub fn panic_file(err: &impl Display, action: FileAction, path: &Path) -> ! { + panic!("error {} `{}`: {}", action.as_str(), path.display(), *err) } /// Wrapper around `std::fs::File` which panics with a path on failure. @@ -30,7 +50,7 @@ impl<'a> File<'a> { let path = path.as_ref(); match options.open(path) { Ok(inner) => Self { inner, path }, - Err(e) => panic_io(&e, "opening", path), + Err(e) => panic_file(&e, FileAction::Open, path), } } @@ -41,7 +61,7 @@ impl<'a> File<'a> { match options.open(path) { Ok(inner) => Some(Self { inner, path }), Err(e) if e.kind() == io::ErrorKind::NotFound => None, - Err(e) => panic_io(&e, "opening", path), + Err(e) => panic_file(&e, FileAction::Open, path), } } @@ -59,7 +79,7 @@ impl<'a> File<'a> { pub fn read_append_to_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { match self.inner.read_to_string(dst) { Ok(_) => {}, - Err(e) => panic_io(&e, "reading", self.path), + Err(e) => panic_file(&e, FileAction::Read, self.path), } dst } @@ -81,7 +101,7 @@ impl<'a> File<'a> { Err(e) => Err(e), }; if let Err(e) = res { - panic_io(&e, "writing", self.path); + panic_file(&e, FileAction::Write, self.path); } } } @@ -391,7 +411,7 @@ pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { match OpenOptions::new().create_new(true).write(true).open(new_name) { Ok(file) => drop(file), Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, - Err(e) => panic_io(&e, "creating", new_name), + Err(e) => panic_file(&e, FileAction::Create, new_name), } match fs::rename(old_name, new_name) { Ok(()) => true, @@ -400,37 +420,12 @@ pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { if e.kind() == io::ErrorKind::NotFound { false } else { - panic_io(&e, "renaming", old_name); + panic_file(&e, FileAction::Rename, old_name); } }, } } -#[must_use] -pub fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option { - let i = text.find(marker)?; - let (pre, post) = text.split_at(i); - Some([pre, new_text, post].into_iter().collect()) -} - -pub fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { - let mut file = OpenOptions::new() - .write(true) - .read(true) - .open(path) - .unwrap_or_else(|e| panic_io(&e, "opening", path)); - let mut buf = String::new(); - file.read_to_string(&mut buf) - .unwrap_or_else(|e| panic_io(&e, "reading", path)); - if let Some(new_contents) = f(&buf) { - file.rewind().unwrap_or_else(|e| panic_io(&e, "writing", path)); - file.write_all(new_contents.as_bytes()) - .unwrap_or_else(|e| panic_io(&e, "writing", path)); - file.set_len(new_contents.len() as u64) - .unwrap_or_else(|e| panic_io(&e, "writing", path)); - } -} - pub fn write_file(path: &Path, contents: &str) { - fs::write(path, contents).unwrap_or_else(|e| panic_io(&e, "writing", path)); + fs::write(path, contents).unwrap_or_else(|e| panic_file(&e, FileAction::Write, path)); } diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index a1909c5363ff..d37b0efd35d1 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -44,7 +44,6 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), #[clippy::version = "1.86.0"] ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), - // end deprecated lints. used by `cargo dev deprecate_lint` ]} #[rustfmt::skip] @@ -195,5 +194,4 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ ("clippy::transmute_float_to_int", "unnecessary_transmutes"), #[clippy::version = "1.88.0"] ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), - // end renamed lints. used by `cargo dev rename_lint` ]} From a9beb8b68d5bc7e66636fdab0ecf21af793e4b7f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 15 Apr 2025 00:58:31 -0400 Subject: [PATCH 143/245] clippy_dev: Refactor token parsing to avoid macros. --- clippy_dev/src/new_lint.rs | 119 ++++------- clippy_dev/src/update_lints.rs | 282 ++++++++++----------------- clippy_dev/src/utils.rs | 195 ++++++++++++++++++ clippy_lints/src/deprecated_lints.rs | 8 +- 4 files changed, 343 insertions(+), 261 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index a6e8c3ac3249..4121daa85e6d 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,4 +1,4 @@ -use crate::utils::Version; +use crate::utils::{RustSearcher, Token, Version}; use clap::ValueEnum; use indoc::{formatdoc, writedoc}; use std::fmt::{self, Write as _}; @@ -360,8 +360,7 @@ fn get_lint_declaration(version: Version, name_upper: &str, category: &str) -> S pub {name_upper}, {category}, "default lint description" - }} - "#, + }}"#, version.rust_display(), ) } @@ -446,9 +445,6 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R #[allow(clippy::too_many_lines)] fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> { - use super::update_lints::{LintDeclSearchResult, match_tokens}; - use rustc_lexer::TokenKind; - let lint_name_upper = lint.name.to_uppercase(); let mut file_contents = fs::read_to_string(path)?; @@ -459,81 +455,11 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> path.display() ); - let mut offset = 0usize; - let mut last_decl_curly_offset = None; - let mut lint_context = None; - - let mut iter = rustc_lexer::tokenize(&file_contents).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &file_contents[range.clone()], - range, - } - }); - - // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl - while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) { - let mut iter = iter - .by_ref() - .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); - - match content { - "declare_clippy_lint" => { - // matches `!{` - match_tokens!(iter, Bang OpenBrace); - if let Some(LintDeclSearchResult { range, .. }) = - iter.find(|result| result.token_kind == TokenKind::CloseBrace) - { - last_decl_curly_offset = Some(range.end); - } - }, - "impl" => { - let mut token = iter.next(); - match token { - // matches <'foo> - Some(LintDeclSearchResult { - token_kind: TokenKind::Lt, - .. - }) => { - match_tokens!(iter, Lifetime { .. } Gt); - token = iter.next(); - }, - None => break, - _ => {}, - } - - if let Some(LintDeclSearchResult { - token_kind: TokenKind::Ident, - content, - .. - }) = token - { - // Get the appropriate lint context struct - lint_context = match content { - "LateLintPass" => Some("LateContext"), - "EarlyLintPass" => Some("EarlyContext"), - _ => continue, - }; - } - }, - _ => {}, - } - } - - drop(iter); - - let last_decl_curly_offset = - last_decl_curly_offset.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display())); - let lint_context = - lint_context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display())); + let (lint_context, lint_decl_end) = parse_mod_file(path, &file_contents); // Add the lint declaration to `mod.rs` - file_contents.replace_range( - // Remove the trailing newline, which should always be present - last_decl_curly_offset..=last_decl_curly_offset, + file_contents.insert_str( + lint_decl_end, &format!( "\n\n{}", get_lint_declaration(lint.clippy_version, &lint_name_upper, lint.category) @@ -588,6 +514,41 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> Ok(lint_context) } +// Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl +fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) { + #[allow(clippy::enum_glob_use)] + use Token::*; + + let mut context = None; + let mut decl_end = None; + let mut searcher = RustSearcher::new(contents); + while let Some(name) = searcher.find_capture_token(CaptureIdent) { + match name { + "declare_clippy_lint" => { + if searcher.match_tokens(&[Bang, OpenBrace], &mut []) && searcher.find_token(CloseBrace) { + decl_end = Some(searcher.pos()); + } + }, + "impl" => { + let mut capture = ""; + if searcher.match_tokens(&[Lt, Lifetime, Gt, CaptureIdent], &mut [&mut capture]) { + match capture { + "LateLintPass" => context = Some("LateContext"), + "EarlyLintPass" => context = Some("EarlyContext"), + _ => {}, + } + } + }, + _ => {}, + } + } + + ( + context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display())), + decl_end.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display())) as usize, + ) +} + #[test] fn test_camel_case() { let s = "a_lint"; diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 28c988bc19ff..09e97491c510 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,8 +1,7 @@ -use crate::utils::{File, FileAction, FileUpdater, UpdateMode, UpdateStatus, panic_file, update_text_region_fn}; -use core::str; +use crate::utils::{ + File, FileAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_file, update_text_region_fn, +}; use itertools::Itertools; -use rustc_lexer::{LiteralKind, TokenKind, tokenize}; -use rustc_literal_escaper::{Mode, unescape_unicode}; use std::collections::{HashMap, HashSet}; use std::fmt::Write; use std::fs::OpenOptions; @@ -140,17 +139,6 @@ pub struct Lint { } impl Lint { - #[must_use] - fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range) -> Self { - Self { - name: name.to_lowercase(), - group: group.into(), - desc: remove_line_splices(desc), - module: module.into(), - declaration_range, - } - } - /// Returns the lints in a `HashMap`, grouped by the different lint groups #[must_use] fn by_lint_group(lints: impl Iterator) -> HashMap> { @@ -163,27 +151,11 @@ pub struct DeprecatedLint { pub name: String, pub reason: String, } -impl DeprecatedLint { - fn new(name: &str, reason: &str) -> Self { - Self { - name: remove_line_splices(name), - reason: remove_line_splices(reason), - } - } -} pub struct RenamedLint { pub old_name: String, pub new_name: String, } -impl RenamedLint { - fn new(old_name: &str, new_name: &str) -> Self { - Self { - old_name: remove_line_splices(old_name), - new_name: remove_line_splices(new_name), - } - } -} pub fn gen_renamed_lints_test_fn(lints: &[RenamedLint]) -> impl Fn(&Path, &str, &mut String) -> UpdateStatus { move |_, src, dst| { @@ -213,6 +185,7 @@ pub fn find_lint_decls() -> Vec { let mut contents = String::new(); for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) { parse_clippy_lint_decls( + file.path(), File::open_read_to_cleared_string(file.path(), &mut contents), &module, &mut lints, @@ -256,94 +229,34 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator { - { - $(#[allow(clippy::redundant_pattern)] let Some(LintDeclSearchResult { - token_kind: TokenKind::$token $({$($fields)*})?, - content: $($capture @)? _, - .. - }) = $iter.next() else { - continue; - };)* - #[allow(clippy::unused_unit)] - { ($($($capture,)?)*) } - } - } -} - -pub(crate) use match_tokens; - -pub(crate) struct LintDeclSearchResult<'a> { - pub token_kind: TokenKind, - pub content: &'a str, - pub range: Range, -} - /// Parse a source file looking for `declare_clippy_lint` macro invocations. -fn parse_clippy_lint_decls(contents: &str, module: &str, lints: &mut Vec) { - let mut offset = 0usize; - let mut iter = tokenize(contents).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; +fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mut Vec) { + #[allow(clippy::enum_glob_use)] + use Token::*; + #[rustfmt::skip] + static DECL_TOKENS: &[Token] = &[ + // !{ /// docs + Bang, OpenBrace, AnyDoc, + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, + // pub NAME, GROUP, "description" + Ident("pub"), CaptureIdent, Comma, CaptureIdent, Comma, CaptureLitStr, + ]; - LintDeclSearchResult { - token_kind: t.kind, - content: &contents[range.clone()], - range, - } - }); - - while let Some(LintDeclSearchResult { range, .. }) = iter.find( - |LintDeclSearchResult { - token_kind, content, .. - }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint", - ) { - let start = range.start; - let mut iter = iter - .by_ref() - .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); - // matches `!{` - match_tokens!(iter, Bang OpenBrace); - match iter.next() { - // #[clippy::version = "version"] pub - Some(LintDeclSearchResult { - token_kind: TokenKind::Pound, - .. - }) => { - match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident); - }, - // pub - Some(LintDeclSearchResult { - token_kind: TokenKind::Ident, - .. - }) => (), - _ => continue, - } - - let (name, group, desc) = match_tokens!( - iter, - // LINT_NAME - Ident(name) Comma - // group, - Ident(group) Comma - // "description" - Literal{..}(desc) - ); - - if let Some(end) = iter.find_map(|t| { - if let LintDeclSearchResult { - token_kind: TokenKind::CloseBrace, - range, - .. - } = t - { - Some(range.end) - } else { - None - } - }) { - lints.push(Lint::new(name, group, desc, module, start..end)); + let mut searcher = RustSearcher::new(contents); + while searcher.find_token(Ident("declare_clippy_lint")) { + let start = searcher.pos() as usize - "declare_clippy_lint".len(); + let (mut name, mut group, mut desc) = ("", "", ""); + if searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut group, &mut desc]) + && searcher.find_token(CloseBrace) + { + lints.push(Lint { + name: name.to_lowercase(), + group: group.into(), + desc: parse_str_single_line(path, desc), + module: module.into(), + declaration_range: start..searcher.pos() as usize, + }); } } } @@ -358,13 +271,30 @@ pub struct DeprecatedLints { } #[must_use] -#[expect(clippy::cast_possible_truncation)] pub fn read_deprecated_lints() -> DeprecatedLints { + #[allow(clippy::enum_glob_use)] + use Token::*; + #[rustfmt::skip] + static DECL_TOKENS: &[Token] = &[ + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, + // ("first", "second"), + OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma, + ]; + #[rustfmt::skip] + static DEPRECATED_TOKENS: &[Token] = &[ + // !{ DEPRECATED(DEPRECATED_VERSION) = [ + Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + #[rustfmt::skip] + static RENAMED_TOKENS: &[Token] = &[ + // !{ RENAMED(RENAMED_VERSION) = [ + Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + + let path = "clippy_lints/src/deprecated_lints.rs"; let mut res = DeprecatedLints { - file: File::open( - "clippy_lints/src/deprecated_lints.rs", - OpenOptions::new().read(true).write(true), - ), + file: File::open(path, OpenOptions::new().read(true).write(true)), contents: String::new(), deprecated: Vec::with_capacity(30), renamed: Vec::with_capacity(80), @@ -373,81 +303,77 @@ pub fn read_deprecated_lints() -> DeprecatedLints { }; res.file.read_append_to_string(&mut res.contents); + let mut searcher = RustSearcher::new(&res.contents); - let (_, contents) = res.contents.split_once("\ndeclare_with_version! { DEPRECATED").unwrap(); - let (deprecated_src, contents) = contents.split_once("\n]}").unwrap(); - res.deprecated_end = (res.contents.len() - contents.len() - 2) as u32; + // First instance is the macro definition. + assert!( + searcher.find_token(Ident("declare_with_version")), + "error reading deprecated lints" + ); - let (_, contents) = contents.split_once("\ndeclare_with_version! { RENAMED").unwrap(); - let (renamed_src, contents) = contents.split_once("\n]}").unwrap(); - res.renamed_end = (res.contents.len() - contents.len() - 2) as u32; - - for line in deprecated_src.lines() { - let mut offset = 0usize; - let mut iter = tokenize(line).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &line[range.clone()], - range, - } - }); - - let (name, reason) = match_tokens!( - iter, - // ("old_name", - Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(name) Comma - // "new_name"), - Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma - ); - res.deprecated.push(DeprecatedLint::new(name, reason)); + if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(DEPRECATED_TOKENS, &mut []) { + let mut name = ""; + let mut reason = ""; + while searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut reason]) { + res.deprecated.push(DeprecatedLint { + name: parse_str_single_line(path.as_ref(), name), + reason: parse_str_single_line(path.as_ref(), reason), + }); + } + } else { + panic!("error reading deprecated lints"); } - for line in renamed_src.lines() { - let mut offset = 0usize; - let mut iter = tokenize(line).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; + // position of the closing `]}` of `declare_with_version` + res.deprecated_end = searcher.pos(); - LintDeclSearchResult { - token_kind: t.kind, - content: &line[range.clone()], - range, - } - }); - - let (old_name, new_name) = match_tokens!( - iter, - // ("old_name", - Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma - // "new_name"), - Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma - ); - res.renamed.push(RenamedLint::new(old_name, new_name)); + if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(RENAMED_TOKENS, &mut []) { + let mut old_name = ""; + let mut new_name = ""; + while searcher.match_tokens(DECL_TOKENS, &mut [&mut old_name, &mut new_name]) { + res.renamed.push(RenamedLint { + old_name: parse_str_single_line(path.as_ref(), old_name), + new_name: parse_str_single_line(path.as_ref(), new_name), + }); + } + } else { + panic!("error reading renamed lints"); } + // position of the closing `]}` of `declare_with_version` + res.renamed_end = searcher.pos(); res } /// Removes the line splices and surrounding quotes from a string literal -fn remove_line_splices(s: &str) -> String { +fn parse_str_lit(s: &str) -> String { + let (s, mode) = if let Some(s) = s.strip_prefix("r") { + (s.trim_matches('#'), rustc_literal_escaper::Mode::RawStr) + } else { + (s, rustc_literal_escaper::Mode::Str) + }; let s = s - .strip_prefix('r') - .unwrap_or(s) - .trim_matches('#') .strip_prefix('"') .and_then(|s| s.strip_suffix('"')) .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); let mut res = String::with_capacity(s.len()); - unescape_unicode(s, Mode::Str, &mut |range, ch| { - if ch.is_ok() { - res.push_str(&s[range]); + rustc_literal_escaper::unescape_unicode(s, mode, &mut |_, ch| { + if let Ok(ch) = ch { + res.push(ch); } }); res } +fn parse_str_single_line(path: &Path, s: &str) -> String { + let value = parse_str_lit(s); + assert!( + !value.contains('\n'), + "error parsing `{}`: `{s}` should be a single line string", + path.display(), + ); + value +} + #[cfg(test)] mod tests { use super::*; @@ -471,7 +397,7 @@ mod tests { } "#; let mut result = Vec::new(); - parse_clippy_lint_decls(CONTENTS, "module_name", &mut result); + parse_clippy_lint_decls("".as_ref(), CONTENTS, "module_name", &mut result); for r in &mut result { r.declaration_range = Range::default(); } diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 87645aff6742..1fb338d39cc9 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -1,6 +1,8 @@ use aho_corasick::{AhoCorasick, AhoCorasickBuilder}; use core::fmt::{self, Display}; +use core::slice; use core::str::FromStr; +use rustc_lexer as lexer; use std::env; use std::fs::{self, OpenOptions}; use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; @@ -406,6 +408,199 @@ impl<'a> StringReplacer<'a> { } } +#[derive(Clone, Copy)] +pub enum Token { + /// Matches any number of doc comments. + AnyDoc, + Ident(&'static str), + CaptureIdent, + LitStr, + CaptureLitStr, + Bang, + CloseBrace, + CloseBracket, + CloseParen, + /// This will consume the first colon even if the second doesn't exist. + DoubleColon, + Comma, + Eq, + Lifetime, + Lt, + Gt, + OpenBrace, + OpenBracket, + OpenParen, + Pound, +} + +pub struct RustSearcher<'txt> { + text: &'txt str, + cursor: lexer::Cursor<'txt>, + pos: u32, + + // Either the next token or a zero-sized whitespace sentinel. + next_token: lexer::Token, +} +impl<'txt> RustSearcher<'txt> { + #[must_use] + pub fn new(text: &'txt str) -> Self { + Self { + text, + cursor: lexer::Cursor::new(text), + pos: 0, + + // Sentinel value indicating there is no read token. + next_token: lexer::Token { + len: 0, + kind: lexer::TokenKind::Whitespace, + }, + } + } + + #[must_use] + pub fn peek_text(&self) -> &'txt str { + &self.text[self.pos as usize..(self.pos + self.next_token.len) as usize] + } + + #[must_use] + pub fn peek(&self) -> lexer::TokenKind { + self.next_token.kind + } + + #[must_use] + pub fn pos(&self) -> u32 { + self.pos + } + + #[must_use] + pub fn at_end(&self) -> bool { + self.next_token.kind == lexer::TokenKind::Eof + } + + pub fn step(&mut self) { + // `next_len` is zero for the sentinel value and the eof marker. + self.pos += self.next_token.len; + self.next_token = self.cursor.advance_token(); + } + + /// Consumes the next token if it matches the requested value and captures the value if + /// requested. Returns true if a token was matched. + fn read_token(&mut self, token: Token, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool { + loop { + match (token, self.next_token.kind) { + // Has to be the first match arm so the empty sentinel token will be handled. + // This will also skip all whitespace/comments preceding any tokens. + ( + _, + lexer::TokenKind::Whitespace + | lexer::TokenKind::LineComment { doc_style: None } + | lexer::TokenKind::BlockComment { + doc_style: None, + terminated: true, + }, + ) => { + self.step(); + if self.at_end() { + // `AnyDoc` always matches. + return matches!(token, Token::AnyDoc); + } + }, + ( + Token::AnyDoc, + lexer::TokenKind::BlockComment { terminated: true, .. } | lexer::TokenKind::LineComment { .. }, + ) => { + self.step(); + if self.at_end() { + // `AnyDoc` always matches. + return true; + } + }, + (Token::AnyDoc, _) => return true, + (Token::Bang, lexer::TokenKind::Bang) + | (Token::CloseBrace, lexer::TokenKind::CloseBrace) + | (Token::CloseBracket, lexer::TokenKind::CloseBracket) + | (Token::CloseParen, lexer::TokenKind::CloseParen) + | (Token::Comma, lexer::TokenKind::Comma) + | (Token::Eq, lexer::TokenKind::Eq) + | (Token::Lifetime, lexer::TokenKind::Lifetime { .. }) + | (Token::Lt, lexer::TokenKind::Lt) + | (Token::Gt, lexer::TokenKind::Gt) + | (Token::OpenBrace, lexer::TokenKind::OpenBrace) + | (Token::OpenBracket, lexer::TokenKind::OpenBracket) + | (Token::OpenParen, lexer::TokenKind::OpenParen) + | (Token::Pound, lexer::TokenKind::Pound) + | ( + Token::LitStr, + lexer::TokenKind::Literal { + kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. }, + .. + }, + ) => { + self.step(); + return true; + }, + (Token::Ident(x), lexer::TokenKind::Ident) if x == self.peek_text() => { + self.step(); + return true; + }, + (Token::DoubleColon, lexer::TokenKind::Colon) => { + self.step(); + if !self.at_end() && matches!(self.next_token.kind, lexer::TokenKind::Colon) { + self.step(); + return true; + } + return false; + }, + ( + Token::CaptureLitStr, + lexer::TokenKind::Literal { + kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. }, + .. + }, + ) + | (Token::CaptureIdent, lexer::TokenKind::Ident) => { + **captures.next().unwrap() = self.peek_text(); + self.step(); + return true; + }, + _ => return false, + } + } + } + + #[must_use] + pub fn find_token(&mut self, token: Token) -> bool { + let mut capture = [].iter_mut(); + while !self.read_token(token, &mut capture) { + self.step(); + if self.at_end() { + return false; + } + } + true + } + + #[must_use] + pub fn find_capture_token(&mut self, token: Token) -> Option<&'txt str> { + let mut res = ""; + let mut capture = &mut res; + let mut capture = slice::from_mut(&mut capture).iter_mut(); + while !self.read_token(token, &mut capture) { + self.step(); + if self.at_end() { + return None; + } + } + Some(res) + } + + #[must_use] + pub fn match_tokens(&mut self, tokens: &[Token], captures: &mut [&mut &'txt str]) -> bool { + let mut captures = captures.iter_mut(); + tokens.iter().all(|&t| self.read_token(t, &mut captures)) + } +} + #[expect(clippy::must_use_candidate)] pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { match OpenOptions::new().create_new(true).write(true).open(new_name) { diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index d37b0efd35d1..946515386690 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -2,18 +2,18 @@ // Prefer to use those when possible. macro_rules! declare_with_version { - ($name:ident($name_version:ident): &[$ty:ty] = &[$( + ($name:ident($name_version:ident) = [$( #[clippy::version = $version:literal] $e:expr, )*]) => { - pub static $name: &[$ty] = &[$($e),*]; + pub static $name: &[(&str, &str)] = &[$($e),*]; #[allow(unused)] pub static $name_version: &[&str] = &[$($version),*]; }; } #[rustfmt::skip] -declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ +declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ #[clippy::version = "pre 1.29.0"] ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), #[clippy::version = "pre 1.29.0"] @@ -47,7 +47,7 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ ]} #[rustfmt::skip] -declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ +declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), #[clippy::version = ""] From 063612196416cb437650cd4d706996046157997c Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 15 Apr 2025 13:25:46 -0400 Subject: [PATCH 144/245] clippy_dev: Remove print option from `update_lints` --- clippy_dev/src/main.rs | 13 +--- clippy_dev/src/update_lints.rs | 105 ++++++--------------------------- 2 files changed, 20 insertions(+), 98 deletions(-) diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 73248d72d04a..5dce0be742b2 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -27,13 +27,7 @@ fn main() { allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), DevCommand::Fmt { check, verbose } => fmt::run(check, verbose), - DevCommand::UpdateLints { print_only, check } => { - if print_only { - update_lints::print_lints(); - } else { - update_lints::update(utils::UpdateMode::from_check(check)); - } - }, + DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)), DevCommand::NewLint { pass, name, @@ -145,11 +139,6 @@ enum DevCommand { /// * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod` {n} /// * all lints are registered in the lint store UpdateLints { - #[arg(long)] - /// Print a table of lints to STDOUT - /// - /// This does not include deprecated and internal lints. (Does not modify any files) - print_only: bool, #[arg(long)] /// Checks that `cargo dev update_lints` has been run. Used on CI. check: bool, diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 09e97491c510..0c861b729356 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -2,7 +2,7 @@ use crate::utils::{ File, FileAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_file, update_text_region_fn, }; use itertools::Itertools; -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use std::fmt::Write; use std::fs::OpenOptions; use std::ops::Range; @@ -106,24 +106,6 @@ pub fn generate_lint_files( ); } -pub fn print_lints() { - let lints = find_lint_decls(); - let lint_count = lints.len(); - let grouped_by_lint_group = Lint::by_lint_group(lints.into_iter()); - - for (lint_group, mut lints) in grouped_by_lint_group { - println!("\n## {lint_group}"); - - lints.sort_by_key(|l| l.name.clone()); - - for lint in lints { - println!("* [{}]({DOCS_LINK}#{}) ({})", lint.name, lint.name, lint.desc); - } - } - - println!("there are {lint_count} lints"); -} - fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } @@ -133,19 +115,10 @@ fn round_to_fifty(count: usize) -> usize { pub struct Lint { pub name: String, pub group: String, - pub desc: String, pub module: String, pub declaration_range: Range, } -impl Lint { - /// Returns the lints in a `HashMap`, grouped by the different lint groups - #[must_use] - fn by_lint_group(lints: impl Iterator) -> HashMap> { - lints.map(|lint| (lint.group.to_string(), lint)).into_group_map() - } -} - #[derive(Clone, PartialEq, Eq, Debug)] pub struct DeprecatedLint { pub name: String, @@ -185,7 +158,6 @@ pub fn find_lint_decls() -> Vec { let mut contents = String::new(); for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) { parse_clippy_lint_decls( - file.path(), File::open_read_to_cleared_string(file.path(), &mut contents), &module, &mut lints, @@ -230,7 +202,7 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator) { +fn parse_clippy_lint_decls(contents: &str, module: &str, lints: &mut Vec) { #[allow(clippy::enum_glob_use)] use Token::*; #[rustfmt::skip] @@ -239,21 +211,18 @@ fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mu Bang, OpenBrace, AnyDoc, // #[clippy::version = "version"] Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, - // pub NAME, GROUP, "description" - Ident("pub"), CaptureIdent, Comma, CaptureIdent, Comma, CaptureLitStr, + // pub NAME, GROUP, + Ident("pub"), CaptureIdent, Comma, CaptureIdent, Comma, ]; let mut searcher = RustSearcher::new(contents); while searcher.find_token(Ident("declare_clippy_lint")) { let start = searcher.pos() as usize - "declare_clippy_lint".len(); - let (mut name, mut group, mut desc) = ("", "", ""); - if searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut group, &mut desc]) - && searcher.find_token(CloseBrace) - { + let (mut name, mut group) = ("", ""); + if searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut group]) && searcher.find_token(CloseBrace) { lints.push(Lint { name: name.to_lowercase(), group: group.into(), - desc: parse_str_single_line(path, desc), module: module.into(), declaration_range: start..searcher.pos() as usize, }); @@ -397,61 +366,25 @@ mod tests { } "#; let mut result = Vec::new(); - parse_clippy_lint_decls("".as_ref(), CONTENTS, "module_name", &mut result); + parse_clippy_lint_decls(CONTENTS, "module_name", &mut result); for r in &mut result { r.declaration_range = Range::default(); } let expected = vec![ - Lint::new( - "ptr_arg", - "style", - "\"really long text\"", - "module_name", - Range::default(), - ), - Lint::new( - "doc_markdown", - "pedantic", - "\"single line\"", - "module_name", - Range::default(), - ), + Lint { + name: "ptr_arg".into(), + group: "style".into(), + module: "module_name".into(), + declaration_range: Range::default(), + }, + Lint { + name: "doc_markdown".into(), + group: "pedantic".into(), + module: "module_name".into(), + declaration_range: Range::default(), + }, ]; assert_eq!(expected, result); } - - #[test] - fn test_by_lint_group() { - let lints = vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - ), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ]; - let mut expected: HashMap> = HashMap::new(); - expected.insert( - "group1".to_string(), - vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ], - ); - expected.insert( - "group2".to_string(), - vec![Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - )], - ); - assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); - } } From 40e1b0ed00878f3229f053398a8d7a49d8b68bcc Mon Sep 17 00:00:00 2001 From: asdfish Date: Sun, 16 Mar 2025 18:13:31 -0400 Subject: [PATCH 145/245] add lint `cloned_ref_to_slice_refs` remove merge removed false positive and improved tests clarify known problems for `cloned_ref_to_slice_refs` --- CHANGELOG.md | 1 + clippy_lints/src/cloned_ref_to_slice_refs.rs | 100 +++++++++++++++++++ clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_utils/src/msrvs.rs | 4 +- tests/ui/cloned_ref_to_slice_refs.fixed | 64 ++++++++++++ tests/ui/cloned_ref_to_slice_refs.rs | 64 ++++++++++++ tests/ui/cloned_ref_to_slice_refs.stderr | 23 +++++ 8 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/cloned_ref_to_slice_refs.rs create mode 100644 tests/ui/cloned_ref_to_slice_refs.fixed create mode 100644 tests/ui/cloned_ref_to_slice_refs.rs create mode 100644 tests/ui/cloned_ref_to_slice_refs.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b62c9a59aa5..956e31489f12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5583,6 +5583,7 @@ Released 2018-09-13 [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy [`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr [`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied +[`cloned_ref_to_slice_refs`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_ref_to_slice_refs [`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned diff --git a/clippy_lints/src/cloned_ref_to_slice_refs.rs b/clippy_lints/src/cloned_ref_to_slice_refs.rs new file mode 100644 index 000000000000..6b239a1541b0 --- /dev/null +++ b/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -0,0 +1,100 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::{is_in_const_context, is_mutable, is_trait_method}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for slice references with cloned references such as `&[f.clone()]`. + /// + /// ### Why is this bad + /// + /// A reference does not need to be owned in order to used as a slice. + /// + /// ### Known problems + /// + /// This lint does not know whether or not a clone implementation has side effects. + /// + /// ### Example + /// + /// ```ignore + /// let data = 10; + /// let data_ref = &data; + /// take_slice(&[data_ref.clone()]); + /// ``` + /// Use instead: + /// ```ignore + /// use std::slice; + /// let data = 10; + /// let data_ref = &data; + /// take_slice(slice::from_ref(data_ref)); + /// ``` + #[clippy::version = "1.87.0"] + pub CLONED_REF_TO_SLICE_REFS, + perf, + "cloning a reference for slice references" +} + +pub struct ClonedRefToSliceRefs<'a> { + msrv: &'a Msrv, +} +impl<'a> ClonedRefToSliceRefs<'a> { + pub fn new(conf: &'a Conf) -> Self { + Self { msrv: &conf.msrv } + } +} + +impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]); + +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 + // mutable references would have a different meaning + && mutability.is_not() + + // check for single item arrays + && let ExprKind::Array([item]) = &arr.kind + + // check for clones + && let ExprKind::MethodCall(_, val, _, _) = item.kind + && is_trait_method(cx, item, sym::Clone) + + // check for immutability or purity + && (!is_mutable(cx, val) || is_const_evaluatable(cx, val)) + + // get appropriate crate for `slice::from_ref` + && let Some(builtin_crate) = clippy_utils::std_or_core(cx) + { + let mut sugg = Sugg::hir(cx, val, "_"); + if !cx.typeck_results().expr_ty(val).is_ref() { + sugg = sugg.addr(); + } + + span_lint_and_sugg( + cx, + CLONED_REF_TO_SLICE_REFS, + expr.span, + format!("this call to `clone` can be replaced with `{builtin_crate}::slice::from_ref`"), + "try", + format!("{builtin_crate}::slice::from_ref({sugg})"), + Applicability::MaybeIncorrect, + ); + } + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2cccd6ba2702..fbce468141bf 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -75,6 +75,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::ZERO_PTR_INFO, crate::cfg_not_test::CFG_NOT_TEST_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO, + crate::cloned_ref_to_slice_refs::CLONED_REF_TO_SLICE_REFS_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, crate::collapsible_if::COLLAPSIBLE_IF_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5fa8f6f4bf3d..37669a90eb42 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -96,6 +96,7 @@ mod cargo; mod casts; mod cfg_not_test; mod checked_conversions; +mod cloned_ref_to_slice_refs; mod cognitive_complexity; mod collapsible_if; mod collection_is_never_read; @@ -943,5 +944,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))); store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap)); store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)); + store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index c2aca5ee5390..e6f46f0d1de0 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -38,7 +38,7 @@ msrv_aliases! { 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } - 1,63,0 { CLONE_INTO } + 1,63,0 { CLONE_INTO, CONST_SLICE_FROM_REF } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN } 1,60,0 { ABS_DIFF } 1,59,0 { THREAD_LOCAL_CONST_INIT } @@ -68,7 +68,7 @@ msrv_aliases! { 1,31,0 { OPTION_REPLACE } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } - 1,28,0 { FROM_BOOL, REPEAT_WITH } + 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } 1,27,0 { ITERATOR_TRY_FOLD } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } 1,24,0 { IS_ASCII_DIGIT } diff --git a/tests/ui/cloned_ref_to_slice_refs.fixed b/tests/ui/cloned_ref_to_slice_refs.fixed new file mode 100644 index 000000000000..818c6e23259e --- /dev/null +++ b/tests/ui/cloned_ref_to_slice_refs.fixed @@ -0,0 +1,64 @@ +#![warn(clippy::cloned_ref_to_slice_refs)] + +#[derive(Clone)] +struct Data; + +fn main() { + { + let data = Data; + let data_ref = &data; + let _ = std::slice::from_ref(data_ref); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + let _ = std::slice::from_ref(&Data); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + #[derive(Clone)] + struct Point(i32, i32); + + let _ = std::slice::from_ref(&Point(0, 0)); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + // the string was cloned with the intention to not mutate + { + struct BetterString(String); + + let mut message = String::from("good"); + let sender = BetterString(message.clone()); + + message.push_str("bye!"); + + println!("{} {}", message, sender.0) + } + + // the string was cloned with the intention to not mutate + { + let mut x = String::from("Hello"); + let r = &[x.clone()]; + x.push('!'); + println!("r = `{}', x = `{x}'", r[0]); + } + + // mutable borrows may have the intention to clone + { + let data = Data; + let data_ref = &data; + let _ = &mut [data_ref.clone()]; + } + + // `T::clone` is used to denote a clone with side effects + { + use std::sync::Arc; + let data = Arc::new(Data); + let _ = &[Arc::clone(&data)]; + } + + // slices with multiple members can only be made from a singular reference + { + let data_1 = Data; + let data_2 = Data; + let _ = &[data_1.clone(), data_2.clone()]; + } +} diff --git a/tests/ui/cloned_ref_to_slice_refs.rs b/tests/ui/cloned_ref_to_slice_refs.rs new file mode 100644 index 000000000000..9517dbfd1569 --- /dev/null +++ b/tests/ui/cloned_ref_to_slice_refs.rs @@ -0,0 +1,64 @@ +#![warn(clippy::cloned_ref_to_slice_refs)] + +#[derive(Clone)] +struct Data; + +fn main() { + { + let data = Data; + let data_ref = &data; + let _ = &[data_ref.clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + let _ = &[Data.clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + #[derive(Clone)] + struct Point(i32, i32); + + let _ = &[Point(0, 0).clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + // the string was cloned with the intention to not mutate + { + struct BetterString(String); + + let mut message = String::from("good"); + let sender = BetterString(message.clone()); + + message.push_str("bye!"); + + println!("{} {}", message, sender.0) + } + + // the string was cloned with the intention to not mutate + { + let mut x = String::from("Hello"); + let r = &[x.clone()]; + x.push('!'); + println!("r = `{}', x = `{x}'", r[0]); + } + + // mutable borrows may have the intention to clone + { + let data = Data; + let data_ref = &data; + let _ = &mut [data_ref.clone()]; + } + + // `T::clone` is used to denote a clone with side effects + { + use std::sync::Arc; + let data = Arc::new(Data); + let _ = &[Arc::clone(&data)]; + } + + // slices with multiple members can only be made from a singular reference + { + let data_1 = Data; + let data_2 = Data; + let _ = &[data_1.clone(), data_2.clone()]; + } +} diff --git a/tests/ui/cloned_ref_to_slice_refs.stderr b/tests/ui/cloned_ref_to_slice_refs.stderr new file mode 100644 index 000000000000..6a31d8782395 --- /dev/null +++ b/tests/ui/cloned_ref_to_slice_refs.stderr @@ -0,0 +1,23 @@ +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:10:17 + | +LL | let _ = &[data_ref.clone()]; + | ^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(data_ref)` + | + = note: `-D clippy::cloned-ref-to-slice-refs` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cloned_ref_to_slice_refs)]` + +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:14:17 + | +LL | let _ = &[Data.clone()]; + | ^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&Data)` + +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:21:17 + | +LL | let _ = &[Point(0, 0).clone()]; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&Point(0, 0))` + +error: aborting due to 3 previous errors + From ab1c49a7facad5083c20407a2b1fd0b2104e174e Mon Sep 17 00:00:00 2001 From: Julian Knodt Date: Tue, 13 May 2025 11:17:09 +0900 Subject: [PATCH 146/245] Add `#[must_use]` to Array::map The output of Array::map is intended to be an array of the same size, and does not modify the original in place nor is it intended for side-effects. Thus, under normal circumstances it should be consumed. See [discussion](https://internals.rust-lang.org/t/array-map-annotate-with-must-use/22813/26). Attaching to tracking issue #75243 --- library/core/src/array/mod.rs | 1 + library/coretests/tests/array.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index efa7bed7c8e1..4476e3f79238 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -531,6 +531,7 @@ impl [T; N] { /// let y = x.map(|v| v.len()); /// assert_eq!(y, [6, 9, 3, 3]); /// ``` + #[must_use] #[stable(feature = "array_map", since = "1.55.0")] pub fn map(self, f: F) -> [U; N] where diff --git a/library/coretests/tests/array.rs b/library/coretests/tests/array.rs index b6d18f1ec382..30ccbbc32031 100644 --- a/library/coretests/tests/array.rs +++ b/library/coretests/tests/array.rs @@ -325,7 +325,7 @@ fn array_map_drop_safety() { let success = std::panic::catch_unwind(|| { let items = [0; 10]; let mut nth = 0; - items.map(|_| { + let _ = items.map(|_| { assert!(nth < num_to_create); nth += 1; DropCounter From a5e1dba0cda0db75106ea6c5022e874c7ee116b5 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 13 May 2025 07:33:51 +0000 Subject: [PATCH 147/245] trait_sel: deep reject `match_normalize_trait_ref` Spotted during an in-person review of unrelated changes, `match_normalize_trait_ref` could be using `DeepRejectCtxt` to exit early as an optimisation for prejection candidates, like is done in param candidates. --- compiler/rustc_middle/src/ty/mod.rs | 1 + .../rustc_trait_selection/src/traits/select/mod.rs | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index dda0faa3afed..83b318435f99 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -52,6 +52,7 @@ pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol, kw, sym}; pub use rustc_type_ir::data_structures::{DelayedMap, DelayedSet}; +pub use rustc_type_ir::fast_reject::DeepRejectCtxt; #[allow( hidden_glob_reexports, rustc::usage_of_type_ir_inherent, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4ce37db42800..9513d2107edb 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -27,8 +27,8 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths}; use rustc_middle::ty::{ - self, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, - TypingMode, Upcast, elaborate, + self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, + TypeVisitableExt, TypingMode, Upcast, elaborate, }; use rustc_span::{Symbol, sym}; use tracing::{debug, instrument, trace}; @@ -1669,6 +1669,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(()); } + let drcx = DeepRejectCtxt::relate_rigid_rigid(self.infcx.tcx); + let obligation_args = obligation.predicate.skip_binder().trait_ref.args; + if !drcx.args_may_unify(obligation_args, trait_bound.skip_binder().args) { + return Err(()); + } + let trait_bound = self.infcx.instantiate_binder_with_fresh_vars( obligation.cause.span, HigherRankedType, From bd587005fb5aad184e0ee133efea65615c6fa6bc Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 9 May 2025 09:30:01 +0000 Subject: [PATCH 148/245] Some require_lang_item -> is_lang_item replacements --- compiler/rustc_hir_analysis/src/coherence/builtin.rs | 6 ++---- compiler/rustc_hir_typeck/src/closure.rs | 5 ++--- compiler/rustc_middle/src/ty/sty.rs | 3 +-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 52656fc2d900..b92d1d7104f0 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -214,11 +214,9 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() let span = tcx.def_span(impl_did); let trait_name = "DispatchFromDyn"; - let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span)); - let source = trait_ref.self_ty(); let target = { - assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait); + assert!(tcx.is_lang_item(trait_ref.def_id, LangItem::DispatchFromDyn)); trait_ref.args.type_at(1) }; @@ -339,7 +337,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() tcx, cause.clone(), param_env, - ty::TraitRef::new(tcx, dispatch_from_dyn_trait, [ty_a, ty_b]), + ty::TraitRef::new(tcx, trait_ref.def_id, [ty_a, ty_b]), )); let errors = ocx.select_all_or_error(); if !errors.is_empty() { diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 8fd59999fce5..b1cb3ef4d796 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -1080,15 +1080,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check that this is a projection from the `Future` trait. let trait_def_id = predicate.projection_term.trait_def_id(self.tcx); - let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span)); - if trait_def_id != future_trait { + if !self.tcx.is_lang_item(trait_def_id, LangItem::Future) { debug!("deduce_future_output_from_projection: not a future"); return None; } // The `Future` trait has only one associated item, `Output`, // so check that this is what we see. - let output_assoc_item = self.tcx.associated_item_def_ids(future_trait)[0]; + let output_assoc_item = self.tcx.associated_item_def_ids(trait_def_id)[0]; if output_assoc_item != predicate.projection_term.def_id { span_bug!( cause_span, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c31ce1bc6307..ab1f3d6099fc 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1260,8 +1260,7 @@ impl<'tcx> Ty<'tcx> { return true; }; alloc.expect_ty().ty_adt_def().is_some_and(|alloc_adt| { - let global_alloc = tcx.require_lang_item(LangItem::GlobalAlloc, None); - alloc_adt.did() == global_alloc + tcx.is_lang_item(alloc_adt.did(), LangItem::GlobalAlloc) }) } _ => false, From c86e33b77108ae2842b93f4d158280bcd7346147 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 9 May 2025 15:20:23 +0000 Subject: [PATCH 149/245] Run rustc_attrs dumps after typeck --- compiler/rustc_hir_analysis/src/lib.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index d010f1fc8516..fcbd1edf17fe 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -195,17 +195,6 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let _: R = tcx.ensure_ok().crate_inherent_impls_overlap_check(()); }); - if tcx.features().rustc_attrs() { - tcx.sess.time("dumping_rustc_attr_data", || { - outlives::dump::inferred_outlives(tcx); - variance::dump::variances(tcx); - collect::dump::opaque_hidden_types(tcx); - collect::dump::predicates_and_item_bounds(tcx); - collect::dump::def_parents(tcx); - collect::dump::vtables(tcx); - }); - } - // Make sure we evaluate all static and (non-associated) const items, even if unused. // If any of these fail to evaluate, we do not want this crate to pass compilation. tcx.par_hir_body_owners(|item_def_id| { @@ -229,6 +218,17 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } }); + if tcx.features().rustc_attrs() { + tcx.sess.time("dumping_rustc_attr_data", || { + outlives::dump::inferred_outlives(tcx); + variance::dump::variances(tcx); + collect::dump::opaque_hidden_types(tcx); + collect::dump::predicates_and_item_bounds(tcx); + collect::dump::def_parents(tcx); + collect::dump::vtables(tcx); + }); + } + tcx.ensure_ok().check_unused_traits(()); } From 34976ac9fe6bd46ca02631512a27cb8995b2c1ac Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 13 May 2025 08:49:32 +0000 Subject: [PATCH 150/245] Invoke a query only when it doesn't return immediately anyway --- compiler/rustc_interface/src/passes.rs | 5 ++--- compiler/rustc_mir_build/src/check_unsafety.rs | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 7ed32d559c3a..b554cc715fee 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -996,10 +996,9 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { sess.time("MIR_borrow_checking", || { tcx.par_hir_body_owners(|def_id| { - // Run unsafety check because it's responsible for stealing and - // deallocating THIR. - tcx.ensure_ok().check_unsafety(def_id); if !tcx.is_typeck_child(def_id.to_def_id()) { + // Child unsafety and borrowck happens together with the parent + tcx.ensure_ok().check_unsafety(def_id); tcx.ensure_ok().mir_borrowck(def_id) } }); diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index adfce99a9b53..9d0681b19b9d 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -1148,8 +1148,9 @@ impl UnsafeOpKind { pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { // Closures and inline consts are handled by their owner, if it has a body + assert!(!tcx.is_typeck_child(def.to_def_id())); // Also, don't safety check custom MIR - if tcx.is_typeck_child(def.to_def_id()) || tcx.has_attr(def, sym::custom_mir) { + if tcx.has_attr(def, sym::custom_mir) { return; } From a508011b1f0ae84930e2bcd0f6261f7e8843db6d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 12 May 2025 21:04:32 +0000 Subject: [PATCH 151/245] Expect deep norm to fail if query norm failed --- .../src/type_check/liveness/trace.rs | 8 +------ .../src/traits/query/dropck_outlives.rs | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 7718644b9a9a..512288a0f7d8 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -621,13 +621,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { &ocx, op, span, ) { Ok(_) => ocx.select_all_or_error(), - Err(e) => { - if e.is_empty() { - ocx.select_all_or_error() - } else { - e - } - } + Err(e) => e, }; // Could have no errors if a type lowering error, say, caused the query diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index c82d5ca59efc..38cfdcdc22d3 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -204,11 +204,23 @@ where return Err(errors); } - ocx.deeply_normalize(&cause, param_env, ty)?; - - let errors = ocx.select_where_possible(); - debug!("normalize errors: {ty} ~> {errors:#?}"); - return Err(errors); + // When query normalization fails, we don't get back an interesting + // reason that we could use to report an error in borrowck. In order to turn + // this into a reportable error, we deeply normalize again. We don't expect + // this to succeed, so delay a bug if it does. + match ocx.deeply_normalize(&cause, param_env, ty) { + Ok(_) => { + tcx.dcx().span_delayed_bug( + span, + format!( + "query normalize succeeded of {ty}, \ + but deep normalize failed", + ), + ); + ty + } + Err(errors) => return Err(errors), + } }; match ty.kind() { From 9f4ecea24269f6a6c7cd54b4648a1ccd23a58d90 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 6 Mar 2025 06:05:03 -0500 Subject: [PATCH 152/245] Add internal lint `derive_deserialize_allowing_unknown` --- clippy_config/src/lib.rs | 1 + clippy_config/src/types.rs | 3 +- .../derive_deserialize_allowing_unknown.rs | 164 ++++++++++++++++++ clippy_lints_internal/src/lib.rs | 3 + .../derive_deserialize_allowing_unknown.rs | 60 +++++++ ...derive_deserialize_allowing_unknown.stderr | 23 +++ .../clippy.toml | 4 + .../toml_unknown_config_struct_field.rs | 5 + .../toml_unknown_config_struct_field.stderr | 8 + 9 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs create mode 100644 tests/ui-internal/derive_deserialize_allowing_unknown.rs create mode 100644 tests/ui-internal/derive_deserialize_allowing_unknown.stderr create mode 100644 tests/ui-toml/toml_unknown_config_struct_field/clippy.toml create mode 100644 tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs create mode 100644 tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index 33608591fc74..67904b4fcdc8 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -12,6 +12,7 @@ rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic )] +#![deny(clippy::derive_deserialize_allowing_unknown)] extern crate rustc_data_structures; extern crate rustc_errors; diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index 2cb5493f1a98..f64eefa0c232 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -12,6 +12,7 @@ use std::collections::HashMap; use std::fmt; #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] pub struct Rename { pub path: String, pub rename: String, @@ -59,7 +60,7 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath, + /// replacement: Option, + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// #[derive(serde::Deserialize)] + /// #[serde(deny_unknown_fields)] + /// pub struct DisallowedPath { + /// path: String, + /// reason: Option, + /// replacement: Option, + /// } + /// ``` + pub clippy::DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, + Allow, + "`#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]`", + report_in_external_macro: true +} + +declare_lint_pass!(DeriveDeserializeAllowingUnknown => [DERIVE_DESERIALIZE_ALLOWING_UNKNOWN]); + +impl<'tcx> LateLintPass<'tcx> for DeriveDeserializeAllowingUnknown { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + // Is this an `impl` (of a certain form)? + let ItemKind::Impl(Impl { + of_trait: + Some(TraitRef { + path: + Path { + res: Res::Def(_, trait_def_id), + .. + }, + .. + }), + self_ty: + Ty { + kind: + TyKind::Path(QPath::Resolved( + None, + Path { + res: Res::Def(_, self_ty_def_id), + .. + }, + )), + .. + }, + .. + }) = item.kind + else { + return; + }; + + // Is it an `impl` of the trait `serde::Deserialize`? + if !paths::SERDE_DESERIALIZE.get(cx).contains(trait_def_id) { + return; + } + + // Is it derived? + if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) { + return; + } + + // Is `self_ty` local? + let Some(local_def_id) = self_ty_def_id.as_local() else { + return; + }; + + // Does `self_ty` have a variant with named fields? + if !has_variant_with_named_fields(cx.tcx, local_def_id) { + return; + } + + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + + // Does `self_ty` have `#[serde(deny_unknown_fields)]`? + if let Some(tokens) = find_serde_attr_item(cx.tcx, hir_id) + && tokens.iter().any(is_deny_unknown_fields_token) + { + return; + } + + span_lint( + cx, + DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, + item.span, + "`#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]`", + ); + } +} + +// Determines whether `def_id` corresponds to an ADT with at least one variant with named fields. A +// variant has named fields if its `ctor` field is `None`. +fn has_variant_with_named_fields(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + let ty = tcx.type_of(def_id).skip_binder(); + + let rustc_middle::ty::Adt(adt_def, _) = ty.kind() else { + return false; + }; + + adt_def.variants().iter().any(|variant_def| variant_def.ctor.is_none()) +} + +fn find_serde_attr_item(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&TokenStream> { + tcx.hir_attrs(hir_id).iter().find_map(|attribute| { + if let Attribute::Unparsed(attr_item) = attribute + && let AttrItem { + path: AttrPath { segments, .. }, + args: AttrArgs::Delimited(DelimArgs { tokens, .. }), + style: AttrStyle::Outer, + .. + } = &**attr_item + && segments.len() == 1 + && segments[0].as_str() == "serde" + { + Some(tokens) + } else { + None + } + }) +} + +fn is_deny_unknown_fields_token(tt: &TokenTree) -> bool { + if let TokenTree::Token(token, _) = tt + && token + .ident() + .is_some_and(|(token, _)| token.as_str() == "deny_unknown_fields") + { + true + } else { + false + } +} diff --git a/clippy_lints_internal/src/lib.rs b/clippy_lints_internal/src/lib.rs index 308d161b9d6d..43cde86504f5 100644 --- a/clippy_lints_internal/src/lib.rs +++ b/clippy_lints_internal/src/lib.rs @@ -32,6 +32,7 @@ extern crate rustc_span; mod almost_standard_lint_formulation; mod collapsible_calls; +mod derive_deserialize_allowing_unknown; mod internal_paths; mod lint_without_lint_pass; mod msrv_attr_impl; @@ -46,6 +47,7 @@ use rustc_lint::{Lint, LintStore}; static LINTS: &[&Lint] = &[ almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION, collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, + derive_deserialize_allowing_unknown::DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, lint_without_lint_pass::DEFAULT_LINT, lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, @@ -65,6 +67,7 @@ pub fn register_lints(store: &mut LintStore) { store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths)); store.register_early_pass(|| Box::new(produce_ice::ProduceIce)); store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls)); + store.register_late_pass(|_| Box::new(derive_deserialize_allowing_unknown::DeriveDeserializeAllowingUnknown)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath)); diff --git a/tests/ui-internal/derive_deserialize_allowing_unknown.rs b/tests/ui-internal/derive_deserialize_allowing_unknown.rs new file mode 100644 index 000000000000..9dc8e9e8f4c1 --- /dev/null +++ b/tests/ui-internal/derive_deserialize_allowing_unknown.rs @@ -0,0 +1,60 @@ +#![deny(clippy::derive_deserialize_allowing_unknown)] + +use serde::{Deserialize, Deserializer}; + +#[derive(Deserialize)] //~ derive_deserialize_allowing_unknown +struct Struct { + flag: bool, + limit: u64, +} + +#[derive(Deserialize)] //~ derive_deserialize_allowing_unknown +enum Enum { + A(bool), + B { limit: u64 }, +} + +// negative tests + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct StructWithDenyUnknownFields { + flag: bool, + limit: u64, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +enum EnumWithDenyUnknownFields { + A(bool), + B { limit: u64 }, +} + +#[derive(Deserialize)] +#[serde(untagged, deny_unknown_fields)] +enum MultipleSerdeAttributes { + A(bool), + B { limit: u64 }, +} + +#[derive(Deserialize)] +struct TupleStruct(u64, bool); + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +enum EnumWithOnlyTupleVariants { + A(bool), + B(u64), +} + +struct ManualSerdeImplementation; + +impl<'de> Deserialize<'de> for ManualSerdeImplementation { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let () = <() as Deserialize>::deserialize(deserializer)?; + Ok(ManualSerdeImplementation) + } +} diff --git a/tests/ui-internal/derive_deserialize_allowing_unknown.stderr b/tests/ui-internal/derive_deserialize_allowing_unknown.stderr new file mode 100644 index 000000000000..93d64826c993 --- /dev/null +++ b/tests/ui-internal/derive_deserialize_allowing_unknown.stderr @@ -0,0 +1,23 @@ +error: `#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]` + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:5:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:1:9 + | +LL | #![deny(clippy::derive_deserialize_allowing_unknown)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]` + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:11:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml b/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml new file mode 100644 index 000000000000..82560cfd5e2a --- /dev/null +++ b/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml @@ -0,0 +1,4 @@ +# In the following configuration, "recommendation" should be "reason" or "replacement". +disallowed-macros = [ + { path = "std::panic", recommendation = "return a `std::result::Result::Error` instead" }, +] diff --git a/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs new file mode 100644 index 000000000000..9c770c31f6f8 --- /dev/null +++ b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs @@ -0,0 +1,5 @@ +#[rustfmt::skip] +//@error-in-other-file: error reading Clippy's configuration file: data did not match any variant of untagged enum DisallowedPathEnum +fn main() { + panic!(); +} diff --git a/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr new file mode 100644 index 000000000000..b564709721d5 --- /dev/null +++ b/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: data did not match any variant of untagged enum DisallowedPathEnum + --> $DIR/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml:3:5 + | +LL | { path = "std::panic", recommendation = "return a `std::result::Result::Error` instead" }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 4fcd33c3185a969127c532e06aaffac0a8761207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=AE=87=E9=80=B8?= Date: Wed, 14 May 2025 00:45:55 +0800 Subject: [PATCH 153/245] Update rustix to 1.0.7 for bootstrap --- src/bootstrap/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 05ab1b6eddef..ff63b8c62d3a 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -579,9 +579,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" -version = "1.0.2" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", From febb16a40fe2c3722f6e4202ee6783bf6b79b29c Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 13 May 2025 16:47:10 +0000 Subject: [PATCH 154/245] type_ir: add faster exit for `types_may_unify` --- compiler/rustc_type_ir/src/fast_reject.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 11ec1f0a9fb0..34502f495500 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -232,6 +232,9 @@ impl bool { + if lhs == rhs { + return true; + } self.types_may_unify_inner(lhs, rhs, Self::STARTING_DEPTH) } From 36790d28810ac188a8bca7fc9e95fe0869ae9a11 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 24 Feb 2025 22:29:37 -0500 Subject: [PATCH 155/245] Initial implementation of `core_float_math` Since [1], `compiler-builtins` makes a certain set of math symbols weakly available on all platforms. This means we can begin exposing some of the related functions in `core`, so begin this process here. It is not possible to provide inherent methods in both `core` and `std` while giving them different stability gates, so standalone functions are added instead. This provides a way to experiment with the functionality while unstable; once it is time to stabilize, they can be converted to inherent. For `f16` and `f128`, everything is unstable so we can move the inherent methods. The following are included to start: * floor * ceil * round * round_ties_even * trunc * fract * mul_add * div_euclid * rem_euclid * powi * sqrt * abs_sub * cbrt These mirror the set of functions that we have in `compiler-builtins` since [1]. Tracking issue: https://github.com/rust-lang/rust/issues/137578 [1]: https://github.com/rust-lang/compiler-builtins/pull/763 --- library/core/Cargo.toml | 6 + library/core/src/num/f128.rs | 410 ++++++++++++++++++++++++++++++++ library/core/src/num/f16.rs | 445 +++++++++++++++++++++++++++++++++++ library/core/src/num/f32.rs | 410 +++++++++++++++++++++++++++++++- library/core/src/num/f64.rs | 403 ++++++++++++++++++++++++++++++- library/core/src/num/libm.rs | 11 + library/core/src/num/mod.rs | 1 + library/std/src/f128.rs | 396 ------------------------------- library/std/src/f16.rs | 431 --------------------------------- library/std/src/f32.rs | 32 ++- library/std/src/f64.rs | 32 ++- library/std/src/lib.rs | 1 + library/std/src/sys/cmath.rs | 4 - 13 files changed, 1713 insertions(+), 869 deletions(-) create mode 100644 library/core/src/num/libm.rs diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 99e52d0ada0a..83ba17b93f51 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -35,4 +35,10 @@ check-cfg = [ # and to stdarch `core_arch` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg 'cfg(feature, values(any()))', + # Internal features aren't marked known config by default, we use these to + # gate tests. + 'cfg(target_has_reliable_f16)', + 'cfg(target_has_reliable_f16_math)', + 'cfg(target_has_reliable_f128)', + 'cfg(target_has_reliable_f128_math)', ] diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 7e470185c86d..0c2c4155d66c 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1415,3 +1415,413 @@ impl f128 { intrinsics::frem_algebraic(self, rhs) } } + +// Functions in this module fall into `core_float_math` +// FIXME(f16_f128): all doctests must be gated to platforms that have `long double` === `_Float128` +// due to https://github.com/llvm/llvm-project/issues/44744. aarch64 linux matches this. +// #[unstable(feature = "core_float_math", issue = "137578")] +#[cfg(not(test))] +impl f128 { + /// Returns the largest integer less than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn floor(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf128(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.01_f128; + /// let g = 4.0_f128; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf128(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = -3.7_f128; + /// let i = 3.5_f128; + /// let j = 4.5_f128; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf128(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = 3.5_f128; + /// let i = 4.5_f128; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f128 { + intrinsics::round_ties_even_f128(self) + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf128(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let x = 3.6_f128; + /// let y = -3.6_f128; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f128::EPSILON); + /// assert!(abs_difference_y <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f128 { + self - self.trunc() + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let m = 10.0_f128; + /// let x = 4.0_f128; + /// let b = 60.0_f128; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f128 + f128::EPSILON; + /// let one_minus_eps = 1.0_f128 - f128::EPSILON; + /// let minus_one = -1.0_f128; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f128, b: f128) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf128(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f128) -> f128 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f128) -> f128 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif128(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let positive = 4.0_f128; + /// let negative = -4.0_f128; + /// let negative_zero = -0.0_f128; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[doc(alias = "squareRoot")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf128(self) } + } +} diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e47900cba550..1a859f2277ff 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -13,6 +13,8 @@ use crate::convert::FloatToInt; use crate::num::FpCategory; +#[cfg(not(test))] +use crate::num::libm; use crate::panic::const_assert; use crate::{intrinsics, mem}; @@ -1391,3 +1393,446 @@ impl f16 { intrinsics::frem_algebraic(self, rhs) } } + +// Functions in this module fall into `core_float_math` +// #[unstable(feature = "core_float_math", issue = "137578")] +#[cfg(not(test))] +impl f16 { + /// Returns the largest integer less than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn floor(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf16(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.01_f16; + /// let g = 4.0_f16; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf16(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = -3.7_f16; + /// let i = 3.5_f16; + /// let j = 4.5_f16; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf16(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = 3.5_f16; + /// let i = 4.5_f16; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f16 { + intrinsics::round_ties_even_f16(self) + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf16(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let x = 3.6_f16; + /// let y = -3.6_f16; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f16::EPSILON); + /// assert!(abs_difference_y <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f16 { + self - self.trunc() + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let m = 10.0_f16; + /// let x = 4.0_f16; + /// let b = 60.0_f16; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f16 + f16::EPSILON; + /// let one_minus_eps = 1.0_f16 - f16::EPSILON; + /// let minus_one = -1.0_f16; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f16::EPSILON * f16::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f16, b: f16) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf16(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f16) -> f16 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f16::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f16) -> f16 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif16(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let positive = 4.0_f16; + /// let negative = -4.0_f16; + /// let negative_zero = -0.0_f16; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[doc(alias = "squareRoot")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf16(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `cbrtf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let x = 8.0f16; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(self) -> f16 { + libm::cbrtf(self as f32) as f16 + } +} diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 5fbc6eb33f17..326ccd517ced 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -12,7 +12,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::convert::FloatToInt; -use crate::num::FpCategory; +use crate::num::{FpCategory, libm}; use crate::panic::const_assert; use crate::{cfg_match, intrinsics, mem}; @@ -1556,3 +1556,411 @@ impl f32 { intrinsics::frem_algebraic(self, rhs) } } + +/// Experimental version of `floor` in `core`. See [`f32::floor`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let f = 3.7_f32; +/// let g = 3.0_f32; +/// let h = -3.7_f32; +/// +/// assert_eq!(f32::floor(f), 3.0); +/// assert_eq!(f32::floor(g), 3.0); +/// assert_eq!(f32::floor(h), -4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::floor`]: ../../std/primitive.f32.html#method.floor +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn floor(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf32(x) } +} + +/// Experimental version of `ceil` in `core`. See [`f32::ceil`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let f = 3.01_f32; +/// let g = 4.0_f32; +/// +/// assert_eq!(f32::ceil(f), 4.0); +/// assert_eq!(f32::ceil(g), 4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::ceil`]: ../../std/primitive.f32.html#method.ceil +#[inline] +#[doc(alias = "ceiling")] +#[must_use = "method returns a new number and does not mutate the original value"] +#[unstable(feature = "core_float_math", issue = "137578")] +pub fn ceil(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf32(x) } +} + +/// Experimental version of `round` in `core`. See [`f32::round`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let f = 3.3_f32; +/// let g = -3.3_f32; +/// let h = -3.7_f32; +/// let i = 3.5_f32; +/// let j = 4.5_f32; +/// +/// assert_eq!(f32::round(f), 3.0); +/// assert_eq!(f32::round(g), -3.0); +/// assert_eq!(f32::round(h), -4.0); +/// assert_eq!(f32::round(i), 4.0); +/// assert_eq!(f32::round(j), 5.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::round`]: ../../std/primitive.f32.html#method.round +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn round(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf32(x) } +} + +/// Experimental version of `round_ties_even` in `core`. See [`f32::round_ties_even`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let f = 3.3_f32; +/// let g = -3.3_f32; +/// let h = 3.5_f32; +/// let i = 4.5_f32; +/// +/// assert_eq!(f32::round_ties_even(f), 3.0); +/// assert_eq!(f32::round_ties_even(g), -3.0); +/// assert_eq!(f32::round_ties_even(h), 4.0); +/// assert_eq!(f32::round_ties_even(i), 4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::round_ties_even`]: ../../std/primitive.f32.html#method.round_ties_even +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn round_ties_even(x: f32) -> f32 { + intrinsics::round_ties_even_f32(x) +} + +/// Experimental version of `trunc` in `core`. See [`f32::trunc`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let f = 3.7_f32; +/// let g = 3.0_f32; +/// let h = -3.7_f32; +/// +/// assert_eq!(f32::trunc(f), 3.0); +/// assert_eq!(f32::trunc(g), 3.0); +/// assert_eq!(f32::trunc(h), -3.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::trunc`]: ../../std/primitive.f32.html#method.trunc +#[inline] +#[doc(alias = "truncate")] +#[must_use = "method returns a new number and does not mutate the original value"] +#[unstable(feature = "core_float_math", issue = "137578")] +pub fn trunc(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf32(x) } +} + +/// Experimental version of `fract` in `core`. See [`f32::fract`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let x = 3.6_f32; +/// let y = -3.6_f32; +/// let abs_difference_x = (f32::fract(x) - 0.6).abs(); +/// let abs_difference_y = (f32::fract(y) - (-0.6)).abs(); +/// +/// assert!(abs_difference_x <= f32::EPSILON); +/// assert!(abs_difference_y <= f32::EPSILON); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::fract`]: ../../std/primitive.f32.html#method.fract +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn fract(x: f32) -> f32 { + x - trunc(x) +} + +/// Experimental version of `mul_add` in `core`. See [`f32::mul_add`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let m = 10.0_f32; +/// let x = 4.0_f32; +/// let b = 60.0_f32; +/// +/// assert_eq!(f32::mul_add(m, x, b), 100.0); +/// assert_eq!(m * x + b, 100.0); +/// +/// let one_plus_eps = 1.0_f32 + f32::EPSILON; +/// let one_minus_eps = 1.0_f32 - f32::EPSILON; +/// let minus_one = -1.0_f32; +/// +/// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. +/// assert_eq!(f32::mul_add(one_plus_eps, one_minus_eps, minus_one), -f32::EPSILON * f32::EPSILON); +/// // Different rounding with the non-fused multiply and add. +/// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::mul_add`]: ../../std/primitive.f32.html#method.mul_add +#[inline] +#[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] +#[must_use = "method returns a new number and does not mutate the original value"] +#[unstable(feature = "core_float_math", issue = "137578")] +pub fn mul_add(x: f32, y: f32, z: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf32(x, y, z) } +} + +/// Experimental version of `div_euclid` in `core`. See [`f32::div_euclid`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let a: f32 = 7.0; +/// let b = 4.0; +/// assert_eq!(f32::div_euclid(a, b), 1.0); // 7.0 > 4.0 * 1.0 +/// assert_eq!(f32::div_euclid(-a, b), -2.0); // -7.0 >= 4.0 * -2.0 +/// assert_eq!(f32::div_euclid(a, -b), -1.0); // 7.0 >= -4.0 * -1.0 +/// assert_eq!(f32::div_euclid(-a, -b), 2.0); // -7.0 >= -4.0 * 2.0 +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::div_euclid`]: ../../std/primitive.f32.html#method.div_euclid +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn div_euclid(x: f32, rhs: f32) -> f32 { + let q = trunc(x / rhs); + if x % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q +} + +/// Experimental version of `rem_euclid` in `core`. See [`f32::rem_euclid`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let a: f32 = 7.0; +/// let b = 4.0; +/// assert_eq!(f32::rem_euclid(a, b), 3.0); +/// assert_eq!(f32::rem_euclid(-a, b), 1.0); +/// assert_eq!(f32::rem_euclid(a, -b), 3.0); +/// assert_eq!(f32::rem_euclid(-a, -b), 1.0); +/// // limitation due to round-off error +/// assert!(f32::rem_euclid(-f32::EPSILON, 3.0) != 0.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::rem_euclid`]: ../../std/primitive.f32.html#method.rem_euclid +#[inline] +#[doc(alias = "modulo", alias = "mod")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn rem_euclid(x: f32, rhs: f32) -> f32 { + let r = x % rhs; + if r < 0.0 { r + rhs.abs() } else { r } +} + +/// Experimental version of `powi` in `core`. See [`f32::powi`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let x = 2.0_f32; +/// let abs_difference = (f32::powi(x, 2) - (x * x)).abs(); +/// assert!(abs_difference <= f32::EPSILON); +/// +/// assert_eq!(f32::powi(f32::NAN, 0), 1.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::powi`]: ../../std/primitive.f32.html#method.powi +#[inline] +#[must_use = "method returns a new number and does not mutate the original value"] +#[unstable(feature = "core_float_math", issue = "137578")] +pub fn powi(x: f32, n: i32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif32(x, n) } +} + +/// Experimental version of `sqrt` in `core`. See [`f32::sqrt`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let positive = 4.0_f32; +/// let negative = -4.0_f32; +/// let negative_zero = -0.0_f32; +/// +/// assert_eq!(f32::sqrt(positive), 2.0); +/// assert!(f32::sqrt(negative).is_nan()); +/// assert_eq!(f32::sqrt(negative_zero), negative_zero); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::sqrt`]: ../../std/primitive.f32.html#method.sqrt +#[inline] +#[doc(alias = "squareRoot")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn sqrt(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf32(x) } +} + +/// Experimental version of `abs_sub` in `core`. See [`f32::abs_sub`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let x = 3.0f32; +/// let y = -3.0f32; +/// +/// let abs_difference_x = (f32::abs_sub(x, 1.0) - 2.0).abs(); +/// let abs_difference_y = (f32::abs_sub(y, 1.0) - 0.0).abs(); +/// +/// assert!(abs_difference_x <= f32::EPSILON); +/// assert!(abs_difference_y <= f32::EPSILON); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::abs_sub`]: ../../std/primitive.f32.html#method.abs_sub +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +#[deprecated( + since = "1.10.0", + note = "you probably meant `(self - other).abs()`: \ + this operation is `(self - other).max(0.0)` \ + except that `abs_sub` also propagates NaNs (also \ + known as `fdimf` in C). If you truly need the positive \ + difference, consider using that expression or the C function \ + `fdimf`, depending on how you wish to handle NaN (please consider \ + filing an issue describing your use-case too)." +)] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn abs_sub(x: f32, other: f32) -> f32 { + libm::fdimf(x, other) +} + +/// Experimental version of `cbrt` in `core`. See [`f32::cbrt`] for details. +/// +/// # Unspecified precision +/// +/// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and +/// can even differ within the same execution from one invocation to the next. +/// This function currently corresponds to the `cbrtf` from libc on Unix +/// and Windows. Note that this might change in the future. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f32; +/// +/// let x = 8.0f32; +/// +/// // x^(1/3) - 2 == 0 +/// let abs_difference = (f32::cbrt(x) - 2.0).abs(); +/// +/// assert!(abs_difference <= f32::EPSILON); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f32::cbrt`]: ../../std/primitive.f32.html#method.cbrt +#[inline] +#[must_use = "method returns a new number and does not mutate the original value"] +#[unstable(feature = "core_float_math", issue = "137578")] +pub fn cbrt(x: f32) -> f32 { + libm::cbrtf(x) +} diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 81ab0f14c2bc..66aba73aec10 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -12,7 +12,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::convert::FloatToInt; -use crate::num::FpCategory; +use crate::num::{FpCategory, libm}; use crate::panic::const_assert; use crate::{intrinsics, mem}; @@ -1555,3 +1555,404 @@ impl f64 { intrinsics::frem_algebraic(self, rhs) } } + +/// Experimental version of `floor` in `core`. See [`f64::floor`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let f = 3.7_f64; +/// let g = 3.0_f64; +/// let h = -3.7_f64; +/// +/// assert_eq!(f64::floor(f), 3.0); +/// assert_eq!(f64::floor(g), 3.0); +/// assert_eq!(f64::floor(h), -4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::floor`]: ../../std/primitive.f64.html#method.floor +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn floor(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf64(x) } +} + +/// Experimental version of `ceil` in `core`. See [`f64::ceil`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let f = 3.01_f64; +/// let g = 4.0_f64; +/// +/// assert_eq!(f64::ceil(f), 4.0); +/// assert_eq!(f64::ceil(g), 4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::ceil`]: ../../std/primitive.f64.html#method.ceil +#[inline] +#[doc(alias = "ceiling")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn ceil(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf64(x) } +} + +/// Experimental version of `round` in `core`. See [`f64::round`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let f = 3.3_f64; +/// let g = -3.3_f64; +/// let h = -3.7_f64; +/// let i = 3.5_f64; +/// let j = 4.5_f64; +/// +/// assert_eq!(f64::round(f), 3.0); +/// assert_eq!(f64::round(g), -3.0); +/// assert_eq!(f64::round(h), -4.0); +/// assert_eq!(f64::round(i), 4.0); +/// assert_eq!(f64::round(j), 5.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::round`]: ../../std/primitive.f64.html#method.round +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn round(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf64(x) } +} + +/// Experimental version of `round_ties_even` in `core`. See [`f64::round_ties_even`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let f = 3.3_f64; +/// let g = -3.3_f64; +/// let h = 3.5_f64; +/// let i = 4.5_f64; +/// +/// assert_eq!(f64::round_ties_even(f), 3.0); +/// assert_eq!(f64::round_ties_even(g), -3.0); +/// assert_eq!(f64::round_ties_even(h), 4.0); +/// assert_eq!(f64::round_ties_even(i), 4.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::round_ties_even`]: ../../std/primitive.f64.html#method.round_ties_even +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn round_ties_even(x: f64) -> f64 { + intrinsics::round_ties_even_f64(x) +} + +/// Experimental version of `trunc` in `core`. See [`f64::trunc`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let f = 3.7_f64; +/// let g = 3.0_f64; +/// let h = -3.7_f64; +/// +/// assert_eq!(f64::trunc(f), 3.0); +/// assert_eq!(f64::trunc(g), 3.0); +/// assert_eq!(f64::trunc(h), -3.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::trunc`]: ../../std/primitive.f64.html#method.trunc +#[inline] +#[doc(alias = "truncate")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn trunc(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf64(x) } +} + +/// Experimental version of `fract` in `core`. See [`f64::fract`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let x = 3.6_f64; +/// let y = -3.6_f64; +/// let abs_difference_x = (f64::fract(x) - 0.6).abs(); +/// let abs_difference_y = (f64::fract(y) - (-0.6)).abs(); +/// +/// assert!(abs_difference_x < 1e-10); +/// assert!(abs_difference_y < 1e-10); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::fract`]: ../../std/primitive.f64.html#method.fract +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn fract(x: f64) -> f64 { + x - trunc(x) +} + +/// Experimental version of `mul_add` in `core`. See [`f64::mul_add`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let m = 10.0_f64; +/// let x = 4.0_f64; +/// let b = 60.0_f64; +/// +/// assert_eq!(f64::mul_add(m, x, b), 100.0); +/// assert_eq!(m * x + b, 100.0); +/// +/// let one_plus_eps = 1.0_f64 + f64::EPSILON; +/// let one_minus_eps = 1.0_f64 - f64::EPSILON; +/// let minus_one = -1.0_f64; +/// +/// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. +/// assert_eq!(f64::mul_add(one_plus_eps, one_minus_eps, minus_one), -f64::EPSILON * f64::EPSILON); +/// // Different rounding with the non-fused multiply and add. +/// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::mul_add`]: ../../std/primitive.f64.html#method.mul_add +#[inline] +#[doc(alias = "fma", alias = "fusedMultiplyAdd")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn mul_add(x: f64, a: f64, b: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf64(x, a, b) } +} + +/// Experimental version of `div_euclid` in `core`. See [`f64::div_euclid`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let a: f64 = 7.0; +/// let b = 4.0; +/// assert_eq!(f64::div_euclid(a, b), 1.0); // 7.0 > 4.0 * 1.0 +/// assert_eq!(f64::div_euclid(-a, b), -2.0); // -7.0 >= 4.0 * -2.0 +/// assert_eq!(f64::div_euclid(a, -b), -1.0); // 7.0 >= -4.0 * -1.0 +/// assert_eq!(f64::div_euclid(-a, -b), 2.0); // -7.0 >= -4.0 * 2.0 +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::div_euclid`]: ../../std/primitive.f64.html#method.div_euclid +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn div_euclid(x: f64, rhs: f64) -> f64 { + let q = trunc(x / rhs); + if x % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q +} + +/// Experimental version of `rem_euclid` in `core`. See [`f64::rem_euclid`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let a: f64 = 7.0; +/// let b = 4.0; +/// assert_eq!(f64::rem_euclid(a, b), 3.0); +/// assert_eq!(f64::rem_euclid(-a, b), 1.0); +/// assert_eq!(f64::rem_euclid(a, -b), 3.0); +/// assert_eq!(f64::rem_euclid(-a, -b), 1.0); +/// // limitation due to round-off error +/// assert!(f64::rem_euclid(-f64::EPSILON, 3.0) != 0.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::rem_euclid`]: ../../std/primitive.f64.html#method.rem_euclid +#[inline] +#[doc(alias = "modulo", alias = "mod")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn rem_euclid(x: f64, rhs: f64) -> f64 { + let r = x % rhs; + if r < 0.0 { r + rhs.abs() } else { r } +} + +/// Experimental version of `powi` in `core`. See [`f64::powi`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let x = 2.0_f64; +/// let abs_difference = (f64::powi(x, 2) - (x * x)).abs(); +/// assert!(abs_difference <= f64::EPSILON); +/// +/// assert_eq!(f64::powi(f64::NAN, 0), 1.0); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::powi`]: ../../std/primitive.f64.html#method.powi +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn powi(x: f64, n: i32) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif64(x, n) } +} + +/// Experimental version of `sqrt` in `core`. See [`f64::sqrt`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let positive = 4.0_f64; +/// let negative = -4.0_f64; +/// let negative_zero = -0.0_f64; +/// +/// assert_eq!(f64::sqrt(positive), 2.0); +/// assert!(f64::sqrt(negative).is_nan()); +/// assert_eq!(f64::sqrt(negative_zero), negative_zero); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::sqrt`]: ../../std/primitive.f64.html#method.sqrt +#[inline] +#[doc(alias = "squareRoot")] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn sqrt(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf64(x) } +} + +/// Experimental version of `abs_sub` in `core`. See [`f64::abs_sub`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let x = 3.0_f64; +/// let y = -3.0_f64; +/// +/// let abs_difference_x = (f64::abs_sub(x, 1.0) - 2.0).abs(); +/// let abs_difference_y = (f64::abs_sub(y, 1.0) - 0.0).abs(); +/// +/// assert!(abs_difference_x < 1e-10); +/// assert!(abs_difference_y < 1e-10); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::abs_sub`]: ../../std/primitive.f64.html#method.abs_sub +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[deprecated( + since = "1.10.0", + note = "you probably meant `(self - other).abs()`: \ + this operation is `(self - other).max(0.0)` \ + except that `abs_sub` also propagates NaNs (also \ + known as `fdim` in C). If you truly need the positive \ + difference, consider using that expression or the C function \ + `fdim`, depending on how you wish to handle NaN (please consider \ + filing an issue describing your use-case too)." +)] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn abs_sub(x: f64, other: f64) -> f64 { + libm::fdim(x, other) +} + +/// Experimental version of `cbrt` in `core`. See [`f64::cbrt`] for details. +/// +/// # Examples +/// +/// ``` +/// #![feature(core_float_math)] +/// +/// use core::f64; +/// +/// let x = 8.0_f64; +/// +/// // x^(1/3) - 2 == 0 +/// let abs_difference = (f64::cbrt(x) - 2.0).abs(); +/// +/// assert!(abs_difference < 1e-10); +/// ``` +/// +/// _This standalone function is for testing only. It will be stabilized as an inherent method._ +/// +/// [`f64::cbrt`]: ../../std/primitive.f64.html#method.cbrt +#[inline] +#[unstable(feature = "core_float_math", issue = "137578")] +#[must_use = "method returns a new number and does not mutate the original value"] +pub fn cbrt(x: f64) -> f64 { + libm::cbrt(x) +} diff --git a/library/core/src/num/libm.rs b/library/core/src/num/libm.rs new file mode 100644 index 000000000000..aeabb0872309 --- /dev/null +++ b/library/core/src/num/libm.rs @@ -0,0 +1,11 @@ +//! Bindings to math functions provided by the system `libm` or by the `libm` crate, exposed +//! via `compiler-builtins`. + +// SAFETY: These symbols have standard interfaces in C and are defined by `libm`, or are +// provided by `compiler-builtins` on unsupported platforms. +unsafe extern "C" { + pub(crate) safe fn cbrt(n: f64) -> f64; + pub(crate) safe fn cbrtf(n: f32) -> f32; + pub(crate) safe fn fdim(a: f64, b: f64) -> f64; + pub(crate) safe fn fdimf(a: f32, b: f32) -> f32; +} diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index ecc1c7bf9021..3bb0c4c52fc6 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -46,6 +46,7 @@ mod uint_macros; // import uint_impl! mod error; mod int_log10; mod int_sqrt; +pub(crate) mod libm; mod nonzero; mod overflow_panic; mod saturating; diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 6b2ba2e714c9..bb4acde48224 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -14,365 +14,6 @@ use crate::sys::cmath; #[cfg(not(test))] impl f128 { - /// Returns the largest integer less than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 3.7_f128; - /// let g = 3.0_f128; - /// let h = -3.7_f128; - /// - /// assert_eq!(f.floor(), 3.0); - /// assert_eq!(g.floor(), 3.0); - /// assert_eq!(h.floor(), -4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn floor(self) -> f128 { - unsafe { intrinsics::floorf128(self) } - } - - /// Returns the smallest integer greater than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 3.01_f128; - /// let g = 4.0_f128; - /// - /// assert_eq!(f.ceil(), 4.0); - /// assert_eq!(g.ceil(), 4.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "ceiling")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ceil(self) -> f128 { - unsafe { intrinsics::ceilf128(self) } - } - - /// Returns the nearest integer to `self`. If a value is half-way between two - /// integers, round away from `0.0`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 3.3_f128; - /// let g = -3.3_f128; - /// let h = -3.7_f128; - /// let i = 3.5_f128; - /// let j = 4.5_f128; - /// - /// assert_eq!(f.round(), 3.0); - /// assert_eq!(g.round(), -3.0); - /// assert_eq!(h.round(), -4.0); - /// assert_eq!(i.round(), 4.0); - /// assert_eq!(j.round(), 5.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round(self) -> f128 { - unsafe { intrinsics::roundf128(self) } - } - - /// Returns the nearest integer to a number. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 3.3_f128; - /// let g = -3.3_f128; - /// let h = 3.5_f128; - /// let i = 4.5_f128; - /// - /// assert_eq!(f.round_ties_even(), 3.0); - /// assert_eq!(g.round_ties_even(), -3.0); - /// assert_eq!(h.round_ties_even(), 4.0); - /// assert_eq!(i.round_ties_even(), 4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round_ties_even(self) -> f128 { - intrinsics::round_ties_even_f128(self) - } - - /// Returns the integer part of `self`. - /// This means that non-integer numbers are always truncated towards zero. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 3.7_f128; - /// let g = 3.0_f128; - /// let h = -3.7_f128; - /// - /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), 3.0); - /// assert_eq!(h.trunc(), -3.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "truncate")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn trunc(self) -> f128 { - unsafe { intrinsics::truncf128(self) } - } - - /// Returns the fractional part of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 3.6_f128; - /// let y = -3.6_f128; - /// let abs_difference_x = (x.fract() - 0.6).abs(); - /// let abs_difference_y = (y.fract() - (-0.6)).abs(); - /// - /// assert!(abs_difference_x <= f128::EPSILON); - /// assert!(abs_difference_y <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn fract(self) -> f128 { - self - self.trunc() - } - - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error, yielding a more accurate result than an unfused multiply-add. - /// - /// Using `mul_add` *may* be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. However, - /// this is not always true, and will be heavily dependant on designing - /// algorithms with specific target hardware in mind. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as - /// `fusedMultiplyAdd` and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let m = 10.0_f128; - /// let x = 4.0_f128; - /// let b = 60.0_f128; - /// - /// assert_eq!(m.mul_add(x, b), 100.0); - /// assert_eq!(m * x + b, 100.0); - /// - /// let one_plus_eps = 1.0_f128 + f128::EPSILON; - /// let one_minus_eps = 1.0_f128 - f128::EPSILON; - /// let minus_one = -1.0_f128; - /// - /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. - /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON); - /// // Different rounding with the non-fused multiply and add. - /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn mul_add(self, a: f128, b: f128) -> f128 { - unsafe { intrinsics::fmaf128(self, a, b) } - } - - /// Calculates Euclidean division, the matching method for `rem_euclid`. - /// - /// This computes the integer `n` such that - /// `self = n * rhs + self.rem_euclid(rhs)`. - /// In other words, the result is `self / rhs` rounded to the integer `n` - /// such that `self >= n * rhs`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let a: f128 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 - /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn div_euclid(self, rhs: f128) -> f128 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; - } - q - } - - /// Calculates the least nonnegative remainder of `self (mod rhs)`. - /// - /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in - /// most cases. However, due to a floating point round-off error it can - /// result in `r == rhs.abs()`, violating the mathematical definition, if - /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. - /// This result is not an element of the function's codomain, but it is the - /// closest floating point number in the real numbers and thus fulfills the - /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` - /// approximately. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let a: f128 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.rem_euclid(b), 3.0); - /// assert_eq!((-a).rem_euclid(b), 1.0); - /// assert_eq!(a.rem_euclid(-b), 3.0); - /// assert_eq!((-a).rem_euclid(-b), 1.0); - /// // limitation due to round-off error - /// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[doc(alias = "modulo", alias = "mod")] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn rem_euclid(self, rhs: f128) -> f128 { - let r = self % rhs; - if r < 0.0 { r + rhs.abs() } else { r } - } - - /// Raises a number to an integer power. - /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 2.0_f128; - /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// assert!(abs_difference <= f128::EPSILON); - /// - /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f128 { - unsafe { intrinsics::powif128(self, n) } - } - /// Raises a number to a floating point power. /// /// # Unspecified precision @@ -405,43 +46,6 @@ impl f128 { unsafe { intrinsics::powf128(self, n) } } - /// Returns the square root of a number. - /// - /// Returns NaN if `self` is a negative number other than `-0.0`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` - /// and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let positive = 4.0_f128; - /// let negative = -4.0_f128; - /// let negative_zero = -0.0_f128; - /// - /// assert_eq!(positive.sqrt(), 2.0); - /// assert!(negative.sqrt().is_nan()); - /// assert!(negative_zero.sqrt() == negative_zero); - /// # } - /// ``` - #[inline] - #[doc(alias = "squareRoot")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn sqrt(self) -> f128 { - unsafe { intrinsics::sqrtf128(self) } - } - /// Returns `e^(self)`, (the exponential function). /// /// # Unspecified precision diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index d6bc1d3118ae..4792eac1f9e2 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -14,365 +14,6 @@ use crate::sys::cmath; #[cfg(not(test))] impl f16 { - /// Returns the largest integer less than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 3.7_f16; - /// let g = 3.0_f16; - /// let h = -3.7_f16; - /// - /// assert_eq!(f.floor(), 3.0); - /// assert_eq!(g.floor(), 3.0); - /// assert_eq!(h.floor(), -4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn floor(self) -> f16 { - unsafe { intrinsics::floorf16(self) } - } - - /// Returns the smallest integer greater than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 3.01_f16; - /// let g = 4.0_f16; - /// - /// assert_eq!(f.ceil(), 4.0); - /// assert_eq!(g.ceil(), 4.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "ceiling")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ceil(self) -> f16 { - unsafe { intrinsics::ceilf16(self) } - } - - /// Returns the nearest integer to `self`. If a value is half-way between two - /// integers, round away from `0.0`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 3.3_f16; - /// let g = -3.3_f16; - /// let h = -3.7_f16; - /// let i = 3.5_f16; - /// let j = 4.5_f16; - /// - /// assert_eq!(f.round(), 3.0); - /// assert_eq!(g.round(), -3.0); - /// assert_eq!(h.round(), -4.0); - /// assert_eq!(i.round(), 4.0); - /// assert_eq!(j.round(), 5.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round(self) -> f16 { - unsafe { intrinsics::roundf16(self) } - } - - /// Returns the nearest integer to a number. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 3.3_f16; - /// let g = -3.3_f16; - /// let h = 3.5_f16; - /// let i = 4.5_f16; - /// - /// assert_eq!(f.round_ties_even(), 3.0); - /// assert_eq!(g.round_ties_even(), -3.0); - /// assert_eq!(h.round_ties_even(), 4.0); - /// assert_eq!(i.round_ties_even(), 4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round_ties_even(self) -> f16 { - intrinsics::round_ties_even_f16(self) - } - - /// Returns the integer part of `self`. - /// This means that non-integer numbers are always truncated towards zero. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 3.7_f16; - /// let g = 3.0_f16; - /// let h = -3.7_f16; - /// - /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), 3.0); - /// assert_eq!(h.trunc(), -3.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "truncate")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn trunc(self) -> f16 { - unsafe { intrinsics::truncf16(self) } - } - - /// Returns the fractional part of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 3.6_f16; - /// let y = -3.6_f16; - /// let abs_difference_x = (x.fract() - 0.6).abs(); - /// let abs_difference_y = (y.fract() - (-0.6)).abs(); - /// - /// assert!(abs_difference_x <= f16::EPSILON); - /// assert!(abs_difference_y <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn fract(self) -> f16 { - self - self.trunc() - } - - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error, yielding a more accurate result than an unfused multiply-add. - /// - /// Using `mul_add` *may* be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. However, - /// this is not always true, and will be heavily dependant on designing - /// algorithms with specific target hardware in mind. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as - /// `fusedMultiplyAdd` and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let m = 10.0_f16; - /// let x = 4.0_f16; - /// let b = 60.0_f16; - /// - /// assert_eq!(m.mul_add(x, b), 100.0); - /// assert_eq!(m * x + b, 100.0); - /// - /// let one_plus_eps = 1.0_f16 + f16::EPSILON; - /// let one_minus_eps = 1.0_f16 - f16::EPSILON; - /// let minus_one = -1.0_f16; - /// - /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. - /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f16::EPSILON * f16::EPSILON); - /// // Different rounding with the non-fused multiply and add. - /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn mul_add(self, a: f16, b: f16) -> f16 { - unsafe { intrinsics::fmaf16(self, a, b) } - } - - /// Calculates Euclidean division, the matching method for `rem_euclid`. - /// - /// This computes the integer `n` such that - /// `self = n * rhs + self.rem_euclid(rhs)`. - /// In other words, the result is `self / rhs` rounded to the integer `n` - /// such that `self >= n * rhs`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let a: f16 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 - /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn div_euclid(self, rhs: f16) -> f16 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; - } - q - } - - /// Calculates the least nonnegative remainder of `self (mod rhs)`. - /// - /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in - /// most cases. However, due to a floating point round-off error it can - /// result in `r == rhs.abs()`, violating the mathematical definition, if - /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. - /// This result is not an element of the function's codomain, but it is the - /// closest floating point number in the real numbers and thus fulfills the - /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` - /// approximately. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let a: f16 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.rem_euclid(b), 3.0); - /// assert_eq!((-a).rem_euclid(b), 1.0); - /// assert_eq!(a.rem_euclid(-b), 3.0); - /// assert_eq!((-a).rem_euclid(-b), 1.0); - /// // limitation due to round-off error - /// assert!((-f16::EPSILON).rem_euclid(3.0) != 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[doc(alias = "modulo", alias = "mod")] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn rem_euclid(self, rhs: f16) -> f16 { - let r = self % rhs; - if r < 0.0 { r + rhs.abs() } else { r } - } - - /// Raises a number to an integer power. - /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 2.0_f16; - /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// assert!(abs_difference <= f16::EPSILON); - /// - /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f16 { - unsafe { intrinsics::powif16(self, n) } - } - /// Raises a number to a floating point power. /// /// # Unspecified precision @@ -405,43 +46,6 @@ impl f16 { unsafe { intrinsics::powf16(self, n) } } - /// Returns the square root of a number. - /// - /// Returns NaN if `self` is a negative number other than `-0.0`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` - /// and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let positive = 4.0_f16; - /// let negative = -4.0_f16; - /// let negative_zero = -0.0_f16; - /// - /// assert_eq!(positive.sqrt(), 2.0); - /// assert!(negative.sqrt().is_nan()); - /// assert!(negative_zero.sqrt() == negative_zero); - /// # } - /// ``` - #[inline] - #[doc(alias = "squareRoot")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn sqrt(self) -> f16 { - unsafe { intrinsics::sqrtf16(self) } - } - /// Returns `e^(self)`, (the exponential function). /// /// # Unspecified precision @@ -702,41 +306,6 @@ impl f16 { unsafe { intrinsics::log10f16(self) } } - /// Returns the cube root of a number. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `cbrtf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #![feature(cfg_target_has_reliable_f16_f128)] - /// # #![expect(internal_features)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 8.0f16; - /// - /// // x^(1/3) - 2 == 0 - /// let abs_difference = (x.cbrt() - 2.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn cbrt(self) -> f16 { - cmath::cbrtf(self as f32) as f16 - } - /// Compute the distance between the origin and a point (`x`, `y`) on the /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a /// right-angle triangle with other sides having length `x.abs()` and diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index baf7002f3803..94140d01d8b7 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -46,7 +46,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn floor(self) -> f32 { - unsafe { intrinsics::floorf32(self) } + core::f32::floor(self) } /// Returns the smallest integer greater than or equal to `self`. @@ -68,7 +68,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ceil(self) -> f32 { - unsafe { intrinsics::ceilf32(self) } + core::f32::ceil(self) } /// Returns the nearest integer to `self`. If a value is half-way between two @@ -96,7 +96,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn round(self) -> f32 { - unsafe { intrinsics::roundf32(self) } + core::f32::round(self) } /// Returns the nearest integer to a number. Rounds half-way cases to the number @@ -122,7 +122,7 @@ impl f32 { #[stable(feature = "round_ties_even", since = "1.77.0")] #[inline] pub fn round_ties_even(self) -> f32 { - intrinsics::round_ties_even_f32(self) + core::f32::round_ties_even(self) } /// Returns the integer part of `self`. @@ -147,7 +147,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn trunc(self) -> f32 { - unsafe { intrinsics::truncf32(self) } + core::f32::trunc(self) } /// Returns the fractional part of `self`. @@ -170,7 +170,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn fract(self) -> f32 { - self - self.trunc() + core::f32::fract(self) } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding @@ -212,7 +212,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn mul_add(self, a: f32, b: f32) -> f32 { - unsafe { intrinsics::fmaf32(self, a, b) } + core::f32::mul_add(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. @@ -242,11 +242,7 @@ impl f32 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn div_euclid(self, rhs: f32) -> f32 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; - } - q + core::f32::div_euclid(self, rhs) } /// Calculates the least nonnegative remainder of `self (mod rhs)`. @@ -283,8 +279,7 @@ impl f32 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn rem_euclid(self, rhs: f32) -> f32 { - let r = self % rhs; - if r < 0.0 { r + rhs.abs() } else { r } + core::f32::rem_euclid(self, rhs) } /// Raises a number to an integer power. @@ -312,7 +307,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn powi(self, n: i32) -> f32 { - unsafe { intrinsics::powif32(self, n) } + core::f32::powi(self, n) } /// Raises a number to a floating point power. @@ -367,7 +362,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sqrt(self) -> f32 { - unsafe { intrinsics::sqrtf32(self) } + core::f32::sqrt(self) } /// Returns `e^(self)`, (the exponential function). @@ -599,7 +594,8 @@ impl f32 { filing an issue describing your use-case too)." )] pub fn abs_sub(self, other: f32) -> f32 { - cmath::fdimf(self, other) + #[allow(deprecated)] + core::f32::abs_sub(self, other) } /// Returns the cube root of a number. @@ -626,7 +622,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cbrt(self) -> f32 { - cmath::cbrtf(self) + core::f32::cbrt(self) } /// Compute the distance between the origin and a point (`x`, `y`) on the diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 84fd9bfb7b68..051061ae6055 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -46,7 +46,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn floor(self) -> f64 { - unsafe { intrinsics::floorf64(self) } + core::f64::floor(self) } /// Returns the smallest integer greater than or equal to `self`. @@ -68,7 +68,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ceil(self) -> f64 { - unsafe { intrinsics::ceilf64(self) } + core::f64::ceil(self) } /// Returns the nearest integer to `self`. If a value is half-way between two @@ -96,7 +96,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn round(self) -> f64 { - unsafe { intrinsics::roundf64(self) } + core::f64::round(self) } /// Returns the nearest integer to a number. Rounds half-way cases to the number @@ -122,7 +122,7 @@ impl f64 { #[stable(feature = "round_ties_even", since = "1.77.0")] #[inline] pub fn round_ties_even(self) -> f64 { - intrinsics::round_ties_even_f64(self) + core::f64::round_ties_even(self) } /// Returns the integer part of `self`. @@ -147,7 +147,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn trunc(self) -> f64 { - unsafe { intrinsics::truncf64(self) } + core::f64::trunc(self) } /// Returns the fractional part of `self`. @@ -170,7 +170,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn fract(self) -> f64 { - self - self.trunc() + core::f64::fract(self) } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding @@ -212,7 +212,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn mul_add(self, a: f64, b: f64) -> f64 { - unsafe { intrinsics::fmaf64(self, a, b) } + core::f64::mul_add(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. @@ -242,11 +242,7 @@ impl f64 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn div_euclid(self, rhs: f64) -> f64 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; - } - q + core::f64::div_euclid(self, rhs) } /// Calculates the least nonnegative remainder of `self (mod rhs)`. @@ -283,8 +279,7 @@ impl f64 { #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn rem_euclid(self, rhs: f64) -> f64 { - let r = self % rhs; - if r < 0.0 { r + rhs.abs() } else { r } + core::f64::rem_euclid(self, rhs) } /// Raises a number to an integer power. @@ -312,7 +307,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn powi(self, n: i32) -> f64 { - unsafe { intrinsics::powif64(self, n) } + core::f64::powi(self, n) } /// Raises a number to a floating point power. @@ -367,7 +362,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sqrt(self) -> f64 { - unsafe { intrinsics::sqrtf64(self) } + core::f64::sqrt(self) } /// Returns `e^(self)`, (the exponential function). @@ -599,7 +594,8 @@ impl f64 { filing an issue describing your use-case too)." )] pub fn abs_sub(self, other: f64) -> f64 { - cmath::fdim(self, other) + #[allow(deprecated)] + core::f64::abs_sub(self, other) } /// Returns the cube root of a number. @@ -626,7 +622,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cbrt(self) -> f64 { - cmath::cbrt(self) + core::f64::cbrt(self) } /// Compute the distance between the origin and a point (`x`, `y`) on the diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 0bb40ee4b317..5c1d2deb4811 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -287,6 +287,7 @@ #![feature(cfi_encoding)] #![feature(char_max_len)] #![feature(concat_idents)] +#![feature(core_float_math)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(doc_cfg)] diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index 668fd9285340..299ce1a6ff06 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -7,13 +7,9 @@ unsafe extern "C" { pub safe fn asin(n: f64) -> f64; pub safe fn atan(n: f64) -> f64; pub safe fn atan2(a: f64, b: f64) -> f64; - pub safe fn cbrt(n: f64) -> f64; - pub safe fn cbrtf(n: f32) -> f32; pub safe fn cosh(n: f64) -> f64; pub safe fn expm1(n: f64) -> f64; pub safe fn expm1f(n: f32) -> f32; - pub safe fn fdim(a: f64, b: f64) -> f64; - pub safe fn fdimf(a: f32, b: f32) -> f32; #[cfg_attr(target_env = "msvc", link_name = "_hypot")] pub safe fn hypot(x: f64, y: f64) -> f64; #[cfg_attr(target_env = "msvc", link_name = "_hypotf")] From 48f3e63f709ec4a19fa2bdce33893fdc45006e46 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 25 Feb 2025 06:09:33 +0000 Subject: [PATCH 156/245] Move float tests from std to core Many float-related tests in `std` only depend on `core`, so move the tests there. This also allows us to verify functions from `core_float_math`. Since the majority of test files need to be moved to `coretests`, move the files here without any cleanup; this is done in a followup commit. This makes git history slightly cleaner, but coretests will not build immediately after this commit. --- library/coretests/Cargo.toml | 11 +++++++++++ library/{std => coretests}/tests/floats/f128.rs | 0 library/{std => coretests}/tests/floats/f16.rs | 0 library/{std => coretests}/tests/floats/f32.rs | 0 library/{std => coretests}/tests/floats/f64.rs | 0 .../floats/lib.rs => coretests/tests/floats/mod.rs} | 0 library/coretests/tests/lib.rs | 1 + 7 files changed, 12 insertions(+) rename library/{std => coretests}/tests/floats/f128.rs (100%) rename library/{std => coretests}/tests/floats/f16.rs (100%) rename library/{std => coretests}/tests/floats/f32.rs (100%) rename library/{std => coretests}/tests/floats/f64.rs (100%) rename library/{std/tests/floats/lib.rs => coretests/tests/floats/mod.rs} (100%) diff --git a/library/coretests/Cargo.toml b/library/coretests/Cargo.toml index 7656388d24be..e0ddcd466aea 100644 --- a/library/coretests/Cargo.toml +++ b/library/coretests/Cargo.toml @@ -26,3 +26,14 @@ test = true [dev-dependencies] rand = { version = "0.9.0", default-features = false } rand_xorshift = { version = "0.4.0", default-features = false } + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # Internal features aren't marked known config by default, we use these to + # gate tests. + 'cfg(target_has_reliable_f16)', + 'cfg(target_has_reliable_f16_math)', + 'cfg(target_has_reliable_f128)', + 'cfg(target_has_reliable_f128_math)', +] diff --git a/library/std/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs similarity index 100% rename from library/std/tests/floats/f128.rs rename to library/coretests/tests/floats/f128.rs diff --git a/library/std/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs similarity index 100% rename from library/std/tests/floats/f16.rs rename to library/coretests/tests/floats/f16.rs diff --git a/library/std/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs similarity index 100% rename from library/std/tests/floats/f32.rs rename to library/coretests/tests/floats/f32.rs diff --git a/library/std/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs similarity index 100% rename from library/std/tests/floats/f64.rs rename to library/coretests/tests/floats/f64.rs diff --git a/library/std/tests/floats/lib.rs b/library/coretests/tests/floats/mod.rs similarity index 100% rename from library/std/tests/floats/lib.rs rename to library/coretests/tests/floats/mod.rs diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 0575375cf4f0..acea0b2a0356 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -144,6 +144,7 @@ mod cmp; mod const_ptr; mod convert; mod ffi; +mod floats; mod fmt; mod future; mod hash; From b9f4350fbc8198f4805557d9b0c5367ebc2390d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=81owicki?= Date: Tue, 13 May 2025 22:00:56 -0400 Subject: [PATCH 157/245] fix doc for UnixStream --- library/std/src/os/unix/net/stream.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 1cab04a454dc..1bd3bab5e373 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -307,11 +307,11 @@ impl UnixStream { /// /// ```no_run /// use std::io; - /// use std::net::UdpSocket; + /// use std::os::unix::net::UnixStream; /// use std::time::Duration; /// /// fn main() -> std::io::Result<()> { - /// let socket = UdpSocket::bind("127.0.0.1:34254")?; + /// let socket = UnixStream::connect("/tmp/sock")?; /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); /// let err = result.unwrap_err(); /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); From c610177558bc62c40bacc105c7eb4130ea66db72 Mon Sep 17 00:00:00 2001 From: Mathis Bottinelli Date: Wed, 14 May 2025 09:02:22 +0200 Subject: [PATCH 158/245] MaybeUninit::write: fix doc --- library/core/src/mem/maybe_uninit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index d0be82adb6b1..63a479ed8dd4 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -391,7 +391,7 @@ impl MaybeUninit { /// For your convenience, this also returns a mutable reference to the /// (now safely initialized) contents of `self`. /// - /// As the content is stored inside a `MaybeUninit`, the destructor is not + /// As the content is stored inside a `ManuallyDrop`, the destructor is not /// run for the inner data if the MaybeUninit leaves scope without a call to /// [`assume_init`], [`assume_init_drop`], or similar. Code that receives /// the mutable reference returned by this function needs to keep this in From dca57c6714a03fe7c7b71604c51f9b4c06ae699a Mon Sep 17 00:00:00 2001 From: xizheyin Date: Wed, 14 May 2025 15:08:17 +0800 Subject: [PATCH 159/245] Add ui test suggest-box-for-expr-field-issue-139631 Signed-off-by: xizheyin --- ...suggest-box-for-expr-field-issue-139631.rs | 14 ++++++ ...est-box-for-expr-field-issue-139631.stderr | 44 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 tests/ui/box/suggest-box-for-expr-field-issue-139631.rs create mode 100644 tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr diff --git a/tests/ui/box/suggest-box-for-expr-field-issue-139631.rs b/tests/ui/box/suggest-box-for-expr-field-issue-139631.rs new file mode 100644 index 000000000000..8d040da1ef7c --- /dev/null +++ b/tests/ui/box/suggest-box-for-expr-field-issue-139631.rs @@ -0,0 +1,14 @@ +struct X { + a: Box, +} + +struct Y { + y: Box, +} + +fn main() { + let a = 8; + let v2 = X { a }; //~ ERROR mismatched types [E0308] + let v3 = Y { y: a }; //~ ERROR mismatched types [E0308] + let v4 = Y { a }; //~ ERROR struct `Y` has no field named `a` [E0560] +} diff --git a/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr b/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr new file mode 100644 index 000000000000..167e892d11a8 --- /dev/null +++ b/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr @@ -0,0 +1,44 @@ +error[E0308]: mismatched types + --> $DIR/suggest-box-for-expr-field-issue-139631.rs:11:18 + | +LL | let v2 = X { a }; + | ^ expected `Box`, found integer + | + = note: expected struct `Box` + found type `{integer}` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +help: store this in the heap by calling `Box::new` + | +LL | let v2 = X { Box::new(a) }; + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/suggest-box-for-expr-field-issue-139631.rs:12:21 + | +LL | let v3 = Y { y: a }; + | ^ expected `Box`, found integer + | + = note: expected struct `Box` + found type `{integer}` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +help: store this in the heap by calling `Box::new` + | +LL | let v3 = Y { y: Box::new(a) }; + | +++++++++ + + +error[E0560]: struct `Y` has no field named `a` + --> $DIR/suggest-box-for-expr-field-issue-139631.rs:13:18 + | +LL | let v4 = Y { a }; + | ^ unknown field + | +help: a field with a similar name exists + | +LL - let v4 = Y { a }; +LL + let v4 = Y { y }; + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0560. +For more information about an error, try `rustc --explain E0308`. From 32be4599090d62cd7f70767e27c289583fd6501d Mon Sep 17 00:00:00 2001 From: xizheyin Date: Wed, 14 May 2025 15:14:07 +0800 Subject: [PATCH 160/245] Suggest replace `f` with `f: Box` when expr field is short hand Signed-off-by: xizheyin --- compiler/rustc_hir_typeck/src/errors.rs | 12 ++++++++++++ compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 9 +++++++++ .../suggest-box-for-expr-field-issue-139631.stderr | 4 ++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 732795535087..06103fe1c91b 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -680,6 +680,18 @@ pub(crate) enum SuggestBoxing { hir_typeck_suggest_boxing_when_appropriate, applicability = "machine-applicable" )] + ExprFieldShorthand { + #[suggestion_part(code = "{ident}: Box::new(")] + start: Span, + #[suggestion_part(code = ")")] + end: Span, + ident: Ident, + }, + #[note(hir_typeck_suggest_boxing_note)] + #[multipart_suggestion( + hir_typeck_suggest_boxing_when_appropriate, + applicability = "machine-applicable" + )] Other { #[suggestion_part(code = "Box::new(")] start: Span, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 91eb1989864f..251801f479ef 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -585,6 +585,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { errors::SuggestBoxing::AsyncBody } + _ if let Node::ExprField(expr_field) = self.tcx.parent_hir_node(hir_id) + && expr_field.is_shorthand => + { + errors::SuggestBoxing::ExprFieldShorthand { + start: span.shrink_to_lo(), + end: span.shrink_to_hi(), + ident: expr_field.ident, + } + } _ => errors::SuggestBoxing::Other { start: span.shrink_to_lo(), end: span.shrink_to_hi(), diff --git a/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr b/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr index 167e892d11a8..01bd0523a162 100644 --- a/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr +++ b/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr @@ -9,8 +9,8 @@ LL | let v2 = X { a }; = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html help: store this in the heap by calling `Box::new` | -LL | let v2 = X { Box::new(a) }; - | +++++++++ + +LL | let v2 = X { a: Box::new(a) }; + | ++++++++++++ + error[E0308]: mismatched types --> $DIR/suggest-box-for-expr-field-issue-139631.rs:12:21 From 7b1ba425e7b29e81b0d9d2e8354fc0164b134de3 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 13 May 2025 21:22:45 +0200 Subject: [PATCH 161/245] Add `Ipv4Addr` and `Ipv6Addr` diagnostic items They will be used in Clippy to detect runtime parsing of known-valid IP addresses. --- compiler/rustc_span/src/symbol.rs | 2 ++ library/core/src/net/ip_addr.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3a95447308a9..efae6250b072 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -280,6 +280,8 @@ symbols! { IoSeek, IoWrite, IpAddr, + Ipv4Addr, + Ipv6Addr, IrTyKind, Is, Item, diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 2f027be69286..aaa68e8d7d1a 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -68,6 +68,7 @@ pub enum IpAddr { /// assert!("0000000.0.0.0".parse::().is_err()); // first octet is a zero in octal /// assert!("0xcb.0x0.0x71.0x00".parse::().is_err()); // all octets are in hex /// ``` +#[rustc_diagnostic_item = "Ipv4Addr"] #[derive(Copy, Clone, PartialEq, Eq)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Ipv4Addr { @@ -160,6 +161,7 @@ impl Hash for Ipv4Addr { /// assert_eq!("::1".parse(), Ok(localhost)); /// assert_eq!(localhost.is_loopback(), true); /// ``` +#[rustc_diagnostic_item = "Ipv6Addr"] #[derive(Copy, Clone, PartialEq, Eq)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Ipv6Addr { From e011c43bcf3bc01e04738b47b7d8dc9affda8659 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 9 May 2025 15:36:22 +0000 Subject: [PATCH 162/245] Merge mir query analysis invocations The reasons I'm doing it is that * merging those blocks allows for more parallelism as you don't run parallel blocks in sequence * merging blocks allows merging analysis queries shrinking the dep graph * should allow us to do more early aborting in case of errors and/or moving query calls from the analysis query into others that allow early aborting the others (and doing more tainting and stuff) --- compiler/rustc_interface/src/passes.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 7ed32d559c3a..6932b09f0cda 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1002,10 +1002,6 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { if !tcx.is_typeck_child(def_id.to_def_id()) { tcx.ensure_ok().mir_borrowck(def_id) } - }); - }); - sess.time("MIR_effect_checking", || { - tcx.par_hir_body_owners(|def_id| { tcx.ensure_ok().has_ffi_unwind_calls(def_id); // If we need to codegen, ensure that we emit all errors from From e1f1878da550408e800742217881730c2b06d1ec Mon Sep 17 00:00:00 2001 From: B I Mohammed Abbas Date: Wed, 14 May 2025 13:52:04 +0530 Subject: [PATCH 163/245] Fix set_name for vxworks. Length of name should be truncated to VX_TASK_RENAME_LENGTH-1 --- library/std/src/sys/pal/unix/thread.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index afda7c65e108..d8b189413f4a 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -222,16 +222,8 @@ impl Thread { #[cfg(target_os = "vxworks")] pub fn set_name(name: &CStr) { - // FIXME(libc): adding real STATUS, ERROR type eventually. - unsafe extern "C" { - fn taskNameSet(task_id: libc::TASK_ID, task_name: *mut libc::c_char) -> libc::c_int; - } - - // VX_TASK_NAME_LEN is 31 in VxWorks 7. - const VX_TASK_NAME_LEN: usize = 31; - - let mut name = truncate_cstr::<{ VX_TASK_NAME_LEN }>(name); - let res = unsafe { taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; + let mut name = truncate_cstr::<{ libc::VX_TASK_RENAME_LENGTH - 1 }>(name); + let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; debug_assert_eq!(res, libc::OK); } From 544c8ce535c834d726b5dd252046aa5eeb1bf5ec Mon Sep 17 00:00:00 2001 From: B I Mohammed Abbas Date: Wed, 14 May 2025 13:52:23 +0530 Subject: [PATCH 164/245] Fix settimes for vxworks --- library/std/src/sys/fs/unix.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 863358596c19..a3e520fdeef4 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1498,11 +1498,10 @@ impl File { None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), }; cfg_if::cfg_if! { - if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks", target_os = "nuttx"))] { + if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx"))] { // Redox doesn't appear to support `UTIME_OMIT`. // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore // the same as for Redox. - // `futimens` and `UTIME_OMIT` are a work in progress for vxworks. let _ = times; Err(io::const_error!( io::ErrorKind::Unsupported, From 488e4e53d25b01b01e09fbf8b2745fd32db2c6ab Mon Sep 17 00:00:00 2001 From: SLUCHABLUB <81807027+SLUCHABLUB@users.noreply.github.com> Date: Wed, 14 May 2025 10:49:01 +0200 Subject: [PATCH 165/245] Add the `allow_exact_repetitions` option to the `module_name_repetitions` lint. --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 10 +++++++++ clippy_config/src/conf.rs | 3 +++ clippy_lints/src/item_name_repetitions.rs | 22 ++++++++++++++----- .../allow_exact_repetitions/clippy.toml | 1 + .../item_name_repetitions.rs | 13 +++++++++++ .../item_name_repetitions.stderr | 11 ++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 3 +++ 8 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml create mode 100644 tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs create mode 100644 tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index d707afb01997..28147dfbea3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6483,6 +6483,7 @@ Released 2018-09-13 [`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement [`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero [`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests +[`allow-exact-repetitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-exact-repetitions [`allow-expect-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-consts [`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests [`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 282d892951cf..0db4182dbcdb 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -71,6 +71,16 @@ Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` * [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro) +## `allow-exact-repetitions` +Whether an item should be allowed to have the same name as its containing module + +**Default Value:** `true` + +--- +**Affected lints:** +* [`module_name_repetitions`](https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions) + + ## `allow-expect-in-consts` Whether `expect` should be allowed in code always evaluated at compile time diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 8a1d38ed600e..ad0aea39d41a 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -360,6 +360,9 @@ define_Conf! { /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` #[lints(dbg_macro)] allow_dbg_in_tests: bool = false, + /// Whether an item should be allowed to have the same name as its containing module + #[lints(module_name_repetitions)] + allow_exact_repetitions: bool = true, /// Whether `expect` should be allowed in code always evaluated at compile time #[lints(expect_used)] allow_expect_in_consts: bool = true, diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 30f61af29e59..3d4dcd020702 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -162,6 +162,7 @@ pub struct ItemNameRepetitions { enum_threshold: u64, struct_threshold: u64, avoid_breaking_exported_api: bool, + allow_exact_repetitions: bool, allow_private_module_inception: bool, allowed_prefixes: FxHashSet, } @@ -173,6 +174,7 @@ impl ItemNameRepetitions { enum_threshold: conf.enum_variant_name_threshold, struct_threshold: conf.struct_field_name_threshold, avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + allow_exact_repetitions: conf.allow_exact_repetitions, allow_private_module_inception: conf.allow_private_module_inception, allowed_prefixes: conf.allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(), } @@ -486,11 +488,21 @@ impl LateLintPass<'_> for ItemNameRepetitions { } // The `module_name_repetitions` lint should only trigger if the item has the module in its - // name. Having the same name is accepted. - if cx.tcx.visibility(item.owner_id).is_public() - && cx.tcx.visibility(mod_owner_id.def_id).is_public() - && item_camel.len() > mod_camel.len() - { + // name. Having the same name is only accepted if `allow_exact_repetition` is set to `true`. + + let both_are_public = + cx.tcx.visibility(item.owner_id).is_public() && cx.tcx.visibility(mod_owner_id.def_id).is_public(); + + if both_are_public && !self.allow_exact_repetitions && item_camel == *mod_camel { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name is the same as its containing module's name", + ); + } + + if both_are_public && item_camel.len() > mod_camel.len() { let matching = count_match_start(mod_camel, &item_camel); let rmatching = count_match_end(mod_camel, &item_camel); let nchars = mod_camel.chars().count(); diff --git a/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml new file mode 100644 index 000000000000..3ed7cedbd147 --- /dev/null +++ b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml @@ -0,0 +1 @@ +allow-exact-repetitions = false diff --git a/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs new file mode 100644 index 000000000000..20603766624c --- /dev/null +++ b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs @@ -0,0 +1,13 @@ +#![warn(clippy::module_name_repetitions)] +#![allow(dead_code)] + +pub mod foo { + // this line should produce a warning: + pub fn foo() {} + //~^ module_name_repetitions + + // but this line shouldn't + pub fn to_foo() {} +} + +fn main() {} diff --git a/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr new file mode 100644 index 000000000000..8e6f726d02c0 --- /dev/null +++ b/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr @@ -0,0 +1,11 @@ +error: item name is the same as its containing module's name + --> tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs:6:12 + | +LL | pub fn foo() {} + | ^^^ + | + = note: `-D clippy::module-name-repetitions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]` + +error: aborting due to 1 previous error + 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 0a36cd3cf26d..6ee77ebd8ece 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -5,6 +5,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests @@ -98,6 +99,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests @@ -191,6 +193,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests From 878a1732bc14e33f14ae2350d4a4ba75f7992035 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Mon, 12 May 2025 11:52:18 -0400 Subject: [PATCH 166/245] Fix for the fminimum intrinsics --- src/intrinsic/mod.rs | 113 +++++++++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 37 deletions(-) diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs index f292c467418f..4e5018ae0118 100644 --- a/src/intrinsic/mod.rs +++ b/src/intrinsic/mod.rs @@ -72,44 +72,8 @@ fn get_simple_intrinsic<'gcc, 'tcx>( sym::fabsf64 => "fabs", sym::minnumf32 => "fminf", sym::minnumf64 => "fmin", - sym::minimumf32 => "fminimumf", - sym::minimumf64 => "fminimum", - sym::minimumf128 => { - // GCC doesn't have the intrinsic we want so we use the compiler-builtins one - // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html - let f128_type = cx.type_f128(); - return Some(cx.context.new_function( - None, - FunctionType::Extern, - f128_type, - &[ - cx.context.new_parameter(None, f128_type, "a"), - cx.context.new_parameter(None, f128_type, "b"), - ], - "fminimumf128", - false, - )); - } sym::maxnumf32 => "fmaxf", sym::maxnumf64 => "fmax", - sym::maximumf32 => "fmaximumf", - sym::maximumf64 => "fmaximum", - sym::maximumf128 => { - // GCC doesn't have the intrinsic we want so we use the compiler-builtins one - // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html - let f128_type = cx.type_f128(); - return Some(cx.context.new_function( - None, - FunctionType::Extern, - f128_type, - &[ - cx.context.new_parameter(None, f128_type, "a"), - cx.context.new_parameter(None, f128_type, "b"), - ], - "fmaximumf128", - false, - )); - } sym::copysignf32 => "copysignf", sym::copysignf64 => "copysign", sym::copysignf128 => "copysignl", @@ -130,6 +94,72 @@ fn get_simple_intrinsic<'gcc, 'tcx>( Some(cx.context.get_builtin_function(gcc_name)) } +// TODO(antoyo): We can probably remove these and use the fallback intrinsic implementation. +fn get_simple_function<'gcc, 'tcx>( + cx: &CodegenCx<'gcc, 'tcx>, + name: Symbol, +) -> Option> { + let (return_type, parameters, func_name) = match name { + sym::minimumf32 => { + let parameters = [ + cx.context.new_parameter(None, cx.float_type, "a"), + cx.context.new_parameter(None, cx.float_type, "b"), + ]; + (cx.float_type, parameters, "fminimumf") + } + sym::minimumf64 => { + let parameters = [ + cx.context.new_parameter(None, cx.double_type, "a"), + cx.context.new_parameter(None, cx.double_type, "b"), + ]; + (cx.double_type, parameters, "fminimum") + } + sym::minimumf128 => { + let f128_type = cx.type_f128(); + // GCC doesn't have the intrinsic we want so we use the compiler-builtins one + // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html + let parameters = [ + cx.context.new_parameter(None, f128_type, "a"), + cx.context.new_parameter(None, f128_type, "b"), + ]; + (f128_type, parameters, "fminimumf128") + } + sym::maximumf32 => { + let parameters = [ + cx.context.new_parameter(None, cx.float_type, "a"), + cx.context.new_parameter(None, cx.float_type, "b"), + ]; + (cx.float_type, parameters, "fmaximumf") + } + sym::maximumf64 => { + let parameters = [ + cx.context.new_parameter(None, cx.double_type, "a"), + cx.context.new_parameter(None, cx.double_type, "b"), + ]; + (cx.double_type, parameters, "fmaximum") + } + sym::maximumf128 => { + let f128_type = cx.type_f128(); + // GCC doesn't have the intrinsic we want so we use the compiler-builtins one + // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html + let parameters = [ + cx.context.new_parameter(None, f128_type, "a"), + cx.context.new_parameter(None, f128_type, "b"), + ]; + (f128_type, parameters, "fmaximumf128") + } + _ => return None, + }; + Some(cx.context.new_function( + None, + FunctionType::Extern, + return_type, + ¶meters, + func_name, + false, + )) +} + impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn codegen_intrinsic_call( &mut self, @@ -158,6 +188,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); let simple = get_simple_intrinsic(self, name); + let simple_func = get_simple_function(self, name); // FIXME(tempdragon): Re-enable `clippy::suspicious_else_formatting` if the following issue is solved: // https://github.com/rust-lang/rust-clippy/issues/12497 @@ -165,7 +196,15 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc #[allow(clippy::suspicious_else_formatting)] let value = match name { _ if simple.is_some() => { - let func = simple.expect("simple function"); + let func = simple.expect("simple intrinsic function"); + self.cx.context.new_call( + self.location, + func, + &args.iter().map(|arg| arg.immediate()).collect::>(), + ) + } + _ if simple_func.is_some() => { + let func = simple_func.expect("simple function"); self.cx.context.new_call( self.location, func, From 44af0a8b6c7be8bc9411ec8539729cf94f2b1a20 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Wed, 14 May 2025 04:59:23 -0400 Subject: [PATCH 167/245] Stop ignoring test_mm512_stream_ps stdarch test --- .github/workflows/stdarch.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/stdarch.yml b/.github/workflows/stdarch.yml index 4b9f48e7b183..0d92a6a37ff8 100644 --- a/.github/workflows/stdarch.yml +++ b/.github/workflows/stdarch.yml @@ -101,9 +101,8 @@ jobs: if: ${{ matrix.cargo_runner }} run: | # FIXME: these tests fail when the sysroot is compiled with LTO because of a missing symbol in proc-macro. - # TODO: remove --skip test_mm512_stream_ps when stdarch is updated in rustc. # TODO: remove --skip test_tile_ when it's implemented. - STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features --cfg stdarch_intel_sde" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml -- --skip rtm --skip tbm --skip sse4a --skip test_mm512_stream_ps --skip test_tile_ + STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features --cfg stdarch_intel_sde" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml -- --skip rtm --skip tbm --skip sse4a --skip test_tile_ # Summary job for the merge queue. # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! From f38d6d0a8fdeee797a6e32a04bd0895bc4f7b322 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Wed, 14 May 2025 04:59:02 -0400 Subject: [PATCH 168/245] Fix for xsave test on Intel SDE --- .github/workflows/stdarch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stdarch.yml b/.github/workflows/stdarch.yml index 0d92a6a37ff8..f26ac3b755fb 100644 --- a/.github/workflows/stdarch.yml +++ b/.github/workflows/stdarch.yml @@ -102,7 +102,7 @@ jobs: run: | # FIXME: these tests fail when the sysroot is compiled with LTO because of a missing symbol in proc-macro. # TODO: remove --skip test_tile_ when it's implemented. - STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features --cfg stdarch_intel_sde" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml -- --skip rtm --skip tbm --skip sse4a --skip test_tile_ + STDARCH_TEST_SKIP_FUNCTION="xsave,xsaveopt,xsave64,xsaveopt64" STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml -- --skip rtm --skip tbm --skip sse4a --skip test_tile_ # Summary job for the merge queue. # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! From 1267333ef102d854cf0cefef877ba0d9adb07107 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 14 May 2025 13:32:41 +0100 Subject: [PATCH 169/245] Improve ternary operator recovery --- compiler/rustc_parse/messages.ftl | 3 +- compiler/rustc_parse/src/errors.rs | 20 ++++++++++- .../rustc_parse/src/parser/diagnostics.rs | 34 ++++++++++++++----- compiler/rustc_parse/src/parser/stmt.rs | 7 +++- tests/ui/parser/ternary_operator.rs | 6 ++++ tests/ui/parser/ternary_operator.stderr | 22 +++++++++--- 6 files changed, 75 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index f88c15785d33..a6919afef12c 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -815,7 +815,6 @@ parse_switch_ref_box_order = switch the order of `ref` and `box` .suggestion = swap them parse_ternary_operator = Rust has no ternary operator - .help = use an `if-else` expression instead parse_tilde_is_not_unary_operator = `~` cannot be used as a unary operator .suggestion = use `!` to perform bitwise not @@ -963,6 +962,8 @@ parse_use_empty_block_not_semi = expected { "`{}`" }, found `;` parse_use_eq_instead = unexpected `==` .suggestion = try using `=` instead +parse_use_if_else = use an `if-else` expression instead + parse_use_let_not_auto = write `let` instead of `auto` to introduce a new variable parse_use_let_not_var = write `let` instead of `var` to introduce a new variable diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 766baf6f80c7..31a48b22cfee 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -436,10 +436,28 @@ pub(crate) enum IfExpressionMissingThenBlockSub { #[derive(Diagnostic)] #[diag(parse_ternary_operator)] -#[help] pub(crate) struct TernaryOperator { #[primary_span] pub span: Span, + /// If we have a span for the condition expression, suggest the if/else + #[subdiagnostic] + pub sugg: Option, + /// Otherwise, just print the suggestion message + #[help(parse_use_if_else)] + pub no_sugg: bool, +} + +#[derive(Subdiagnostic, Copy, Clone)] +#[multipart_suggestion(parse_use_if_else, applicability = "maybe-incorrect", style = "verbose")] +pub(crate) struct TernaryOperatorSuggestion { + #[suggestion_part(code = "if ")] + pub before_cond: Span, + #[suggestion_part(code = "{{")] + pub question: Span, + #[suggestion_part(code = "}} else {{")] + pub colon: Span, + #[suggestion_part(code = " }}")] + pub end: Span, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 23c8db7bca78..6277dde7c974 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -41,8 +41,9 @@ use crate::errors::{ IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, - TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, - UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, + TernaryOperator, TernaryOperatorSuggestion, UnexpectedConstInGenericParam, + UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, + UseEqInstead, WrapType, }; use crate::parser::attr::InnerAttrPolicy; use crate::{exp, fluent_generated as fluent}; @@ -497,7 +498,7 @@ impl<'a> Parser<'a> { // If the user is trying to write a ternary expression, recover it and // return an Err to prevent a cascade of irrelevant diagnostics. if self.prev_token == token::Question - && let Err(e) = self.maybe_recover_from_ternary_operator() + && let Err(e) = self.maybe_recover_from_ternary_operator(None) { return Err(e); } @@ -1602,12 +1603,18 @@ impl<'a> Parser<'a> { /// Rust has no ternary operator (`cond ? then : else`). Parse it and try /// to recover from it if `then` and `else` are valid expressions. Returns /// an err if this appears to be a ternary expression. - pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> PResult<'a, ()> { + /// If we have the span of the condition, we can provide a better error span + /// and code suggestion. + pub(super) fn maybe_recover_from_ternary_operator( + &mut self, + cond: Option, + ) -> PResult<'a, ()> { if self.prev_token != token::Question { return PResult::Ok(()); } - let lo = self.prev_token.span.lo(); + let question = self.prev_token.span; + let lo = cond.unwrap_or(question).lo(); let snapshot = self.create_snapshot_for_diagnostic(); if match self.parse_expr() { @@ -1620,11 +1627,20 @@ impl<'a> Parser<'a> { } } { if self.eat_noexpect(&token::Colon) { + let colon = self.prev_token.span; match self.parse_expr() { - Ok(_) => { - return Err(self - .dcx() - .create_err(TernaryOperator { span: self.token.span.with_lo(lo) })); + Ok(expr) => { + let sugg = cond.map(|cond| TernaryOperatorSuggestion { + before_cond: cond.shrink_to_lo(), + question, + colon, + end: expr.span.shrink_to_hi(), + }); + return Err(self.dcx().create_err(TernaryOperator { + span: self.prev_token.span.with_lo(lo), + sugg, + no_sugg: sugg.is_none(), + })); } Err(err) => { err.cancel(); diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 885a65d4de7a..396ded96bde1 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -879,7 +879,12 @@ impl<'a> Parser<'a> { { // Just check for errors and recover; do not eat semicolon yet. - let expect_result = self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)]); + let expect_result = + if let Err(e) = self.maybe_recover_from_ternary_operator(Some(expr.span)) { + Err(e) + } else { + self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)]) + }; // Try to both emit a better diagnostic, and avoid further errors by replacing // the `expr` with `ExprKind::Err`. diff --git a/tests/ui/parser/ternary_operator.rs b/tests/ui/parser/ternary_operator.rs index c8810781b3d8..08f6a4b2a244 100644 --- a/tests/ui/parser/ternary_operator.rs +++ b/tests/ui/parser/ternary_operator.rs @@ -28,3 +28,9 @@ fn main() { //~| HELP use an `if-else` expression instead //~| ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `:` } + +fn expr(a: u64, b: u64) -> u64 { + a > b ? a : b + //~^ ERROR Rust has no ternary operator + //~| HELP use an `if-else` expression instead +} diff --git a/tests/ui/parser/ternary_operator.stderr b/tests/ui/parser/ternary_operator.stderr index e12a7ff3718e..d4a633e5e557 100644 --- a/tests/ui/parser/ternary_operator.stderr +++ b/tests/ui/parser/ternary_operator.stderr @@ -2,7 +2,7 @@ error: Rust has no ternary operator --> $DIR/ternary_operator.rs:2:19 | LL | let x = 5 > 2 ? true : false; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead @@ -10,7 +10,7 @@ error: Rust has no ternary operator --> $DIR/ternary_operator.rs:8:19 | LL | let x = 5 > 2 ? { true } : { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead @@ -18,7 +18,7 @@ error: Rust has no ternary operator --> $DIR/ternary_operator.rs:14:19 | LL | let x = 5 > 2 ? f32::MAX : f32::MIN; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead @@ -38,9 +38,21 @@ error: Rust has no ternary operator --> $DIR/ternary_operator.rs:26:19 | LL | let x = 5 > 2 ? { let x = vec![]: Vec; x } : { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead -error: aborting due to 6 previous errors +error: Rust has no ternary operator + --> $DIR/ternary_operator.rs:33:5 + | +LL | a > b ? a : b + | ^^^^^^^^^^^^^ + | +help: use an `if-else` expression instead + | +LL - a > b ? a : b +LL + if a > b { a } else { b } + | + +error: aborting due to 7 previous errors From 2b9256e1c8454255441983446ca6bb63a6d8a199 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 25 Feb 2025 06:06:18 +0000 Subject: [PATCH 170/245] Move applicable float tests from `coretests` back to `std` The previous commit moved all test files from `std` to `core` so git understands the move. Not all functionality is actually testable in `core`, however, so perform move the relevant portions back. Changes from inherent to module methods is also done since this is the form of math operations available in `core` (as `core_float_math`). --- library/coretests/tests/floats/f128.rs | 287 +----------------- library/coretests/tests/floats/f16.rs | 271 +---------------- library/coretests/tests/floats/f32.rs | 390 +++++-------------------- library/coretests/tests/floats/f64.rs | 363 ++++------------------- library/coretests/tests/floats/mod.rs | 4 - library/coretests/tests/lib.rs | 6 + library/std/tests/floats/f128.rs | 321 ++++++++++++++++++++ library/std/tests/floats/f16.rs | 300 +++++++++++++++++++ library/std/tests/floats/f32.rs | 253 ++++++++++++++++ library/std/tests/floats/f64.rs | 249 ++++++++++++++++ library/std/tests/floats/lib.rs | 43 +++ 11 files changed, 1314 insertions(+), 1173 deletions(-) create mode 100644 library/std/tests/floats/f128.rs create mode 100644 library/std/tests/floats/f16.rs create mode 100644 library/std/tests/floats/f32.rs create mode 100644 library/std/tests/floats/f64.rs create mode 100644 library/std/tests/floats/lib.rs diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index c2618f3b315e..12cf651f03f4 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -10,19 +10,13 @@ use std::ops::{Add, Div, Mul, Sub}; // Note these tolerances make sense around zero, but not for more extreme exponents. -/// For operations that are near exact, usually not involving math of different -/// signs. -const TOL_PRECISE: f128 = 1e-28; - /// Default tolerances. Works for values that should be near precise but not exact. Roughly /// the precision carried by `100 * 100`. const TOL: f128 = 1e-12; -/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained -/// operations. -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -const TOL_IMPR: f128 = 1e-10; +/// For operations that are near exact, usually not involving math of different +/// signs. +const TOL_PRECISE: f128 = 1e-28; /// Smallest number const TINY_BITS: u128 = 0x1; @@ -500,8 +494,6 @@ fn test_recip() { assert_eq!(neg_inf.recip(), 0.0); } -// Many math functions allow for less accurate results, so the next tolerance up is used - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f128_math)] @@ -518,24 +510,6 @@ fn test_powi() { assert_eq!(neg_inf.powi(2), inf); } -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_powf() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.powf(1.0), 1.0); - assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR); - assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR); - assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR); - assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR); - assert_eq!(8.3f128.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f128_math)] @@ -549,111 +523,6 @@ fn test_sqrt_domain() { assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); } -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_exp() { - assert_eq!(1.0, 0.0f128.exp()); - assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); - assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_exp2() { - assert_eq!(32.0, 5.0f128.exp2()); - assert_eq!(1.0, 0.0f128.exp2()); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_ln() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f128).ln().is_nan()); - assert_eq!((-0.0f128).ln(), neg_inf); - assert_eq!(0.0f128.ln(), neg_inf); - assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_log() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(10.0f128.log(10.0), 1.0); - assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL); - assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0); - assert!(1.0f128.log(1.0).is_nan()); - assert!(1.0f128.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f128).log(0.1).is_nan()); - assert_eq!((-0.0f128).log(2.0), neg_inf); - assert_eq!(0.0f128.log(7.0), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_log2() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL); - assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL); - assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f128).log2().is_nan()); - assert_eq!((-0.0f128).log2(), neg_inf); - assert_eq!(0.0f128.log2(), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_log10() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(10.0f128.log10(), 1.0); - assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL); - assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL); - assert_eq!(1.0f128.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f128).log10().is_nan()); - assert_eq!((-0.0f128).log10(), neg_inf); - assert_eq!(0.0f128.log10(), neg_inf); -} - #[test] fn test_to_degrees() { let pi: f128 = consts::PI; @@ -686,156 +555,6 @@ fn test_to_radians() { assert_eq!(neg_inf.to_radians(), neg_inf); } -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_asinh() { - // Lower accuracy results are allowed, use increased tolerances - assert_eq!(0.0f128.asinh(), 0.0f128); - assert_eq!((-0.0f128).asinh(), -0.0f128); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f128).asinh().is_sign_negative()); - - // issue 63271 - assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR); - assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!( - (-67452098.07139316f128).asinh(), - -18.720075426274544393985484294000831757220, - TOL_IMPR - ); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_acosh() { - assert_eq!(1.0f128.acosh(), 0.0f128); - assert!(0.999f128.acosh().is_nan()); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR); - assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_atanh() { - assert_eq!(0.0f128.atanh(), 0.0f128); - assert_eq!((-0.0f128).atanh(), -0.0f128); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(1.0f128.atanh(), inf); - assert_eq!((-1.0f128).atanh(), neg_inf); - assert!(2f128.atanh().atanh().is_nan()); - assert!((-2f128).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR); - assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_gamma() { - // precision can differ among platforms - assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); - assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR); - assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR); - assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR); - assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR); - assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR); - assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR); - assert_eq!(0.0f128.gamma(), f128::INFINITY); - assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY); - assert!((-1.0f128).gamma().is_nan()); - assert!((-2.0f128).gamma().is_nan()); - assert!(f128::NAN.gamma().is_nan()); - assert!(f128::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f128::INFINITY.gamma(), f128::INFINITY); - assert_eq!(1760.9f128.gamma(), f128::INFINITY); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_ln_gamma() { - assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); - assert_eq!(1.0f128.ln_gamma().1, 1); - assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); - assert_eq!(2.0f128.ln_gamma().1, 1); - assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR); - assert_eq!(3.0f128.ln_gamma().1, 1); - assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR); - assert_eq!((-0.5f128).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - let pi: f128 = consts::PI; - let frac_pi_2: f128 = consts::FRAC_PI_2; - let frac_pi_3: f128 = consts::FRAC_PI_3; - let frac_pi_4: f128 = consts::FRAC_PI_4; - let frac_pi_6: f128 = consts::FRAC_PI_6; - let frac_pi_8: f128 = consts::FRAC_PI_8; - let frac_1_pi: f128 = consts::FRAC_1_PI; - let frac_2_pi: f128 = consts::FRAC_2_PI; - - assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE); - assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); - assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); - - #[cfg(not(miri))] - #[cfg(target_has_reliable_f128_math)] - { - let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; - let sqrt2: f128 = consts::SQRT_2; - let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; - let e: f128 = consts::E; - let log2_e: f128 = consts::LOG2_E; - let log10_e: f128 = consts::LOG10_E; - let ln_2: f128 = consts::LN_2; - let ln_10: f128 = consts::LN_10; - - assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE); - assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE); - assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE); - assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE); - assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE); - assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE); - assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); - } -} - #[test] fn test_float_bits_conv() { assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000); diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index 70bbcd07160e..db98181226c8 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -54,7 +54,7 @@ macro_rules! assert_f16_biteq { #[test] fn test_num_f16() { - crate::test_num(10f16, 2f16); + super::test_num(10f16, 2f16); } // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support @@ -492,24 +492,6 @@ fn test_powi() { assert_eq!(neg_inf.powi(2), inf); } -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_powf() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(1.0f16.powf(1.0), 1.0); - assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2); - assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2); - assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2); - assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2); - assert_eq!(8.3f16.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f16_math)] @@ -523,111 +505,6 @@ fn test_sqrt_domain() { assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); } -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_exp() { - assert_eq!(1.0, 0.0f16.exp()); - assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); - assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_exp2() { - assert_eq!(32.0, 5.0f16.exp2()); - assert_eq!(1.0, 0.0f16.exp2()); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_ln() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f16).ln().is_nan()); - assert_eq!((-0.0f16).ln(), neg_inf); - assert_eq!(0.0f16.ln(), neg_inf); - assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_log() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(10.0f16.log(10.0), 1.0); - assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0); - assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0); - assert!(1.0f16.log(1.0).is_nan()); - assert!(1.0f16.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f16).log(0.1).is_nan()); - assert_eq!((-0.0f16).log(2.0), neg_inf); - assert_eq!(0.0f16.log(7.0), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_log2() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0); - assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0); - assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f16).log2().is_nan()); - assert_eq!((-0.0f16).log2(), neg_inf); - assert_eq!(0.0f16.log2(), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_log10() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(10.0f16.log10(), 1.0); - assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0); - assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0); - assert_eq!(1.0f16.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f16).log10().is_nan()); - assert_eq!((-0.0f16).log10(), neg_inf); - assert_eq!(0.0f16.log10(), neg_inf); -} - #[test] fn test_to_degrees() { let pi: f16 = consts::PI; @@ -658,152 +535,6 @@ fn test_to_radians() { assert_eq!(neg_inf.to_radians(), neg_inf); } -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_asinh() { - assert_eq!(0.0f16.asinh(), 0.0f16); - assert_eq!((-0.0f16).asinh(), -0.0f16); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f16).asinh().is_sign_negative()); - // issue 63271 - assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0); - assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0); - - // test for low accuracy from issue 104548 - assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_acosh() { - assert_eq!(1.0f16.acosh(), 0.0f16); - assert!(0.999f16.acosh().is_nan()); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0); - assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0); - - // test for low accuracy from issue 104548 - assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_atanh() { - assert_eq!(0.0f16.atanh(), 0.0f16); - assert_eq!((-0.0f16).atanh(), -0.0f16); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(1.0f16.atanh(), inf); - assert_eq!((-1.0f16).atanh(), neg_inf); - assert!(2f16.atanh().atanh().is_nan()); - assert!((-2f16).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0); - assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_gamma() { - // precision can differ among platforms - assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); - assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0); - assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0); - assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0); - assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0); - assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0); - assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0); - assert_eq!(0.0f16.gamma(), f16::INFINITY); - assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY); - assert!((-1.0f16).gamma().is_nan()); - assert!((-2.0f16).gamma().is_nan()); - assert!(f16::NAN.gamma().is_nan()); - assert!(f16::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f16::INFINITY.gamma(), f16::INFINITY); - assert_eq!(171.71f16.gamma(), f16::INFINITY); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_ln_gamma() { - assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); - assert_eq!(1.0f16.ln_gamma().1, 1); - assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0); - assert_eq!(2.0f16.ln_gamma().1, 1); - assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0); - assert_eq!(3.0f16.ln_gamma().1, 1); - assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0); - assert_eq!((-0.5f16).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - // FIXME(f16_f128): add math tests when available - - let pi: f16 = consts::PI; - let frac_pi_2: f16 = consts::FRAC_PI_2; - let frac_pi_3: f16 = consts::FRAC_PI_3; - let frac_pi_4: f16 = consts::FRAC_PI_4; - let frac_pi_6: f16 = consts::FRAC_PI_6; - let frac_pi_8: f16 = consts::FRAC_PI_8; - let frac_1_pi: f16 = consts::FRAC_1_PI; - let frac_2_pi: f16 = consts::FRAC_2_PI; - - assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0); - assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0); - assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0); - assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0); - assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0); - assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); - assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); - - #[cfg(not(miri))] - #[cfg(target_has_reliable_f16_math)] - { - let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; - let sqrt2: f16 = consts::SQRT_2; - let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; - let e: f16 = consts::E; - let log2_e: f16 = consts::LOG2_E; - let log10_e: f16 = consts::LOG10_E; - let ln_2: f16 = consts::LN_2; - let ln_10: f16 = consts::LN_10; - - assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0); - assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0); - assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0); - assert_approx_eq!(log2_e, e.log2(), TOL_0); - assert_approx_eq!(log10_e, e.log10(), TOL_0); - assert_approx_eq!(ln_2, 2f16.ln(), TOL_0); - assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); - } -} - #[test] fn test_float_bits_conv() { assert_eq!((1f16).to_bits(), 0x3c00); diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 9af23afc5bbf..1c018a5e7b52 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -1,5 +1,6 @@ -use std::f32::consts; -use std::num::FpCategory as Fp; +use core::f32; +use core::f32::consts; +use core::num::FpCategory as Fp; /// Smallest number const TINY_BITS: u32 = 0x1; @@ -35,7 +36,7 @@ macro_rules! assert_f32_biteq { #[test] fn test_num_f32() { - crate::test_num(10f32, 2f32); + super::test_num(10f32, 2f32); } #[test] @@ -214,88 +215,88 @@ fn test_classify() { #[test] fn test_floor() { - assert_approx_eq!(1.0f32.floor(), 1.0f32); - assert_approx_eq!(1.3f32.floor(), 1.0f32); - assert_approx_eq!(1.5f32.floor(), 1.0f32); - assert_approx_eq!(1.7f32.floor(), 1.0f32); - assert_approx_eq!(0.0f32.floor(), 0.0f32); - assert_approx_eq!((-0.0f32).floor(), -0.0f32); - assert_approx_eq!((-1.0f32).floor(), -1.0f32); - assert_approx_eq!((-1.3f32).floor(), -2.0f32); - assert_approx_eq!((-1.5f32).floor(), -2.0f32); - assert_approx_eq!((-1.7f32).floor(), -2.0f32); + assert_approx_eq!(f32::floor(1.0f32), 1.0f32); + assert_approx_eq!(f32::floor(1.3f32), 1.0f32); + assert_approx_eq!(f32::floor(1.5f32), 1.0f32); + assert_approx_eq!(f32::floor(1.7f32), 1.0f32); + assert_approx_eq!(f32::floor(0.0f32), 0.0f32); + assert_approx_eq!(f32::floor(-0.0f32), -0.0f32); + assert_approx_eq!(f32::floor(-1.0f32), -1.0f32); + assert_approx_eq!(f32::floor(-1.3f32), -2.0f32); + assert_approx_eq!(f32::floor(-1.5f32), -2.0f32); + assert_approx_eq!(f32::floor(-1.7f32), -2.0f32); } #[test] fn test_ceil() { - assert_approx_eq!(1.0f32.ceil(), 1.0f32); - assert_approx_eq!(1.3f32.ceil(), 2.0f32); - assert_approx_eq!(1.5f32.ceil(), 2.0f32); - assert_approx_eq!(1.7f32.ceil(), 2.0f32); - assert_approx_eq!(0.0f32.ceil(), 0.0f32); - assert_approx_eq!((-0.0f32).ceil(), -0.0f32); - assert_approx_eq!((-1.0f32).ceil(), -1.0f32); - assert_approx_eq!((-1.3f32).ceil(), -1.0f32); - assert_approx_eq!((-1.5f32).ceil(), -1.0f32); - assert_approx_eq!((-1.7f32).ceil(), -1.0f32); + assert_approx_eq!(f32::ceil(1.0f32), 1.0f32); + assert_approx_eq!(f32::ceil(1.3f32), 2.0f32); + assert_approx_eq!(f32::ceil(1.5f32), 2.0f32); + assert_approx_eq!(f32::ceil(1.7f32), 2.0f32); + assert_approx_eq!(f32::ceil(0.0f32), 0.0f32); + assert_approx_eq!(f32::ceil(-0.0f32), -0.0f32); + assert_approx_eq!(f32::ceil(-1.0f32), -1.0f32); + assert_approx_eq!(f32::ceil(-1.3f32), -1.0f32); + assert_approx_eq!(f32::ceil(-1.5f32), -1.0f32); + assert_approx_eq!(f32::ceil(-1.7f32), -1.0f32); } #[test] fn test_round() { - assert_approx_eq!(2.5f32.round(), 3.0f32); - assert_approx_eq!(1.0f32.round(), 1.0f32); - assert_approx_eq!(1.3f32.round(), 1.0f32); - assert_approx_eq!(1.5f32.round(), 2.0f32); - assert_approx_eq!(1.7f32.round(), 2.0f32); - assert_approx_eq!(0.0f32.round(), 0.0f32); - assert_approx_eq!((-0.0f32).round(), -0.0f32); - assert_approx_eq!((-1.0f32).round(), -1.0f32); - assert_approx_eq!((-1.3f32).round(), -1.0f32); - assert_approx_eq!((-1.5f32).round(), -2.0f32); - assert_approx_eq!((-1.7f32).round(), -2.0f32); + assert_approx_eq!(f32::round(2.5f32), 3.0f32); + assert_approx_eq!(f32::round(1.0f32), 1.0f32); + assert_approx_eq!(f32::round(1.3f32), 1.0f32); + assert_approx_eq!(f32::round(1.5f32), 2.0f32); + assert_approx_eq!(f32::round(1.7f32), 2.0f32); + assert_approx_eq!(f32::round(0.0f32), 0.0f32); + assert_approx_eq!(f32::round(-0.0f32), -0.0f32); + assert_approx_eq!(f32::round(-1.0f32), -1.0f32); + assert_approx_eq!(f32::round(-1.3f32), -1.0f32); + assert_approx_eq!(f32::round(-1.5f32), -2.0f32); + assert_approx_eq!(f32::round(-1.7f32), -2.0f32); } #[test] fn test_round_ties_even() { - assert_approx_eq!(2.5f32.round_ties_even(), 2.0f32); - assert_approx_eq!(1.0f32.round_ties_even(), 1.0f32); - assert_approx_eq!(1.3f32.round_ties_even(), 1.0f32); - assert_approx_eq!(1.5f32.round_ties_even(), 2.0f32); - assert_approx_eq!(1.7f32.round_ties_even(), 2.0f32); - assert_approx_eq!(0.0f32.round_ties_even(), 0.0f32); - assert_approx_eq!((-0.0f32).round_ties_even(), -0.0f32); - assert_approx_eq!((-1.0f32).round_ties_even(), -1.0f32); - assert_approx_eq!((-1.3f32).round_ties_even(), -1.0f32); - assert_approx_eq!((-1.5f32).round_ties_even(), -2.0f32); - assert_approx_eq!((-1.7f32).round_ties_even(), -2.0f32); + assert_approx_eq!(f32::round_ties_even(2.5f32), 2.0f32); + assert_approx_eq!(f32::round_ties_even(1.0f32), 1.0f32); + assert_approx_eq!(f32::round_ties_even(1.3f32), 1.0f32); + assert_approx_eq!(f32::round_ties_even(1.5f32), 2.0f32); + assert_approx_eq!(f32::round_ties_even(1.7f32), 2.0f32); + assert_approx_eq!(f32::round_ties_even(0.0f32), 0.0f32); + assert_approx_eq!(f32::round_ties_even(-0.0f32), -0.0f32); + assert_approx_eq!(f32::round_ties_even(-1.0f32), -1.0f32); + assert_approx_eq!(f32::round_ties_even(-1.3f32), -1.0f32); + assert_approx_eq!(f32::round_ties_even(-1.5f32), -2.0f32); + assert_approx_eq!(f32::round_ties_even(-1.7f32), -2.0f32); } #[test] fn test_trunc() { - assert_approx_eq!(1.0f32.trunc(), 1.0f32); - assert_approx_eq!(1.3f32.trunc(), 1.0f32); - assert_approx_eq!(1.5f32.trunc(), 1.0f32); - assert_approx_eq!(1.7f32.trunc(), 1.0f32); - assert_approx_eq!(0.0f32.trunc(), 0.0f32); - assert_approx_eq!((-0.0f32).trunc(), -0.0f32); - assert_approx_eq!((-1.0f32).trunc(), -1.0f32); - assert_approx_eq!((-1.3f32).trunc(), -1.0f32); - assert_approx_eq!((-1.5f32).trunc(), -1.0f32); - assert_approx_eq!((-1.7f32).trunc(), -1.0f32); + assert_approx_eq!(f32::trunc(1.0f32), 1.0f32); + assert_approx_eq!(f32::trunc(1.3f32), 1.0f32); + assert_approx_eq!(f32::trunc(1.5f32), 1.0f32); + assert_approx_eq!(f32::trunc(1.7f32), 1.0f32); + assert_approx_eq!(f32::trunc(0.0f32), 0.0f32); + assert_approx_eq!(f32::trunc(-0.0f32), -0.0f32); + assert_approx_eq!(f32::trunc(-1.0f32), -1.0f32); + assert_approx_eq!(f32::trunc(-1.3f32), -1.0f32); + assert_approx_eq!(f32::trunc(-1.5f32), -1.0f32); + assert_approx_eq!(f32::trunc(-1.7f32), -1.0f32); } #[test] fn test_fract() { - assert_approx_eq!(1.0f32.fract(), 0.0f32); - assert_approx_eq!(1.3f32.fract(), 0.3f32); - assert_approx_eq!(1.5f32.fract(), 0.5f32); - assert_approx_eq!(1.7f32.fract(), 0.7f32); - assert_approx_eq!(0.0f32.fract(), 0.0f32); - assert_approx_eq!((-0.0f32).fract(), -0.0f32); - assert_approx_eq!((-1.0f32).fract(), -0.0f32); - assert_approx_eq!((-1.3f32).fract(), -0.3f32); - assert_approx_eq!((-1.5f32).fract(), -0.5f32); - assert_approx_eq!((-1.7f32).fract(), -0.7f32); + assert_approx_eq!(f32::fract(1.0f32), 0.0f32); + assert_approx_eq!(f32::fract(1.3f32), 0.3f32); + assert_approx_eq!(f32::fract(1.5f32), 0.5f32); + assert_approx_eq!(f32::fract(1.7f32), 0.7f32); + assert_approx_eq!(f32::fract(0.0f32), 0.0f32); + assert_approx_eq!(f32::fract(-0.0f32), -0.0f32); + assert_approx_eq!(f32::fract(-1.0f32), -0.0f32); + assert_approx_eq!(f32::fract(-1.3f32), -0.3f32); + assert_approx_eq!(f32::fract(-1.5f32), -0.5f32); + assert_approx_eq!(f32::fract(-1.7f32), -0.7f32); } #[test] @@ -414,15 +415,15 @@ fn test_mul_add() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(12.3f32.mul_add(4.5, 6.7), 62.05); - assert_approx_eq!((-12.3f32).mul_add(-4.5, -6.7), 48.65); - assert_approx_eq!(0.0f32.mul_add(8.9, 1.2), 1.2); - assert_approx_eq!(3.4f32.mul_add(-0.0, 5.6), 5.6); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f32.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f32).mul_add(2.4, neg_inf), neg_inf); + assert_approx_eq!(f32::mul_add(12.3f32, 4.5, 6.7), 62.05); + assert_approx_eq!(f32::mul_add(-12.3f32, -4.5, -6.7), 48.65); + assert_approx_eq!(f32::mul_add(0.0f32, 8.9, 1.2), 1.2); + assert_approx_eq!(f32::mul_add(3.4f32, -0.0, 5.6), 5.6); + assert!(f32::mul_add(nan, 7.8, 9.0).is_nan()); + assert_eq!(f32::mul_add(inf, 7.8, 9.0), inf); + assert_eq!(f32::mul_add(neg_inf, 7.8, 9.0), neg_inf); + assert_eq!(f32::mul_add(8.9f32, inf, 3.2), inf); + assert_eq!(f32::mul_add(-3.2f32, 2.4, neg_inf), neg_inf); } #[test] @@ -453,22 +454,6 @@ fn test_powi() { assert_eq!(neg_inf.powi(2), inf); } -#[test] -fn test_powf() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.powf(1.0), 1.0); - assert_approx_eq!(3.4f32.powf(4.5), 246.408218); - assert_approx_eq!(2.7f32.powf(-3.2), 0.041652); - assert_approx_eq!((-3.1f32).powf(2.0), 9.61); - assert_approx_eq!(5.9f32.powf(-2.0), 0.028727); - assert_eq!(8.3f32.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - #[test] fn test_sqrt_domain() { assert!(f32::NAN.sqrt().is_nan()); @@ -480,99 +465,6 @@ fn test_sqrt_domain() { assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); } -#[test] -fn test_exp() { - assert_eq!(1.0, 0.0f32.exp()); - assert_approx_eq!(2.718282, 1.0f32.exp()); - assert_approx_eq!(148.413162, 5.0f32.exp()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -fn test_exp2() { - assert_eq!(32.0, 5.0f32.exp2()); - assert_eq!(1.0, 0.0f32.exp2()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -fn test_ln() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(1.0f32.exp().ln(), 1.0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f32).ln().is_nan()); - assert_eq!((-0.0f32).ln(), neg_inf); - assert_eq!(0.0f32.ln(), neg_inf); - assert_approx_eq!(4.0f32.ln(), 1.386294); -} - -#[test] -fn test_log() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(10.0f32.log(10.0), 1.0); - assert_approx_eq!(2.3f32.log(3.5), 0.664858); - assert_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0); - assert!(1.0f32.log(1.0).is_nan()); - assert!(1.0f32.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f32).log(0.1).is_nan()); - assert_eq!((-0.0f32).log(2.0), neg_inf); - assert_eq!(0.0f32.log(7.0), neg_inf); -} - -#[test] -fn test_log2() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(10.0f32.log2(), 3.321928); - assert_approx_eq!(2.3f32.log2(), 1.201634); - assert_approx_eq!(1.0f32.exp().log2(), 1.442695); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f32).log2().is_nan()); - assert_eq!((-0.0f32).log2(), neg_inf); - assert_eq!(0.0f32.log2(), neg_inf); -} - -#[test] -fn test_log10() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(10.0f32.log10(), 1.0); - assert_approx_eq!(2.3f32.log10(), 0.361728); - assert_approx_eq!(1.0f32.exp().log10(), 0.434294); - assert_eq!(1.0f32.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f32).log10().is_nan()); - assert_eq!((-0.0f32).log10(), neg_inf); - assert_eq!(0.0f32.log10(), neg_inf); -} - #[test] fn test_to_degrees() { let pi: f32 = consts::PI; @@ -603,138 +495,6 @@ fn test_to_radians() { assert_eq!(neg_inf.to_radians(), neg_inf); } -#[test] -fn test_asinh() { - assert_eq!(0.0f32.asinh(), 0.0f32); - assert_eq!((-0.0f32).asinh(), -0.0f32); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271 - assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); - assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f32, 60.0f32.sinh().asinh()); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f32, 1e-15f32.sinh().asinh() * 1e15f32); -} - -#[test] -fn test_acosh() { - assert_eq!(1.0f32.acosh(), 0.0f32); - assert!(0.999f32.acosh().is_nan()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32); - assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f32, 60.0f32.cosh().acosh()); -} - -#[test] -fn test_atanh() { - assert_eq!(0.0f32.atanh(), 0.0f32); - assert_eq!((-0.0f32).atanh(), -0.0f32); - - let inf32: f32 = f32::INFINITY; - let neg_inf32: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.atanh(), inf32); - assert_eq!((-1.0f32).atanh(), neg_inf32); - - assert!(2f64.atanh().atanh().is_nan()); - assert!((-2f64).atanh().atanh().is_nan()); - - let inf64: f32 = f32::INFINITY; - let neg_inf64: f32 = f32::NEG_INFINITY; - let nan32: f32 = f32::NAN; - assert!(inf64.atanh().is_nan()); - assert!(neg_inf64.atanh().is_nan()); - assert!(nan32.atanh().is_nan()); - - assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32); - assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32); -} - -#[test] -fn test_gamma() { - // precision can differ between platforms - assert_approx_eq!(1.0f32.gamma(), 1.0f32); - assert_approx_eq!(2.0f32.gamma(), 1.0f32); - assert_approx_eq!(3.0f32.gamma(), 2.0f32); - assert_approx_eq!(4.0f32.gamma(), 6.0f32); - assert_approx_eq!(5.0f32.gamma(), 24.0f32); - assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt()); - assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt()); - assert_eq!(0.0f32.gamma(), f32::INFINITY); - assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY); - assert!((-1.0f32).gamma().is_nan()); - assert!((-2.0f32).gamma().is_nan()); - assert!(f32::NAN.gamma().is_nan()); - assert!(f32::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f32::INFINITY.gamma(), f32::INFINITY); - assert_eq!(171.71f32.gamma(), f32::INFINITY); -} - -#[test] -fn test_ln_gamma() { - assert_approx_eq!(1.0f32.ln_gamma().0, 0.0f32); - assert_eq!(1.0f32.ln_gamma().1, 1); - assert_approx_eq!(2.0f32.ln_gamma().0, 0.0f32); - assert_eq!(2.0f32.ln_gamma().1, 1); - assert_approx_eq!(3.0f32.ln_gamma().0, 2.0f32.ln()); - assert_eq!(3.0f32.ln_gamma().1, 1); - assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); - assert_eq!((-0.5f32).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - let pi: f32 = consts::PI; - let frac_pi_2: f32 = consts::FRAC_PI_2; - let frac_pi_3: f32 = consts::FRAC_PI_3; - let frac_pi_4: f32 = consts::FRAC_PI_4; - let frac_pi_6: f32 = consts::FRAC_PI_6; - let frac_pi_8: f32 = consts::FRAC_PI_8; - let frac_1_pi: f32 = consts::FRAC_1_PI; - let frac_2_pi: f32 = consts::FRAC_2_PI; - let frac_2_sqrtpi: f32 = consts::FRAC_2_SQRT_PI; - let sqrt2: f32 = consts::SQRT_2; - let frac_1_sqrt2: f32 = consts::FRAC_1_SQRT_2; - let e: f32 = consts::E; - let log2_e: f32 = consts::LOG2_E; - let log10_e: f32 = consts::LOG10_E; - let ln_2: f32 = consts::LN_2; - let ln_10: f32 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f32); - assert_approx_eq!(frac_pi_3, pi / 3f32); - assert_approx_eq!(frac_pi_4, pi / 4f32); - assert_approx_eq!(frac_pi_6, pi / 6f32); - assert_approx_eq!(frac_pi_8, pi / 8f32); - assert_approx_eq!(frac_1_pi, 1f32 / pi); - assert_approx_eq!(frac_2_pi, 2f32 / pi); - assert_approx_eq!(frac_2_sqrtpi, 2f32 / pi.sqrt()); - assert_approx_eq!(sqrt2, 2f32.sqrt()); - assert_approx_eq!(frac_1_sqrt2, 1f32 / 2f32.sqrt()); - assert_approx_eq!(log2_e, e.log2()); - assert_approx_eq!(log10_e, e.log10()); - assert_approx_eq!(ln_2, 2f32.ln()); - assert_approx_eq!(ln_10, 10f32.ln()); -} - #[test] fn test_float_bits_conv() { assert_eq!((1f32).to_bits(), 0x3f800000); diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index de9c27eb33d3..4a79a31853ec 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -35,7 +35,7 @@ macro_rules! assert_f64_biteq { #[test] fn test_num_f64() { - crate::test_num(10f64, 2f64); + super::test_num(10f64, 2f64); } #[test] @@ -201,88 +201,88 @@ fn test_classify() { #[test] fn test_floor() { - assert_approx_eq!(1.0f64.floor(), 1.0f64); - assert_approx_eq!(1.3f64.floor(), 1.0f64); - assert_approx_eq!(1.5f64.floor(), 1.0f64); - assert_approx_eq!(1.7f64.floor(), 1.0f64); - assert_approx_eq!(0.0f64.floor(), 0.0f64); - assert_approx_eq!((-0.0f64).floor(), -0.0f64); - assert_approx_eq!((-1.0f64).floor(), -1.0f64); - assert_approx_eq!((-1.3f64).floor(), -2.0f64); - assert_approx_eq!((-1.5f64).floor(), -2.0f64); - assert_approx_eq!((-1.7f64).floor(), -2.0f64); + assert_approx_eq!(f64::floor(1.0f64), 1.0f64); + assert_approx_eq!(f64::floor(1.3f64), 1.0f64); + assert_approx_eq!(f64::floor(1.5f64), 1.0f64); + assert_approx_eq!(f64::floor(1.7f64), 1.0f64); + assert_approx_eq!(f64::floor(0.0f64), 0.0f64); + assert_approx_eq!(f64::floor(-0.0f64), -0.0f64); + assert_approx_eq!(f64::floor(-1.0f64), -1.0f64); + assert_approx_eq!(f64::floor(-1.3f64), -2.0f64); + assert_approx_eq!(f64::floor(-1.5f64), -2.0f64); + assert_approx_eq!(f64::floor(-1.7f64), -2.0f64); } #[test] fn test_ceil() { - assert_approx_eq!(1.0f64.ceil(), 1.0f64); - assert_approx_eq!(1.3f64.ceil(), 2.0f64); - assert_approx_eq!(1.5f64.ceil(), 2.0f64); - assert_approx_eq!(1.7f64.ceil(), 2.0f64); - assert_approx_eq!(0.0f64.ceil(), 0.0f64); - assert_approx_eq!((-0.0f64).ceil(), -0.0f64); - assert_approx_eq!((-1.0f64).ceil(), -1.0f64); - assert_approx_eq!((-1.3f64).ceil(), -1.0f64); - assert_approx_eq!((-1.5f64).ceil(), -1.0f64); - assert_approx_eq!((-1.7f64).ceil(), -1.0f64); + assert_approx_eq!(f64::ceil(1.0f64), 1.0f64); + assert_approx_eq!(f64::ceil(1.3f64), 2.0f64); + assert_approx_eq!(f64::ceil(1.5f64), 2.0f64); + assert_approx_eq!(f64::ceil(1.7f64), 2.0f64); + assert_approx_eq!(f64::ceil(0.0f64), 0.0f64); + assert_approx_eq!(f64::ceil(-0.0f64), -0.0f64); + assert_approx_eq!(f64::ceil(-1.0f64), -1.0f64); + assert_approx_eq!(f64::ceil(-1.3f64), -1.0f64); + assert_approx_eq!(f64::ceil(-1.5f64), -1.0f64); + assert_approx_eq!(f64::ceil(-1.7f64), -1.0f64); } #[test] fn test_round() { - assert_approx_eq!(2.5f64.round(), 3.0f64); - assert_approx_eq!(1.0f64.round(), 1.0f64); - assert_approx_eq!(1.3f64.round(), 1.0f64); - assert_approx_eq!(1.5f64.round(), 2.0f64); - assert_approx_eq!(1.7f64.round(), 2.0f64); - assert_approx_eq!(0.0f64.round(), 0.0f64); - assert_approx_eq!((-0.0f64).round(), -0.0f64); - assert_approx_eq!((-1.0f64).round(), -1.0f64); - assert_approx_eq!((-1.3f64).round(), -1.0f64); - assert_approx_eq!((-1.5f64).round(), -2.0f64); - assert_approx_eq!((-1.7f64).round(), -2.0f64); + assert_approx_eq!(f64::round(2.5f64), 3.0f64); + assert_approx_eq!(f64::round(1.0f64), 1.0f64); + assert_approx_eq!(f64::round(1.3f64), 1.0f64); + assert_approx_eq!(f64::round(1.5f64), 2.0f64); + assert_approx_eq!(f64::round(1.7f64), 2.0f64); + assert_approx_eq!(f64::round(0.0f64), 0.0f64); + assert_approx_eq!(f64::round(-0.0f64), -0.0f64); + assert_approx_eq!(f64::round(-1.0f64), -1.0f64); + assert_approx_eq!(f64::round(-1.3f64), -1.0f64); + assert_approx_eq!(f64::round(-1.5f64), -2.0f64); + assert_approx_eq!(f64::round(-1.7f64), -2.0f64); } #[test] fn test_round_ties_even() { - assert_approx_eq!(2.5f64.round_ties_even(), 2.0f64); - assert_approx_eq!(1.0f64.round_ties_even(), 1.0f64); - assert_approx_eq!(1.3f64.round_ties_even(), 1.0f64); - assert_approx_eq!(1.5f64.round_ties_even(), 2.0f64); - assert_approx_eq!(1.7f64.round_ties_even(), 2.0f64); - assert_approx_eq!(0.0f64.round_ties_even(), 0.0f64); - assert_approx_eq!((-0.0f64).round_ties_even(), -0.0f64); - assert_approx_eq!((-1.0f64).round_ties_even(), -1.0f64); - assert_approx_eq!((-1.3f64).round_ties_even(), -1.0f64); - assert_approx_eq!((-1.5f64).round_ties_even(), -2.0f64); - assert_approx_eq!((-1.7f64).round_ties_even(), -2.0f64); + assert_approx_eq!(f64::round_ties_even(2.5f64), 2.0f64); + assert_approx_eq!(f64::round_ties_even(1.0f64), 1.0f64); + assert_approx_eq!(f64::round_ties_even(1.3f64), 1.0f64); + assert_approx_eq!(f64::round_ties_even(1.5f64), 2.0f64); + assert_approx_eq!(f64::round_ties_even(1.7f64), 2.0f64); + assert_approx_eq!(f64::round_ties_even(0.0f64), 0.0f64); + assert_approx_eq!(f64::round_ties_even(-0.0f64), -0.0f64); + assert_approx_eq!(f64::round_ties_even(-1.0f64), -1.0f64); + assert_approx_eq!(f64::round_ties_even(-1.3f64), -1.0f64); + assert_approx_eq!(f64::round_ties_even(-1.5f64), -2.0f64); + assert_approx_eq!(f64::round_ties_even(-1.7f64), -2.0f64); } #[test] fn test_trunc() { - assert_approx_eq!(1.0f64.trunc(), 1.0f64); - assert_approx_eq!(1.3f64.trunc(), 1.0f64); - assert_approx_eq!(1.5f64.trunc(), 1.0f64); - assert_approx_eq!(1.7f64.trunc(), 1.0f64); - assert_approx_eq!(0.0f64.trunc(), 0.0f64); - assert_approx_eq!((-0.0f64).trunc(), -0.0f64); - assert_approx_eq!((-1.0f64).trunc(), -1.0f64); - assert_approx_eq!((-1.3f64).trunc(), -1.0f64); - assert_approx_eq!((-1.5f64).trunc(), -1.0f64); - assert_approx_eq!((-1.7f64).trunc(), -1.0f64); + assert_approx_eq!(f64::trunc(1.0f64), 1.0f64); + assert_approx_eq!(f64::trunc(1.3f64), 1.0f64); + assert_approx_eq!(f64::trunc(1.5f64), 1.0f64); + assert_approx_eq!(f64::trunc(1.7f64), 1.0f64); + assert_approx_eq!(f64::trunc(0.0f64), 0.0f64); + assert_approx_eq!(f64::trunc(-0.0f64), -0.0f64); + assert_approx_eq!(f64::trunc(-1.0f64), -1.0f64); + assert_approx_eq!(f64::trunc(-1.3f64), -1.0f64); + assert_approx_eq!(f64::trunc(-1.5f64), -1.0f64); + assert_approx_eq!(f64::trunc(-1.7f64), -1.0f64); } #[test] fn test_fract() { - assert_approx_eq!(1.0f64.fract(), 0.0f64); - assert_approx_eq!(1.3f64.fract(), 0.3f64); - assert_approx_eq!(1.5f64.fract(), 0.5f64); - assert_approx_eq!(1.7f64.fract(), 0.7f64); - assert_approx_eq!(0.0f64.fract(), 0.0f64); - assert_approx_eq!((-0.0f64).fract(), -0.0f64); - assert_approx_eq!((-1.0f64).fract(), -0.0f64); - assert_approx_eq!((-1.3f64).fract(), -0.3f64); - assert_approx_eq!((-1.5f64).fract(), -0.5f64); - assert_approx_eq!((-1.7f64).fract(), -0.7f64); + assert_approx_eq!(f64::fract(1.0f64), 0.0f64); + assert_approx_eq!(f64::fract(1.3f64), 0.3f64); + assert_approx_eq!(f64::fract(1.5f64), 0.5f64); + assert_approx_eq!(f64::fract(1.7f64), 0.7f64); + assert_approx_eq!(f64::fract(0.0f64), 0.0f64); + assert_approx_eq!(f64::fract(-0.0f64), -0.0f64); + assert_approx_eq!(f64::fract(-1.0f64), -0.0f64); + assert_approx_eq!(f64::fract(-1.3f64), -0.3f64); + assert_approx_eq!(f64::fract(-1.5f64), -0.5f64); + assert_approx_eq!(f64::fract(-1.7f64), -0.7f64); } #[test] @@ -438,22 +438,6 @@ fn test_powi() { assert_eq!(neg_inf.powi(2), inf); } -#[test] -fn test_powf() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.powf(1.0), 1.0); - assert_approx_eq!(3.4f64.powf(4.5), 246.408183); - assert_approx_eq!(2.7f64.powf(-3.2), 0.041652); - assert_approx_eq!((-3.1f64).powf(2.0), 9.61); - assert_approx_eq!(5.9f64.powf(-2.0), 0.028727); - assert_eq!(8.3f64.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - #[test] fn test_sqrt_domain() { assert!(f64::NAN.sqrt().is_nan()); @@ -465,99 +449,6 @@ fn test_sqrt_domain() { assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); } -#[test] -fn test_exp() { - assert_eq!(1.0, 0.0f64.exp()); - assert_approx_eq!(2.718282, 1.0f64.exp()); - assert_approx_eq!(148.413159, 5.0f64.exp()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -fn test_exp2() { - assert_eq!(32.0, 5.0f64.exp2()); - assert_eq!(1.0, 0.0f64.exp2()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -fn test_ln() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(1.0f64.exp().ln(), 1.0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f64).ln().is_nan()); - assert_eq!((-0.0f64).ln(), neg_inf); - assert_eq!(0.0f64.ln(), neg_inf); - assert_approx_eq!(4.0f64.ln(), 1.386294); -} - -#[test] -fn test_log() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(10.0f64.log(10.0), 1.0); - assert_approx_eq!(2.3f64.log(3.5), 0.664858); - assert_eq!(1.0f64.exp().log(1.0f64.exp()), 1.0); - assert!(1.0f64.log(1.0).is_nan()); - assert!(1.0f64.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f64).log(0.1).is_nan()); - assert_eq!((-0.0f64).log(2.0), neg_inf); - assert_eq!(0.0f64.log(7.0), neg_inf); -} - -#[test] -fn test_log2() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(10.0f64.log2(), 3.321928); - assert_approx_eq!(2.3f64.log2(), 1.201634); - assert_approx_eq!(1.0f64.exp().log2(), 1.442695); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f64).log2().is_nan()); - assert_eq!((-0.0f64).log2(), neg_inf); - assert_eq!(0.0f64.log2(), neg_inf); -} - -#[test] -fn test_log10() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(10.0f64.log10(), 1.0); - assert_approx_eq!(2.3f64.log10(), 0.361728); - assert_approx_eq!(1.0f64.exp().log10(), 0.434294); - assert_eq!(1.0f64.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f64).log10().is_nan()); - assert_eq!((-0.0f64).log10(), neg_inf); - assert_eq!(0.0f64.log10(), neg_inf); -} - #[test] fn test_to_degrees() { let pi: f64 = consts::PI; @@ -587,134 +478,6 @@ fn test_to_radians() { assert_eq!(neg_inf.to_radians(), neg_inf); } -#[test] -fn test_asinh() { - assert_eq!(0.0f64.asinh(), 0.0f64); - assert_eq!((-0.0f64).asinh(), -0.0f64); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f64).asinh().is_sign_negative()); - // issue 63271 - assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); - assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f64, 60.0f64.sinh().asinh()); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f64, 1e-15f64.sinh().asinh() * 1e15f64); -} - -#[test] -fn test_acosh() { - assert_eq!(1.0f64.acosh(), 0.0f64); - assert!(0.999f64.acosh().is_nan()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64); - assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f64, 60.0f64.cosh().acosh()); -} - -#[test] -fn test_atanh() { - assert_eq!(0.0f64.atanh(), 0.0f64); - assert_eq!((-0.0f64).atanh(), -0.0f64); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(1.0f64.atanh(), inf); - assert_eq!((-1.0f64).atanh(), neg_inf); - assert!(2f64.atanh().atanh().is_nan()); - assert!((-2f64).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64); - assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64); -} - -#[test] -fn test_gamma() { - // precision can differ between platforms - assert_approx_eq!(1.0f64.gamma(), 1.0f64); - assert_approx_eq!(2.0f64.gamma(), 1.0f64); - assert_approx_eq!(3.0f64.gamma(), 2.0f64); - assert_approx_eq!(4.0f64.gamma(), 6.0f64); - assert_approx_eq!(5.0f64.gamma(), 24.0f64); - assert_approx_eq!(0.5f64.gamma(), consts::PI.sqrt()); - assert_approx_eq!((-0.5f64).gamma(), -2.0 * consts::PI.sqrt()); - assert_eq!(0.0f64.gamma(), f64::INFINITY); - assert_eq!((-0.0f64).gamma(), f64::NEG_INFINITY); - assert!((-1.0f64).gamma().is_nan()); - assert!((-2.0f64).gamma().is_nan()); - assert!(f64::NAN.gamma().is_nan()); - assert!(f64::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f64::INFINITY.gamma(), f64::INFINITY); - assert_eq!(171.71f64.gamma(), f64::INFINITY); -} - -#[test] -fn test_ln_gamma() { - assert_approx_eq!(1.0f64.ln_gamma().0, 0.0f64); - assert_eq!(1.0f64.ln_gamma().1, 1); - assert_approx_eq!(2.0f64.ln_gamma().0, 0.0f64); - assert_eq!(2.0f64.ln_gamma().1, 1); - assert_approx_eq!(3.0f64.ln_gamma().0, 2.0f64.ln()); - assert_eq!(3.0f64.ln_gamma().1, 1); - assert_approx_eq!((-0.5f64).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); - assert_eq!((-0.5f64).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - let pi: f64 = consts::PI; - let frac_pi_2: f64 = consts::FRAC_PI_2; - let frac_pi_3: f64 = consts::FRAC_PI_3; - let frac_pi_4: f64 = consts::FRAC_PI_4; - let frac_pi_6: f64 = consts::FRAC_PI_6; - let frac_pi_8: f64 = consts::FRAC_PI_8; - let frac_1_pi: f64 = consts::FRAC_1_PI; - let frac_2_pi: f64 = consts::FRAC_2_PI; - let frac_2_sqrtpi: f64 = consts::FRAC_2_SQRT_PI; - let sqrt2: f64 = consts::SQRT_2; - let frac_1_sqrt2: f64 = consts::FRAC_1_SQRT_2; - let e: f64 = consts::E; - let log2_e: f64 = consts::LOG2_E; - let log10_e: f64 = consts::LOG10_E; - let ln_2: f64 = consts::LN_2; - let ln_10: f64 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f64); - assert_approx_eq!(frac_pi_3, pi / 3f64); - assert_approx_eq!(frac_pi_4, pi / 4f64); - assert_approx_eq!(frac_pi_6, pi / 6f64); - assert_approx_eq!(frac_pi_8, pi / 8f64); - assert_approx_eq!(frac_1_pi, 1f64 / pi); - assert_approx_eq!(frac_2_pi, 2f64 / pi); - assert_approx_eq!(frac_2_sqrtpi, 2f64 / pi.sqrt()); - assert_approx_eq!(sqrt2, 2f64.sqrt()); - assert_approx_eq!(frac_1_sqrt2, 1f64 / 2f64.sqrt()); - assert_approx_eq!(log2_e, e.log2()); - assert_approx_eq!(log10_e, e.log10()); - assert_approx_eq!(ln_2, 2f64.ln()); - assert_approx_eq!(ln_10, 10f64.ln()); -} - #[test] fn test_float_bits_conv() { assert_eq!((1f64).to_bits(), 0x3ff0000000000000); diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 453a2d533ab8..7de34271ad05 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -1,7 +1,3 @@ -#![feature(f16, f128, float_algebraic, float_gamma, float_minimum_maximum)] -#![feature(cfg_target_has_reliable_f16_f128)] -#![expect(internal_features)] // for reliable_f16_f128 - use std::fmt; use std::ops::{Add, Div, Mul, Rem, Sub}; diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index acea0b2a0356..b98e52718f60 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -12,10 +12,12 @@ #![feature(async_iterator)] #![feature(bigint_helper_methods)] #![feature(bstr)] +#![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(const_eval_select)] #![feature(const_trait_impl)] +#![feature(core_float_math)] #![feature(core_intrinsics)] #![feature(core_intrinsics_fallbacks)] #![feature(core_io_borrowed_buf)] @@ -29,6 +31,10 @@ #![feature(exact_size_is_empty)] #![feature(extend_one)] #![feature(extern_types)] +#![feature(f128)] +#![feature(f16)] +#![feature(float_algebraic)] +#![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(flt2dec)] #![feature(fmt_internals)] diff --git a/library/std/tests/floats/f128.rs b/library/std/tests/floats/f128.rs new file mode 100644 index 000000000000..e7c90faa05c2 --- /dev/null +++ b/library/std/tests/floats/f128.rs @@ -0,0 +1,321 @@ +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(target_has_reliable_f128)] + +use std::f128::consts; +use std::ops::{Add, Div, Mul, Sub}; + +// Note these tolerances make sense around zero, but not for more extreme exponents. + +/// Default tolerances. Works for values that should be near precise but not exact. Roughly +/// the precision carried by `100 * 100`. +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +const TOL: f128 = 1e-12; + +/// For operations that are near exact, usually not involving math of different +/// signs. +const TOL_PRECISE: f128 = 1e-28; + +/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained +/// operations. +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +const TOL_IMPR: f128 = 1e-10; + +/// Compare by representation +#[allow(unused_macros)] +macro_rules! assert_f128_biteq { + ($a:expr, $b:expr) => { + let (l, r): (&f128, &f128) = (&$a, &$b); + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}"); + }; +} + +#[test] +fn test_num_f128() { + // FIXME(f16_f128): replace with a `test_num` call once the required `fmodl`/`fmodf128` + // function is available on all platforms. + let ten = 10f128; + let two = 2f128; + assert_eq!(ten.add(two), ten + two); + assert_eq!(ten.sub(two), ten - two); + assert_eq!(ten.mul(two), ten * two); + assert_eq!(ten.div(two), ten / two); +} + +// Many math functions allow for less accurate results, so the next tolerance up is used + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_powf() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powf(1.0), 1.0); + assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR); + assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR); + assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR); + assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR); + assert_eq!(8.3f128.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f128.exp()); + assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); + assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f128.exp2()); + assert_eq!(1.0, 0.0f128.exp2()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_ln() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f128).ln().is_nan()); + assert_eq!((-0.0f128).ln(), neg_inf); + assert_eq!(0.0f128.ln(), neg_inf); + assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_log() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log(10.0), 1.0); + assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL); + assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0); + assert!(1.0f128.log(1.0).is_nan()); + assert!(1.0f128.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f128).log(0.1).is_nan()); + assert_eq!((-0.0f128).log(2.0), neg_inf); + assert_eq!(0.0f128.log(7.0), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_log2() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL); + assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL); + assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f128).log2().is_nan()); + assert_eq!((-0.0f128).log2(), neg_inf); + assert_eq!(0.0f128.log2(), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_log10() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log10(), 1.0); + assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL); + assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL); + assert_eq!(1.0f128.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f128).log10().is_nan()); + assert_eq!((-0.0f128).log10(), neg_inf); + assert_eq!(0.0f128.log10(), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_asinh() { + // Lower accuracy results are allowed, use increased tolerances + assert_eq!(0.0f128.asinh(), 0.0f128); + assert_eq!((-0.0f128).asinh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f128).asinh().is_sign_negative()); + + // issue 63271 + assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR); + assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!( + (-67452098.07139316f128).asinh(), + -18.720075426274544393985484294000831757220, + TOL_IMPR + ); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_acosh() { + assert_eq!(1.0f128.acosh(), 0.0f128); + assert!(0.999f128.acosh().is_nan()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR); + assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_atanh() { + assert_eq!(0.0f128.atanh(), 0.0f128); + assert_eq!((-0.0f128).atanh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(1.0f128.atanh(), inf); + assert_eq!((-1.0f128).atanh(), neg_inf); + assert!(2f128.atanh().atanh().is_nan()); + assert!((-2f128).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR); + assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR); + assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR); + assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR); + assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR); + assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR); + assert_eq!(0.0f128.gamma(), f128::INFINITY); + assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY); + assert!((-1.0f128).gamma().is_nan()); + assert!((-2.0f128).gamma().is_nan()); + assert!(f128::NAN.gamma().is_nan()); + assert!(f128::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f128::INFINITY.gamma(), f128::INFINITY); + assert_eq!(1760.9f128.gamma(), f128::INFINITY); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(1.0f128.ln_gamma().1, 1); + assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(2.0f128.ln_gamma().1, 1); + assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR); + assert_eq!(3.0f128.ln_gamma().1, 1); + assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR); + assert_eq!((-0.5f128).ln_gamma().1, -1); +} + +#[test] +fn test_real_consts() { + let pi: f128 = consts::PI; + let frac_pi_2: f128 = consts::FRAC_PI_2; + let frac_pi_3: f128 = consts::FRAC_PI_3; + let frac_pi_4: f128 = consts::FRAC_PI_4; + let frac_pi_6: f128 = consts::FRAC_PI_6; + let frac_pi_8: f128 = consts::FRAC_PI_8; + let frac_1_pi: f128 = consts::FRAC_1_PI; + let frac_2_pi: f128 = consts::FRAC_2_PI; + + assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE); + assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); + assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); + + #[cfg(not(miri))] + #[cfg(target_has_reliable_f128_math)] + { + let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; + let sqrt2: f128 = consts::SQRT_2; + let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; + let e: f128 = consts::E; + let log2_e: f128 = consts::LOG2_E; + let log10_e: f128 = consts::LOG10_E; + let ln_2: f128 = consts::LN_2; + let ln_10: f128 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE); + assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE); + assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE); + assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE); + assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); + } +} diff --git a/library/std/tests/floats/f16.rs b/library/std/tests/floats/f16.rs new file mode 100644 index 000000000000..0f8b4138d226 --- /dev/null +++ b/library/std/tests/floats/f16.rs @@ -0,0 +1,300 @@ +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(target_has_reliable_f16)] + +use std::f16::consts; + +/// Tolerance for results on the order of 10.0e-2 +#[allow(unused)] +const TOL_N2: f16 = 0.0001; + +/// Tolerance for results on the order of 10.0e+0 +#[allow(unused)] +const TOL_0: f16 = 0.01; + +/// Tolerance for results on the order of 10.0e+2 +#[allow(unused)] +const TOL_P2: f16 = 0.5; + +/// Tolerance for results on the order of 10.0e+4 +#[allow(unused)] +const TOL_P4: f16 = 10.0; + +/// Compare by representation +#[allow(unused_macros)] +macro_rules! assert_f16_biteq { + ($a:expr, $b:expr) => { + let (l, r): (&f16, &f16) = (&$a, &$b); + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})"); + }; +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_powf() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.powf(1.0), 1.0); + assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2); + assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2); + assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2); + assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2); + assert_eq!(8.3f16.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f16.exp()); + assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); + assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f16.exp2()); + assert_eq!(1.0, 0.0f16.exp2()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_ln() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f16).ln().is_nan()); + assert_eq!((-0.0f16).ln(), neg_inf); + assert_eq!(0.0f16.ln(), neg_inf); + assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_log() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log(10.0), 1.0); + assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0); + assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0); + assert!(1.0f16.log(1.0).is_nan()); + assert!(1.0f16.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f16).log(0.1).is_nan()); + assert_eq!((-0.0f16).log(2.0), neg_inf); + assert_eq!(0.0f16.log(7.0), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_log2() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0); + assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0); + assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f16).log2().is_nan()); + assert_eq!((-0.0f16).log2(), neg_inf); + assert_eq!(0.0f16.log2(), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_log10() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log10(), 1.0); + assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0); + assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0); + assert_eq!(1.0f16.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f16).log10().is_nan()); + assert_eq!((-0.0f16).log10(), neg_inf); + assert_eq!(0.0f16.log10(), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_asinh() { + assert_eq!(0.0f16.asinh(), 0.0f16); + assert_eq!((-0.0f16).asinh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f16).asinh().is_sign_negative()); + // issue 63271 + assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0); + assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_acosh() { + assert_eq!(1.0f16.acosh(), 0.0f16); + assert!(0.999f16.acosh().is_nan()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0); + assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_atanh() { + assert_eq!(0.0f16.atanh(), 0.0f16); + assert_eq!((-0.0f16).atanh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(1.0f16.atanh(), inf); + assert_eq!((-1.0f16).atanh(), neg_inf); + assert!(2f16.atanh().atanh().is_nan()); + assert!((-2f16).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0); + assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0); + assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0); + assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0); + assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0); + assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0); + assert_eq!(0.0f16.gamma(), f16::INFINITY); + assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY); + assert!((-1.0f16).gamma().is_nan()); + assert!((-2.0f16).gamma().is_nan()); + assert!(f16::NAN.gamma().is_nan()); + assert!(f16::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f16::INFINITY.gamma(), f16::INFINITY); + assert_eq!(171.71f16.gamma(), f16::INFINITY); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(1.0f16.ln_gamma().1, 1); + assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(2.0f16.ln_gamma().1, 1); + assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0); + assert_eq!(3.0f16.ln_gamma().1, 1); + assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0); + assert_eq!((-0.5f16).ln_gamma().1, -1); +} + +#[test] +fn test_real_consts() { + // FIXME(f16_f128): add math tests when available + + let pi: f16 = consts::PI; + let frac_pi_2: f16 = consts::FRAC_PI_2; + let frac_pi_3: f16 = consts::FRAC_PI_3; + let frac_pi_4: f16 = consts::FRAC_PI_4; + let frac_pi_6: f16 = consts::FRAC_PI_6; + let frac_pi_8: f16 = consts::FRAC_PI_8; + let frac_1_pi: f16 = consts::FRAC_1_PI; + let frac_2_pi: f16 = consts::FRAC_2_PI; + + assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0); + assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0); + assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0); + assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0); + assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0); + assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); + assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); + + #[cfg(not(miri))] + #[cfg(target_has_reliable_f16_math)] + { + let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; + let sqrt2: f16 = consts::SQRT_2; + let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; + let e: f16 = consts::E; + let log2_e: f16 = consts::LOG2_E; + let log10_e: f16 = consts::LOG10_E; + let ln_2: f16 = consts::LN_2; + let ln_10: f16 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0); + assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0); + assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0); + assert_approx_eq!(log2_e, e.log2(), TOL_0); + assert_approx_eq!(log10_e, e.log10(), TOL_0); + assert_approx_eq!(ln_2, 2f16.ln(), TOL_0); + assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); + } +} diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs new file mode 100644 index 000000000000..e54f227bb774 --- /dev/null +++ b/library/std/tests/floats/f32.rs @@ -0,0 +1,253 @@ +use std::f32::consts; + +#[allow(unused_macros)] +macro_rules! assert_f32_biteq { + ($left : expr, $right : expr) => { + let l: &f32 = &$left; + let r: &f32 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})"); + }; +} + +#[test] +fn test_powf() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.powf(1.0), 1.0); + assert_approx_eq!(3.4f32.powf(4.5), 246.408218); + assert_approx_eq!(2.7f32.powf(-3.2), 0.041652); + assert_approx_eq!((-3.1f32).powf(2.0), 9.61); + assert_approx_eq!(5.9f32.powf(-2.0), 0.028727); + assert_eq!(8.3f32.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +fn test_exp() { + assert_eq!(1.0, 0.0f32.exp()); + assert_approx_eq!(2.718282, 1.0f32.exp()); + assert_approx_eq!(148.413162, 5.0f32.exp()); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +fn test_exp2() { + assert_eq!(32.0, 5.0f32.exp2()); + assert_eq!(1.0, 0.0f32.exp2()); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +fn test_ln() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_approx_eq!(1.0f32.exp().ln(), 1.0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f32).ln().is_nan()); + assert_eq!((-0.0f32).ln(), neg_inf); + assert_eq!(0.0f32.ln(), neg_inf); + assert_approx_eq!(4.0f32.ln(), 1.386294); +} + +#[test] +fn test_log() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(10.0f32.log(10.0), 1.0); + assert_approx_eq!(2.3f32.log(3.5), 0.664858); + assert_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0); + assert!(1.0f32.log(1.0).is_nan()); + assert!(1.0f32.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f32).log(0.1).is_nan()); + assert_eq!((-0.0f32).log(2.0), neg_inf); + assert_eq!(0.0f32.log(7.0), neg_inf); +} + +#[test] +fn test_log2() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_approx_eq!(10.0f32.log2(), 3.321928); + assert_approx_eq!(2.3f32.log2(), 1.201634); + assert_approx_eq!(1.0f32.exp().log2(), 1.442695); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f32).log2().is_nan()); + assert_eq!((-0.0f32).log2(), neg_inf); + assert_eq!(0.0f32.log2(), neg_inf); +} + +#[test] +fn test_log10() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(10.0f32.log10(), 1.0); + assert_approx_eq!(2.3f32.log10(), 0.361728); + assert_approx_eq!(1.0f32.exp().log10(), 0.434294); + assert_eq!(1.0f32.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f32).log10().is_nan()); + assert_eq!((-0.0f32).log10(), neg_inf); + assert_eq!(0.0f32.log10(), neg_inf); +} + +#[test] +fn test_asinh() { + assert_eq!(0.0f32.asinh(), 0.0f32); + assert_eq!((-0.0f32).asinh(), -0.0f32); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271 + assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); + assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f32, 60.0f32.sinh().asinh()); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f32, 1e-15f32.sinh().asinh() * 1e15f32); +} + +#[test] +fn test_acosh() { + assert_eq!(1.0f32.acosh(), 0.0f32); + assert!(0.999f32.acosh().is_nan()); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32); + assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f32, 60.0f32.cosh().acosh()); +} + +#[test] +fn test_atanh() { + assert_eq!(0.0f32.atanh(), 0.0f32); + assert_eq!((-0.0f32).atanh(), -0.0f32); + + let inf32: f32 = f32::INFINITY; + let neg_inf32: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.atanh(), inf32); + assert_eq!((-1.0f32).atanh(), neg_inf32); + + assert!(2f64.atanh().atanh().is_nan()); + assert!((-2f64).atanh().atanh().is_nan()); + + let inf64: f32 = f32::INFINITY; + let neg_inf64: f32 = f32::NEG_INFINITY; + let nan32: f32 = f32::NAN; + assert!(inf64.atanh().is_nan()); + assert!(neg_inf64.atanh().is_nan()); + assert!(nan32.atanh().is_nan()); + + assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32); + assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32); +} + +#[test] +fn test_gamma() { + // precision can differ between platforms + assert_approx_eq!(1.0f32.gamma(), 1.0f32); + assert_approx_eq!(2.0f32.gamma(), 1.0f32); + assert_approx_eq!(3.0f32.gamma(), 2.0f32); + assert_approx_eq!(4.0f32.gamma(), 6.0f32); + assert_approx_eq!(5.0f32.gamma(), 24.0f32); + assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt()); + assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt()); + assert_eq!(0.0f32.gamma(), f32::INFINITY); + assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY); + assert!((-1.0f32).gamma().is_nan()); + assert!((-2.0f32).gamma().is_nan()); + assert!(f32::NAN.gamma().is_nan()); + assert!(f32::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f32::INFINITY.gamma(), f32::INFINITY); + assert_eq!(171.71f32.gamma(), f32::INFINITY); +} + +#[test] +fn test_ln_gamma() { + assert_approx_eq!(1.0f32.ln_gamma().0, 0.0f32); + assert_eq!(1.0f32.ln_gamma().1, 1); + assert_approx_eq!(2.0f32.ln_gamma().0, 0.0f32); + assert_eq!(2.0f32.ln_gamma().1, 1); + assert_approx_eq!(3.0f32.ln_gamma().0, 2.0f32.ln()); + assert_eq!(3.0f32.ln_gamma().1, 1); + assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); + assert_eq!((-0.5f32).ln_gamma().1, -1); +} + +#[test] +fn test_real_consts() { + let pi: f32 = consts::PI; + let frac_pi_2: f32 = consts::FRAC_PI_2; + let frac_pi_3: f32 = consts::FRAC_PI_3; + let frac_pi_4: f32 = consts::FRAC_PI_4; + let frac_pi_6: f32 = consts::FRAC_PI_6; + let frac_pi_8: f32 = consts::FRAC_PI_8; + let frac_1_pi: f32 = consts::FRAC_1_PI; + let frac_2_pi: f32 = consts::FRAC_2_PI; + let frac_2_sqrtpi: f32 = consts::FRAC_2_SQRT_PI; + let sqrt2: f32 = consts::SQRT_2; + let frac_1_sqrt2: f32 = consts::FRAC_1_SQRT_2; + let e: f32 = consts::E; + let log2_e: f32 = consts::LOG2_E; + let log10_e: f32 = consts::LOG10_E; + let ln_2: f32 = consts::LN_2; + let ln_10: f32 = consts::LN_10; + + assert_approx_eq!(frac_pi_2, pi / 2f32); + assert_approx_eq!(frac_pi_3, pi / 3f32); + assert_approx_eq!(frac_pi_4, pi / 4f32); + assert_approx_eq!(frac_pi_6, pi / 6f32); + assert_approx_eq!(frac_pi_8, pi / 8f32); + assert_approx_eq!(frac_1_pi, 1f32 / pi); + assert_approx_eq!(frac_2_pi, 2f32 / pi); + assert_approx_eq!(frac_2_sqrtpi, 2f32 / pi.sqrt()); + assert_approx_eq!(sqrt2, 2f32.sqrt()); + assert_approx_eq!(frac_1_sqrt2, 1f32 / 2f32.sqrt()); + assert_approx_eq!(log2_e, e.log2()); + assert_approx_eq!(log10_e, e.log10()); + assert_approx_eq!(ln_2, 2f32.ln()); + assert_approx_eq!(ln_10, 10f32.ln()); +} diff --git a/library/std/tests/floats/f64.rs b/library/std/tests/floats/f64.rs new file mode 100644 index 000000000000..2d8dd1cf0915 --- /dev/null +++ b/library/std/tests/floats/f64.rs @@ -0,0 +1,249 @@ +use std::f64::consts; + +#[allow(unused_macros)] +macro_rules! assert_f64_biteq { + ($left : expr, $right : expr) => { + let l: &f64 = &$left; + let r: &f64 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l} ({lb:#018x}) is not bitequal to {r} ({rb:#018x})"); + }; +} + +#[test] +fn test_powf() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(1.0f64.powf(1.0), 1.0); + assert_approx_eq!(3.4f64.powf(4.5), 246.408183); + assert_approx_eq!(2.7f64.powf(-3.2), 0.041652); + assert_approx_eq!((-3.1f64).powf(2.0), 9.61); + assert_approx_eq!(5.9f64.powf(-2.0), 0.028727); + assert_eq!(8.3f64.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +fn test_exp() { + assert_eq!(1.0, 0.0f64.exp()); + assert_approx_eq!(2.718282, 1.0f64.exp()); + assert_approx_eq!(148.413159, 5.0f64.exp()); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +fn test_exp2() { + assert_eq!(32.0, 5.0f64.exp2()); + assert_eq!(1.0, 0.0f64.exp2()); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +fn test_ln() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_approx_eq!(1.0f64.exp().ln(), 1.0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f64).ln().is_nan()); + assert_eq!((-0.0f64).ln(), neg_inf); + assert_eq!(0.0f64.ln(), neg_inf); + assert_approx_eq!(4.0f64.ln(), 1.386294); +} + +#[test] +fn test_log() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(10.0f64.log(10.0), 1.0); + assert_approx_eq!(2.3f64.log(3.5), 0.664858); + assert_eq!(1.0f64.exp().log(1.0f64.exp()), 1.0); + assert!(1.0f64.log(1.0).is_nan()); + assert!(1.0f64.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f64).log(0.1).is_nan()); + assert_eq!((-0.0f64).log(2.0), neg_inf); + assert_eq!(0.0f64.log(7.0), neg_inf); +} + +#[test] +fn test_log2() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_approx_eq!(10.0f64.log2(), 3.321928); + assert_approx_eq!(2.3f64.log2(), 1.201634); + assert_approx_eq!(1.0f64.exp().log2(), 1.442695); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f64).log2().is_nan()); + assert_eq!((-0.0f64).log2(), neg_inf); + assert_eq!(0.0f64.log2(), neg_inf); +} + +#[test] +fn test_log10() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(10.0f64.log10(), 1.0); + assert_approx_eq!(2.3f64.log10(), 0.361728); + assert_approx_eq!(1.0f64.exp().log10(), 0.434294); + assert_eq!(1.0f64.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f64).log10().is_nan()); + assert_eq!((-0.0f64).log10(), neg_inf); + assert_eq!(0.0f64.log10(), neg_inf); +} + +#[test] +fn test_asinh() { + assert_eq!(0.0f64.asinh(), 0.0f64); + assert_eq!((-0.0f64).asinh(), -0.0f64); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f64).asinh().is_sign_negative()); + // issue 63271 + assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); + assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f64, 60.0f64.sinh().asinh()); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f64, 1e-15f64.sinh().asinh() * 1e15f64); +} + +#[test] +fn test_acosh() { + assert_eq!(1.0f64.acosh(), 0.0f64); + assert!(0.999f64.acosh().is_nan()); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64); + assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f64, 60.0f64.cosh().acosh()); +} + +#[test] +fn test_atanh() { + assert_eq!(0.0f64.atanh(), 0.0f64); + assert_eq!((-0.0f64).atanh(), -0.0f64); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(1.0f64.atanh(), inf); + assert_eq!((-1.0f64).atanh(), neg_inf); + assert!(2f64.atanh().atanh().is_nan()); + assert!((-2f64).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64); + assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64); +} + +#[test] +fn test_gamma() { + // precision can differ between platforms + assert_approx_eq!(1.0f64.gamma(), 1.0f64); + assert_approx_eq!(2.0f64.gamma(), 1.0f64); + assert_approx_eq!(3.0f64.gamma(), 2.0f64); + assert_approx_eq!(4.0f64.gamma(), 6.0f64); + assert_approx_eq!(5.0f64.gamma(), 24.0f64); + assert_approx_eq!(0.5f64.gamma(), consts::PI.sqrt()); + assert_approx_eq!((-0.5f64).gamma(), -2.0 * consts::PI.sqrt()); + assert_eq!(0.0f64.gamma(), f64::INFINITY); + assert_eq!((-0.0f64).gamma(), f64::NEG_INFINITY); + assert!((-1.0f64).gamma().is_nan()); + assert!((-2.0f64).gamma().is_nan()); + assert!(f64::NAN.gamma().is_nan()); + assert!(f64::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f64::INFINITY.gamma(), f64::INFINITY); + assert_eq!(171.71f64.gamma(), f64::INFINITY); +} + +#[test] +fn test_ln_gamma() { + assert_approx_eq!(1.0f64.ln_gamma().0, 0.0f64); + assert_eq!(1.0f64.ln_gamma().1, 1); + assert_approx_eq!(2.0f64.ln_gamma().0, 0.0f64); + assert_eq!(2.0f64.ln_gamma().1, 1); + assert_approx_eq!(3.0f64.ln_gamma().0, 2.0f64.ln()); + assert_eq!(3.0f64.ln_gamma().1, 1); + assert_approx_eq!((-0.5f64).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); + assert_eq!((-0.5f64).ln_gamma().1, -1); +} + +#[test] +fn test_real_consts() { + let pi: f64 = consts::PI; + let frac_pi_2: f64 = consts::FRAC_PI_2; + let frac_pi_3: f64 = consts::FRAC_PI_3; + let frac_pi_4: f64 = consts::FRAC_PI_4; + let frac_pi_6: f64 = consts::FRAC_PI_6; + let frac_pi_8: f64 = consts::FRAC_PI_8; + let frac_1_pi: f64 = consts::FRAC_1_PI; + let frac_2_pi: f64 = consts::FRAC_2_PI; + let frac_2_sqrtpi: f64 = consts::FRAC_2_SQRT_PI; + let sqrt2: f64 = consts::SQRT_2; + let frac_1_sqrt2: f64 = consts::FRAC_1_SQRT_2; + let e: f64 = consts::E; + let log2_e: f64 = consts::LOG2_E; + let log10_e: f64 = consts::LOG10_E; + let ln_2: f64 = consts::LN_2; + let ln_10: f64 = consts::LN_10; + + assert_approx_eq!(frac_pi_2, pi / 2f64); + assert_approx_eq!(frac_pi_3, pi / 3f64); + assert_approx_eq!(frac_pi_4, pi / 4f64); + assert_approx_eq!(frac_pi_6, pi / 6f64); + assert_approx_eq!(frac_pi_8, pi / 8f64); + assert_approx_eq!(frac_1_pi, 1f64 / pi); + assert_approx_eq!(frac_2_pi, 2f64 / pi); + assert_approx_eq!(frac_2_sqrtpi, 2f64 / pi.sqrt()); + assert_approx_eq!(sqrt2, 2f64.sqrt()); + assert_approx_eq!(frac_1_sqrt2, 1f64 / 2f64.sqrt()); + assert_approx_eq!(log2_e, e.log2()); + assert_approx_eq!(log10_e, e.log10()); + assert_approx_eq!(ln_2, 2f64.ln()); + assert_approx_eq!(ln_10, 10f64.ln()); +} diff --git a/library/std/tests/floats/lib.rs b/library/std/tests/floats/lib.rs new file mode 100644 index 000000000000..8bb8eb4bfc1a --- /dev/null +++ b/library/std/tests/floats/lib.rs @@ -0,0 +1,43 @@ +#![feature(f16, f128, float_gamma, float_minimum_maximum, cfg_target_has_reliable_f16_f128)] +#![expect(internal_features)] // for reliable_f16_f128 + +use std::fmt; +use std::ops::{Add, Div, Mul, Rem, Sub}; + +/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. +macro_rules! assert_approx_eq { + ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; + ($a:expr, $b:expr, $lim:expr) => {{ + let (a, b) = (&$a, &$b); + let diff = (*a - *b).abs(); + assert!( + diff <= $lim, + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", + lim = $lim + ); + }}; +} + +/// Helper function for testing numeric operations +pub fn test_num(ten: T, two: T) +where + T: PartialEq + + Add + + Sub + + Mul + + Div + + Rem + + fmt::Debug + + Copy, +{ + assert_eq!(ten.add(two), ten + two); + assert_eq!(ten.sub(two), ten - two); + assert_eq!(ten.mul(two), ten * two); + assert_eq!(ten.div(two), ten / two); + assert_eq!(ten.rem(two), ten % two); +} + +mod f128; +mod f16; +mod f32; +mod f64; From 65117eeda8dd672fe1420538d8cf228ee41e13f7 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 30 Apr 2025 18:02:24 -0400 Subject: [PATCH 171/245] Skip {f32,f64}::mul_add tests on MinGW Per [1], MinGW has an incorrect fma implementation. This showed up in tests run with cranelift after adding float math operations to `core`. Presumably we hadn't noticed this when running tests with LLVM because LLVM was constant folding the result away. Rust issue: https://github.com/rust-lang/rust/issues/140515 [1]: https://sourceforge.net/p/mingw-w64/bugs/848/ --- library/core/src/num/f32.rs | 3 +++ library/core/src/num/f64.rs | 3 +++ library/coretests/tests/floats/f32.rs | 2 ++ library/coretests/tests/floats/f64.rs | 2 ++ 4 files changed, 10 insertions(+) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 326ccd517ced..9525bdb6762a 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1742,6 +1742,8 @@ pub fn fract(x: f32) -> f32 { /// ``` /// #![feature(core_float_math)] /// +/// # // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ +/// # #[cfg(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")))] { /// use core::f32; /// /// let m = 10.0_f32; @@ -1759,6 +1761,7 @@ pub fn fract(x: f32) -> f32 { /// assert_eq!(f32::mul_add(one_plus_eps, one_minus_eps, minus_one), -f32::EPSILON * f32::EPSILON); /// // Different rounding with the non-fused multiply and add. /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); +/// # } /// ``` /// /// _This standalone function is for testing only. It will be stabilized as an inherent method._ diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 66aba73aec10..76c4e5d1a6f7 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1741,6 +1741,8 @@ pub fn fract(x: f64) -> f64 { /// ``` /// #![feature(core_float_math)] /// +/// # // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ +/// # #[cfg(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")))] { /// use core::f64; /// /// let m = 10.0_f64; @@ -1758,6 +1760,7 @@ pub fn fract(x: f64) -> f64 { /// assert_eq!(f64::mul_add(one_plus_eps, one_minus_eps, minus_one), -f64::EPSILON * f64::EPSILON); /// // Different rounding with the non-fused multiply and add. /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); +/// # } /// ``` /// /// _This standalone function is for testing only. It will be stabilized as an inherent method._ diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 1c018a5e7b52..9b551643bae2 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -410,6 +410,8 @@ fn test_next_down() { assert_f32_biteq!(nan2.next_down(), nan2); } +// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ +#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] #[test] fn test_mul_add() { let nan: f32 = f32::NAN; diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 4a79a31853ec..988108371d73 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -394,6 +394,8 @@ fn test_next_down() { assert_f64_biteq!(nan2.next_down(), nan2); } +// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ +#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] #[test] fn test_mul_add() { let nan: f64 = f64::NAN; From 754b06f97869253968e0d6abf8105d047c327b62 Mon Sep 17 00:00:00 2001 From: Emmanuel Ferdman Date: Wed, 14 May 2025 08:41:30 -0700 Subject: [PATCH 172/245] Migrate to modern datetime API Signed-off-by: Emmanuel Ferdman --- src/ci/cpu-usage-over-time.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/cpu-usage-over-time.py b/src/ci/cpu-usage-over-time.py index 3d9dc86734fc..19a39f349532 100755 --- a/src/ci/cpu-usage-over-time.py +++ b/src/ci/cpu-usage-over-time.py @@ -170,7 +170,7 @@ print("Time,Idle") while True: time.sleep(1) next_state = State() - now = datetime.datetime.utcnow().isoformat() + now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None).isoformat() idle = next_state.idle_since(cur_state) print("%s,%s" % (now, idle)) sys.stdout.flush() From 7a47a6a9c7b6a320ce18b45e39da5249a78e867a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 14 May 2025 18:00:19 +0200 Subject: [PATCH 173/245] Update gcc version used in rustc_codegen_version --- compiler/rustc_codegen_gcc/libgccjit.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_gcc/libgccjit.version b/compiler/rustc_codegen_gcc/libgccjit.version index d06646dacc34..f62154968d3d 100644 --- a/compiler/rustc_codegen_gcc/libgccjit.version +++ b/compiler/rustc_codegen_gcc/libgccjit.version @@ -1 +1 @@ -8b194529188f9d3a98cc211caa805a5355bfa8f0 +04ce66d8c918de9273bd7101638ad8724edf5e21 From d5c5a8251d0a9477ff6383638d5a8fdb2e109819 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 14 May 2025 18:02:02 +0200 Subject: [PATCH 174/245] Update `src/gcc` submodule to `04ce66d8c918de9273bd7101638ad8724edf5e21` --- src/gcc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gcc b/src/gcc index 0ea98a1365b8..04ce66d8c918 160000 --- a/src/gcc +++ b/src/gcc @@ -1 +1 @@ -Subproject commit 0ea98a1365b81f7488073512c850e8ee951a4afd +Subproject commit 04ce66d8c918de9273bd7101638ad8724edf5e21 From ac1df15f86a911cd0f2fd54dd5df6fc58f6d4eb5 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 7 May 2025 22:16:51 +0200 Subject: [PATCH 175/245] Improve `dangerous_implicit_aurorefs` diagnostic output --- compiler/rustc_lint/messages.ftl | 4 + compiler/rustc_lint/src/autorefs.rs | 27 +-- compiler/rustc_lint/src/lints.rs | 13 +- tests/ui/lint/implicit_autorefs.fixed | 6 + tests/ui/lint/implicit_autorefs.rs | 6 + tests/ui/lint/implicit_autorefs.stderr | 228 ++++++++++++++++++++++--- 6 files changed, 253 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 99b42ee54802..baa017f3084a 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -362,6 +362,10 @@ lint_impl_trait_redundant_captures = all possible in-scope parameters are alread lint_implicit_unsafe_autorefs = implicit autoref creates a reference to the dereference of a raw pointer .note = creating a reference requires the pointer target to be valid and imposes aliasing requirements + .raw_ptr = this raw pointer has type `{$raw_ptr_ty}` + .autoref = autoref is being applied to this expression, resulting in: `{$autoref_ty}` + .through_overloaded_deref = reference(s) created through call(s) to overloaded `Deref(Mut)::deref(_mut)` implementation + .method_def = method calls to `{$method_name}` require a reference .suggestion = try using a raw pointer method instead; or if this reference is intentional, make it explicit lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 91d58d92466a..9bb235263625 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -2,7 +2,7 @@ use rustc_ast::{BorrowKind, UnOp}; use rustc_hir::{Expr, ExprKind, Mutability}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDeref}; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::sym; +use rustc_span::{kw, sym}; use crate::lints::{ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsSuggestion}; use crate::{LateContext, LateLintPass, LintContext}; @@ -92,25 +92,30 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs { && let adjustments = peel_derefs_adjustments(&**adjustments) // 3. An automatically inserted reference (might come from a deref). && let [adjustment] = adjustments - && let Some(borrow_mutbl) = has_implicit_borrow(adjustment) + && let Some((borrow_mutbl, through_overloaded_deref)) = has_implicit_borrow(adjustment) && let ExprKind::Unary(UnOp::Deref, dereferenced) = // 2. Any number of place projections. peel_place_mappers(inner).kind // 1. Deref of a raw pointer. && typeck.expr_ty(dereferenced).is_raw_ptr() // PERF: 5. b. A method call annotated with `#[rustc_no_implicit_refs]` - && match expr.kind { - ExprKind::MethodCall(..) => matches!( - cx.typeck_results().type_dependent_def_id(expr.hir_id), - Some(def_id) if cx.tcx.has_attr(def_id, sym::rustc_no_implicit_autorefs) - ), - _ => true, + && let method_did = match expr.kind { + ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), + _ => None, } + && method_did.map(|did| cx.tcx.has_attr(did, sym::rustc_no_implicit_autorefs)).unwrap_or(true) { cx.emit_span_lint( DANGEROUS_IMPLICIT_AUTOREFS, expr.span.source_callsite(), ImplicitUnsafeAutorefsDiag { + raw_ptr_span: dereferenced.span, + raw_ptr_ty: typeck.expr_ty(dereferenced), + autoref_span: inner.span, + autoref_ty: typeck.expr_ty_adjusted(inner), + method_def_span: method_did.map(|did| cx.tcx.def_span(did)), + method_name: method_did.map(|did| cx.tcx.item_name(did)).unwrap_or(kw::Empty), + through_overloaded_deref, suggestion: ImplicitUnsafeAutorefsSuggestion { mutbl: borrow_mutbl.ref_prefix_str(), deref: if is_coming_from_deref { "*" } else { "" }, @@ -147,10 +152,10 @@ fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustmen /// Test if some adjustment has some implicit borrow. /// /// Returns `Some(mutability)` if the argument adjustment has implicit borrow in it. -fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option { +fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Mutability, bool)> { match kind { - &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some(mutbl), - &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some(mutbl.into()), + &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some((mutbl, true)), + &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some((mutbl.into(), false)), Adjust::NeverToAny | Adjust::Pointer(..) | Adjust::ReborrowPin(..) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 487184b836a4..978bb6d08d5a 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -59,7 +59,18 @@ pub(crate) enum ShadowedIntoIterDiagSub { #[derive(LintDiagnostic)] #[diag(lint_implicit_unsafe_autorefs)] #[note] -pub(crate) struct ImplicitUnsafeAutorefsDiag { +pub(crate) struct ImplicitUnsafeAutorefsDiag<'a> { + #[label(lint_raw_ptr)] + pub raw_ptr_span: Span, + pub raw_ptr_ty: Ty<'a>, + #[note(lint_autoref)] + pub autoref_span: Span, + pub autoref_ty: Ty<'a>, + #[note(lint_method_def)] + pub method_def_span: Option, + pub method_name: Symbol, + #[note(lint_through_overloaded_deref)] + pub through_overloaded_deref: bool, #[subdiagnostic] pub suggestion: ImplicitUnsafeAutorefsSuggestion, } diff --git a/tests/ui/lint/implicit_autorefs.fixed b/tests/ui/lint/implicit_autorefs.fixed index 96a617b20c91..454dfe763726 100644 --- a/tests/ui/lint/implicit_autorefs.fixed +++ b/tests/ui/lint/implicit_autorefs.fixed @@ -96,4 +96,10 @@ unsafe fn test_string(ptr: *mut String) { //~^ WARN implicit autoref } +unsafe fn slice_ptr_len_because_of_msrv(slice: *const [T]) { + let _ = (&(&(*slice))[..]).len(); + //~^ WARN implicit autoref + //~^^ WARN implicit autoref +} + fn main() {} diff --git a/tests/ui/lint/implicit_autorefs.rs b/tests/ui/lint/implicit_autorefs.rs index 61dd0ac50ce7..507d65368283 100644 --- a/tests/ui/lint/implicit_autorefs.rs +++ b/tests/ui/lint/implicit_autorefs.rs @@ -96,4 +96,10 @@ unsafe fn test_string(ptr: *mut String) { //~^ WARN implicit autoref } +unsafe fn slice_ptr_len_because_of_msrv(slice: *const [T]) { + let _ = (*slice)[..].len(); + //~^ WARN implicit autoref + //~^^ WARN implicit autoref +} + fn main() {} diff --git a/tests/ui/lint/implicit_autorefs.stderr b/tests/ui/lint/implicit_autorefs.stderr index 6dd1ac65ada9..29279a268eb4 100644 --- a/tests/ui/lint/implicit_autorefs.stderr +++ b/tests/ui/lint/implicit_autorefs.stderr @@ -2,9 +2,16 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:10:13 | LL | let _ = (*ptr)[..16]; - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const [u8]` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:10:13 + | +LL | let _ = (*ptr)[..16]; + | ^^^^^^ = note: `#[warn(dangerous_implicit_autorefs)]` on by default help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | @@ -15,9 +22,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:19:13 | LL | let l = (*ptr).field.len(); - | ^^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^^ + | | + | this raw pointer has type `*const Test` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:19:13 + | +LL | let l = (*ptr).field.len(); + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let l = (&(*ptr).field).len(); @@ -27,9 +43,16 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:22:16 | LL | &raw const (*ptr).field[..l - 1] - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*const Test` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:22:16 + | +LL | &raw const (*ptr).field[..l - 1] + | ^^^^^^^^^^^^ help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | &raw const (&(*ptr).field)[..l - 1] @@ -39,9 +62,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:27:9 | LL | _ = (*a)[0].len(); - | ^^^^^^^^^^^^^ + | ^^-^^^^^^^^^^ + | | + | this raw pointer has type `*mut [String]` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:27:9 + | +LL | _ = (*a)[0].len(); + | ^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | _ = (&(*a)[0]).len(); @@ -51,9 +83,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:30:9 | LL | _ = (*a)[..1][0].len(); - | ^^^^^^^^^^^^^^^^^^ + | ^^-^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut [String]` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:30:9 + | +LL | _ = (*a)[..1][0].len(); + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | _ = (&(*a)[..1][0]).len(); @@ -63,9 +104,16 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:30:9 | LL | _ = (*a)[..1][0].len(); - | ^^^^^^^^^ + | ^^-^^^^^^ + | | + | this raw pointer has type `*mut [String]` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[String]` + --> $DIR/implicit_autorefs.rs:30:9 + | +LL | _ = (*a)[..1][0].len(); + | ^^^^ help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | _ = (&(*a))[..1][0].len(); @@ -75,9 +123,17 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:36:13 | LL | let _ = (*ptr).field; - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const ManuallyDrop` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `Test` + --> $DIR/implicit_autorefs.rs:36:13 + | +LL | let _ = (*ptr).field; + | ^^^^^^ + = note: reference(s) created through call(s) to overloaded `Deref(Mut)::deref(_mut)` implementation help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).field; @@ -87,9 +143,17 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:38:24 | LL | let _ = &raw const (*ptr).field; - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const ManuallyDrop` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `Test` + --> $DIR/implicit_autorefs.rs:38:24 + | +LL | let _ = &raw const (*ptr).field; + | ^^^^^^ + = note: reference(s) created through call(s) to overloaded `Deref(Mut)::deref(_mut)` implementation help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = &raw const (&(*ptr)).field; @@ -99,9 +163,17 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:43:13 | LL | let _ = (*ptr).field; - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*mut ManuallyDrop` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `Test` + --> $DIR/implicit_autorefs.rs:43:13 + | +LL | let _ = (*ptr).field; + | ^^^^^^ + = note: reference(s) created through call(s) to overloaded `Deref(Mut)::deref(_mut)` implementation help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).field; @@ -111,9 +183,17 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:48:13 | LL | let _ = (*ptr).field; - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const ManuallyDrop>` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `Test` + --> $DIR/implicit_autorefs.rs:48:13 + | +LL | let _ = (*ptr).field; + | ^^^^^^ + = note: reference(s) created through call(s) to overloaded `Deref(Mut)::deref(_mut)` implementation help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).field; @@ -123,9 +203,16 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:62:26 | LL | let _p: *const i32 = &raw const **w; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^- + | | + | this raw pointer has type `*const W` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&W` + --> $DIR/implicit_autorefs.rs:62:38 + | +LL | let _p: *const i32 = &raw const **w; + | ^^ help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _p: *const i32 = &raw const *(&**w); @@ -135,9 +222,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:72:14 | LL | unsafe { (*ptr).field.len() } - | ^^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^^ + | | + | this raw pointer has type `*const Test2` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:72:14 + | +LL | unsafe { (*ptr).field.len() } + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | unsafe { (&(*ptr).field).len() } @@ -147,9 +243,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:82:13 | LL | let _ = (*ptr).get(0); - | ^^^^^^^^^^^^^ + | ^^---^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:82:13 + | +LL | let _ = (*ptr).get(0); + | ^^^^^^ +note: method calls to `get` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).get(0); @@ -159,9 +264,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:84:13 | LL | let _ = (*ptr).get_unchecked(0); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:84:13 + | +LL | let _ = (*ptr).get_unchecked(0); + | ^^^^^^ +note: method calls to `get_unchecked` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).get_unchecked(0); @@ -171,9 +285,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:86:13 | LL | let _ = (*ptr).get_mut(0); - | ^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&mut [u8]` + --> $DIR/implicit_autorefs.rs:86:13 + | +LL | let _ = (*ptr).get_mut(0); + | ^^^^^^ +note: method calls to `get_mut` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&mut (*ptr)).get_mut(0); @@ -183,9 +306,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:88:13 | LL | let _ = (*ptr).get_unchecked_mut(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&mut [u8]` + --> $DIR/implicit_autorefs.rs:88:13 + | +LL | let _ = (*ptr).get_unchecked_mut(0); + | ^^^^^^ +note: method calls to `get_unchecked_mut` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&mut (*ptr)).get_unchecked_mut(0); @@ -195,9 +327,18 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:93:13 | LL | let _ = (*ptr).len(); - | ^^^^^^^^^^^^ + | ^^---^^^^^^^ + | | + | this raw pointer has type `*mut String` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:93:13 + | +LL | let _ = (*ptr).len(); + | ^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).len(); @@ -207,13 +348,62 @@ warning: implicit autoref creates a reference to the dereference of a raw pointe --> $DIR/implicit_autorefs.rs:95:13 | LL | let _ = (*ptr).is_empty(); - | ^^^^^^^^^^^^^^^^^ + | ^^---^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut String` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:95:13 + | +LL | let _ = (*ptr).is_empty(); + | ^^^^^^ +note: method calls to `is_empty` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).is_empty(); | ++ + -warning: 18 warnings emitted +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^-----^^^^^^^^^^^ + | | + | this raw pointer has type `*const [T]` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[T]` + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*slice)[..]).len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^-----^^^^^ + | | + | this raw pointer has type `*const [T]` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[T]` + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^^^^^^^ +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*slice))[..].len(); + | ++ + + +warning: 20 warnings emitted From e0b6363974c7894d61addc96e677166173a01e5f Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Wed, 14 May 2025 19:00:56 +0200 Subject: [PATCH 176/245] wire up startupinfo methods --- library/std/src/os/windows/process.rs | 36 ++++++++++++++++++++++++++ library/std/src/sys/process/windows.rs | 36 ++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index a084f452e55d..c223eee95b5f 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -344,6 +344,27 @@ pub trait CommandExt: Sealed { &mut self, attribute_list: &ProcThreadAttributeList<'_>, ) -> io::Result; + + /// When true, sets the `STARTF_RUNFULLSCREEN` flag on the [STARTUPINFO][1] struct before passing it to `CreateProcess`. + /// + /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")] + fn startupinfo_fullscreen(&mut self, enabled: bool) -> &mut process::Command; + + /// When true, sets the `STARTF_UNTRUSTEDSOURCE` flag on the [STARTUPINFO][1] struct before passing it to `CreateProcess`. + /// + /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")] + fn startupinfo_untrusted_source(&mut self, enabled: bool) -> &mut process::Command; + + /// When specified, sets the following flags on the [STARTUPINFO][1] struct before passing it to `CreateProcess`: + /// - If `Some(true)`, sets `STARTF_FORCEONFEEDBACK` + /// - If `Some(false)`, sets `STARTF_FORCEOFFFEEDBACK` + /// - If `None`, does not set any flags + /// + /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")] + fn startupinfo_force_feedback(&mut self, enabled: Option) -> &mut process::Command; } #[stable(feature = "windows_process_extensions", since = "1.16.0")] @@ -385,6 +406,21 @@ impl CommandExt for process::Command { .spawn_with_attributes(sys::process::Stdio::Inherit, true, Some(attribute_list)) .map(process::Child::from_inner) } + + fn startupinfo_fullscreen(&mut self, enabled: bool) -> &mut process::Command { + self.as_inner_mut().startupinfo_fullscreen(enabled); + self + } + + fn startupinfo_untrusted_source(&mut self, enabled: bool) -> &mut process::Command { + self.as_inner_mut().startupinfo_untrusted_source(enabled); + self + } + + fn startupinfo_force_feedback(&mut self, enabled: Option) -> &mut process::Command { + self.as_inner_mut().startupinfo_force_feedback(enabled); + self + } } #[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")] diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index 4acd753eec91..1ee3fbd285f5 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -155,6 +155,9 @@ pub struct Command { stdout: Option, stderr: Option, force_quotes_enabled: bool, + startupinfo_fullscreen: bool, + startupinfo_untrusted_source: bool, + startupinfo_force_feedback: Option, } pub enum Stdio { @@ -186,6 +189,9 @@ impl Command { stdout: None, stderr: None, force_quotes_enabled: false, + startupinfo_fullscreen: false, + startupinfo_untrusted_source: false, + startupinfo_force_feedback: None, } } @@ -222,6 +228,18 @@ impl Command { self.args.push(Arg::Raw(command_str_to_append.to_os_string())) } + pub fn startupinfo_fullscreen(&mut self, enabled: bool) { + self.startupinfo_fullscreen = enabled; + } + + pub fn startupinfo_untrusted_source(&mut self, enabled: bool) { + self.startupinfo_untrusted_source = enabled; + } + + pub fn startupinfo_force_feedback(&mut self, enabled: Option) { + self.startupinfo_force_feedback = enabled; + } + pub fn get_program(&self) -> &OsStr { &self.program } @@ -343,6 +361,24 @@ impl Command { si.wShowWindow = cmd_show; } + if self.startupinfo_fullscreen { + si.dwFlags |= c::STARTF_RUNFULLSCREEN; + } + + if self.startupinfo_untrusted_source { + si.dwFlags |= c::STARTF_UNTRUSTEDSOURCE; + } + + match self.startupinfo_force_feedback { + Some(true) => { + si.dwFlags |= c::STARTF_FORCEONFEEDBACK; + } + Some(false) => { + si.dwFlags |= c::STARTF_FORCEOFFFEEDBACK; + } + None => {} + } + let si_ptr: *mut c::STARTUPINFOW; let mut si_ex; From 9b3abe79d73d476d317a7a06fea5a11e5dd34950 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 14 May 2025 23:44:24 +0200 Subject: [PATCH 177/245] Use more subdiagnostics and reword the overloaded deref note --- compiler/rustc_lint/messages.ftl | 2 +- compiler/rustc_lint/src/autorefs.rs | 29 +++++++++++++++-------- compiler/rustc_lint/src/lints.rs | 32 +++++++++++++++++++------- tests/ui/lint/implicit_autorefs.stderr | 28 ++++------------------ 4 files changed, 49 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index baa017f3084a..08180bf8f8b2 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -364,7 +364,7 @@ lint_implicit_unsafe_autorefs = implicit autoref creates a reference to the dere .note = creating a reference requires the pointer target to be valid and imposes aliasing requirements .raw_ptr = this raw pointer has type `{$raw_ptr_ty}` .autoref = autoref is being applied to this expression, resulting in: `{$autoref_ty}` - .through_overloaded_deref = reference(s) created through call(s) to overloaded `Deref(Mut)::deref(_mut)` implementation + .overloaded_deref = references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations .method_def = method calls to `{$method_name}` require a reference .suggestion = try using a raw pointer method instead; or if this reference is intentional, make it explicit diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 9bb235263625..5de2cbf9939d 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -2,9 +2,12 @@ use rustc_ast::{BorrowKind, UnOp}; use rustc_hir::{Expr, ExprKind, Mutability}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDeref}; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::{kw, sym}; +use rustc_span::sym; -use crate::lints::{ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsSuggestion}; +use crate::lints::{ + ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsMethodNote, ImplicitUnsafeAutorefsOrigin, + ImplicitUnsafeAutorefsSuggestion, +}; use crate::{LateContext, LateLintPass, LintContext}; declare_lint! { @@ -98,8 +101,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs { peel_place_mappers(inner).kind // 1. Deref of a raw pointer. && typeck.expr_ty(dereferenced).is_raw_ptr() - // PERF: 5. b. A method call annotated with `#[rustc_no_implicit_refs]` && let method_did = match expr.kind { + // PERF: 5. b. A method call annotated with `#[rustc_no_implicit_refs]` ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), _ => None, } @@ -111,11 +114,18 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs { ImplicitUnsafeAutorefsDiag { raw_ptr_span: dereferenced.span, raw_ptr_ty: typeck.expr_ty(dereferenced), - autoref_span: inner.span, - autoref_ty: typeck.expr_ty_adjusted(inner), - method_def_span: method_did.map(|did| cx.tcx.def_span(did)), - method_name: method_did.map(|did| cx.tcx.item_name(did)).unwrap_or(kw::Empty), - through_overloaded_deref, + origin: if through_overloaded_deref { + ImplicitUnsafeAutorefsOrigin::OverloadedDeref + } else { + ImplicitUnsafeAutorefsOrigin::Autoref { + autoref_span: inner.span, + autoref_ty: typeck.expr_ty_adjusted(inner), + } + }, + method: method_did.map(|did| ImplicitUnsafeAutorefsMethodNote { + def_span: cx.tcx.def_span(did), + method_name: cx.tcx.item_name(did), + }), suggestion: ImplicitUnsafeAutorefsSuggestion { mutbl: borrow_mutbl.ref_prefix_str(), deref: if is_coming_from_deref { "*" } else { "" }, @@ -151,7 +161,8 @@ fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustmen /// Test if some adjustment has some implicit borrow. /// -/// Returns `Some(mutability)` if the argument adjustment has implicit borrow in it. +/// Returns `Some((mutability, was_an_overloaded_deref))` if the argument adjustment is +/// an implicit borrow (or has an implicit borrow via an overloaded deref). fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Mutability, bool)> { match kind { &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some((mutbl, true)), diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 978bb6d08d5a..7268a7f704fc 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -63,18 +63,34 @@ pub(crate) struct ImplicitUnsafeAutorefsDiag<'a> { #[label(lint_raw_ptr)] pub raw_ptr_span: Span, pub raw_ptr_ty: Ty<'a>, - #[note(lint_autoref)] - pub autoref_span: Span, - pub autoref_ty: Ty<'a>, - #[note(lint_method_def)] - pub method_def_span: Option, - pub method_name: Symbol, - #[note(lint_through_overloaded_deref)] - pub through_overloaded_deref: bool, + #[subdiagnostic] + pub origin: ImplicitUnsafeAutorefsOrigin<'a>, + #[subdiagnostic] + pub method: Option, #[subdiagnostic] pub suggestion: ImplicitUnsafeAutorefsSuggestion, } +#[derive(Subdiagnostic)] +pub(crate) enum ImplicitUnsafeAutorefsOrigin<'a> { + #[note(lint_autoref)] + Autoref { + #[primary_span] + autoref_span: Span, + autoref_ty: Ty<'a>, + }, + #[note(lint_overloaded_deref)] + OverloadedDeref, +} + +#[derive(Subdiagnostic)] +#[note(lint_method_def)] +pub(crate) struct ImplicitUnsafeAutorefsMethodNote { + #[primary_span] + pub def_span: Span, + pub method_name: Symbol, +} + #[derive(Subdiagnostic)] #[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")] pub(crate) struct ImplicitUnsafeAutorefsSuggestion { diff --git a/tests/ui/lint/implicit_autorefs.stderr b/tests/ui/lint/implicit_autorefs.stderr index 29279a268eb4..80ba8ae2fd27 100644 --- a/tests/ui/lint/implicit_autorefs.stderr +++ b/tests/ui/lint/implicit_autorefs.stderr @@ -128,12 +128,7 @@ LL | let _ = (*ptr).field; | this raw pointer has type `*const ManuallyDrop` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements -note: autoref is being applied to this expression, resulting in: `Test` - --> $DIR/implicit_autorefs.rs:36:13 - | -LL | let _ = (*ptr).field; - | ^^^^^^ - = note: reference(s) created through call(s) to overloaded `Deref(Mut)::deref(_mut)` implementation + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).field; @@ -148,12 +143,7 @@ LL | let _ = &raw const (*ptr).field; | this raw pointer has type `*const ManuallyDrop` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements -note: autoref is being applied to this expression, resulting in: `Test` - --> $DIR/implicit_autorefs.rs:38:24 - | -LL | let _ = &raw const (*ptr).field; - | ^^^^^^ - = note: reference(s) created through call(s) to overloaded `Deref(Mut)::deref(_mut)` implementation + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = &raw const (&(*ptr)).field; @@ -168,12 +158,7 @@ LL | let _ = (*ptr).field; | this raw pointer has type `*mut ManuallyDrop` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements -note: autoref is being applied to this expression, resulting in: `Test` - --> $DIR/implicit_autorefs.rs:43:13 - | -LL | let _ = (*ptr).field; - | ^^^^^^ - = note: reference(s) created through call(s) to overloaded `Deref(Mut)::deref(_mut)` implementation + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).field; @@ -188,12 +173,7 @@ LL | let _ = (*ptr).field; | this raw pointer has type `*const ManuallyDrop>` | = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements -note: autoref is being applied to this expression, resulting in: `Test` - --> $DIR/implicit_autorefs.rs:48:13 - | -LL | let _ = (*ptr).field; - | ^^^^^^ - = note: reference(s) created through call(s) to overloaded `Deref(Mut)::deref(_mut)` implementation + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations help: try using a raw pointer method instead; or if this reference is intentional, make it explicit | LL | let _ = (&(*ptr)).field; From f4ce73ede2f4df0f9983b337ceeed96c11f52c14 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 15 May 2025 16:32:45 +0800 Subject: [PATCH 178/245] Temporarily use Windows Server 2022 instead of Windows Server 2025 images At the moment, it seems like Windows Server 2025 20250504.1.0 is misconfigured causing insufficient disk space failures. Temporarily go back to Windows Server 2022 in the hope that those are not also misconfigured. --- src/ci/github-actions/jobs.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index afcc092e78e8..88e9c71a9e31 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -35,6 +35,8 @@ runners: os: windows-2022 <<: *base-job + # FIXME(#141022): Windows Server 2025 20250504.1.0 currently experiences + # insufficient disk space. - &job-windows-25 os: windows-2025 <<: *base-job @@ -476,13 +478,17 @@ auto: env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler SCRIPT: make ci-msvc-py - <<: *job-windows-25 + # FIXME(#141022): Windows Server 2025 20250504.1.0 currently experiences + # insufficient disk space. + <<: *job-windows - name: x86_64-msvc-2 env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler SCRIPT: make ci-msvc-ps1 - <<: *job-windows-25 + # FIXME(#141022): Windows Server 2025 20250504.1.0 currently experiences + # insufficient disk space. + <<: *job-windows # i686-msvc is split into two jobs to run tests in parallel. - name: i686-msvc-1 From 3e051afec7a0223c86bdab9cd751b32f6a988465 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 14 May 2025 17:58:00 +0800 Subject: [PATCH 179/245] Add some negative test coverage for malformed `-Clink-self-contained` flags --- ...ontained-malformed.invalid_modifier.stderr | 2 ++ ...k-self-contained-malformed.no_value.stderr | 2 ++ .../linking/link-self-contained-malformed.rs | 23 +++++++++++++++++++ ...contained-malformed.unknown_boolean.stderr | 2 ++ ...ed-malformed.unknown_modifier_value.stderr | 2 ++ ...f-contained-malformed.unknown_value.stderr | 2 ++ 6 files changed, 33 insertions(+) create mode 100644 tests/ui/linking/link-self-contained-malformed.invalid_modifier.stderr create mode 100644 tests/ui/linking/link-self-contained-malformed.no_value.stderr create mode 100644 tests/ui/linking/link-self-contained-malformed.rs create mode 100644 tests/ui/linking/link-self-contained-malformed.unknown_boolean.stderr create mode 100644 tests/ui/linking/link-self-contained-malformed.unknown_modifier_value.stderr create mode 100644 tests/ui/linking/link-self-contained-malformed.unknown_value.stderr diff --git a/tests/ui/linking/link-self-contained-malformed.invalid_modifier.stderr b/tests/ui/linking/link-self-contained-malformed.invalid_modifier.stderr new file mode 100644 index 000000000000..28e2c74fda22 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.invalid_modifier.stderr @@ -0,0 +1,2 @@ +error: incorrect value `*lld` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.no_value.stderr b/tests/ui/linking/link-self-contained-malformed.no_value.stderr new file mode 100644 index 000000000000..dd8e8af074bf --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.no_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.rs b/tests/ui/linking/link-self-contained-malformed.rs new file mode 100644 index 000000000000..8ccb82eee270 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.rs @@ -0,0 +1,23 @@ +//! Check that malformed `-Clink-self-contained` invocations are properly rejected. + +//@ revisions: no_value +//@[no_value] compile-flags: -Clink-self-contained= +//[no_value]~? ERROR incorrect value `` for codegen option `link-self-contained` + +//@ revisions: invalid_modifier +//@[invalid_modifier] compile-flags: -Clink-self-contained=*lld +//[invalid_modifier]~? ERROR incorrect value `*lld` for codegen option `link-self-contained` + +//@ revisions: unknown_value +//@[unknown_value] compile-flags: -Clink-self-contained=unknown +//[unknown_value]~? ERROR incorrect value `unknown` for codegen option `link-self-contained` + +//@ revisions: unknown_modifier_value +//@[unknown_modifier_value] compile-flags: -Clink-self-contained=-unknown +//[unknown_modifier_value]~? ERROR incorrect value `-unknown` for codegen option `link-self-contained` + +//@ revisions: unknown_boolean +//@[unknown_boolean] compile-flags: -Clink-self-contained=maybe +//[unknown_boolean]~? ERROR incorrect value `maybe` for codegen option `link-self-contained` + +fn main() {} diff --git a/tests/ui/linking/link-self-contained-malformed.unknown_boolean.stderr b/tests/ui/linking/link-self-contained-malformed.unknown_boolean.stderr new file mode 100644 index 000000000000..7924074d1bf4 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.unknown_boolean.stderr @@ -0,0 +1,2 @@ +error: incorrect value `maybe` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.unknown_modifier_value.stderr b/tests/ui/linking/link-self-contained-malformed.unknown_modifier_value.stderr new file mode 100644 index 000000000000..2dc58c0f7e8f --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.unknown_modifier_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `-unknown` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.unknown_value.stderr b/tests/ui/linking/link-self-contained-malformed.unknown_value.stderr new file mode 100644 index 000000000000..ce4c44299cd2 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.unknown_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `unknown` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + From a716f1a523a3dad5e5981fef45e25d7e2100f725 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 14 May 2025 18:11:17 +0800 Subject: [PATCH 180/245] Add some negative test coverage for `-Zlinker-features` flag --- ...features-malformed.invalid_modifier.stderr | 2 ++ ...eatures-malformed.invalid_separator.stderr | 2 ++ .../linker-features-malformed.no_value.stderr | 2 ++ tests/ui/linking/linker-features-malformed.rs | 27 +++++++++++++++++++ ...-features-malformed.unknown_boolean.stderr | 2 ++ ...es-malformed.unknown_modifier_value.stderr | 2 ++ ...er-features-malformed.unknown_value.stderr | 2 ++ 7 files changed, 39 insertions(+) create mode 100644 tests/ui/linking/linker-features-malformed.invalid_modifier.stderr create mode 100644 tests/ui/linking/linker-features-malformed.invalid_separator.stderr create mode 100644 tests/ui/linking/linker-features-malformed.no_value.stderr create mode 100644 tests/ui/linking/linker-features-malformed.rs create mode 100644 tests/ui/linking/linker-features-malformed.unknown_boolean.stderr create mode 100644 tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr create mode 100644 tests/ui/linking/linker-features-malformed.unknown_value.stderr diff --git a/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr b/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr new file mode 100644 index 000000000000..909b277089f4 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr @@ -0,0 +1,2 @@ +error: incorrect value `*lld` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.invalid_separator.stderr b/tests/ui/linking/linker-features-malformed.invalid_separator.stderr new file mode 100644 index 000000000000..0f84898a7742 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.invalid_separator.stderr @@ -0,0 +1,2 @@ +error: incorrect value `-lld@+lld` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.no_value.stderr b/tests/ui/linking/linker-features-malformed.no_value.stderr new file mode 100644 index 000000000000..e93a4e79bb1d --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.no_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.rs b/tests/ui/linking/linker-features-malformed.rs new file mode 100644 index 000000000000..0bdcfa39920f --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.rs @@ -0,0 +1,27 @@ +//! Check that malformed `-Zlinker-features` flags are properly rejected. + +//@ revisions: no_value +//@[no_value] compile-flags: -Zlinker-features= +//[no_value]~? ERROR incorrect value `` for unstable option `linker-features` + +//@ revisions: invalid_modifier +//@[invalid_modifier] compile-flags: -Zlinker-features=*lld +//[invalid_modifier]~? ERROR incorrect value `*lld` for unstable option `linker-features` + +//@ revisions: unknown_value +//@[unknown_value] compile-flags: -Zlinker-features=unknown +//[unknown_value]~? ERROR incorrect value `unknown` for unstable option `linker-features` + +//@ revisions: unknown_modifier_value +//@[unknown_modifier_value] compile-flags: -Zlinker-features=-unknown +//[unknown_modifier_value]~? ERROR incorrect value `-unknown` for unstable option `linker-features` + +//@ revisions: unknown_boolean +//@[unknown_boolean] compile-flags: -Zlinker-features=maybe +//[unknown_boolean]~? ERROR incorrect value `maybe` for unstable option `linker-features` + +//@ revisions: invalid_separator +//@[invalid_separator] compile-flags: -Zlinker-features=-lld@+lld +//[invalid_separator]~? ERROR incorrect value `-lld@+lld` for unstable option `linker-features` + +fn main() {} diff --git a/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr b/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr new file mode 100644 index 000000000000..865738d0ccc1 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr @@ -0,0 +1,2 @@ +error: incorrect value `maybe` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr b/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr new file mode 100644 index 000000000000..03b9620ca263 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `-unknown` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.unknown_value.stderr b/tests/ui/linking/linker-features-malformed.unknown_value.stderr new file mode 100644 index 000000000000..566632a3df38 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.unknown_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `unknown` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + From 55ad9cd516709b52899d01b2d7be2c4da2af3f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 15 May 2025 08:50:01 +0000 Subject: [PATCH 181/245] silence unexpected lld warning on old gccs --- compiler/rustc_codegen_ssa/src/back/link.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 159c17b0af75..c5792da26781 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -768,7 +768,7 @@ fn link_natively( && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-fuse-ld=lld") { info!("linker output: {:?}", out); - warn!("The linker driver does not support `-fuse-ld=lld`. Retrying without it."); + info!("The linker driver does not support `-fuse-ld=lld`. Retrying without it."); for arg in cmd.take_args() { if arg.to_string_lossy() != "-fuse-ld=lld" { cmd.arg(arg); From 734a5b1aa7888db3d86faffea1a15254022d68c9 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 15 May 2025 16:51:32 +0800 Subject: [PATCH 182/245] Revert "Fix linking statics on Arm64EC #140176" Unfortunately, multiple people are reporting linker warnings related to `__rust_no_alloc_shim_is_unstable` after this change. The solution isn't quite clear yet, let's revert to green for now, and try a reland with a determined solution for `__rust_no_alloc_shim_is_unstable`. This reverts commit c8b7f32434c0306db5c1b974ee43443746098a92, reversing changes made to 667247db71ea18c4130dd018d060e7f09d589490. --- compiler/rustc_codegen_llvm/src/consts.rs | 7 +- compiler/rustc_codegen_ssa/src/back/linker.rs | 120 +++++------------- .../src/back/symbol_export.rs | 11 +- compiler/rustc_codegen_ssa/src/base.rs | 47 +++---- compiler/rustc_codegen_ssa/src/lib.rs | 2 +- .../src/middle/exported_symbols.rs | 2 +- compiler/rustc_target/src/spec/base/msvc.rs | 14 +- .../arm64ec-import-export-static/export.rs | 23 ---- .../arm64ec-import-export-static/import.rs | 12 -- .../arm64ec-import-export-static/rmake.rs | 15 --- .../run-make/sanitizer-dylib-link/program.rs | 2 +- 11 files changed, 54 insertions(+), 201 deletions(-) delete mode 100644 tests/run-make/arm64ec-import-export-static/export.rs delete mode 100644 tests/run-make/arm64ec-import-export-static/import.rs delete mode 100644 tests/run-make/arm64ec-import-export-static/rmake.rs diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index cbac55c71531..bf81eb648f8a 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -364,12 +364,7 @@ impl<'ll> CodegenCx<'ll, '_> { if !def_id.is_local() { let needs_dll_storage_attr = self.use_dll_storage_attrs - // If the symbol is a foreign item, then don't automatically apply DLLImport, as - // we'll rely on the #[link] attribute instead. BUT, if this is an internal symbol - // then it may be generated by the compiler in some crate, so we do need to apply - // DLLImport when linking with the MSVC linker. - && (!self.tcx.is_foreign_item(def_id) - || (self.sess().target.is_like_msvc && fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL))) + && !self.tcx.is_foreign_item(def_id) // Local definitions can never be imported, so we must not apply // the DLLImport annotation. && !dso_local diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 80ee8ea22288..8fc83908efbc 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -337,12 +337,7 @@ pub(crate) trait Linker { fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]); fn no_crt_objects(&mut self); fn no_default_libraries(&mut self); - fn export_symbols( - &mut self, - tmpdir: &Path, - crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ); + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]); fn subsystem(&mut self, subsystem: &str); fn linker_plugin_lto(&mut self); fn add_eh_frame_header(&mut self) {} @@ -775,12 +770,7 @@ impl<'a> Linker for GccLinker<'a> { } } - fn export_symbols( - &mut self, - tmpdir: &Path, - crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) { // Symbol visibility in object files typically takes care of this. if crate_type == CrateType::Executable { let should_export_executable_symbols = @@ -809,7 +799,7 @@ impl<'a> Linker for GccLinker<'a> { // Write a plain, newline-separated list of symbols let res: io::Result<()> = try { let mut f = File::create_buffered(&path)?; - for (sym, _) in symbols { + for sym in symbols { debug!(" _{sym}"); writeln!(f, "_{sym}")?; } @@ -824,12 +814,11 @@ impl<'a> Linker for GccLinker<'a> { // .def file similar to MSVC one but without LIBRARY section // because LD doesn't like when it's empty writeln!(f, "EXPORTS")?; - for (symbol, kind) in symbols { - let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" }; + for symbol in symbols { debug!(" _{symbol}"); // Quote the name in case it's reserved by linker in some way // (this accounts for names with dots in particular). - writeln!(f, " \"{symbol}\"{kind_marker}")?; + writeln!(f, " \"{symbol}\"")?; } }; if let Err(error) = res { @@ -842,7 +831,7 @@ impl<'a> Linker for GccLinker<'a> { writeln!(f, "{{")?; if !symbols.is_empty() { writeln!(f, " global:")?; - for (sym, _) in symbols { + for sym in symbols { debug!(" {sym};"); writeln!(f, " {sym};")?; } @@ -1109,12 +1098,7 @@ impl<'a> Linker for MsvcLinker<'a> { // crates. Upstream rlibs may be linked statically to this dynamic library, // in which case they may continue to transitively be used and hence need // their symbols exported. - fn export_symbols( - &mut self, - tmpdir: &Path, - crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) { // Symbol visibility takes care of this typically if crate_type == CrateType::Executable { let should_export_executable_symbols = @@ -1132,10 +1116,9 @@ impl<'a> Linker for MsvcLinker<'a> { // straight to exports. writeln!(f, "LIBRARY")?; writeln!(f, "EXPORTS")?; - for (symbol, kind) in symbols { - let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" }; + for symbol in symbols { debug!(" _{symbol}"); - writeln!(f, " {symbol}{kind_marker}")?; + writeln!(f, " {symbol}")?; } }; if let Err(error) = res { @@ -1276,19 +1259,14 @@ impl<'a> Linker for EmLinker<'a> { self.cc_arg("-nodefaultlibs"); } - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { debug!("EXPORTED SYMBOLS:"); self.cc_arg("-s"); let mut arg = OsString::from("EXPORTED_FUNCTIONS="); let encoded = serde_json::to_string( - &symbols.iter().map(|(sym, _)| "_".to_owned() + sym).collect::>(), + &symbols.iter().map(|sym| "_".to_owned() + sym).collect::>(), ) .unwrap(); debug!("{encoded}"); @@ -1450,13 +1428,8 @@ impl<'a> Linker for WasmLd<'a> { fn no_default_libraries(&mut self) {} - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { - for (sym, _) in symbols { + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { + for sym in symbols { self.link_args(&["--export", sym]); } @@ -1590,7 +1563,7 @@ impl<'a> Linker for L4Bender<'a> { self.cc_arg("-nostdlib"); } - fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[(String, SymbolExportKind)]) { + fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[String]) { // ToDo, not implemented, copy from GCC self.sess.dcx().emit_warn(errors::L4BenderExportingSymbolsUnimplemented); } @@ -1747,17 +1720,12 @@ impl<'a> Linker for AixLinker<'a> { fn no_default_libraries(&mut self) {} - fn export_symbols( - &mut self, - tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { let path = tmpdir.join("list.exp"); let res: io::Result<()> = try { let mut f = File::create_buffered(&path)?; // FIXME: use llvm-nm to generate export list. - for (symbol, _) in symbols { + for symbol in symbols { debug!(" _{symbol}"); writeln!(f, " {symbol}")?; } @@ -1801,12 +1769,9 @@ fn for_each_exported_symbols_include_dep<'tcx>( } } -pub(crate) fn exported_symbols( - tcx: TyCtxt<'_>, - crate_type: CrateType, -) -> Vec<(String, SymbolExportKind)> { +pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec { if let Some(ref exports) = tcx.sess.target.override_export_symbols { - return exports.iter().map(|name| (name.to_string(), SymbolExportKind::Text)).collect(); + return exports.iter().map(ToString::to_string).collect(); } if let CrateType::ProcMacro = crate_type { @@ -1816,10 +1781,7 @@ pub(crate) fn exported_symbols( } } -fn exported_symbols_for_non_proc_macro( - tcx: TyCtxt<'_>, - crate_type: CrateType, -) -> Vec<(String, SymbolExportKind)> { +fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec { let mut symbols = Vec::new(); let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { @@ -1827,18 +1789,17 @@ fn exported_symbols_for_non_proc_macro( // from any cdylib. The latter doesn't work anyway as we use hidden visibility for // compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning. if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) { - symbols.push(( - symbol_export::exporting_symbol_name_for_instance_in_crate(tcx, symbol, cnum), - info.kind, + symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate( + tcx, symbol, cnum, )); - symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, info, cnum); + symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, cnum); } }); symbols } -fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, SymbolExportKind)> { +fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec { // `exported_symbols` will be empty when !should_codegen. if !tcx.sess.opts.output_types.should_codegen() { return Vec::new(); @@ -1848,10 +1809,7 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, Symbol let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id); let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx); - vec![ - (proc_macro_decls_name, SymbolExportKind::Text), - (metadata_symbol_name, SymbolExportKind::Text), - ] + vec![proc_macro_decls_name, metadata_symbol_name] } pub(crate) fn linked_symbols( @@ -1873,9 +1831,7 @@ pub(crate) fn linked_symbols( || info.used { symbols.push(( - symbol_export::linking_symbol_name_for_instance_in_crate( - tcx, symbol, info.kind, cnum, - ), + symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum), info.kind, )); } @@ -1950,13 +1906,7 @@ impl<'a> Linker for PtxLinker<'a> { fn ehcont_guard(&mut self) {} - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - _symbols: &[(String, SymbolExportKind)], - ) { - } + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, _symbols: &[String]) {} fn subsystem(&mut self, _subsystem: &str) {} @@ -2025,15 +1975,10 @@ impl<'a> Linker for LlbcLinker<'a> { fn ehcont_guard(&mut self) {} - fn export_symbols( - &mut self, - _tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { match _crate_type { CrateType::Cdylib => { - for (sym, _) in symbols { + for sym in symbols { self.link_args(&["--export-symbol", sym]); } } @@ -2107,16 +2052,11 @@ impl<'a> Linker for BpfLinker<'a> { fn ehcont_guard(&mut self) {} - fn export_symbols( - &mut self, - tmpdir: &Path, - _crate_type: CrateType, - symbols: &[(String, SymbolExportKind)], - ) { + fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { let path = tmpdir.join("symbols"); let res: io::Result<()> = try { let mut f = File::create_buffered(&path)?; - for (sym, _) in symbols { + for sym in symbols { writeln!(f, "{sym}")?; } }; diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 06ba5b4f6a75..5f0a0cf922af 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -692,7 +692,6 @@ fn calling_convention_for_symbol<'tcx>( pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>( tcx: TyCtxt<'tcx>, symbol: ExportedSymbol<'tcx>, - export_kind: SymbolExportKind, instantiating_crate: CrateNum, ) -> String { let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate); @@ -713,9 +712,8 @@ pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>( let prefix = match &target.arch[..] { "x86" => Some('_'), "x86_64" => None, - // Only functions are decorated for arm64ec. - "arm64ec" if export_kind == SymbolExportKind::Text => Some('#'), - // Only x86/64 and arm64ec use symbol decorations. + "arm64ec" => Some('#'), + // Only x86/64 use symbol decorations. _ => return undecorated, }; @@ -755,10 +753,9 @@ pub(crate) fn exporting_symbol_name_for_instance_in_crate<'tcx>( /// Add it to the symbols list for all kernel functions, so that it is exported in the linked /// object. pub(crate) fn extend_exported_symbols<'tcx>( - symbols: &mut Vec<(String, SymbolExportKind)>, + symbols: &mut Vec, tcx: TyCtxt<'tcx>, symbol: ExportedSymbol<'tcx>, - info: SymbolExportInfo, instantiating_crate: CrateNum, ) { let (conv, _) = calling_convention_for_symbol(tcx, symbol); @@ -770,7 +767,7 @@ pub(crate) fn extend_exported_symbols<'tcx>( let undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate); // Add the symbol for the kernel descriptor (with .kd suffix) - symbols.push((format!("{undecorated}.kd"), info.kind)); + symbols.push(format!("{undecorated}.kd")); } fn maybe_emutls_symbol_name<'tcx>( diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 93cbd4cbb7cc..775ab9071e74 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -12,9 +12,9 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_data_structures::unord::UnordMap; +use rustc_hir::ItemId; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ItemId, Target}; use rustc_metadata::EncodedMetadata; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; @@ -1038,35 +1038,21 @@ impl CrateInfo { // by the compiler, but that's ok because all this stuff is unstable anyway. let target = &tcx.sess.target; if !are_upstream_rust_objects_already_included(tcx.sess) { - let add_prefix = match (target.is_like_windows, target.arch.as_ref()) { - (true, "x86") => |name: String, _: SymbolExportKind| format!("_{name}"), - (true, "arm64ec") => { - // Only functions are decorated for arm64ec. - |name: String, export_kind: SymbolExportKind| match export_kind { - SymbolExportKind::Text => format!("#{name}"), - _ => name, - } - } - _ => |name: String, _: SymbolExportKind| name, - }; - let missing_weak_lang_items: FxIndexSet<(Symbol, SymbolExportKind)> = info + let missing_weak_lang_items: FxIndexSet = info .used_crates .iter() .flat_map(|&cnum| tcx.missing_lang_items(cnum)) .filter(|l| l.is_weak()) .filter_map(|&l| { let name = l.link_name()?; - let export_kind = match l.target() { - Target::Fn => SymbolExportKind::Text, - Target::Static => SymbolExportKind::Data, - _ => bug!( - "Don't know what the export kind is for lang item of kind {:?}", - l.target() - ), - }; - lang_items::required(tcx, l).then_some((name, export_kind)) + lang_items::required(tcx, l).then_some(name) }) .collect(); + let prefix = match (target.is_like_windows, target.arch.as_ref()) { + (true, "x86") => "_", + (true, "arm64ec") => "#", + _ => "", + }; // This loop only adds new items to values of the hash map, so the order in which we // iterate over the values is not important. @@ -1079,13 +1065,10 @@ impl CrateInfo { .for_each(|(_, linked_symbols)| { let mut symbols = missing_weak_lang_items .iter() - .map(|(item, export_kind)| { + .map(|item| { ( - add_prefix( - mangle_internal_symbol(tcx, item.as_str()), - *export_kind, - ), - *export_kind, + format!("{prefix}{}", mangle_internal_symbol(tcx, item.as_str())), + SymbolExportKind::Text, ) }) .collect::>(); @@ -1100,12 +1083,12 @@ impl CrateInfo { // errors. linked_symbols.extend(ALLOCATOR_METHODS.iter().map(|method| { ( - add_prefix( + format!( + "{prefix}{}", mangle_internal_symbol( tcx, - global_fn_name(method.name).as_str(), - ), - SymbolExportKind::Text, + global_fn_name(method.name).as_str() + ) ), SymbolExportKind::Text, ) diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 0e410be5a062..84919645cf07 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -218,7 +218,7 @@ pub struct CrateInfo { pub target_cpu: String, pub target_features: Vec, pub crate_types: Vec, - pub exported_symbols: UnordMap>, + pub exported_symbols: UnordMap>, pub linked_symbols: FxIndexMap>, pub local_crate_name: Symbol, pub compiler_builtins: Option, diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index 64a1f2aff15c..1d67d0fe3bbf 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -22,7 +22,7 @@ impl SymbolExportLevel { } /// Kind of exported symbols. -#[derive(Eq, PartialEq, Debug, Copy, Clone, Encodable, Decodable, HashStable, Hash)] +#[derive(Eq, PartialEq, Debug, Copy, Clone, Encodable, Decodable, HashStable)] pub enum SymbolExportKind { Text, Data, diff --git a/compiler/rustc_target/src/spec/base/msvc.rs b/compiler/rustc_target/src/spec/base/msvc.rs index bd59678d2366..486d7158723f 100644 --- a/compiler/rustc_target/src/spec/base/msvc.rs +++ b/compiler/rustc_target/src/spec/base/msvc.rs @@ -5,19 +5,7 @@ use crate::spec::{BinaryFormat, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo pub(crate) fn opts() -> TargetOptions { // Suppress the verbose logo and authorship debugging output, which would needlessly // clog any log files. - let pre_link_args = TargetOptions::link_args( - LinkerFlavor::Msvc(Lld::No), - &[ - "/NOLOGO", - // "Symbol is marked as dllimport, but defined in an object file" - // Harmless warning that flags a potential performance improvement: marking a symbol as - // dllimport indirects usage via the `__imp_` symbol, which isn't required if the symbol - // is in the current binary. This is tripped by __rust_no_alloc_shim_is_unstable as it - // is generated by the compiler, but marked as a foreign item (hence the dllimport) in - // the standard library. - "/IGNORE:4286", - ], - ); + let pre_link_args = TargetOptions::link_args(LinkerFlavor::Msvc(Lld::No), &["/NOLOGO"]); TargetOptions { linker_flavor: LinkerFlavor::Msvc(Lld::No), diff --git a/tests/run-make/arm64ec-import-export-static/export.rs b/tests/run-make/arm64ec-import-export-static/export.rs deleted file mode 100644 index 98b3a66d80c0..000000000000 --- a/tests/run-make/arm64ec-import-export-static/export.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![crate_type = "dylib"] -#![allow(internal_features)] -#![feature(no_core, lang_items)] -#![no_core] -#![no_std] - -// This is needed because of #![no_core]: -#[lang = "sized"] -trait Sized {} -#[lang = "sync"] -trait Sync {} -impl Sync for i32 {} -#[lang = "copy"] -pub trait Copy {} -impl Copy for i32 {} -#[lang = "drop_in_place"] -pub unsafe fn drop_in_place(_: *mut T) {} -#[no_mangle] -extern "system" fn _DllMainCRTStartup(_: *const u8, _: u32, _: *const u8) -> u32 { - 1 -} - -pub static VALUE: i32 = 42; diff --git a/tests/run-make/arm64ec-import-export-static/import.rs b/tests/run-make/arm64ec-import-export-static/import.rs deleted file mode 100644 index 9d52db251250..000000000000 --- a/tests/run-make/arm64ec-import-export-static/import.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![crate_type = "cdylib"] -#![allow(internal_features)] -#![feature(no_core)] -#![no_std] -#![no_core] - -extern crate export; - -#[no_mangle] -pub extern "C" fn func() -> i32 { - export::VALUE -} diff --git a/tests/run-make/arm64ec-import-export-static/rmake.rs b/tests/run-make/arm64ec-import-export-static/rmake.rs deleted file mode 100644 index 7fa31144810d..000000000000 --- a/tests/run-make/arm64ec-import-export-static/rmake.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Test that a static can be exported from one crate and imported into another. -// -// This was broken for Arm64EC as only functions, not variables, should be -// decorated with `#`. -// See https://github.com/rust-lang/rust/issues/138541 - -//@ needs-llvm-components: aarch64 -//@ only-windows - -use run_make_support::rustc; - -fn main() { - rustc().input("export.rs").target("aarch64-pc-windows-msvc").panic("abort").run(); - rustc().input("import.rs").target("aarch64-pc-windows-msvc").panic("abort").run(); -} diff --git a/tests/run-make/sanitizer-dylib-link/program.rs b/tests/run-make/sanitizer-dylib-link/program.rs index dbf885d343fe..1026c7f89ba9 100644 --- a/tests/run-make/sanitizer-dylib-link/program.rs +++ b/tests/run-make/sanitizer-dylib-link/program.rs @@ -1,4 +1,4 @@ -#[cfg_attr(windows, link(name = "library", kind = "raw-dylib"))] +#[cfg_attr(windows, link(name = "library.dll.lib", modifiers = "+verbatim"))] #[cfg_attr(not(windows), link(name = "library"))] extern "C" { fn overflow(); From b7f2cd3a2b1606934018cc64bac52bb887ea892a Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 29 Mar 2025 12:16:49 +0100 Subject: [PATCH 183/245] deduplicate abort implementations Currently, the code for process aborts is duplicated across `panic_abort` and `std`. This PR uses `#[rustc_std_internal_symbol]` to make the `std` implementation available to `panic_abort` via the linker, thereby deduplicating the code. --- library/Cargo.lock | 1 - library/panic_abort/Cargo.toml | 7 ++- library/panic_abort/src/lib.rs | 76 ++------------------------ library/panic_unwind/src/hermit.rs | 20 +++---- library/std/src/rt.rs | 9 ++- library/std/src/sys/pal/hermit/mod.rs | 9 --- library/std/src/sys/pal/sgx/mod.rs | 7 ++- library/std/src/sys/pal/uefi/mod.rs | 8 --- library/std/src/sys/pal/windows/mod.rs | 9 ++- library/std/src/sys/pal/xous/mod.rs | 6 ++ library/std/src/sys/pal/xous/os.rs | 8 --- 11 files changed, 43 insertions(+), 117 deletions(-) diff --git a/library/Cargo.lock b/library/Cargo.lock index 97ca3cb06b24..02018057ed53 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -196,7 +196,6 @@ name = "panic_abort" version = "0.0.0" dependencies = [ "alloc", - "cfg-if", "compiler_builtins", "core", "libc", diff --git a/library/panic_abort/Cargo.toml b/library/panic_abort/Cargo.toml index 6f43ac4809a3..d7d169671f01 100644 --- a/library/panic_abort/Cargo.toml +++ b/library/panic_abort/Cargo.toml @@ -12,10 +12,11 @@ bench = false doc = false [dependencies] -alloc = { path = "../alloc" } -cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } core = { path = "../core" } compiler_builtins = "0.1.0" -[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] +[target.'cfg(target_os = "android")'.dependencies] libc = { version = "0.2", default-features = false } + +[target.'cfg(any(target_os = "android", target_os = "zkvm"))'.dependencies] +alloc = { path = "../alloc" } diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index b2ad0f4ac3d0..d1706b652529 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -7,15 +7,11 @@ #![unstable(feature = "panic_abort", issue = "32837")] #![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] #![panic_runtime] -#![allow(unused_features)] -#![feature(asm_experimental_arch)] -#![feature(core_intrinsics)] #![feature(panic_runtime)] #![feature(std_internals)] #![feature(staged_api)] #![feature(rustc_attrs)] #![allow(internal_features)] -#![deny(unsafe_op_in_unsafe_fn)] #[cfg(target_os = "android")] mod android; @@ -45,75 +41,13 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { zkvm::zkvm_set_abort_message(_payload); } - unsafe { - abort(); + unsafe extern "Rust" { + // This is defined in std::rt. + #[rustc_std_internal_symbol] + safe fn __rust_abort() -> !; } - cfg_if::cfg_if! { - if #[cfg(any(unix, target_os = "solid_asp3"))] { - unsafe fn abort() -> ! { - unsafe { libc::abort(); } - } - } else if #[cfg(any(target_os = "hermit", - all(target_vendor = "fortanix", target_env = "sgx"), - target_os = "xous", - target_os = "uefi", - ))] { - unsafe fn abort() -> ! { - // call std::sys::abort_internal - unsafe extern "C" { - pub fn __rust_abort() -> !; - } - unsafe { __rust_abort(); } - } - } else if #[cfg(all(windows, not(miri)))] { - // On Windows, use the processor-specific __fastfail mechanism. In Windows 8 - // and later, this will terminate the process immediately without running any - // in-process exception handlers. In earlier versions of Windows, this - // sequence of instructions will be treated as an access violation, - // terminating the process but without necessarily bypassing all exception - // handlers. - // - // https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail - // - // Note: this is the same implementation as in std's `abort_internal` - unsafe fn abort() -> ! { - #[allow(unused)] - const FAST_FAIL_FATAL_APP_EXIT: usize = 7; - cfg_if::cfg_if! { - if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { - unsafe { - core::arch::asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } - } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] { - unsafe { - core::arch::asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } - } else if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] { - unsafe { - core::arch::asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } - } else { - core::intrinsics::abort(); - } - } - } - } else if #[cfg(target_os = "teeos")] { - mod teeos { - unsafe extern "C" { - pub fn TEE_Panic(code: u32) -> !; - } - } - - unsafe fn abort() -> ! { - unsafe { teeos::TEE_Panic(1); } - } - } else { - unsafe fn abort() -> ! { - core::intrinsics::abort(); - } - } - } + __rust_abort() } // This... is a bit of an oddity. The tl;dr; is that this is required to link diff --git a/library/panic_unwind/src/hermit.rs b/library/panic_unwind/src/hermit.rs index 8f4562d07fc4..b36d1a019fdd 100644 --- a/library/panic_unwind/src/hermit.rs +++ b/library/panic_unwind/src/hermit.rs @@ -5,20 +5,16 @@ use alloc::boxed::Box; use core::any::Any; +unsafe extern "Rust" { + // This is defined in std::rt + #[rustc_std_internal_symbol] + safe fn __rust_abort() -> !; +} + pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box { - unsafe extern "C" { - fn __rust_abort() -> !; - } - unsafe { - __rust_abort(); - } + __rust_abort() } pub(crate) unsafe fn panic(_data: Box) -> u32 { - unsafe extern "C" { - fn __rust_abort() -> !; - } - unsafe { - __rust_abort(); - } + __rust_abort() } diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 9737b2f5bfe6..b3f3b301e3db 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -26,6 +26,13 @@ use crate::sync::Once; use crate::thread::{self, main_thread}; use crate::{mem, panic, sys}; +// This function is needed by the panic runtime. +#[cfg(not(test))] +#[rustc_std_internal_symbol] +fn __rust_abort() { + crate::process::abort(); +} + // Prints to the "panic output", depending on the platform this may be: // - the standard error output // - some dedicated platform specific output @@ -47,7 +54,7 @@ macro_rules! rtabort { ($($t:tt)*) => { { rtprintpanic!("fatal runtime error: {}, aborting\n", format_args!($($t)*)); - crate::sys::abort_internal(); + crate::process::abort(); } } } diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index ea636938d703..fb8d69b73759 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -43,15 +43,6 @@ pub fn abort_internal() -> ! { unsafe { hermit_abi::abort() } } -// This function is needed by the panic runtime. The symbol is named in -// pre-link args for the target specification, so keep that in sync. -#[cfg(not(test))] -#[unsafe(no_mangle)] -// NB. used by both libunwind and libpanic_abort -pub extern "C" fn __rust_abort() { - abort_internal(); -} - // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 3932f64c0ef4..6e43a79ddec2 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -112,11 +112,14 @@ pub fn abort_internal() -> ! { abi::usercalls::exit(true) } -// This function is needed by the panic runtime. The symbol is named in +// This function is needed by libunwind. The symbol is named in // pre-link args for the target specification, so keep that in sync. +// Note: contrary to the `__rust_abort` in `crate::rt`, this uses `no_mangle` +// because it is actually used from C code. Because symbols annotated with +// #[rustc_std_internal_symbol] get mangled, this will not lead to linker +// conflicts. #[cfg(not(test))] #[unsafe(no_mangle)] -// NB. used by both libunwind and libpanic_abort pub extern "C" fn __rust_abort() { abort_internal(); } diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 78fcfcb3b77d..8911a2ee5194 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -161,14 +161,6 @@ pub fn abort_internal() -> ! { core::intrinsics::abort(); } -// This function is needed by the panic runtime. The symbol is named in -// pre-link args for the target specification, so keep that in sync. -#[cfg(not(test))] -#[unsafe(no_mangle)] -pub extern "C" fn __rust_abort() { - abort_internal(); -} - /// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { uefi::env::disable_boot_services(); diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 4f18c4009ab6..ebe8cfb66813 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -328,8 +328,13 @@ pub fn dur2timeout(dur: Duration) -> u32 { /// Use `__fastfail` to abort the process /// -/// This is the same implementation as in libpanic_abort's `__rust_start_panic`. See -/// that function for more information on `__fastfail` +/// In Windows 8 and later, this will terminate the process immediately without +/// running any in-process exception handlers. In earlier versions of Windows, +/// this sequence of instructions will be treated as an access violation, +/// terminating the process but without necessarily bypassing all exception +/// handlers. +/// +/// https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail #[cfg(not(miri))] // inline assembly does not work in Miri pub fn abort_internal() -> ! { unsafe { diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index 383d031ed435..042c4ff862ff 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -1,5 +1,7 @@ #![forbid(unsafe_op_in_unsafe_fn)] +use crate::os::xous::ffi::exit; + pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -9,3 +11,7 @@ pub mod time; #[path = "../unsupported/common.rs"] mod common; pub use common::*; + +pub fn abort_internal() -> ! { + exit(101); +} diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index 2230dabe096f..d612a27d2bdb 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -62,14 +62,6 @@ mod c_compat { } exit(unsafe { main() }); } - - // This function is needed by the panic runtime. The symbol is named in - // pre-link args for the target specification, so keep that in sync. - #[unsafe(no_mangle)] - // NB. used by both libunwind and libpanic_abort - pub extern "C" fn __rust_abort() -> ! { - exit(101); - } } pub fn errno() -> i32 { From 6647cbb515711774b0dff6c682c176715dcb9d51 Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 21 Apr 2025 15:18:36 +0200 Subject: [PATCH 184/245] improve internal fastfail explainer --- library/std/src/sys/pal/windows/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index ebe8cfb66813..8f54e2376eb8 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -330,9 +330,8 @@ pub fn dur2timeout(dur: Duration) -> u32 { /// /// In Windows 8 and later, this will terminate the process immediately without /// running any in-process exception handlers. In earlier versions of Windows, -/// this sequence of instructions will be treated as an access violation, -/// terminating the process but without necessarily bypassing all exception -/// handlers. +/// this sequence of instructions will be treated as an access violation, which +/// will still terminate the process but might run some exception handlers. /// /// https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail #[cfg(not(miri))] // inline assembly does not work in Miri From f77c16000701491d38e76a1dddb6b35a7658d550 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 15 May 2025 11:37:50 +0200 Subject: [PATCH 185/245] normalize abort calls in miri tests --- src/tools/miri/tests/fail/alloc/alloc_error_handler.rs | 2 +- .../miri/tests/fail/alloc/alloc_error_handler.stderr | 2 +- .../exported_symbol_bad_unwind2.both.stderr | 2 +- .../exported_symbol_bad_unwind2.definition.stderr | 2 +- .../fail/function_calls/exported_symbol_bad_unwind2.rs | 2 +- .../miri/tests/fail/intrinsics/uninit_uninhabited_type.rs | 2 +- .../tests/fail/intrinsics/uninit_uninhabited_type.stderr | 2 +- src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs | 2 +- src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr | 2 +- src/tools/miri/tests/fail/panic/abort_unwind.rs | 2 +- src/tools/miri/tests/fail/panic/abort_unwind.stderr | 2 +- src/tools/miri/tests/fail/panic/double_panic.rs | 2 +- src/tools/miri/tests/fail/panic/double_panic.stderr | 2 +- src/tools/miri/tests/fail/panic/panic_abort1.rs | 2 +- src/tools/miri/tests/fail/panic/panic_abort1.stderr | 8 +++++--- src/tools/miri/tests/fail/panic/panic_abort2.rs | 2 +- src/tools/miri/tests/fail/panic/panic_abort2.stderr | 8 +++++--- src/tools/miri/tests/fail/panic/panic_abort3.rs | 2 +- src/tools/miri/tests/fail/panic/panic_abort3.stderr | 8 +++++--- src/tools/miri/tests/fail/panic/panic_abort4.rs | 2 +- src/tools/miri/tests/fail/panic/panic_abort4.stderr | 8 +++++--- src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs | 2 +- src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr | 2 +- src/tools/miri/tests/fail/terminate-terminator.rs | 2 +- src/tools/miri/tests/fail/terminate-terminator.stderr | 2 +- src/tools/miri/tests/fail/unwind-action-terminate.rs | 2 +- src/tools/miri/tests/fail/unwind-action-terminate.stderr | 2 +- 27 files changed, 43 insertions(+), 35 deletions(-) diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs index 2097126e16b9..4a87411d755c 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs @@ -1,5 +1,5 @@ //@error-in-other-file: aborted -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" #![feature(allocator_api)] diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr index 3642f3f28ca2..fa84da841fd4 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr @@ -2,7 +2,7 @@ memory allocation of 4 bytes failed error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr index b2a501db7763..7cb2bf996781 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr @@ -11,7 +11,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr index b2a501db7763..7cb2bf996781 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr @@ -11,7 +11,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs index 1382e9571f31..9d993786d57f 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs @@ -1,5 +1,5 @@ //@revisions: extern_block definition both -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs index 6e0e0ca9f53e..dd3246d81203 100644 --- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs +++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.rs @@ -1,4 +1,4 @@ -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr index 2c9bea1724d6..ba96e595bee2 100644 --- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr +++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr @@ -7,7 +7,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs index 0e8d3d08c128..3d355bad626a 100644 --- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs +++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.rs @@ -1,4 +1,4 @@ -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr index 0634298a38f3..7e1f4160cc0e 100644 --- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr +++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr @@ -7,7 +7,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/panic/abort_unwind.rs b/src/tools/miri/tests/fail/panic/abort_unwind.rs index e313d9c11dec..bd819362da41 100644 --- a/src/tools/miri/tests/fail/panic/abort_unwind.rs +++ b/src/tools/miri/tests/fail/panic/abort_unwind.rs @@ -1,5 +1,5 @@ //@error-in-other-file: the program aborted execution -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/panic/abort_unwind.stderr b/src/tools/miri/tests/fail/panic/abort_unwind.stderr index 3a63cb38ad06..e6668b09f66b 100644 --- a/src/tools/miri/tests/fail/panic/abort_unwind.stderr +++ b/src/tools/miri/tests/fail/panic/abort_unwind.stderr @@ -11,7 +11,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/panic/double_panic.rs b/src/tools/miri/tests/fail/panic/double_panic.rs index ddc75521ecad..4d8f4cb6fb7b 100644 --- a/src/tools/miri/tests/fail/panic/double_panic.rs +++ b/src/tools/miri/tests/fail/panic/double_panic.rs @@ -1,4 +1,4 @@ -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/panic/double_panic.stderr b/src/tools/miri/tests/fail/panic/double_panic.stderr index 16e933be4348..67f88955defb 100644 --- a/src/tools/miri/tests/fail/panic/double_panic.stderr +++ b/src/tools/miri/tests/fail/panic/double_panic.stderr @@ -14,7 +14,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.rs b/src/tools/miri/tests/fail/panic/panic_abort1.rs index 7552c7b7e808..06cb673778a2 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort1.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort1.rs @@ -1,6 +1,6 @@ //@error-in-other-file: the program aborted execution //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.stderr b/src/tools/miri/tests/fail/panic/panic_abort1.stderr index c950b2b4ea63..6d56874ebde9 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort1.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort1.stderr @@ -4,13 +4,15 @@ panicking from libstd note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/panic_abort/src/lib.rs:LL:CC + --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: - = note: inside `panic_abort::__rust_start_panic::abort` at RUSTLIB/panic_abort/src/lib.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC + = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.rs b/src/tools/miri/tests/fail/panic/panic_abort2.rs index 624f99335455..c011b3ee7eb8 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort2.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort2.rs @@ -1,6 +1,6 @@ //@error-in-other-file: the program aborted execution //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.stderr b/src/tools/miri/tests/fail/panic/panic_abort2.stderr index 9a9266ec493a..dbb56f13f486 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort2.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort2.stderr @@ -4,13 +4,15 @@ thread 'main' panicked at tests/fail/panic/panic_abort2.rs:LL:CC: note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/panic_abort/src/lib.rs:LL:CC + --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: - = note: inside `panic_abort::__rust_start_panic::abort` at RUSTLIB/panic_abort/src/lib.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC + = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.rs b/src/tools/miri/tests/fail/panic/panic_abort3.rs index d1435b55946c..911dc4a44abd 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort3.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort3.rs @@ -1,6 +1,6 @@ //@error-in-other-file: the program aborted execution //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.stderr b/src/tools/miri/tests/fail/panic/panic_abort3.stderr index f04a2b0f3f14..7f0564879e47 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort3.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort3.stderr @@ -4,13 +4,15 @@ panicking from libcore note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/panic_abort/src/lib.rs:LL:CC + --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: - = note: inside `panic_abort::__rust_start_panic::abort` at RUSTLIB/panic_abort/src/lib.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC + = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.rs b/src/tools/miri/tests/fail/panic/panic_abort4.rs index 54b9c9cbfdb0..696fdff74223 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort4.rs +++ b/src/tools/miri/tests/fail/panic/panic_abort4.rs @@ -1,6 +1,6 @@ //@error-in-other-file: the program aborted execution //@normalize-stderr-test: "\| +\^+" -> "| ^" -//@normalize-stderr-test: "unsafe \{ libc::abort\(\); \}|core::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@compile-flags: -C panic=abort fn main() { diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.stderr b/src/tools/miri/tests/fail/panic/panic_abort4.stderr index e71c4879ea32..ce6910b99331 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort4.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort4.stderr @@ -4,13 +4,15 @@ thread 'main' panicked at tests/fail/panic/panic_abort4.rs:LL:CC: note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution - --> RUSTLIB/panic_abort/src/lib.rs:LL:CC + --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: - = note: inside `panic_abort::__rust_start_panic::abort` at RUSTLIB/panic_abort/src/lib.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC + = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs index 6f627c416b0f..6119e8604b46 100644 --- a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs +++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs @@ -1,6 +1,6 @@ //! This is a regression test for : The precondition //! check in `ptr::swap_nonoverlapping` was incorrectly disabled in Miri. -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr index 80dd2f39b426..f57487e3ffe1 100644 --- a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr +++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr @@ -9,7 +9,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/terminate-terminator.rs b/src/tools/miri/tests/fail/terminate-terminator.rs index 465625c75725..31ae829a2de7 100644 --- a/src/tools/miri/tests/fail/terminate-terminator.rs +++ b/src/tools/miri/tests/fail/terminate-terminator.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmir-opt-level=3 -Zinline-mir-hint-threshold=1000 -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/terminate-terminator.stderr b/src/tools/miri/tests/fail/terminate-terminator.stderr index f2548bf5cdb2..d16119a30e6f 100644 --- a/src/tools/miri/tests/fail/terminate-terminator.stderr +++ b/src/tools/miri/tests/fail/terminate-terminator.stderr @@ -13,7 +13,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.rs b/src/tools/miri/tests/fail/unwind-action-terminate.rs index 465e07c8db4b..f0fbcfd88675 100644 --- a/src/tools/miri/tests/fail/unwind-action-terminate.rs +++ b/src/tools/miri/tests/fail/unwind-action-terminate.rs @@ -1,5 +1,5 @@ //@error-in-other-file: aborted execution -//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.stderr b/src/tools/miri/tests/fail/unwind-action-terminate.stderr index 7b9a4383fc4c..222d4fb28669 100644 --- a/src/tools/miri/tests/fail/unwind-action-terminate.stderr +++ b/src/tools/miri/tests/fail/unwind-action-terminate.stderr @@ -11,7 +11,7 @@ thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC | -LL | ABORT(); +LL | ABORT() | ^ the program aborted execution | = note: BACKTRACE: From 92116bcfa6d47ebd021296a3f3cabb62f021b74c Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 15 May 2025 10:17:20 +0000 Subject: [PATCH 186/245] remove `RustfmtState` to reduce `initial_rustfmt` complexity The current use of `RustfmtState` doesn't serve its main purpose as it never does the lazy evaulation since `Build::build` forces it to be ready on the early stage. If we want rustfmt to be ready on the early stage, we don't need to have `RustfmtState` complexity at all. Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/format.rs | 4 +- src/bootstrap/src/core/build_steps/test.rs | 2 +- src/bootstrap/src/core/config/config.rs | 45 ++------------------ src/bootstrap/src/core/download.rs | 6 ++- src/bootstrap/src/lib.rs | 5 --- 5 files changed, 12 insertions(+), 50 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index 93900a9043e7..1c317ce4b86b 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -58,7 +58,7 @@ fn rustfmt( fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, BuildStamp)> { let stamp_file = BuildStamp::new(&build.out).with_prefix("rustfmt"); - let mut cmd = command(build.initial_rustfmt()?); + let mut cmd = command(build.config.initial_rustfmt.as_ref()?); cmd.arg("--version"); let output = cmd.allow_failure().run_capture(build); @@ -243,7 +243,7 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { let override_ = override_builder.build().unwrap(); // `override` is a reserved keyword - let rustfmt_path = build.initial_rustfmt().unwrap_or_else(|| { + let rustfmt_path = build.config.initial_rustfmt.clone().unwrap_or_else(|| { eprintln!("fmt error: `x fmt` is not supported on this channel"); crate::exit!(1); }); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index f708b6e9fd69..b2dc509ddca0 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1102,7 +1102,7 @@ impl Step for Tidy { if builder.config.channel == "dev" || builder.config.channel == "nightly" { if !builder.config.json_output { builder.info("fmt check"); - if builder.initial_rustfmt().is_none() { + if builder.config.initial_rustfmt.is_none() { let inferred_rustfmt_dir = builder.initial_sysroot.join("bin"); eprintln!( "\ diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index c8beca25bccc..3b8c3655b8d1 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -3,7 +3,7 @@ //! This module implements parsing `bootstrap.toml` configuration files to tweak //! how the build runs. -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::{self, Display}; use std::hash::Hash; @@ -406,11 +406,7 @@ pub struct Config { pub initial_rustc: PathBuf, pub initial_cargo_clippy: Option, pub initial_sysroot: PathBuf, - - #[cfg(not(test))] - initial_rustfmt: RefCell, - #[cfg(test)] - pub initial_rustfmt: RefCell, + pub initial_rustfmt: Option, /// The paths to work with. For example: with `./x check foo bar` we get /// `paths=["foo", "bar"]`. @@ -428,15 +424,6 @@ pub struct Config { pub path_modification_cache: Arc, PathFreshness>>>, } -#[derive(Clone, Debug, Default)] -pub enum RustfmtState { - SystemToolchain(PathBuf), - Downloaded(PathBuf), - Unavailable, - #[default] - LazyEvaluated, -} - #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum LlvmLibunwind { #[default] @@ -2448,13 +2435,8 @@ impl Config { }); } - if let Some(r) = rustfmt { - *config.initial_rustfmt.borrow_mut() = if r.exists() { - RustfmtState::SystemToolchain(r) - } else { - RustfmtState::Unavailable - }; - } + config.initial_rustfmt = + if let Some(r) = rustfmt { Some(r) } else { config.maybe_download_rustfmt() }; // Now that we've reached the end of our configuration, infer the // default values for all options that we haven't otherwise stored yet. @@ -2851,25 +2833,6 @@ impl Config { .as_deref() } - pub(crate) fn initial_rustfmt(&self) -> Option { - match &mut *self.initial_rustfmt.borrow_mut() { - RustfmtState::SystemToolchain(p) | RustfmtState::Downloaded(p) => Some(p.clone()), - RustfmtState::Unavailable => None, - r @ RustfmtState::LazyEvaluated => { - if self.dry_run() { - return Some(PathBuf::new()); - } - let path = self.maybe_download_rustfmt(); - *r = if let Some(p) = &path { - RustfmtState::Downloaded(p.clone()) - } else { - RustfmtState::Unavailable - }; - path - } - } - } - /// Runs a function if verbosity is greater than 0 pub fn verbose(&self, f: impl Fn()) { if self.is_verbose() { diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 64298964dad5..e0c9877cd55d 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -446,7 +446,7 @@ impl Config { #[cfg(test)] pub(crate) fn maybe_download_rustfmt(&self) -> Option { - None + Some(PathBuf::new()) } /// NOTE: rustfmt is a completely different toolchain than the bootstrap compiler, so it can't @@ -455,6 +455,10 @@ impl Config { pub(crate) fn maybe_download_rustfmt(&self) -> Option { use build_helper::stage0_parser::VersionMetadata; + if self.dry_run() { + return Some(PathBuf::new()); + } + let VersionMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?; let channel = format!("{version}-{date}"); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 1e6acad5c0fc..9492ffaed756 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -325,7 +325,6 @@ forward! { tempdir() -> PathBuf, llvm_link_shared() -> bool, download_rustc() -> bool, - initial_rustfmt() -> Option, } impl Build { @@ -614,10 +613,6 @@ impl Build { crate::utils::job::setup(self); } - // Download rustfmt early so that it can be used in rust-analyzer configs. - trace!("downloading rustfmt early"); - let _ = &builder::Builder::new(self).initial_rustfmt(); - // Handle hard-coded subcommands. { #[cfg(feature = "tracing")] From 7b2dcf298909f1493a57387be564fc2f07a3c6ad Mon Sep 17 00:00:00 2001 From: Andrew Zhogin Date: Thu, 15 May 2025 19:03:23 +0700 Subject: [PATCH 187/245] Async drop fix for dropee from another crate (#140858) --- compiler/rustc_middle/src/ty/mod.rs | 2 +- compiler/rustc_middle/src/ty/util.rs | 2 +- .../async-drop/auxiliary/async-drop-dep.rs | 28 +++++++++++++++ .../async-drop/dependency-dropped.rs | 34 +++++++++++++++++++ .../async-drop/dependency-dropped.run.stdout | 1 + 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs create mode 100644 tests/ui/async-await/async-drop/dependency-dropped.rs create mode 100644 tests/ui/async-await/async-drop/dependency-dropped.run.stdout diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index dda0faa3afed..51eb9461f2d6 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1184,7 +1184,7 @@ pub struct Destructor { #[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)] pub struct AsyncDestructor { /// The `DefId` of the `impl AsyncDrop` - pub impl_did: LocalDefId, + pub impl_did: DefId, } #[derive(Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 6fe5927c29fc..9676aa404482 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -465,7 +465,7 @@ impl<'tcx> TyCtxt<'tcx> { dtor_candidate = Some(impl_did); } - Some(ty::AsyncDestructor { impl_did: dtor_candidate? }) + Some(ty::AsyncDestructor { impl_did: dtor_candidate?.into() }) } /// Returns the set of types that are required to be alive in diff --git a/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs b/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs new file mode 100644 index 000000000000..1729599f7b3f --- /dev/null +++ b/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs @@ -0,0 +1,28 @@ +//@ edition:2021 + +#![feature(async_drop)] +#![allow(incomplete_features)] + +pub struct HasDrop; +impl Drop for HasDrop{ + fn drop(&mut self) { + println!("Sync drop"); + } +} + +pub struct MongoDrop; +impl MongoDrop { + pub async fn new() -> Result { + Ok(Self) + } +} +impl Drop for MongoDrop{ + fn drop(&mut self) { + println!("Sync drop"); + } +} +impl std::future::AsyncDrop for MongoDrop { + async fn drop(self: std::pin::Pin<&mut Self>) { + println!("Async drop"); + } +} diff --git a/tests/ui/async-await/async-drop/dependency-dropped.rs b/tests/ui/async-await/async-drop/dependency-dropped.rs new file mode 100644 index 000000000000..f763bb32b173 --- /dev/null +++ b/tests/ui/async-await/async-drop/dependency-dropped.rs @@ -0,0 +1,34 @@ +//@ run-pass +//@ check-run-results +//@ aux-build:async-drop-dep.rs +//@ edition:2021 + +#![feature(async_drop)] +#![allow(incomplete_features)] + +extern crate async_drop_dep; + +use async_drop_dep::MongoDrop; +use std::pin::pin; +use std::task::{Context, Poll, Waker}; +use std::future::Future; + +async fn asyncdrop() { + let _ = MongoDrop::new().await; +} + +pub fn block_on(fut: impl Future) -> T { + let mut fut = pin!(fut); + let ctx = &mut Context::from_waker(Waker::noop()); + + loop { + match fut.as_mut().poll(ctx) { + Poll::Pending => {} + Poll::Ready(t) => break t, + } + } +} + +fn main() { + let _ = block_on(asyncdrop()); +} diff --git a/tests/ui/async-await/async-drop/dependency-dropped.run.stdout b/tests/ui/async-await/async-drop/dependency-dropped.run.stdout new file mode 100644 index 000000000000..7aaf70c12d60 --- /dev/null +++ b/tests/ui/async-await/async-drop/dependency-dropped.run.stdout @@ -0,0 +1 @@ +Async drop From 839534e801b3658fbfa80d8405e0d90623923c9e Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Thu, 15 May 2025 15:41:11 +0200 Subject: [PATCH 188/245] ci: split the dist-ohos job --- .../Dockerfile | 18 ------- .../host-x86_64/dist-ohos-armv7/Dockerfile | 53 +++++++++++++++++++ .../host-x86_64/dist-ohos-x86_64/Dockerfile | 53 +++++++++++++++++++ src/ci/github-actions/jobs.yml | 10 +++- 4 files changed, 114 insertions(+), 20 deletions(-) rename src/ci/docker/host-x86_64/{dist-ohos => dist-ohos-aarch64}/Dockerfile (56%) create mode 100644 src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile create mode 100644 src/ci/docker/host-x86_64/dist-ohos-x86_64/Dockerfile diff --git a/src/ci/docker/host-x86_64/dist-ohos/Dockerfile b/src/ci/docker/host-x86_64/dist-ohos-aarch64/Dockerfile similarity index 56% rename from src/ci/docker/host-x86_64/dist-ohos/Dockerfile rename to src/ci/docker/host-x86_64/dist-ohos-aarch64/Dockerfile index 2c514fa0d4dd..adeaf809f420 100644 --- a/src/ci/docker/host-x86_64/dist-ohos/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-ohos-aarch64/Dockerfile @@ -27,36 +27,18 @@ RUN sh /scripts/ohos-openssl.sh COPY scripts/ohos/aarch64-unknown-linux-ohos-clang.sh /usr/local/bin/ COPY scripts/ohos/aarch64-unknown-linux-ohos-clang++.sh /usr/local/bin/ -COPY scripts/ohos/armv7-unknown-linux-ohos-clang.sh /usr/local/bin/ -COPY scripts/ohos/armv7-unknown-linux-ohos-clang++.sh /usr/local/bin/ -COPY scripts/ohos/x86_64-unknown-linux-ohos-clang.sh /usr/local/bin/ -COPY scripts/ohos/x86_64-unknown-linux-ohos-clang++.sh /usr/local/bin/ # env ENV AARCH64_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/arm64-v8a -ENV ARMV7_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/armeabi-v7a -ENV X86_64_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/x86_64 ENV AARCH64_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 -ENV ARMV7_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 -ENV X86_64_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 ENV TARGETS=aarch64-unknown-linux-ohos -ENV TARGETS=$TARGETS,armv7-unknown-linux-ohos -ENV TARGETS=$TARGETS,x86_64-unknown-linux-ohos ENV \ CC_aarch64_unknown_linux_ohos=/usr/local/bin/aarch64-unknown-linux-ohos-clang.sh \ AR_aarch64_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ CXX_aarch64_unknown_linux_ohos=/usr/local/bin/aarch64-unknown-linux-ohos-clang++.sh -ENV \ - CC_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang.sh \ - AR_armv7_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ - CXX_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang++.sh -ENV \ - CC_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang.sh \ - AR_x86_64_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ - CXX_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang++.sh ENV RUST_CONFIGURE_ARGS \ --enable-profiler \ diff --git a/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile b/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile new file mode 100644 index 000000000000..2a23d8aec237 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile @@ -0,0 +1,53 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + libssl-dev \ + pkg-config \ + xz-utils \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/ohos-sdk.sh /scripts/ +RUN sh /scripts/ohos-sdk.sh + +COPY scripts/ohos-openssl.sh /scripts/ +RUN sh /scripts/ohos-openssl.sh + +COPY scripts/ohos/armv7-unknown-linux-ohos-clang.sh /usr/local/bin/ +COPY scripts/ohos/armv7-unknown-linux-ohos-clang++.sh /usr/local/bin/ + +# env +ENV ARMV7_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/armeabi-v7a + +ENV ARMV7_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 + +ENV TARGETS=armv7-unknown-linux-ohos + +ENV \ + CC_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang.sh \ + AR_armv7_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ + CXX_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang++.sh + +ENV RUST_CONFIGURE_ARGS \ + --enable-profiler \ + --disable-docs \ + --tools=cargo,clippy,rustdocs,rustfmt,rust-analyzer,rust-analyzer-proc-macro-srv,analysis,src,wasm-component-ld \ + --enable-extended \ + --enable-sanitizers + +ENV SCRIPT python3 ../x.py dist --host=$TARGETS --target $TARGETS + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/dist-ohos-x86_64/Dockerfile b/src/ci/docker/host-x86_64/dist-ohos-x86_64/Dockerfile new file mode 100644 index 000000000000..98e402adf2a6 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-ohos-x86_64/Dockerfile @@ -0,0 +1,53 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + libssl-dev \ + pkg-config \ + xz-utils \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/ohos-sdk.sh /scripts/ +RUN sh /scripts/ohos-sdk.sh + +COPY scripts/ohos-openssl.sh /scripts/ +RUN sh /scripts/ohos-openssl.sh + +COPY scripts/ohos/x86_64-unknown-linux-ohos-clang.sh /usr/local/bin/ +COPY scripts/ohos/x86_64-unknown-linux-ohos-clang++.sh /usr/local/bin/ + +# env +ENV X86_64_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/x86_64 + +ENV X86_64_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 + +ENV TARGETS=x86_64-unknown-linux-ohos + +ENV \ + CC_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang.sh \ + AR_x86_64_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ + CXX_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang++.sh + +ENV RUST_CONFIGURE_ARGS \ + --enable-profiler \ + --disable-docs \ + --tools=cargo,clippy,rustdocs,rustfmt,rust-analyzer,rust-analyzer-proc-macro-srv,analysis,src,wasm-component-ld \ + --enable-extended \ + --enable-sanitizers + +ENV SCRIPT python3 ../x.py dist --host=$TARGETS --target $TARGETS + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 88e9c71a9e31..42ad5acbdac1 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -188,8 +188,14 @@ auto: - name: dist-loongarch64-musl <<: *job-linux-4c - - name: dist-ohos - <<: *job-linux-4c-largedisk + - name: dist-ohos-aarch64 + <<: *job-linux-4c + + - name: dist-ohos-armv7 + <<: *job-linux-4c + + - name: dist-ohos-x86_64 + <<: *job-linux-4c - name: dist-powerpc-linux <<: *job-linux-4c From 102cc2f8835c2ca73ca9a20172e3c6dda0e4d139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 15 May 2025 14:05:02 +0000 Subject: [PATCH 189/245] move expensive layout sanity check to debug assertions --- .../rustc_ty_utils/src/layout/invariant.rs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 7423a156a217..c929de116241 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -8,15 +8,6 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { let tcx = cx.tcx(); - // Type-level uninhabitedness should always imply ABI uninhabitedness. - if layout.ty.is_privately_uninhabited(tcx, cx.typing_env) { - assert!( - layout.is_uninhabited(), - "{:?} is type-level uninhabited but not ABI-uninhabited?", - layout.ty - ); - } - if layout.size.bytes() % layout.align.abi.bytes() != 0 { bug!("size is not a multiple of align, in the following layout:\n{layout:#?}"); } @@ -29,6 +20,19 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou return; } + // Type-level uninhabitedness should always imply ABI uninhabitedness. This can be expensive on + // big non-exhaustive types, and is [hard to + // fix](https://github.com/rust-lang/rust/issues/141006#issuecomment-2883415000) in general. + // Only doing this sanity check when debug assertions are turned on avoids the issue for the + // very specific case of #140944. + if layout.ty.is_privately_uninhabited(tcx, cx.typing_env) { + assert!( + layout.is_uninhabited(), + "{:?} is type-level uninhabited but not ABI-uninhabited?", + layout.ty + ); + } + /// Yields non-ZST fields of the type fn non_zst_fields<'tcx, 'a>( cx: &'a LayoutCx<'tcx>, From 49553be30784711d7dc6976ef1335b74df7cbe44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=AE=87=E9=80=B8?= Date: Tue, 22 Apr 2025 16:16:23 +0800 Subject: [PATCH 190/245] Experimental cygwin support in rustc Co-authored-by: Ookiineko --- compiler/rustc_llvm/build.rs | 1 + compiler/rustc_session/src/filesearch.rs | 19 ++++++++++++++++++- src/bootstrap/src/core/builder/mod.rs | 2 +- src/bootstrap/src/utils/helpers.rs | 2 +- src/bootstrap/src/utils/shared_helpers.rs | 4 ++-- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 6692ea735401..a662694ac38f 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -255,6 +255,7 @@ fn main() { } else if target.contains("haiku") || target.contains("darwin") || (is_crossed && (target.contains("dragonfly") || target.contains("solaris"))) + || target.contains("cygwin") { println!("cargo:rustc-link-lib=z"); } else if target.contains("netbsd") { diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index bdeca91eb64a..2a85f44278a0 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -58,7 +58,7 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf { sysroot.join(rustlib_path).join("bin") } -#[cfg(unix)] +#[cfg(all(unix, not(target_os = "cygwin")))] fn current_dll_path() -> Result { use std::sync::OnceLock; @@ -132,6 +132,23 @@ fn current_dll_path() -> Result { .clone() } +#[cfg(target_os = "cygwin")] +fn current_dll_path() -> Result { + use std::ffi::{CStr, OsStr}; + use std::os::unix::prelude::*; + + unsafe { + let addr = current_dll_path as usize as *mut _; + let mut info = std::mem::zeroed(); + if libc::dladdr(addr, &mut info) == 0 { + return Err("dladdr failed".into()); + } + let bytes = CStr::from_ptr(info.dli_fname.as_ptr()).to_bytes(); + let os = OsStr::from_bytes(bytes); + Ok(PathBuf::from(os)) + } +} + #[cfg(windows)] fn current_dll_path() -> Result { use std::ffi::OsString; diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index cc4fa953ddc5..af3e3cc37b92 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1388,7 +1388,7 @@ impl<'a> Builder<'a> { // Windows doesn't need dylib path munging because the dlls for the // compiler live next to the compiler and the system will find them // automatically. - if cfg!(windows) { + if cfg!(any(windows, target_os = "cygwin")) { return; } diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index b29c1fb3889c..f2c3e8c0df46 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -130,7 +130,7 @@ pub fn is_debug_info(name: &str) -> bool { /// Returns the corresponding relative library directory that the compiler's /// dylibs will be found in. pub fn libdir(target: TargetSelection) -> &'static str { - if target.is_windows() { "bin" } else { "lib" } + if target.is_windows() || target.contains("cygwin") { "bin" } else { "lib" } } /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path. diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs index 1297a53d4880..3011322158f3 100644 --- a/src/bootstrap/src/utils/shared_helpers.rs +++ b/src/bootstrap/src/utils/shared_helpers.rs @@ -20,7 +20,7 @@ use std::str::FromStr; /// Returns the environment variable which the dynamic library lookup path /// resides in for this platform. pub fn dylib_path_var() -> &'static str { - if cfg!(target_os = "windows") { + if cfg!(any(target_os = "windows", target_os = "cygwin")) { "PATH" } else if cfg!(target_vendor = "apple") { "DYLD_LIBRARY_PATH" @@ -46,7 +46,7 @@ pub fn dylib_path() -> Vec { /// Given an executable called `name`, return the filename for the /// executable for a particular target. pub fn exe(name: &str, target: &str) -> String { - if target.contains("windows") { + if target.contains("windows") || target.contains("cygwin") { format!("{name}.exe") } else if target.contains("uefi") { format!("{name}.efi") From 12fda865f2f67081a96b116cae5194c791b20fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=AE=87=E9=80=B8?= Date: Sat, 10 May 2025 00:17:09 +0800 Subject: [PATCH 191/245] Fix exe() to make rustc wrapper happy --- src/bootstrap/src/utils/shared_helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs index 3011322158f3..08e1c21e58e7 100644 --- a/src/bootstrap/src/utils/shared_helpers.rs +++ b/src/bootstrap/src/utils/shared_helpers.rs @@ -46,7 +46,7 @@ pub fn dylib_path() -> Vec { /// Given an executable called `name`, return the filename for the /// executable for a particular target. pub fn exe(name: &str, target: &str) -> String { - if target.contains("windows") || target.contains("cygwin") { + if target.contains("windows") { format!("{name}.exe") } else if target.contains("uefi") { format!("{name}.efi") From 5be888c4799a4326e545ca23d228824774d5f232 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 15 May 2025 19:19:34 +0200 Subject: [PATCH 192/245] Bump nightly version -> 2025-05-14 --- 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 66192f866fa0..d4080d06d3ca 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-05-01 +nightly-2025-05-14 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 39c7f0e4ad5a..da41bdd27bcb 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-05-01" +channel = "nightly-2025-05-14" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 367073195a0be6476c57e96dcd4354ee6f4830ee Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 15 May 2025 19:19:46 +0200 Subject: [PATCH 193/245] Bump Clippy version -> 0.1.89 --- Cargo.toml | 2 +- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0df9f2e55598..b6a1b9314c65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 93fd2e35d1ba..1134b0e97af2 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_config" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version edition = "2024" publish = false diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 20951afccbb7..7e3cb4042479 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index b98e99017503..ac970e1c4b0a 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" # begin autogenerated version -version = "0.1.88" +version = "0.1.89" # end autogenerated version edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" From afa6d67f36dc0edf130d538ec208f88286aefecd Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 15 May 2025 19:30:30 +0200 Subject: [PATCH 194/245] Update Cargo.lock --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f26d858b698d..6e950f287f9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -537,7 +537,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clippy" -version = "0.1.88" +version = "0.1.89" dependencies = [ "anstream", "askama", @@ -569,7 +569,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.88" +version = "0.1.89" dependencies = [ "clippy_utils", "itertools", @@ -594,7 +594,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.88" +version = "0.1.89" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -624,7 +624,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.88" +version = "0.1.89" dependencies = [ "arrayvec", "itertools", @@ -5084,9 +5084,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "getrandom 0.3.2", From 60750ca0a1e71f0ad8d2a66fbcecd75cd4e8bd3a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 15 May 2025 19:55:43 +0200 Subject: [PATCH 195/245] tempfile dep bump fallout --- compiler/rustc_data_structures/src/temp_dir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_data_structures/src/temp_dir.rs b/compiler/rustc_data_structures/src/temp_dir.rs index 4dbe11d707d7..9adae049261e 100644 --- a/compiler/rustc_data_structures/src/temp_dir.rs +++ b/compiler/rustc_data_structures/src/temp_dir.rs @@ -17,7 +17,7 @@ impl Drop for MaybeTempDir { // occur. let dir = unsafe { ManuallyDrop::take(&mut self.dir) }; if self.keep { - let _ = dir.into_path(); + let _ = dir.keep(); } } } From 6128fca0b0b70ac6497bf5da38a5d3db37219f78 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Thu, 15 May 2025 11:15:52 -0700 Subject: [PATCH 196/245] [win][arm64] Remove 'Arm64 Hazard' undocumented MSVC option and instead disable problematic test --- .../src/spec/targets/aarch64_pc_windows_msvc.rs | 7 +------ .../llvm-location-discriminator-limit-dummy-span/rmake.rs | 7 +++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs index c5704c574483..f1b6fa123deb 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs @@ -1,4 +1,4 @@ -use crate::spec::{FramePointer, LinkerFlavor, Lld, Target, TargetMetadata, base}; +use crate::spec::{FramePointer, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut base = base::windows_msvc::opts(); @@ -11,11 +11,6 @@ pub(crate) fn target() -> Target { // and other services. It must point to the previous {x29, x30} pair on the stack." base.frame_pointer = FramePointer::NonLeaf; - // MSVC emits a warning about code that may trip "Cortex-A53 MPCore processor bug #843419" (see - // https://developer.arm.com/documentation/epm048406/latest) which is sometimes emitted by LLVM. - // Since Arm64 Windows 10+ isn't supported on that processor, it's safe to disable the warning. - base.add_pre_link_args(LinkerFlavor::Msvc(Lld::No), &["/arm64hazardfree"]); - Target { llvm_target: "aarch64-pc-windows-msvc".into(), metadata: TargetMetadata { diff --git a/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs b/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs index 2727effe8188..d28c8463016c 100644 --- a/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs +++ b/tests/run-make/llvm-location-discriminator-limit-dummy-span/rmake.rs @@ -11,6 +11,13 @@ //@ needs-dynamic-linking //@ only-nightly (requires unstable rustc flag) +// This test trips a check in the MSVC linker for an outdated processor: +// "LNK1322: cannot avoid potential ARM hazard (Cortex-A53 MPCore processor bug #843419)" +// Until MSVC removes this check: +// https://developercommunity.microsoft.com/t/Remove-checking-for-and-fixing-Cortex-A/10905134 +// we'll need to disable this test on Arm64 Windows. +//@ ignore-aarch64-pc-windows-msvc + #![deny(warnings)] use run_make_support::{dynamic_lib_name, rfs, rust_lib_name, rustc}; From a64ed161e271a373358bf292de8dab858f8b6bce Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Thu, 15 May 2025 21:26:46 +0000 Subject: [PATCH 197/245] Lowercase git url for rust-lang/enzyme.git On Fuchsia, we have an internal Gerrit mirrors of the rust repositories to avoid excess load on the public github servers. Since rust uses submodules, we need to then use git's `url..insteadOf` to point our checkouts at our mirrors. We'd prefer to be able to point all repositories under `https://github.com/rust-lang` to `https://rust.googlesource.com/rust-lang`, but unfortunately it seems that when Rust mirrored Enzyme, the repository name was lower cased to `https://github.com/rust-lang/enzyme`, but kept the name capitalized in the .gitmodules file. This didn't cause a problem for Github, which seems to handle repository names in a case insensitive way, Gerrit is case sensitive, so we can't use a glob rule. Instead we have to setup `insteadOf` rules for each repository. This renames the URL to match the case of the repository name, which should avoid the issue. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index d09d81ccadcb..fbf2f59b38da 100644 --- a/.gitmodules +++ b/.gitmodules @@ -45,7 +45,7 @@ shallow = true [submodule "src/tools/enzyme"] path = src/tools/enzyme - url = https://github.com/rust-lang/Enzyme.git + url = https://github.com/rust-lang/enzyme.git shallow = true [submodule "src/gcc"] path = src/gcc From cf878d8f260e535fab7e6f6401fc61e5d846ac4d Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 15 May 2025 23:24:47 +0200 Subject: [PATCH 198/245] HIR: explain in comment why `ExprKind::If` "then" is an `Expr` One could be tempted to replace the "then" `hir::Expr` with kind `hir::ExprKind::Block` by a `hir::Block`. Explain why this would not be a good idea. --- compiler/rustc_hir/src/hir.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 107aea4e5a40..fa1d1ec0a860 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2744,6 +2744,8 @@ pub enum ExprKind<'hir> { /// /// The "then" expr is always `ExprKind::Block`. If present, the "else" expr is always /// `ExprKind::Block` (for `else`) or `ExprKind::If` (for `else if`). + /// Note that using an `Expr` instead of a `Block` for the "then" part is intentional, + /// as it simplifies the type coercion machinery. If(&'hir Expr<'hir>, &'hir Expr<'hir>, Option<&'hir Expr<'hir>>), /// A conditionless loop (can be exited with `break`, `continue`, or `return`). /// From c7a2694a803513d09c96dbaf3f3a7b099d93cec6 Mon Sep 17 00:00:00 2001 From: dianqk Date: Fri, 16 May 2025 08:34:16 +0800 Subject: [PATCH 199/245] Update to LLVM 20.1.5 --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index 8448283b4bd3..c1118fdbb302 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 8448283b4bd34ea00d76fd4f18ec730b549d6e1d +Subproject commit c1118fdbb3024157df7f4cfe765f2b0b4339e8a2 From 5e048e0786674db68220b7e0e8b19b2a96bcae7c Mon Sep 17 00:00:00 2001 From: Berrysoft Date: Fri, 16 May 2025 09:41:52 +0800 Subject: [PATCH 200/245] Simplify current_dll_path for Cygwin --- compiler/rustc_session/src/filesearch.rs | 33 ++++++++---------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 2a85f44278a0..207ba5157bdb 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -58,7 +58,7 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf { sysroot.join(rustlib_path).join("bin") } -#[cfg(all(unix, not(target_os = "cygwin")))] +#[cfg(unix)] fn current_dll_path() -> Result { use std::sync::OnceLock; @@ -78,10 +78,16 @@ fn current_dll_path() -> Result { if libc::dladdr(addr, &mut info) == 0 { return Err("dladdr failed".into()); } - if info.dli_fname.is_null() { - return Err("dladdr returned null pointer".into()); - } - let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); + #[cfg(target_os = "cygwin")] + let fname_ptr = info.dli_fname.as_ptr(); + #[cfg(not(target_os = "cygwin"))] + let fname_ptr = { + if info.dli_fname.is_null() { + return Err("dladdr returned null pointer".into()); + } + info.dli_fname + }; + let bytes = CStr::from_ptr(fname_ptr).to_bytes(); let os = OsStr::from_bytes(bytes); Ok(PathBuf::from(os)) } @@ -132,23 +138,6 @@ fn current_dll_path() -> Result { .clone() } -#[cfg(target_os = "cygwin")] -fn current_dll_path() -> Result { - use std::ffi::{CStr, OsStr}; - use std::os::unix::prelude::*; - - unsafe { - let addr = current_dll_path as usize as *mut _; - let mut info = std::mem::zeroed(); - if libc::dladdr(addr, &mut info) == 0 { - return Err("dladdr failed".into()); - } - let bytes = CStr::from_ptr(info.dli_fname.as_ptr()).to_bytes(); - let os = OsStr::from_bytes(bytes); - Ok(PathBuf::from(os)) - } -} - #[cfg(windows)] fn current_dll_path() -> Result { use std::ffi::OsString; From 43ff8855ac55da567a5e2966e0942700d120e067 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Fri, 16 May 2025 16:49:56 +0800 Subject: [PATCH 201/245] Add ui test macro-shorthand-issue-140659 Signed-off-by: xizheyin --- .../ui/typeck/macro-shorthand-issue-140659.rs | 57 +++++++++++++++++++ .../macro-shorthand-issue-140659.stderr | 20 +++++++ 2 files changed, 77 insertions(+) create mode 100644 tests/ui/typeck/macro-shorthand-issue-140659.rs create mode 100644 tests/ui/typeck/macro-shorthand-issue-140659.stderr diff --git a/tests/ui/typeck/macro-shorthand-issue-140659.rs b/tests/ui/typeck/macro-shorthand-issue-140659.rs new file mode 100644 index 000000000000..dba06b43d393 --- /dev/null +++ b/tests/ui/typeck/macro-shorthand-issue-140659.rs @@ -0,0 +1,57 @@ +trait Reencode { + type Error; + fn tag_index(&mut self, tag: u32) -> Result; +} + +struct Reencoder; +impl Reencode for Reencoder { + type Error = &'static str; + fn tag_index(&mut self, tag: u32) -> Result { + Ok(tag) + } +} + + +enum Operator { + Suspend { tag_index: u32 }, +} + +enum Instruction { + Suspend { tag_index: u32 }, +} + + +macro_rules! for_each_operator { + ($m:ident) => { + $m! { + Suspend { tag_index: u32 } => visit_suspend + } + }; +} + + +fn process(op: &Operator, reencoder: &mut T) -> Instruction { + macro_rules! translate { + (Suspend { tag_index: $ty:ty } => $visit:ident) => { + match op { + Operator::Suspend { tag_index } => { + let tag_index = reencoder.tag_index(*tag_index); + + // KEY POINT: Using field shorthand syntax where the compiler gets confused + // Here tag_index is a Result but we're using it where u32 is expected + Instruction::Suspend { tag_index } //~ ERROR mismatched types [E0308] + } + } + }; + } + + // This should give the specific error message with the wrong help suggestion + for_each_operator!(translate) +} + +fn main() { + let mut reencoder = Reencoder; + let op = Operator::Suspend { tag_index: 1 }; + + let _ = process(&op, &mut reencoder); +} diff --git a/tests/ui/typeck/macro-shorthand-issue-140659.stderr b/tests/ui/typeck/macro-shorthand-issue-140659.stderr new file mode 100644 index 000000000000..419db9475710 --- /dev/null +++ b/tests/ui/typeck/macro-shorthand-issue-140659.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/macro-shorthand-issue-140659.rs:42:44 + | +LL | Instruction::Suspend { tag_index } + | ^^^^^^^^^ expected `u32`, found `Result::Error>` +... +LL | for_each_operator!(translate) + | ----------------------------- in this macro invocation + | + = note: expected type `u32` + found enum `Result::Error>` + = note: this error originates in the macro `translate` which comes from the expansion of the macro `for_each_operator` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider using `Result::expect` to unwrap the `Result::Error>` value, panicking if the value is a `Result::Err` + | +LL | }: tag_index.expect("REASON") + | ++++++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From 8fcff8c6457f16cb0a2760f0b6a846e42d08f0e1 Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 21 Apr 2025 14:38:24 +0200 Subject: [PATCH 202/245] Add per page TOC in the rustc book --- src/doc/rustc/book.toml | 2 + src/doc/rustc/theme/pagetoc.css | 104 ++++++++++++++++++++++++++++ src/doc/rustc/theme/pagetoc.js | 118 ++++++++++++++++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 src/doc/rustc/theme/pagetoc.css create mode 100644 src/doc/rustc/theme/pagetoc.js diff --git a/src/doc/rustc/book.toml b/src/doc/rustc/book.toml index 167aece0ed6a..01f127ad3905 100644 --- a/src/doc/rustc/book.toml +++ b/src/doc/rustc/book.toml @@ -6,6 +6,8 @@ title = "The rustc book" [output.html] git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustc" edit-url-template = "https://github.com/rust-lang/rust/edit/master/src/doc/rustc/{path}" +additional-css = ["theme/pagetoc.css"] +additional-js = ["theme/pagetoc.js"] [output.html.search] use-boolean-and = true diff --git a/src/doc/rustc/theme/pagetoc.css b/src/doc/rustc/theme/pagetoc.css new file mode 100644 index 000000000000..f8f45960d940 --- /dev/null +++ b/src/doc/rustc/theme/pagetoc.css @@ -0,0 +1,104 @@ +/* Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL) */ + +:root { + --toc-width: 270px; + --center-content-toc-shift: calc(-1 * var(--toc-width) / 2); +} + +.nav-chapters { + /* adjust width of buttons that bring to the previous or the next page */ + min-width: 50px; +} + +.previous { + /* + adjust the space between the left sidebar or the left side of the screen + and the button that leads to the previous page + */ + margin-left: var(--page-padding); +} + +@media only screen { + main { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + } + + @media (max-width: 1179px) { + .sidebar-hidden #sidetoc { + display: none; + } + } + + @media (max-width: 1439px) { + .sidebar-visible #sidetoc { + display: none; + } + } + + @media (1180px <= width <= 1439px) { + .sidebar-hidden main { + position: relative; + left: var(--center-content-toc-shift); + } + } + + @media (1440px <= width <= 1700px) { + .sidebar-visible main { + position: relative; + left: var(--center-content-toc-shift); + } + } + + .content-wrap { + width: 100%; + } + + #sidetoc { + margin-top: 20px; + margin-left: 20px; + margin-right: auto; + } + #pagetoc { + position: fixed; + /* adjust TOC width */ + width: var(--toc-width); + height: calc(100vh - var(--menu-bar-height) - 0.67em * 4); + overflow: auto; + } + #pagetoc a { + border-left: 1px solid var(--sidebar-bg); + color: var(--sidebar-fg) !important; + display: block; + padding-bottom: 5px; + padding-top: 5px; + padding-left: 10px; + text-align: left; + text-decoration: none; + } + #pagetoc a:hover, + #pagetoc a.active { + background: var(--sidebar-bg); + color: var(--sidebar-active) !important; + } + #pagetoc .active { + background: var(--sidebar-bg); + color: var(--sidebar-active); + } + #pagetoc .pagetoc-H2 { + padding-left: 20px; + } + #pagetoc .pagetoc-H3 { + padding-left: 40px; + } + #pagetoc .pagetoc-H4 { + padding-left: 60px; + } +} + +@media print { + #sidetoc { + display: none; + } +} diff --git a/src/doc/rustc/theme/pagetoc.js b/src/doc/rustc/theme/pagetoc.js new file mode 100644 index 000000000000..29c09903a375 --- /dev/null +++ b/src/doc/rustc/theme/pagetoc.js @@ -0,0 +1,118 @@ +// Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL) + +let activeHref = location.href; +function updatePageToc(elem = undefined) { + let selectedPageTocElem = elem; + const pagetoc = document.getElementById("pagetoc"); + + function getRect(element) { + return element.getBoundingClientRect(); + } + + function overflowTop(container, element) { + return getRect(container).top - getRect(element).top; + } + + function overflowBottom(container, element) { + return getRect(container).bottom - getRect(element).bottom; + } + + // We've not selected a heading to highlight, and the URL needs updating + // so we need to find a heading based on the URL + if (selectedPageTocElem === undefined && location.href !== activeHref) { + activeHref = location.href; + for (const pageTocElement of pagetoc.children) { + if (pageTocElement.href === activeHref) { + selectedPageTocElem = pageTocElement; + } + } + } + + // We still don't have a selected heading, let's try and find the most + // suitable heading based on the scroll position + if (selectedPageTocElem === undefined) { + const margin = window.innerHeight / 3; + + const headers = document.getElementsByClassName("header"); + for (let i = 0; i < headers.length; i++) { + const header = headers[i]; + if (selectedPageTocElem === undefined && getRect(header).top >= 0) { + if (getRect(header).top < margin) { + selectedPageTocElem = header; + } else { + selectedPageTocElem = headers[Math.max(0, i - 1)]; + } + } + // a very long last section's heading is over the screen + if (selectedPageTocElem === undefined && i === headers.length - 1) { + selectedPageTocElem = header; + } + } + } + + // Remove the active flag from all pagetoc elements + for (const pageTocElement of pagetoc.children) { + pageTocElement.classList.remove("active"); + } + + // If we have a selected heading, set it to active and scroll to it + if (selectedPageTocElem !== undefined) { + for (const pageTocElement of pagetoc.children) { + if (selectedPageTocElem.href.localeCompare(pageTocElement.href) === 0) { + pageTocElement.classList.add("active"); + if (overflowTop(pagetoc, pageTocElement) > 0) { + pagetoc.scrollTop = pageTocElement.offsetTop; + } + if (overflowBottom(pagetoc, pageTocElement) < 0) { + pagetoc.scrollTop -= overflowBottom(pagetoc, pageTocElement); + } + } + } + } +} + +if (document.getElementById("sidetoc") === null) { + // Element doesn't exist yet, let's create it + const main = document.querySelector('main'); + const wrapper = document.createElement('div'); + wrapper.className = "content-wrap"; + + // Move all children into the wrapper + while (main.firstChild) { + wrapper.appendChild(main.firstChild); + } + + // Append the wrapper back to main + main.appendChild(wrapper); + + // Create the empty sidetoc and pagetoc elements + const sidetoc = document.createElement("div"); + const pagetoc = document.createElement("div"); + sidetoc.id = "sidetoc"; + pagetoc.id = "pagetoc"; + + // And append them to the current DOM + sidetoc.appendChild(pagetoc); + main.appendChild(sidetoc); +} + +if (document.getElementsByClassName("header").length <= 1) { + // There's one or less headings, we don't need a page table of contents + document.getElementById("sidetoc").remove(); +} else { + // Populate sidebar on load + window.addEventListener("load", () => { + for (const header of document.getElementsByClassName("header")) { + const link = document.createElement("a"); + link.innerHTML = header.innerHTML; + link.href = header.hash; + link.classList.add("pagetoc-" + header.parentElement.tagName); + document.getElementById("pagetoc").appendChild(link); + link.onclick = () => updatePageToc(link); + } + updatePageToc(); + }); + + // Update page table of contents selected heading on scroll + window.addEventListener("scroll", () => updatePageToc()); +} From 72f915aaca3fa3018269de4e57cd1bddf4a17a86 Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 21 Apr 2025 17:15:33 +0200 Subject: [PATCH 203/245] Fix flicker when page loads --- src/doc/rustc/theme/pagetoc.css | 14 +------------- src/doc/rustc/theme/pagetoc.js | 20 +++++--------------- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/src/doc/rustc/theme/pagetoc.css b/src/doc/rustc/theme/pagetoc.css index f8f45960d940..26c7ae78e8ac 100644 --- a/src/doc/rustc/theme/pagetoc.css +++ b/src/doc/rustc/theme/pagetoc.css @@ -19,12 +19,6 @@ } @media only screen { - main { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - } - @media (max-width: 1179px) { .sidebar-hidden #sidetoc { display: none; @@ -51,14 +45,8 @@ } } - .content-wrap { - width: 100%; - } - #sidetoc { - margin-top: 20px; - margin-left: 20px; - margin-right: auto; + margin-left: calc(100% + 20px); } #pagetoc { position: fixed; diff --git a/src/doc/rustc/theme/pagetoc.js b/src/doc/rustc/theme/pagetoc.js index 29c09903a375..f3549a6de5b0 100644 --- a/src/doc/rustc/theme/pagetoc.js +++ b/src/doc/rustc/theme/pagetoc.js @@ -72,28 +72,18 @@ function updatePageToc(elem = undefined) { } if (document.getElementById("sidetoc") === null) { - // Element doesn't exist yet, let's create it - const main = document.querySelector('main'); - const wrapper = document.createElement('div'); - wrapper.className = "content-wrap"; - - // Move all children into the wrapper - while (main.firstChild) { - wrapper.appendChild(main.firstChild); - } - - // Append the wrapper back to main - main.appendChild(wrapper); + // The sidetoc element doesn't exist yet, let's create it // Create the empty sidetoc and pagetoc elements const sidetoc = document.createElement("div"); const pagetoc = document.createElement("div"); sidetoc.id = "sidetoc"; pagetoc.id = "pagetoc"; - - // And append them to the current DOM sidetoc.appendChild(pagetoc); - main.appendChild(sidetoc); + + // And append them to the current DOM + const main = document.querySelector('main'); + main.insertBefore(sidetoc, main.firstChild); } if (document.getElementsByClassName("header").length <= 1) { From 4cbcb44d702e54b29e4f9df509f0b463e9142e5c Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 21 Apr 2025 17:18:22 +0200 Subject: [PATCH 204/245] Cleanup the Javascript and CSS of our custom TOC --- src/doc/rustc/theme/pagetoc.css | 8 -------- src/doc/rustc/theme/pagetoc.js | 10 +++------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/doc/rustc/theme/pagetoc.css b/src/doc/rustc/theme/pagetoc.css index 26c7ae78e8ac..58ca1f8b26f8 100644 --- a/src/doc/rustc/theme/pagetoc.css +++ b/src/doc/rustc/theme/pagetoc.css @@ -10,14 +10,6 @@ min-width: 50px; } -.previous { - /* - adjust the space between the left sidebar or the left side of the screen - and the button that leads to the previous page - */ - margin-left: var(--page-padding); -} - @media only screen { @media (max-width: 1179px) { .sidebar-hidden #sidetoc { diff --git a/src/doc/rustc/theme/pagetoc.js b/src/doc/rustc/theme/pagetoc.js index f3549a6de5b0..927a5b10749b 100644 --- a/src/doc/rustc/theme/pagetoc.js +++ b/src/doc/rustc/theme/pagetoc.js @@ -71,7 +71,8 @@ function updatePageToc(elem = undefined) { } } -if (document.getElementById("sidetoc") === null) { +if (document.getElementById("sidetoc") === null && + document.getElementsByClassName("header").length > 0) { // The sidetoc element doesn't exist yet, let's create it // Create the empty sidetoc and pagetoc elements @@ -80,16 +81,11 @@ if (document.getElementById("sidetoc") === null) { sidetoc.id = "sidetoc"; pagetoc.id = "pagetoc"; sidetoc.appendChild(pagetoc); - + // And append them to the current DOM const main = document.querySelector('main'); main.insertBefore(sidetoc, main.firstChild); -} -if (document.getElementsByClassName("header").length <= 1) { - // There's one or less headings, we don't need a page table of contents - document.getElementById("sidetoc").remove(); -} else { // Populate sidebar on load window.addEventListener("load", () => { for (const header of document.getElementsByClassName("header")) { From 84d7edd07ffd4848a3c08ddd46e4b3a296a7819c Mon Sep 17 00:00:00 2001 From: xizheyin Date: Fri, 16 May 2025 17:11:45 +0800 Subject: [PATCH 205/245] Enable [behind-upstream] triagebot option for rust-lang/rust Signed-off-by: xizheyin --- triagebot.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 9dcdbcecbeca..a3866d62dca4 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1424,3 +1424,6 @@ compiletest = [ # Enable `@rustbot note` functionality # Documentation at: https://forge.rust-lang.org/triagebot/note.html [note] + +[behind-upstream] +days-threshold = 7 From 12c8fb857c97f97a0351eecc94e97e71b04cda70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=AE=87=E9=80=B8?= Date: Fri, 16 May 2025 17:47:29 +0800 Subject: [PATCH 206/245] Fix fname for miri --- src/tools/miri/src/shims/native_lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs index 0258a76c3e70..837e1b31cac5 100644 --- a/src/tools/miri/src/shims/native_lib.rs +++ b/src/tools/miri/src/shims/native_lib.rs @@ -117,7 +117,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut info = std::mem::MaybeUninit::::uninit(); unsafe { if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { - if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() + let info = info.assume_init(); + #[cfg(target_os = "cygwin")] + let fname_ptr = info.dli_fname.as_ptr(); + #[cfg(not(target_os = "cygwin"))] + let fname_ptr = info.dli_fname; + if std::ffi::CStr::from_ptr(fname_ptr).to_str().unwrap() != _lib_path.to_str().unwrap() { return None; From 2cdbd69abfbd5a1f0c5b9ceee2393f1a87181d97 Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 5 May 2025 11:05:29 +0200 Subject: [PATCH 207/245] disable the stack overflow handler on miri --- .../std/src/sys/pal/unix/stack_overflow.rs | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 804353178aac..a3be2cdf738f 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -25,27 +25,36 @@ impl Drop for Handler { } } -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", +#[cfg(all( + not(miri), + any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ), ))] mod thread_info; -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", +// miri doesn't model signals nor stack overflows and this code has some +// synchronization properties that we don't want to expose to user code, +// hence we disable it on miri. +#[cfg(all( + not(miri), + any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ) ))] mod imp { use libc::{ @@ -606,17 +615,20 @@ mod imp { // usually have fewer qualms about forwards compatibility, since the runtime // is shipped with the OS): // -#[cfg(not(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", - target_os = "cygwin", -)))] +#[cfg(any( + miri, + not(any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + target_os = "cygwin", + )) +))] mod imp { pub unsafe fn init() {} From 7d9f437f993e213c1f9aeb9c07454f6fd1648821 Mon Sep 17 00:00:00 2001 From: sam skeoch Date: Sat, 22 Feb 2025 17:22:57 +0000 Subject: [PATCH 208/245] Add as_ascii_unchecked() methods to char, str, and u8 --- library/core/src/char/methods.rs | 14 ++++++++++++++ library/core/src/num/mod.rs | 14 ++++++++++++++ library/core/src/str/mod.rs | 15 +++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 042925a352f3..3c25ab4fdd70 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1202,6 +1202,20 @@ impl char { } } + /// Converts this char into an [ASCII character](`ascii::Char`), without + /// checking whether it is valid. + /// + /// # Safety + /// + /// This char must be within the ASCII range, or else this is UB. + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { + // SAFETY: the caller promised that this char is ASCII. + unsafe { ascii::Char::from_u8_unchecked(*self as u8) } + } + /// Makes a copy of the value in its ASCII upper case equivalent. /// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index ecc1c7bf9021..9332908398cd 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -492,6 +492,20 @@ impl u8 { ascii::Char::from_u8(*self) } + /// Converts this byte to an [ASCII character](ascii::Char), without + /// checking whether or not it's valid. + /// + /// # Safety + /// + /// This byte must be valid ASCII, or else this is UB. + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { + // SAFETY: the caller promised that this byte is ASCII. + unsafe { ascii::Char::from_u8_unchecked(*self) } + } + /// Makes a copy of the value in its ASCII upper case equivalent. /// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 9e7e949b722d..2eb8f4960856 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2634,6 +2634,21 @@ impl str { self.as_bytes().as_ascii() } + /// Converts this string slice into a slice of [ASCII characters](ascii::Char), + /// without checking whether they are valid. + /// + /// # Safety + /// + /// Every character in this string must be ASCII, or else this is UB. + #[unstable(feature = "ascii_char", issue = "110998")] + #[must_use] + #[inline] + pub const unsafe fn as_ascii_unchecked(&self) -> &[ascii::Char] { + // SAFETY: the caller promised that every byte of this string slice + // is ASCII. + unsafe { self.as_bytes().as_ascii_unchecked() } + } + /// Checks that two strings are an ASCII case-insensitive match. /// /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, From 101e24a223f818e17d2c68298d48d4e6008c02aa Mon Sep 17 00:00:00 2001 From: sam skeoch Date: Tue, 13 May 2025 14:55:03 +0100 Subject: [PATCH 209/245] Add assert_unsafe_precondition!()s to as_ascii_unchecked() methods --- library/core/src/char/methods.rs | 7 +++++++ library/core/src/num/mod.rs | 6 ++++++ library/core/src/str/mod.rs | 7 +++++++ 3 files changed, 20 insertions(+) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 3c25ab4fdd70..af2edf141b22 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -4,6 +4,7 @@ use super::*; use crate::panic::const_panic; use crate::slice; use crate::str::from_utf8_unchecked_mut; +use crate::ub_checks::assert_unsafe_precondition; use crate::unicode::printable::is_printable; use crate::unicode::{self, conversions}; @@ -1212,6 +1213,12 @@ impl char { #[unstable(feature = "ascii_char", issue = "110998")] #[inline] pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { + assert_unsafe_precondition!( + check_library_ub, + "as_ascii_unchecked requires that the char is valid ASCII", + (it: &char = self) => it.is_ascii() + ); + // SAFETY: the caller promised that this char is ASCII. unsafe { ascii::Char::from_u8_unchecked(*self as u8) } } diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 9332908398cd..a82b2aa61ce0 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -502,6 +502,12 @@ impl u8 { #[unstable(feature = "ascii_char", issue = "110998")] #[inline] pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { + assert_unsafe_precondition!( + check_library_ub, + "as_ascii_unchecked requires that the byte is valid ASCII", + (it: &u8 = self) => it.is_ascii() + ); + // SAFETY: the caller promised that this byte is ASCII. unsafe { ascii::Char::from_u8_unchecked(*self) } } diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 2eb8f4960856..e505e2280953 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -17,6 +17,7 @@ use self::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; use crate::char::{self, EscapeDebugExtArgs}; use crate::ops::Range; use crate::slice::{self, SliceIndex}; +use crate::ub_checks::assert_unsafe_precondition; use crate::{ascii, mem}; pub mod pattern; @@ -2644,6 +2645,12 @@ impl str { #[must_use] #[inline] pub const unsafe fn as_ascii_unchecked(&self) -> &[ascii::Char] { + assert_unsafe_precondition!( + check_library_ub, + "as_ascii_unchecked requires that the string is valid ASCII", + (it: &str = self) => it.is_ascii() + ); + // SAFETY: the caller promised that every byte of this string slice // is ASCII. unsafe { self.as_bytes().as_ascii_unchecked() } From 258e8808616e4cb5c65d2692c66a6c61e17cc09f Mon Sep 17 00:00:00 2001 From: est31 Date: Tue, 13 May 2025 15:10:51 +0200 Subject: [PATCH 210/245] Remove #![feature(let_chains)] from library and src/librustdoc --- compiler/rustc_pattern_analysis/src/lib.rs | 1 - library/std/src/lib.rs | 1 - src/librustdoc/lib.rs | 1 - tests/run-make/core-no-oom-handling/rmake.rs | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index f63d8b2d79f6..2b85d7b26cea 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -6,7 +6,6 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![allow(unused_crate_dependencies)] -#![cfg_attr(all(feature = "rustc", bootstrap), feature(let_chains))] // tidy-alphabetical-end pub mod constructor; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 0bb40ee4b317..d1f6b450214f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -304,7 +304,6 @@ #![feature(iter_advance_by)] #![feature(iter_next_chunk)] #![feature(lang_items)] -#![feature(let_chains)] #![feature(link_cfg)] #![feature(linkage)] #![feature(macro_metavar_expr_concat)] diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index b4210e7b5181..b4003044e207 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -11,7 +11,6 @@ #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(never_type)] #![feature(round_char_boundary)] #![feature(test)] diff --git a/tests/run-make/core-no-oom-handling/rmake.rs b/tests/run-make/core-no-oom-handling/rmake.rs index a9e2b33e2107..5194d7731144 100644 --- a/tests/run-make/core-no-oom-handling/rmake.rs +++ b/tests/run-make/core-no-oom-handling/rmake.rs @@ -6,7 +6,7 @@ use run_make_support::{rustc, source_root}; fn main() { rustc() - .edition("2021") + .edition("2024") .arg("-Dwarnings") .crate_type("rlib") .input(source_root().join("library/core/src/lib.rs")) From 8604d458eb0ceafaba09322fd34b7c59038424f5 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 16 May 2025 11:35:06 -0400 Subject: [PATCH 211/245] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 056f5f4f3c10..47c911e9e6f6 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 056f5f4f3c100cb36b5e9aed2d20b9ea70aae295 +Subproject commit 47c911e9e6f6461f90ce19142031fe16876a3b95 From 742c27bc524b806d04c34f22810f88cbf5fa4483 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Fri, 16 May 2025 16:55:46 +0800 Subject: [PATCH 212/245] Do not emit help when shorthand from macro when suggest `?` or `expect` Signed-off-by: xizheyin --- compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 1 + tests/ui/typeck/macro-shorthand-issue-140659.rs | 1 - tests/ui/typeck/macro-shorthand-issue-140659.stderr | 4 ---- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 91eb1989864f..d3618a6ae69a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2034,6 +2034,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let sugg = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { + Some(_) if expr.span.from_expansion() => return false, Some(ident) => format!(": {ident}{sugg}"), None => sugg.to_string(), }; diff --git a/tests/ui/typeck/macro-shorthand-issue-140659.rs b/tests/ui/typeck/macro-shorthand-issue-140659.rs index dba06b43d393..d71a7ff1d3d4 100644 --- a/tests/ui/typeck/macro-shorthand-issue-140659.rs +++ b/tests/ui/typeck/macro-shorthand-issue-140659.rs @@ -45,7 +45,6 @@ fn process(op: &Operator, reencoder: &mut T) -> Instruction { }; } - // This should give the specific error message with the wrong help suggestion for_each_operator!(translate) } diff --git a/tests/ui/typeck/macro-shorthand-issue-140659.stderr b/tests/ui/typeck/macro-shorthand-issue-140659.stderr index 419db9475710..12537754d802 100644 --- a/tests/ui/typeck/macro-shorthand-issue-140659.stderr +++ b/tests/ui/typeck/macro-shorthand-issue-140659.stderr @@ -10,10 +10,6 @@ LL | for_each_operator!(translate) = note: expected type `u32` found enum `Result::Error>` = note: this error originates in the macro `translate` which comes from the expansion of the macro `for_each_operator` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider using `Result::expect` to unwrap the `Result::Error>` value, panicking if the value is a `Result::Err` - | -LL | }: tag_index.expect("REASON") - | ++++++++++++++++++++++++++++ error: aborting due to 1 previous error From 1c17324c7d82812b3e23902cd118b8ce77e9592c Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sat, 17 May 2025 00:59:09 +0800 Subject: [PATCH 213/245] Create tests/ui/typeck/suggestions/ and move some tests in tests/ui/typeck/ to it Signed-off-by: xizheyin --- tests/ui/typeck/{ => suggestions}/macro-shorthand-issue-140659.rs | 0 .../typeck/{ => suggestions}/macro-shorthand-issue-140659.stderr | 0 .../suggest-adding-missing-zero-to-floating-point-number.fixed | 0 .../suggest-adding-missing-zero-to-floating-point-number.rs | 0 .../suggest-adding-missing-zero-to-floating-point-number.stderr | 0 tests/ui/typeck/{ => suggestions}/suggest-arg-comma-delete-ice.rs | 0 .../typeck/{ => suggestions}/suggest-arg-comma-delete-ice.stderr | 0 .../{ => suggestions}/suggest-box-on-divergent-if-else-arms.fixed | 0 .../{ => suggestions}/suggest-box-on-divergent-if-else-arms.rs | 0 .../suggest-box-on-divergent-if-else-arms.stderr | 0 .../suggest-similar-impls-for-root-obligation.rs | 0 .../suggest-similar-impls-for-root-obligation.stderr | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/typeck/{ => suggestions}/macro-shorthand-issue-140659.rs (100%) rename tests/ui/typeck/{ => suggestions}/macro-shorthand-issue-140659.stderr (100%) rename tests/ui/typeck/{ => suggestions}/suggest-adding-missing-zero-to-floating-point-number.fixed (100%) rename tests/ui/typeck/{ => suggestions}/suggest-adding-missing-zero-to-floating-point-number.rs (100%) rename tests/ui/typeck/{ => suggestions}/suggest-adding-missing-zero-to-floating-point-number.stderr (100%) rename tests/ui/typeck/{ => suggestions}/suggest-arg-comma-delete-ice.rs (100%) rename tests/ui/typeck/{ => suggestions}/suggest-arg-comma-delete-ice.stderr (100%) rename tests/ui/typeck/{ => suggestions}/suggest-box-on-divergent-if-else-arms.fixed (100%) rename tests/ui/typeck/{ => suggestions}/suggest-box-on-divergent-if-else-arms.rs (100%) rename tests/ui/typeck/{ => suggestions}/suggest-box-on-divergent-if-else-arms.stderr (100%) rename tests/ui/typeck/{ => suggestions}/suggest-similar-impls-for-root-obligation.rs (100%) rename tests/ui/typeck/{ => suggestions}/suggest-similar-impls-for-root-obligation.stderr (100%) diff --git a/tests/ui/typeck/macro-shorthand-issue-140659.rs b/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.rs similarity index 100% rename from tests/ui/typeck/macro-shorthand-issue-140659.rs rename to tests/ui/typeck/suggestions/macro-shorthand-issue-140659.rs diff --git a/tests/ui/typeck/macro-shorthand-issue-140659.stderr b/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.stderr similarity index 100% rename from tests/ui/typeck/macro-shorthand-issue-140659.stderr rename to tests/ui/typeck/suggestions/macro-shorthand-issue-140659.stderr diff --git a/tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed b/tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.fixed similarity index 100% rename from tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed rename to tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.fixed diff --git a/tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs b/tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.rs similarity index 100% rename from tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs rename to tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.rs diff --git a/tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr b/tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.stderr similarity index 100% rename from tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr rename to tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.stderr diff --git a/tests/ui/typeck/suggest-arg-comma-delete-ice.rs b/tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.rs similarity index 100% rename from tests/ui/typeck/suggest-arg-comma-delete-ice.rs rename to tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.rs diff --git a/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr b/tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.stderr similarity index 100% rename from tests/ui/typeck/suggest-arg-comma-delete-ice.stderr rename to tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.stderr diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.fixed b/tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.fixed similarity index 100% rename from tests/ui/typeck/suggest-box-on-divergent-if-else-arms.fixed rename to tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.fixed diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.rs b/tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.rs similarity index 100% rename from tests/ui/typeck/suggest-box-on-divergent-if-else-arms.rs rename to tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.rs diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.stderr b/tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.stderr similarity index 100% rename from tests/ui/typeck/suggest-box-on-divergent-if-else-arms.stderr rename to tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.stderr diff --git a/tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs b/tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.rs similarity index 100% rename from tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs rename to tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.rs diff --git a/tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr b/tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.stderr similarity index 100% rename from tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr rename to tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.stderr From 3a9ee90deda02db57e34c1434960008678d9923d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 May 2025 09:52:27 +0200 Subject: [PATCH 214/245] Preparing for merge from rustc --- 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 801f78ba85cb..79abbfaeaf11 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -718ddf660e6a1802c39b4962cf7eaa4db57025ef +a69bc17fb8026bdc0d24bb1896ff95f0eba1da4e From b99daba38e3005f7e430655abf6138b4d9fc03e3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 May 2025 10:07:20 +0200 Subject: [PATCH 215/245] fix clippy lints --- src/tools/miri/cargo-miri/src/setup.rs | 8 +++----- src/tools/miri/miri-script/src/commands.rs | 14 +++++--------- src/tools/miri/src/shims/panic.rs | 1 + src/tools/miri/tests/ui.rs | 8 ++++---- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs index 7afc8481009c..b9b58c04f9e4 100644 --- a/src/tools/miri/cargo-miri/src/setup.rs +++ b/src/tools/miri/cargo-miri/src/setup.rs @@ -24,11 +24,9 @@ pub fn setup( let ask_user = !only_setup; let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path let show_setup = only_setup && !print_sysroot; - if !only_setup { - if let Some(sysroot) = std::env::var_os("MIRI_SYSROOT") { - // Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`. - return sysroot.into(); - } + if !only_setup && let Some(sysroot) = std::env::var_os("MIRI_SYSROOT") { + // Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`. + return sysroot.into(); } // Determine where the rust sources are located. The env var trumps auto-detection. diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 1c9750e2cbdc..3b7b159aeab7 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -675,11 +675,9 @@ impl Command { let mut early_flags = Vec::::new(); // In `dep` mode, the target is already passed via `MIRI_TEST_TARGET` - if !dep { - if let Some(target) = &target { - early_flags.push("--target".into()); - early_flags.push(target.into()); - } + if !dep && let Some(target) = &target { + early_flags.push("--target".into()); + early_flags.push(target.into()); } early_flags.push("--edition".into()); early_flags.push(edition.as_deref().unwrap_or("2021").into()); @@ -707,10 +705,8 @@ impl Command { // Add Miri flags let mut cmd = cmd.args(&miri_flags).args(&early_flags).args(&flags); // For `--dep` we also need to set the target in the env var. - if dep { - if let Some(target) = &target { - cmd = cmd.env("MIRI_TEST_TARGET", target); - } + if dep && let Some(target) = &target { + cmd = cmd.env("MIRI_TEST_TARGET", target); } // Finally, run the thing. Ok(cmd.run()?) diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 18af82148763..b5ed5ea837b3 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -85,6 +85,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Now we make a function call, and pass `data` as first and only argument. let f_instance = this.get_ptr_fn(try_fn)?.as_instance()?; trace!("try_fn: {:?}", f_instance); + #[allow(clippy::cloned_ref_to_slice_refs)] // the code is clearer as-is this.call_function( f_instance, ExternAbi::Rust, diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index a7b889d966c4..46472b51f9cd 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -318,10 +318,10 @@ fn main() -> Result<()> { let mut args = std::env::args_os(); // Skip the program name and check whether this is a `./miri run-dep` invocation - if let Some(first) = args.nth(1) { - if first == "--miri-run-dep-mode" { - return run_dep_mode(target, args); - } + if let Some(first) = args.nth(1) + && first == "--miri-run-dep-mode" + { + return run_dep_mode(target, args); } ui(Mode::Pass, "tests/pass", &target, WithoutDependencies, tmpdir.path())?; From ace4c6e01587a213bd0cc78b6d8a981ba403dc81 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 May 2025 10:35:40 +0200 Subject: [PATCH 216/245] fix for doctest-xcompile stabilization --- src/tools/miri/cargo-miri/src/main.rs | 42 ++++++++++++------- src/tools/miri/cargo-miri/src/phases.rs | 9 +--- src/tools/miri/test-cargo-miri/run-test.py | 17 +++----- .../test.filter.cross-target.stdout.ref | 12 ------ .../test.multiple_targets.stdout.ref | 10 +++++ ...rget.stdout.ref => test.no-doc.stdout.ref} | 0 .../test.subcrate.cross-target.stdout.ref | 11 ----- 7 files changed, 44 insertions(+), 57 deletions(-) delete mode 100644 src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref rename src/tools/miri/test-cargo-miri/{test.cross-target.stdout.ref => test.no-doc.stdout.ref} (100%) delete mode 100644 src/tools/miri/test-cargo-miri/test.subcrate.cross-target.stdout.ref diff --git a/src/tools/miri/cargo-miri/src/main.rs b/src/tools/miri/cargo-miri/src/main.rs index 322ef0a6c2aa..4c01a81fdfdc 100644 --- a/src/tools/miri/cargo-miri/src/main.rs +++ b/src/tools/miri/cargo-miri/src/main.rs @@ -63,27 +63,37 @@ fn main() { return; } - // The way rustdoc invokes rustc is indistinguishable from the way cargo invokes rustdoc by the - // arguments alone. `phase_cargo_rustdoc` sets this environment variable to let us disambiguate. - if env::var_os("MIRI_CALLED_FROM_RUSTDOC").is_some() { - // ...however, we then also see this variable when rustdoc invokes us as the testrunner! - // The runner is invoked as `$runtool ($runtool-arg)* output_file`; - // since we don't specify any runtool-args, and rustdoc supplies multiple arguments to - // the test-builder unconditionally, we can just check the number of remaining arguments: - if args.len() == 1 { - phase_runner(args, RunnerPhase::Rustdoc); - } else { - phase_rustc(args, RustcPhase::Rustdoc); - } - - return; - } - let Some(first) = args.next() else { show_error!( "`cargo-miri` called without first argument; please only invoke this binary through `cargo miri`" ) }; + + // The way rustdoc invokes rustc is indistinguishable from the way cargo invokes rustdoc by the + // arguments alone. `phase_cargo_rustdoc` sets this environment variable to let us disambiguate. + if env::var_os("MIRI_CALLED_FROM_RUSTDOC").is_some() { + // ...however, we then also see this variable when rustdoc invokes us as the testrunner! + // In that case the first argument is `runner` and there are no more arguments. + match first.as_str() { + "runner" => phase_runner(args, RunnerPhase::Rustdoc), + flag if flag.starts_with("--") || flag.starts_with("@") => { + // This is probably rustdoc invoking us to build the test. But we need to get `first` + // "back onto the iterator", it is some part of the rustc invocation. + phase_rustc(iter::once(first).chain(args), RustcPhase::Rustdoc); + } + _ => { + show_error!( + "`cargo-miri` failed to recognize which phase of the build process this is, please report a bug.\n\ + We are inside MIRI_CALLED_FROM_RUSTDOC.\n\ + The command-line arguments were: {:#?}", + Vec::from_iter(env::args()), + ); + } + } + + return; + } + match first.as_str() { "miri" => phase_cargo_miri(args), "runner" => phase_runner(args, RunnerPhase::Cargo), diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index cb62e12413c8..8ff5ac5d4a23 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -666,11 +666,6 @@ pub fn phase_rustdoc(mut args: impl Iterator) { if arg == "--extern" { // Patch --extern arguments to use *.rmeta files, since phase_cargo_rustc only creates stub *.rlib files. forward_patched_extern_arg(&mut args, &mut cmd); - } else if arg == "--test-runtool" { - // An existing --test-runtool flag indicates cargo is running in cross-target mode, which we don't support. - // Note that this is only passed when cargo is run with the unstable -Zdoctest-xcompile flag; - // otherwise, we won't be called as rustdoc at all. - show_error!("cross-interpreting doctests is not currently supported by Miri."); } else { cmd.arg(arg); } @@ -702,10 +697,10 @@ pub fn phase_rustdoc(mut args: impl Iterator) { // make sure the 'miri' flag is set for rustdoc cmd.arg("--cfg").arg("miri"); - // Make rustdoc call us back. + // Make rustdoc call us back for the build. + // (cargo already sets `--test-runtool` to us since we are the cargo test runner.) let cargo_miri_path = env::current_exe().expect("current executable path invalid"); cmd.arg("--test-builder").arg(&cargo_miri_path); // invoked by forwarding most arguments - cmd.arg("--test-runtool").arg(&cargo_miri_path); // invoked with just a single path argument debug_cmd("[cargo-miri rustdoc]", verbose, &cmd); exec(cmd) diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index a9d09ac7a9d6..40bfe7f845fe 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -142,24 +142,19 @@ def test_cargo_miri_run(): ) def test_cargo_miri_test(): - # rustdoc is not run on foreign targets - is_foreign = ARGS.target is not None - default_ref = "test.cross-target.stdout.ref" if is_foreign else "test.default.stdout.ref" - filter_ref = "test.filter.cross-target.stdout.ref" if is_foreign else "test.filter.stdout.ref" - test("`cargo miri test`", cargo_miri("test"), - default_ref, "test.empty.ref", + "test.default.stdout.ref", "test.empty.ref", env={'MIRIFLAGS': "-Zmiri-seed=4242"}, ) test("`cargo miri test` (no isolation, no doctests)", cargo_miri("test") + ["--bins", "--tests"], # no `--lib`, we disabled that in `Cargo.toml` - "test.cross-target.stdout.ref", "test.empty.ref", + "test.no-doc.stdout.ref", "test.empty.ref", env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) test("`cargo miri test` (with filter)", cargo_miri("test") + ["--", "--format=pretty", "pl"], - filter_ref, "test.empty.ref", + "test.filter.stdout.ref", "test.empty.ref", ) test("`cargo miri test` (test target)", cargo_miri("test") + ["--test", "test", "--", "--format=pretty"], @@ -171,7 +166,7 @@ def test_cargo_miri_test(): ) test("`cargo miri t` (subcrate, no isolation)", cargo_miri("t") + ["-p", "subcrate"], - "test.subcrate.cross-target.stdout.ref" if is_foreign else "test.subcrate.stdout.ref", + "test.subcrate.stdout.ref", "test.empty.ref", env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) @@ -181,12 +176,12 @@ def test_cargo_miri_test(): ) test("`cargo miri test` (custom target dir)", cargo_miri("test") + ["--target-dir=custom-test"], - default_ref, "test.empty.ref", + "test.default.stdout.ref", "test.empty.ref", ) del os.environ["CARGO_TARGET_DIR"] # this overrides `build.target-dir` passed by `--config`, so unset it test("`cargo miri test` (config-cli)", cargo_miri("test") + ["--config=build.target-dir=\"config-cli\""], - default_ref, "test.empty.ref", + "test.default.stdout.ref", "test.empty.ref", ) if ARGS.multi_target: test_cargo_miri_multi_target() diff --git a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref deleted file mode 100644 index 59b4deb1ff32..000000000000 --- a/src/tools/miri/test-cargo-miri/test.filter.cross-target.stdout.ref +++ /dev/null @@ -1,12 +0,0 @@ - -running 0 tests - -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in $TIME - -imported main - -running 1 test -test simple ... ok - -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME - diff --git a/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref index 567c5db07d06..a376530a8cfb 100644 --- a/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref @@ -20,3 +20,13 @@ running 6 tests ...i.. test result: ok. 5 passed; 0 failed; 1 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 + + +running 5 tests +..... +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.no-doc.stdout.ref similarity index 100% rename from src/tools/miri/test-cargo-miri/test.cross-target.stdout.ref rename to src/tools/miri/test-cargo-miri/test.no-doc.stdout.ref diff --git a/src/tools/miri/test-cargo-miri/test.subcrate.cross-target.stdout.ref b/src/tools/miri/test-cargo-miri/test.subcrate.cross-target.stdout.ref deleted file mode 100644 index 436e6e4fbbbc..000000000000 --- a/src/tools/miri/test-cargo-miri/test.subcrate.cross-target.stdout.ref +++ /dev/null @@ -1,11 +0,0 @@ - -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 From 1c9f20f24c9363eedaf5ea16f7e8f8746c80e1e8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 17 May 2025 12:45:32 +0200 Subject: [PATCH 217/245] Enable rust-analyzer to go from query definition to the corresponding provider field --- compiler/rustc_macros/src/query.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 33fb13e23bf8..ee377277017a 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -267,6 +267,18 @@ fn add_query_desc_cached_impl( ) { let Query { name, key, modifiers, .. } = &query; + // This dead code exists to instruct rust-analyzer about the link between the `rustc_queries` + // query names and the corresponding produced provider. The issue is that by nature of this + // macro producing a higher order macro that has all its token in the macro declaration we lose + // any meaningful spans, resulting in rust-analyzer being unable to make the connection between + // the query name and the corresponding providers field. The trick to fix this is to have + // `rustc_queries` emit a field access with the given name's span which allows it to succesfully + // show references / go to definition to the correspondig provider assignment which is usually + // the more interesting place. + let ra_hint = quote! { + let crate::query::Providers { #name: _, .. }; + }; + // Find out if we should cache the query on disk let cache = if let Some((args, expr)) = modifiers.cache.as_ref() { let tcx = args.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ }); @@ -277,6 +289,7 @@ fn add_query_desc_cached_impl( #[allow(unused_variables, unused_braces, rustc::pass_by_value)] #[inline] pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::query::queries::#name::Key<'tcx>) -> bool { + #ra_hint #expr } } @@ -286,6 +299,7 @@ fn add_query_desc_cached_impl( #[allow(rustc::pass_by_value)] #[inline] pub fn #name<'tcx>(_: TyCtxt<'tcx>, _: &crate::query::queries::#name::Key<'tcx>) -> bool { + #ra_hint false } } From 9578b59eacbdf73d577b70439d580ac3522856db Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 17 May 2025 12:01:13 +0000 Subject: [PATCH 218/245] Only select true errors in impossible_predicates --- .../rustc_trait_selection/src/traits/mod.rs | 10 ++++- .../object/ambiguity-vtable-segfault.rs | 37 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/object/ambiguity-vtable-segfault.rs diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 4738a538b297..31b075db04b9 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -701,9 +701,15 @@ pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec::Assoc` and the +// built-in impl for object types. Since they differ by their region responses, +// the goal is ambiguous. This affects codegen since impossible obligations +// for method dispatch will lead to a segfault, since we end up emitting dummy +// call vtable offsets due to . + +// Test for . + +//@ run-pass + +trait Mirror { + type Assoc: ?Sized; +} +impl Mirror for T { + type Assoc = T; +} + +trait Q: 'static { + fn q(&self); +} + +impl Q for i32 { + fn q(&self) { println!("i32"); } +} + +impl Q for ::Assoc where Self: 'static { + fn q(&self) { println!("dyn Q"); } +} + +fn foo(t: &T) { + t.q(); +} + +fn main() { + foo(&1 as &dyn Q); +} From bd9f1fd273102d20aef525d1dd98632f318d39c1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 May 2025 14:22:44 +0200 Subject: [PATCH 219/245] exlicitly pass -Zdoctest-xcompile to avoid cargo version behavior differences --- src/tools/miri/cargo-miri/src/phases.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 8ff5ac5d4a23..171e157789d7 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -176,6 +176,8 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // Set `--target-dir` to `miri` inside the original target directory. let target_dir = get_target_dir(&metadata); cmd.arg("--target-dir").arg(target_dir); + // Enable cross-target doctests (for consistency between different cargo versions). + cmd.arg("-Zdoctest-xcompile"); // *After* we set all the flags that need setting, forward everything else. Make sure to skip // `--target-dir` (which would otherwise be set twice). From 9cacafdd1ae7b23692d9894c6110b7f8f404b4bb Mon Sep 17 00:00:00 2001 From: klensy Date: Sat, 17 May 2025 15:25:32 +0300 Subject: [PATCH 220/245] compiler & tools: bump windows crate to dedupe versions --- Cargo.lock | 71 +++++------------------ compiler/rustc_codegen_ssa/Cargo.toml | 2 +- compiler/rustc_data_structures/Cargo.toml | 2 +- compiler/rustc_driver_impl/Cargo.toml | 2 +- compiler/rustc_errors/Cargo.toml | 2 +- compiler/rustc_session/Cargo.toml | 2 +- src/tools/compiletest/Cargo.toml | 2 +- src/tools/tidy/src/deps.rs | 3 + 8 files changed, 23 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f1fddefcb014..59f7f3dda8fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -738,7 +738,7 @@ dependencies = [ "tracing-subscriber", "unified-diff", "walkdir", - "windows 0.59.0", + "windows", ] [[package]] @@ -1587,7 +1587,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core", ] [[package]] @@ -3493,7 +3493,7 @@ dependencies = [ "thorin-dwp", "tracing", "wasm-encoder 0.219.2", - "windows 0.59.0", + "windows", ] [[package]] @@ -3552,7 +3552,7 @@ dependencies = [ "tempfile", "thin-vec", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -3615,7 +3615,7 @@ dependencies = [ "shlex", "stable_mir", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -3670,7 +3670,7 @@ dependencies = [ "termcolor", "termize", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -4415,7 +4415,7 @@ dependencies = [ "smallvec", "termize", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -5102,7 +5102,7 @@ dependencies = [ "libc", "objc2-core-foundation", "objc2-io-kit", - "windows 0.61.1", + "windows", ] [[package]] @@ -6002,16 +6002,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" -dependencies = [ - "windows-core 0.59.0", - "windows-targets 0.53.0", -] - [[package]] name = "windows" version = "0.61.1" @@ -6019,7 +6009,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core", "windows-future", "windows-link", "windows-numerics", @@ -6042,20 +6032,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", -] - -[[package]] -name = "windows-core" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" -dependencies = [ - "windows-implement 0.59.0", - "windows-interface", - "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-core", ] [[package]] @@ -6064,11 +6041,11 @@ version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.60.0", + "windows-implement", "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.0", + "windows-strings", ] [[package]] @@ -6077,21 +6054,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-implement" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -6126,7 +6092,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] @@ -6139,15 +6105,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-strings" version = "0.4.0" diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 97eebffd1fe8..d4c8ab80a331 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -58,5 +58,5 @@ default-features = false features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"] [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = ["Win32_Globalization"] diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index f48c73b13b96..f6a020116185 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -38,7 +38,7 @@ features = ["nightly"] # for may_dangle version = "0.12" [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_Storage_FileSystem", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 9da4f2dbc273..1971d06aad64 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -60,7 +60,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_System_Diagnostics_Debug", ] diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index b11793c190a1..82e7468211db 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -33,7 +33,7 @@ tracing = "0.1" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_Security", diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 63772a322221..f0ee19e3c677 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -33,7 +33,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_System_LibraryLoader", diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 93f7b1cb7cf2..3b544d8b8281 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -37,7 +37,7 @@ libc = "0.2" miow = "0.6" [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_System_Diagnostics_Debug", diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 4195258af885..9bb06c31c5c0 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -429,10 +429,13 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "winapi-util", "winapi-x86_64-pc-windows-gnu", "windows", + "windows-collections", "windows-core", + "windows-future", "windows-implement", "windows-interface", "windows-link", + "windows-numerics", "windows-result", "windows-strings", "windows-sys", From 667504b176c9e1ab380cef6eb9eaeba4813337c3 Mon Sep 17 00:00:00 2001 From: lcnr Date: Sat, 17 May 2025 10:51:59 +0000 Subject: [PATCH 221/245] check coroutines with TypingMode::Borrowck to avoid cyclic reasoning MIR borrowck taints its output if an obligation fails. This could then cause `check_coroutine_obligations` to silence its error, causing us to not emit and actual error and ICE. --- .../rustc_hir_analysis/src/check/check.rs | 20 +++++++---- .../delayed-obligations-emit.next.stderr | 15 +++++++++ .../ui/coroutine/delayed-obligations-emit.rs | 33 +++++++++++++++++++ 3 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 tests/ui/coroutine/delayed-obligations-emit.next.stderr create mode 100644 tests/ui/coroutine/delayed-obligations-emit.rs diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index f92b2aea160a..da94331aa26f 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1754,17 +1754,19 @@ pub(super) fn check_coroutine_obligations( debug!(?typeck_results.coroutine_stalled_predicates); let mode = if tcx.next_trait_solver_globally() { - TypingMode::post_borrowck_analysis(tcx, def_id) + // This query is conceptually between HIR typeck and + // MIR borrowck. We use the opaque types defined by HIR + // and ignore region constraints. + TypingMode::borrowck(tcx, def_id) } else { TypingMode::analysis_in_body(tcx, def_id) }; - let infcx = tcx - .infer_ctxt() - // typeck writeback gives us predicates with their regions erased. - // As borrowck already has checked lifetimes, we do not need to do it again. - .ignoring_regions() - .build(mode); + // Typeck writeback gives us predicates with their regions erased. + // We only need to check the goals while ignoring lifetimes to give good + // error message and to avoid breaking the assumption of `mir_borrowck` + // that all obligations already hold modulo regions. + let infcx = tcx.infer_ctxt().ignoring_regions().build(mode); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); for (predicate, cause) in &typeck_results.coroutine_stalled_predicates { @@ -1785,6 +1787,10 @@ pub(super) fn check_coroutine_obligations( let key = infcx.resolve_vars_if_possible(key); sanity_check_found_hidden_type(tcx, key, hidden_type)?; } + } else { + // We're not checking region constraints here, so we can simply drop the + // added opaque type uses in `TypingMode::Borrowck`. + let _ = infcx.take_opaque_types(); } Ok(()) diff --git a/tests/ui/coroutine/delayed-obligations-emit.next.stderr b/tests/ui/coroutine/delayed-obligations-emit.next.stderr new file mode 100644 index 000000000000..3a3663398c9a --- /dev/null +++ b/tests/ui/coroutine/delayed-obligations-emit.next.stderr @@ -0,0 +1,15 @@ +error[E0275]: overflow evaluating the requirement `{async block@$DIR/delayed-obligations-emit.rs:17:11: 17:16}: Send` + --> $DIR/delayed-obligations-emit.rs:17:5 + | +LL | spawn(async { build_dependencies().await }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `spawn` + --> $DIR/delayed-obligations-emit.rs:31:13 + | +LL | fn spawn(_: F) {} + | ^^^^ required by this bound in `spawn` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/coroutine/delayed-obligations-emit.rs b/tests/ui/coroutine/delayed-obligations-emit.rs new file mode 100644 index 000000000000..6334f29fcb22 --- /dev/null +++ b/tests/ui/coroutine/delayed-obligations-emit.rs @@ -0,0 +1,33 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ edition: 2024 +//@[current] check-pass + +// This previously caused an ICE with the new solver. +// The delayed coroutine obligations were checked with the +// opaque types inferred by borrowck. +// +// One of these delayed obligations failed with overflow in +// borrowck, causing us to taint `type_of` for the opaque. This +// then caused us to also not emit an error when checking the +// coroutine obligations. + +fn build_multiple<'a>() -> impl Sized { + spawn(async { build_dependencies().await }); + //[next]~^ ERROR overflow evaluating the requirement +} + +// Adding an explicit `Send` bound fixes it. +// Proving `build_dependencies(): Send` in `build_multiple` adds +// addiitional defining uses/placeholders. +fn build_dependencies() -> impl Future /* + Send */ { + async { + Box::pin(build_dependencies()).await; + async { build_multiple() }.await; + } +} + +fn spawn(_: F) {} + +fn main() {} From 40940e1294338080f88c87f0bde86e75ca603a27 Mon Sep 17 00:00:00 2001 From: klensy Date: Sat, 17 May 2025 15:31:02 +0300 Subject: [PATCH 222/245] bootstrap: bump windows too --- src/bootstrap/Cargo.lock | 71 +++++----------------------------- src/bootstrap/Cargo.toml | 2 +- src/bootstrap/src/bin/rustc.rs | 2 +- src/bootstrap/src/utils/job.rs | 1 - 4 files changed, 11 insertions(+), 65 deletions(-) diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index ff63b8c62d3a..d10d2d9bf8ce 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -64,7 +64,7 @@ dependencies = [ "tracing-subscriber", "tracing-tree", "walkdir", - "windows 0.57.0", + "windows", "xz2", ] @@ -703,7 +703,7 @@ dependencies = [ "ntapi", "objc2-core-foundation", "objc2-io-kit", - "windows 0.61.1", + "windows", ] [[package]] @@ -916,16 +916,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets", -] - [[package]] name = "windows" version = "0.61.1" @@ -933,7 +923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core", "windows-future", "windows-link", "windows-numerics", @@ -945,19 +935,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", -] - -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets", + "windows-core", ] [[package]] @@ -966,10 +944,10 @@ version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", + "windows-implement", + "windows-interface", "windows-link", - "windows-result 0.3.2", + "windows-result", "windows-strings", ] @@ -979,21 +957,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -1005,17 +972,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-interface" version = "0.59.1" @@ -1039,19 +995,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-result" version = "0.3.2" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index e34de924cc18..9652d18f1a6c 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -70,7 +70,7 @@ tracing-tree = { version = "0.4.0", optional = true } version = "1.0.0" [target.'cfg(windows)'.dependencies.windows] -version = "0.57" +version = "0.61" features = [ "Win32_Foundation", "Win32_Security", diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 85c682a46c5d..374884d8a9a0 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -342,7 +342,7 @@ fn format_rusage_data(child: Child) -> Option { use windows::Win32::System::Threading::GetProcessTimes; use windows::Win32::System::Time::FileTimeToSystemTime; - let handle = HANDLE(child.as_raw_handle() as isize); + let handle = HANDLE(child.as_raw_handle()); let mut user_filetime = Default::default(); let mut user_time = Default::default(); diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs index 4949518de79b..887deb41ca8b 100644 --- a/src/bootstrap/src/utils/job.rs +++ b/src/bootstrap/src/utils/job.rs @@ -66,7 +66,6 @@ mod for_windows { // Enable the Windows Error Reporting dialog which msys disables, // so we can JIT debug rustc let mode = SetErrorMode(THREAD_ERROR_MODE::default()); - let mode = THREAD_ERROR_MODE(mode); SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX); // Create a new job object for us to use From a4765c9fc2f0285e20f0ff155156ddd760fa8fff Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sat, 17 May 2025 13:05:05 +0000 Subject: [PATCH 223/245] Make some `match`es slightly more ergonomic in `librustdoc` --- src/librustdoc/clean/cfg.rs | 62 ++++++++++------------ src/librustdoc/clean/mod.rs | 22 ++++---- src/librustdoc/clean/types.rs | 20 +++---- src/librustdoc/formats/item_type.rs | 33 +++++------- src/librustdoc/html/format.rs | 81 +++++++++++++---------------- 5 files changed, 98 insertions(+), 120 deletions(-) diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 1541e7201cef..439777843fb0 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -144,7 +144,7 @@ impl Cfg { /// Whether the configuration consists of just `Cfg` or `Not`. fn is_simple(&self) -> bool { - match *self { + match self { Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) => true, Cfg::All(..) | Cfg::Any(..) => false, } @@ -152,7 +152,7 @@ impl Cfg { /// Whether the configuration consists of just `Cfg`, `Not` or `All`. fn is_all(&self) -> bool { - match *self { + match self { Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) | Cfg::All(..) => true, Cfg::Any(..) => false, } @@ -204,7 +204,7 @@ impl Cfg { } fn should_append_only_to_description(&self) -> bool { - match *self { + match self { Cfg::False | Cfg::True => false, Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true, Cfg::Not(box Cfg::Cfg(..)) => true, @@ -261,17 +261,17 @@ impl ops::Not for Cfg { impl ops::BitAndAssign for Cfg { fn bitand_assign(&mut self, other: Cfg) { match (self, other) { - (&mut Cfg::False, _) | (_, Cfg::True) => {} + (Cfg::False, _) | (_, Cfg::True) => {} (s, Cfg::False) => *s = Cfg::False, - (s @ &mut Cfg::True, b) => *s = b, - (&mut Cfg::All(ref mut a), Cfg::All(ref mut b)) => { + (s @ Cfg::True, b) => *s = b, + (Cfg::All(a), Cfg::All(ref mut b)) => { for c in b.drain(..) { if !a.contains(&c) { a.push(c); } } } - (&mut Cfg::All(ref mut a), ref mut b) => { + (Cfg::All(a), ref mut b) => { if !a.contains(b) { a.push(mem::replace(b, Cfg::True)); } @@ -305,15 +305,15 @@ impl ops::BitOrAssign for Cfg { fn bitor_assign(&mut self, other: Cfg) { match (self, other) { (Cfg::True, _) | (_, Cfg::False) | (_, Cfg::True) => {} - (s @ &mut Cfg::False, b) => *s = b, - (&mut Cfg::Any(ref mut a), Cfg::Any(ref mut b)) => { + (s @ Cfg::False, b) => *s = b, + (Cfg::Any(a), Cfg::Any(ref mut b)) => { for c in b.drain(..) { if !a.contains(&c) { a.push(c); } } } - (&mut Cfg::Any(ref mut a), ref mut b) => { + (Cfg::Any(a), ref mut b) => { if !a.contains(b) { a.push(mem::replace(b, Cfg::True)); } @@ -440,40 +440,34 @@ impl Display<'_> { impl fmt::Display for Display<'_> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self.0 { - Cfg::Not(ref child) => match **child { - Cfg::Any(ref sub_cfgs) => { - let separator = - if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " }; - fmt.write_str("neither ")?; + match self.0 { + Cfg::Not(box Cfg::Any(sub_cfgs)) => { + let separator = + if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " }; + fmt.write_str("neither ")?; - sub_cfgs - .iter() - .map(|sub_cfg| { - fmt::from_fn(|fmt| { - write_with_opt_paren( - fmt, - !sub_cfg.is_all(), - Display(sub_cfg, self.1), - ) - }) + sub_cfgs + .iter() + .map(|sub_cfg| { + fmt::from_fn(|fmt| { + write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1)) }) - .joined(separator, fmt) - } - ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Display(simple, self.1)), - ref c => write!(fmt, "not ({})", Display(c, self.1)), - }, + }) + .joined(separator, fmt) + } + Cfg::Not(box simple @ Cfg::Cfg(..)) => write!(fmt, "non-{}", Display(simple, self.1)), + Cfg::Not(box c) => write!(fmt, "not ({})", Display(c, self.1)), - Cfg::Any(ref sub_cfgs) => { + Cfg::Any(sub_cfgs) => { let separator = if sub_cfgs.iter().all(Cfg::is_simple) { " or " } else { ", or " }; self.display_sub_cfgs(fmt, sub_cfgs, separator) } - Cfg::All(ref sub_cfgs) => self.display_sub_cfgs(fmt, sub_cfgs, " and "), + Cfg::All(sub_cfgs) => self.display_sub_cfgs(fmt, sub_cfgs, " and "), Cfg::True => fmt.write_str("everywhere"), Cfg::False => fmt.write_str("nowhere"), - Cfg::Cfg(name, value) => { + &Cfg::Cfg(name, value) => { let human_readable = match (name, value) { (sym::unix, None) => "Unix", (sym::windows, None) => "Windows", diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 622a410837b5..28dfa01534ea 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -224,9 +224,9 @@ fn clean_generic_bound<'tcx>( bound: &hir::GenericBound<'tcx>, cx: &mut DocContext<'tcx>, ) -> Option { - Some(match *bound { + Some(match bound { hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)), - hir::GenericBound::Trait(ref t) => { + hir::GenericBound::Trait(t) => { // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op. if let hir::BoundConstness::Maybe(_) = t.modifiers.constness && cx.tcx.lang_items().destruct_trait() == Some(t.trait_ref.trait_def_id().unwrap()) @@ -352,8 +352,8 @@ fn clean_where_predicate<'tcx>( if !predicate.kind.in_where_clause() { return None; } - Some(match *predicate.kind { - hir::WherePredicateKind::BoundPredicate(ref wbp) => { + Some(match predicate.kind { + hir::WherePredicateKind::BoundPredicate(wbp) => { let bound_params = wbp .bound_generic_params .iter() @@ -366,12 +366,12 @@ fn clean_where_predicate<'tcx>( } } - hir::WherePredicateKind::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate { + hir::WherePredicateKind::RegionPredicate(wrp) => WherePredicate::RegionPredicate { lifetime: clean_lifetime(wrp.lifetime, cx), bounds: wrp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }, - hir::WherePredicateKind::EqPredicate(ref wrp) => WherePredicate::EqPredicate { + hir::WherePredicateKind::EqPredicate(wrp) => WherePredicate::EqPredicate { lhs: clean_ty(wrp.lhs_ty, cx), rhs: clean_ty(wrp.rhs_ty, cx).into(), }, @@ -2112,7 +2112,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ); Type::Path { path } } - ty::Dynamic(obj, ref reg, _) => { + ty::Dynamic(obj, reg, _) => { // HACK: pick the first `did` as the `did` of the trait object. Someone // might want to implement "native" support for marker-trait-only // trait objects. @@ -2129,7 +2129,7 @@ pub(crate) fn clean_middle_ty<'tcx>( inline::record_extern_fqn(cx, did, ItemType::Trait); - let lifetime = clean_trait_object_lifetime_bound(*reg, container, obj, cx.tcx); + let lifetime = clean_trait_object_lifetime_bound(reg, container, obj, cx.tcx); let mut bounds = dids .map(|did| { @@ -2846,7 +2846,7 @@ fn clean_maybe_renamed_item<'tcx>( )); return ret; } - ItemKind::Enum(_, ref def, generics) => EnumItem(Enum { + ItemKind::Enum(_, def, generics) => EnumItem(Enum { variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(), generics: clean_generics(generics, cx), }), @@ -2854,11 +2854,11 @@ fn clean_maybe_renamed_item<'tcx>( generics: clean_generics(generics, cx), bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }), - ItemKind::Union(_, ref variant_data, generics) => UnionItem(Union { + ItemKind::Union(_, variant_data, generics) => UnionItem(Union { generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), - ItemKind::Struct(_, ref variant_data, generics) => StructItem(Struct { + ItemKind::Struct(_, variant_data, generics) => StructItem(Struct { ctor_kind: variant_data.ctor_kind(), generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 15890fff0c33..75f1bc9549cb 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1337,9 +1337,9 @@ pub(crate) enum WherePredicate { impl WherePredicate { pub(crate) fn get_bounds(&self) -> Option<&[GenericBound]> { - match *self { - WherePredicate::BoundPredicate { ref bounds, .. } => Some(bounds), - WherePredicate::RegionPredicate { ref bounds, .. } => Some(bounds), + match self { + WherePredicate::BoundPredicate { bounds, .. } => Some(bounds), + WherePredicate::RegionPredicate { bounds, .. } => Some(bounds), _ => None, } } @@ -1709,13 +1709,13 @@ impl Type { /// /// [clean]: crate::clean pub(crate) fn def_id(&self, cache: &Cache) -> Option { - let t: PrimitiveType = match *self { - Type::Path { ref path } => return Some(path.def_id()), - DynTrait(ref bounds, _) => return bounds.first().map(|b| b.trait_.def_id()), - Primitive(p) => return cache.primitive_locations.get(&p).cloned(), + let t: PrimitiveType = match self { + Type::Path { path } => return Some(path.def_id()), + DynTrait(bounds, _) => return bounds.first().map(|b| b.trait_.def_id()), + Primitive(p) => return cache.primitive_locations.get(p).cloned(), BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, - BorrowedRef { ref type_, .. } => return type_.def_id(cache), - Tuple(ref tys) => { + BorrowedRef { type_, .. } => return type_.def_id(cache), + Tuple(tys) => { if tys.is_empty() { PrimitiveType::Unit } else { @@ -1727,7 +1727,7 @@ impl Type { Array(..) => PrimitiveType::Array, Type::Pat(..) => PrimitiveType::Pat, RawPointer(..) => PrimitiveType::RawPointer, - QPath(box QPathData { ref self_type, .. }) => return self_type.def_id(cache), + QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache), Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None, }; Primitive(t).def_id(cache) diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index de6537e992f1..3aba7a370adb 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -70,12 +70,12 @@ impl Serialize for ItemType { impl<'a> From<&'a clean::Item> for ItemType { fn from(item: &'a clean::Item) -> ItemType { - let kind = match item.kind { - clean::StrippedItem(box ref item) => item, - ref kind => kind, + let kind = match &item.kind { + clean::StrippedItem(box item) => item, + kind => kind, }; - match *kind { + match kind { clean::ModuleItem(..) => ItemType::Module, clean::ExternCrateItem { .. } => ItemType::ExternCrate, clean::ImportItem(..) => ItemType::Import, @@ -103,7 +103,7 @@ impl<'a> From<&'a clean::Item> for ItemType { clean::ForeignTypeItem => ItemType::ForeignType, clean::KeywordItem => ItemType::Keyword, clean::TraitAliasItem(..) => ItemType::TraitAlias, - clean::ProcMacroItem(ref mac) => match mac.kind { + clean::ProcMacroItem(mac) => match mac.kind { MacroKind::Bang => ItemType::Macro, MacroKind::Attr => ItemType::ProcAttribute, MacroKind::Derive => ItemType::ProcDerive, @@ -134,22 +134,15 @@ impl ItemType { DefKind::Trait => Self::Trait, DefKind::TyAlias => Self::TypeAlias, DefKind::TraitAlias => Self::TraitAlias, - DefKind::Macro(kind) => match kind { - MacroKind::Bang => ItemType::Macro, - MacroKind::Attr => ItemType::ProcAttribute, - MacroKind::Derive => ItemType::ProcDerive, - }, + DefKind::Macro(MacroKind::Bang) => ItemType::Macro, + DefKind::Macro(MacroKind::Attr) => ItemType::ProcAttribute, + DefKind::Macro(MacroKind::Derive) => ItemType::ProcDerive, DefKind::ForeignTy => Self::ForeignType, DefKind::Variant => Self::Variant, DefKind::Field => Self::StructField, DefKind::AssocTy => Self::AssocType, - DefKind::AssocFn => { - if let Some(DefKind::Trait) = parent_kind { - Self::TyMethod - } else { - Self::Method - } - } + DefKind::AssocFn if let Some(DefKind::Trait) = parent_kind => Self::TyMethod, + DefKind::AssocFn => Self::Method, DefKind::Ctor(CtorOf::Struct, _) => Self::Struct, DefKind::Ctor(CtorOf::Variant, _) => Self::Variant, DefKind::AssocConst => Self::AssocConst, @@ -170,7 +163,7 @@ impl ItemType { } pub(crate) fn as_str(&self) -> &'static str { - match *self { + match self { ItemType::Module => "mod", ItemType::ExternCrate => "externcrate", ItemType::Import => "import", @@ -199,10 +192,10 @@ impl ItemType { } } pub(crate) fn is_method(&self) -> bool { - matches!(*self, ItemType::Method | ItemType::TyMethod) + matches!(self, ItemType::Method | ItemType::TyMethod) } pub(crate) fn is_adt(&self) -> bool { - matches!(*self, ItemType::Struct | ItemType::Union | ItemType::Enum) + matches!(self, ItemType::Struct | ItemType::Union | ItemType::Enum) } } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 299fd6b9adbb..8c7ab640bed3 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -856,15 +856,15 @@ fn fmt_type( ) -> fmt::Result { trace!("fmt_type(t = {t:?})"); - match *t { + match t { clean::Generic(name) => f.write_str(name.as_str()), clean::SelfTy => f.write_str("Self"), - clean::Type::Path { ref path } => { + clean::Type::Path { path } => { // Paths like `T::Output` and `Self::Output` should be rendered with all segments. let did = path.def_id(); resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx) } - clean::DynTrait(ref bounds, ref lt) => { + clean::DynTrait(bounds, lt) => { f.write_str("dyn ")?; tybounds(bounds, lt, cx).fmt(f) } @@ -872,8 +872,8 @@ fn fmt_type( clean::Primitive(clean::PrimitiveType::Never) => { primitive_link(f, PrimitiveType::Never, format_args!("!"), cx) } - clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx), - clean::BareFunction(ref decl) => { + &clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx), + clean::BareFunction(decl) => { print_higher_ranked_params_with_space(&decl.generic_params, cx, "for").fmt(f)?; decl.safety.print_with_space().fmt(f)?; print_abi_with_space(decl.abi).fmt(f)?; @@ -884,11 +884,11 @@ fn fmt_type( } decl.decl.print(cx).fmt(f) } - clean::UnsafeBinder(ref binder) => { + clean::UnsafeBinder(binder) => { print_higher_ranked_params_with_space(&binder.generic_params, cx, "unsafe").fmt(f)?; binder.ty.print(cx).fmt(f) } - clean::Tuple(ref typs) => match &typs[..] { + clean::Tuple(typs) => match &typs[..] { &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx), [one] => { if let clean::Generic(name) = one { @@ -925,45 +925,36 @@ fn fmt_type( } } }, - clean::Slice(ref t) => match **t { - clean::Generic(name) => { - primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx) - } - _ => { - write!(f, "[")?; - t.print(cx).fmt(f)?; - write!(f, "]") - } - }, - clean::Type::Pat(ref t, ref pat) => { + clean::Slice(box clean::Generic(name)) => { + primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx) + } + clean::Slice(t) => { + write!(f, "[")?; + t.print(cx).fmt(f)?; + write!(f, "]") + } + clean::Type::Pat(t, pat) => { fmt::Display::fmt(&t.print(cx), f)?; write!(f, " is {pat}") } - clean::Array(ref t, ref n) => match **t { - clean::Generic(name) if !f.alternate() => primitive_link( - f, - PrimitiveType::Array, - format_args!("[{name}; {n}]", n = Escape(n)), - cx, - ), - _ => { - write!(f, "[")?; - t.print(cx).fmt(f)?; - if f.alternate() { - write!(f, "; {n}")?; - } else { - write!(f, "; ")?; - primitive_link( - f, - PrimitiveType::Array, - format_args!("{n}", n = Escape(n)), - cx, - )?; - } - write!(f, "]") + clean::Array(box clean::Generic(name), n) if !f.alternate() => primitive_link( + f, + PrimitiveType::Array, + format_args!("[{name}; {n}]", n = Escape(n)), + cx, + ), + clean::Array(t, n) => { + write!(f, "[")?; + t.print(cx).fmt(f)?; + if f.alternate() { + write!(f, "; {n}")?; + } else { + write!(f, "; ")?; + primitive_link(f, PrimitiveType::Array, format_args!("{n}", n = Escape(n)), cx)?; } - }, - clean::RawPointer(m, ref t) => { + write!(f, "]") + } + clean::RawPointer(m, t) => { let m = match m { hir::Mutability::Mut => "mut", hir::Mutability::Not => "const", @@ -991,7 +982,7 @@ fn fmt_type( t.print(cx).fmt(f) } } - clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => { + clean::BorrowedRef { lifetime: l, mutability, type_: ty } => { let lt = fmt::from_fn(|f| match l { Some(l) => write!(f, "{} ", l.print()), _ => Ok(()), @@ -1028,11 +1019,11 @@ fn fmt_type( } Ok(()) } - clean::ImplTrait(ref bounds) => { + clean::ImplTrait(bounds) => { f.write_str("impl ")?; print_generic_bounds(bounds, cx).fmt(f) } - clean::QPath(box clean::QPathData { + &clean::QPath(box clean::QPathData { ref assoc, ref self_type, ref trait_, From 1adfdb42b923d432e8a163350115819e6c0cb887 Mon Sep 17 00:00:00 2001 From: bohan Date: Sat, 17 May 2025 22:01:57 +0800 Subject: [PATCH 224/245] Use `crate::` prefix for root macro suggestions --- compiler/rustc_resolve/src/diagnostics.rs | 2 +- tests/ui/imports/issue-99695-b.fixed | 2 +- tests/ui/imports/issue-99695-b.stderr | 2 +- ...5.fixed => issue-99695.edition_2015.fixed} | 6 +++++- ...stderr => issue-99695.edition_2015.stderr} | 4 ++-- .../ui/imports/issue-99695.edition_2018.fixed | 21 +++++++++++++++++++ .../imports/issue-99695.edition_2018.stderr | 16 ++++++++++++++ tests/ui/imports/issue-99695.rs | 4 ++++ 8 files changed, 51 insertions(+), 6 deletions(-) rename tests/ui/imports/{issue-99695.fixed => issue-99695.edition_2015.fixed} (71%) rename tests/ui/imports/{issue-99695.stderr => issue-99695.edition_2015.stderr} (90%) create mode 100644 tests/ui/imports/issue-99695.edition_2018.fixed create mode 100644 tests/ui/imports/issue-99695.edition_2018.stderr diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 0b16983c2c79..d09750fa281b 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2493,7 +2493,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else { return None; }; - let module_name = crate_module.kind.name().unwrap_or(kw::Empty); + let module_name = crate_module.kind.name().unwrap_or(kw::Crate); let import_snippet = match import.kind { ImportKind::Single { source, target, .. } if source != target => { format!("{source} as {target}") diff --git a/tests/ui/imports/issue-99695-b.fixed b/tests/ui/imports/issue-99695-b.fixed index 0108f762400f..ae63b0c4627c 100644 --- a/tests/ui/imports/issue-99695-b.fixed +++ b/tests/ui/imports/issue-99695-b.fixed @@ -11,7 +11,7 @@ mod m { pub struct other_item; } - use ::nu; + use crate::nu; pub use self::p::{other_item as _}; //~^ ERROR unresolved import `self::p::nu` [E0432] //~| HELP a macro with this name exists at the root of the crate diff --git a/tests/ui/imports/issue-99695-b.stderr b/tests/ui/imports/issue-99695-b.stderr index d58d2798746c..ad752d5c45a7 100644 --- a/tests/ui/imports/issue-99695-b.stderr +++ b/tests/ui/imports/issue-99695-b.stderr @@ -7,7 +7,7 @@ LL | pub use self::p::{nu, other_item as _}; = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined help: a macro with this name exists at the root of the crate | -LL ~ use ::nu; +LL ~ use crate::nu; LL ~ pub use self::p::{other_item as _}; | diff --git a/tests/ui/imports/issue-99695.fixed b/tests/ui/imports/issue-99695.edition_2015.fixed similarity index 71% rename from tests/ui/imports/issue-99695.fixed rename to tests/ui/imports/issue-99695.edition_2015.fixed index 51ccd3f8c48d..798acfd58746 100644 --- a/tests/ui/imports/issue-99695.fixed +++ b/tests/ui/imports/issue-99695.edition_2015.fixed @@ -1,4 +1,8 @@ //@ run-rustfix +//@ revisions: edition_2015 edition_2018 +//@ [edition_2015] edition: 2015 +//@ [edition_2018] edition: 2018 + #![allow(unused, nonstandard_style)] mod m { #[macro_export] @@ -8,7 +12,7 @@ mod m { pub struct other_item; - use ::nu; + use crate::nu; pub use self::{other_item as _}; //~^ ERROR unresolved import `self::nu` [E0432] //~| HELP a macro with this name exists at the root of the crate diff --git a/tests/ui/imports/issue-99695.stderr b/tests/ui/imports/issue-99695.edition_2015.stderr similarity index 90% rename from tests/ui/imports/issue-99695.stderr rename to tests/ui/imports/issue-99695.edition_2015.stderr index 536f51dcb3b2..4ef8e6426fb2 100644 --- a/tests/ui/imports/issue-99695.stderr +++ b/tests/ui/imports/issue-99695.edition_2015.stderr @@ -1,5 +1,5 @@ error[E0432]: unresolved import `self::nu` - --> $DIR/issue-99695.rs:11:20 + --> $DIR/issue-99695.rs:15:20 | LL | pub use self::{nu, other_item as _}; | ^^ no `nu` in `m` @@ -7,7 +7,7 @@ LL | pub use self::{nu, other_item as _}; = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined help: a macro with this name exists at the root of the crate | -LL ~ use ::nu; +LL ~ use crate::nu; LL ~ pub use self::{other_item as _}; | diff --git a/tests/ui/imports/issue-99695.edition_2018.fixed b/tests/ui/imports/issue-99695.edition_2018.fixed new file mode 100644 index 000000000000..798acfd58746 --- /dev/null +++ b/tests/ui/imports/issue-99695.edition_2018.fixed @@ -0,0 +1,21 @@ +//@ run-rustfix +//@ revisions: edition_2015 edition_2018 +//@ [edition_2015] edition: 2015 +//@ [edition_2018] edition: 2018 + +#![allow(unused, nonstandard_style)] +mod m { + #[macro_export] + macro_rules! nu { + {} => {}; + } + + pub struct other_item; + + use crate::nu; +pub use self::{other_item as _}; + //~^ ERROR unresolved import `self::nu` [E0432] + //~| HELP a macro with this name exists at the root of the crate +} + +fn main() {} diff --git a/tests/ui/imports/issue-99695.edition_2018.stderr b/tests/ui/imports/issue-99695.edition_2018.stderr new file mode 100644 index 000000000000..4ef8e6426fb2 --- /dev/null +++ b/tests/ui/imports/issue-99695.edition_2018.stderr @@ -0,0 +1,16 @@ +error[E0432]: unresolved import `self::nu` + --> $DIR/issue-99695.rs:15:20 + | +LL | pub use self::{nu, other_item as _}; + | ^^ no `nu` in `m` + | + = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined +help: a macro with this name exists at the root of the crate + | +LL ~ use crate::nu; +LL ~ pub use self::{other_item as _}; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/issue-99695.rs b/tests/ui/imports/issue-99695.rs index a52370e0eb08..dc0a8f438e00 100644 --- a/tests/ui/imports/issue-99695.rs +++ b/tests/ui/imports/issue-99695.rs @@ -1,4 +1,8 @@ //@ run-rustfix +//@ revisions: edition_2015 edition_2018 +//@ [edition_2015] edition: 2015 +//@ [edition_2018] edition: 2018 + #![allow(unused, nonstandard_style)] mod m { #[macro_export] From b3b2153c0b26fa1cfb491c863b2b0196949a9e3e Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sat, 17 May 2025 22:26:42 +0800 Subject: [PATCH 225/245] Update triagebot.toml Co-authored-by: Urgau <3616612+Urgau@users.noreply.github.com> --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index a3866d62dca4..36d5838d1e19 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1426,4 +1426,4 @@ compiletest = [ [note] [behind-upstream] -days-threshold = 7 +days-threshold = 14 From 65d381b0cc2b7b8404e7c910055dd3cf806f1cca Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sat, 17 May 2025 23:48:57 +0800 Subject: [PATCH 226/245] triagebot: fix Rust for Linux ping group rust-lang/rust label --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 9dcdbcecbeca..f566a947e0e9 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -133,7 +133,7 @@ In case it's useful, here are some [instructions] for tackling these sorts of is [instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/rust-for-linux.html """ -label = "O-rfl" +label = "A-rust-for-linux" [ping.wasm] alias = ["webassembly"] From 2dddbd1ecb586b7a7dc67c078c2f7785a5cb63ba Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sat, 17 May 2025 23:51:00 +0800 Subject: [PATCH 227/245] rustc-dev-guide: fix Rust for Linux rust-lang/rust label --- .../rustc-dev-guide/src/notification-groups/rust-for-linux.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md b/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md index 9ba4eff629e7..696f2038e1a4 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md @@ -1,9 +1,9 @@ # Rust for Linux notification group -**Github Label:** [O-rfl]
+**Github Label:** [A-rust-for-linux]
**Ping command:** `@rustbot ping rfl` -[O-rfl]: https://github.com/rust-lang/rust/labels/O-rfl +[A-rust-for-linux]: https://github.com/rust-lang/rust/labels/A-rust-for-linux This list will be used to notify [Rust for Linux (RfL)][rfl] maintainers when the compiler or the standard library changes in a way that would From 98cdb829a8e751a4fa82849bfe3195e3f0756e36 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 17 May 2025 19:35:01 +0000 Subject: [PATCH 228/245] Fast path for register_region_obligation --- compiler/rustc_infer/src/infer/outlives/obligations.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 8dde99c45cfa..5fd98e35e5ce 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -98,6 +98,14 @@ impl<'tcx> InferCtxt<'tcx> { sub_region: Region<'tcx>, cause: &ObligationCause<'tcx>, ) { + // `is_global` means the type has no params, infer, placeholder, or non-`'static` + // free regions. If the type has none of these things, then we can skip registering + // this outlives obligation since it has no components which affect lifetime + // checking in an interesting way. + if sup_type.is_global() { + return; + } + debug!(?sup_type, ?sub_region, ?cause); let origin = SubregionOrigin::from_obligation_cause(cause, || { infer::RelateParamBound( From cd22c1b88385b65d657dedd520abaf1a0aaf7e3f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 29 Apr 2025 20:25:59 +0200 Subject: [PATCH 229/245] determine later whether an explicit reg was used --- compiler/rustc_builtin_macros/src/asm.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 3e8ddb8abd43..58aba3e59032 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -135,9 +135,8 @@ pub fn parse_asm_args<'a>( None }; - let mut explicit_reg = false; let op = if eat_operand_keyword(p, exp!(In), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; + let reg = parse_reg(p)?; if p.eat_keyword(exp!(Underscore)) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); return Err(err); @@ -145,15 +144,15 @@ pub fn parse_asm_args<'a>( let expr = p.parse_expr()?; ast::InlineAsmOperand::In { reg, expr } } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; + let reg = parse_reg(p)?; let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: false } } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; + let reg = parse_reg(p)?; let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: true } } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; + let reg = parse_reg(p)?; if p.eat_keyword(exp!(Underscore)) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); return Err(err); @@ -167,7 +166,7 @@ pub fn parse_asm_args<'a>( ast::InlineAsmOperand::InOut { reg, expr, late: false } } } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; + let reg = parse_reg(p)?; if p.eat_keyword(exp!(Underscore)) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); return Err(err); @@ -223,6 +222,8 @@ pub fn parse_asm_args<'a>( p.unexpected_any()? }; + let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); + allow_templates = false; let span = span_start.to(p.prev_token.span); let slot = args.operands.len(); @@ -231,6 +232,7 @@ pub fn parse_asm_args<'a>( // Validate the order of named, positional & explicit register operands and // clobber_abi/options. We do this at the end once we have the full span // of the argument available. + if explicit_reg { if name.is_some() { dcx.emit_err(errors::AsmExplicitRegisterName { span }); @@ -478,15 +480,11 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, Ok(()) } -fn parse_reg<'a>( - p: &mut Parser<'a>, - explicit_reg: &mut bool, -) -> PResult<'a, ast::InlineAsmRegOrRegClass> { +fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> { p.expect(exp!(OpenParen))?; let result = match p.token.uninterpolate().kind { token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name), token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { - *explicit_reg = true; ast::InlineAsmRegOrRegClass::Reg(symbol) } _ => { From 5af9652e5c733eb14c9a28b92c7a2608cbf7ea59 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 29 Apr 2025 20:35:39 +0200 Subject: [PATCH 230/245] extract operand parser --- compiler/rustc_builtin_macros/src/asm.rs | 176 ++++++++++++----------- 1 file changed, 93 insertions(+), 83 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 58aba3e59032..3afa2d3dd8ee 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -69,6 +69,76 @@ fn parse_args<'a>( parse_asm_args(&mut p, sp, asm_macro) } +fn parse_asm_operand<'a>( + p: &mut Parser<'a>, + asm_macro: AsmMacro, +) -> PResult<'a, Option> { + let dcx = p.dcx(); + + Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + ast::InlineAsmOperand::In { reg, expr } + } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: false } + } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: true } + } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: false } + } + } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: true } + } + } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { + let block = p.parse_block()?; + ast::InlineAsmOperand::Label { block } + } else if p.eat_keyword(exp!(Const)) { + let anon_const = p.parse_expr_anon_const()?; + ast::InlineAsmOperand::Const { anon_const } + } else if p.eat_keyword(exp!(Sym)) { + let expr = p.parse_expr()?; + let ast::ExprKind::Path(qself, path) = &expr.kind else { + let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); + return Err(err); + }; + let sym = + ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() }; + ast::InlineAsmOperand::Sym { sym } + } else { + return Ok(None); + })) +} + // Primarily public for rustfmt consumption. // Internal consumers should continue to leverage `expand_asm`/`expand__global_asm` pub fn parse_asm_args<'a>( @@ -135,91 +205,31 @@ pub fn parse_asm_args<'a>( None }; - let op = if eat_operand_keyword(p, exp!(In), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - ast::InlineAsmOperand::In { reg, expr } - } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { - let reg = parse_reg(p)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: false } - } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { - let reg = parse_reg(p)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: true } - } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: false } - } - } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { - let reg = parse_reg(p)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: true } - } - } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { - let block = p.parse_block()?; - ast::InlineAsmOperand::Label { block } - } else if p.eat_keyword(exp!(Const)) { - let anon_const = p.parse_expr_anon_const()?; - ast::InlineAsmOperand::Const { anon_const } - } else if p.eat_keyword(exp!(Sym)) { - let expr = p.parse_expr()?; - let ast::ExprKind::Path(qself, path) = &expr.kind else { - let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); - return Err(err); - }; - let sym = ast::InlineAsmSym { - id: ast::DUMMY_NODE_ID, - qself: qself.clone(), - path: path.clone(), - }; - ast::InlineAsmOperand::Sym { sym } - } else if allow_templates { - let template = p.parse_expr()?; - // If it can't possibly expand to a string, provide diagnostics here to include other - // things it could have been. - match template.kind { - ast::ExprKind::Lit(token_lit) - if matches!( - token_lit.kind, - token::LitKind::Str | token::LitKind::StrRaw(_) - ) => {} - ast::ExprKind::MacCall(..) => {} - _ => { - let err = dcx.create_err(errors::AsmExpectedOther { - span: template.span, - is_inline_asm: matches!(asm_macro, AsmMacro::Asm), - }); - return Err(err); + let Some(op) = parse_asm_operand(p, asm_macro)? else { + if allow_templates { + let template = p.parse_expr()?; + // If it can't possibly expand to a string, provide diagnostics here to include other + // things it could have been. + match template.kind { + ast::ExprKind::Lit(token_lit) + if matches!( + token_lit.kind, + token::LitKind::Str | token::LitKind::StrRaw(_) + ) => {} + ast::ExprKind::MacCall(..) => {} + _ => { + let err = dcx.create_err(errors::AsmExpectedOther { + span: template.span, + is_inline_asm: matches!(asm_macro, AsmMacro::Asm), + }); + return Err(err); + } } + args.templates.push(template); + continue; + } else { + p.unexpected_any()? } - args.templates.push(template); - continue; - } else { - p.unexpected_any()? }; let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); From 4320e6f474ba042f235327c4afe0f27cd454a565 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 18 May 2025 04:54:45 +0000 Subject: [PATCH 231/245] Preparing for merge from rustc --- 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 79abbfaeaf11..8b98fe3c4fc3 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -a69bc17fb8026bdc0d24bb1896ff95f0eba1da4e +ac17c3486c6fdfbb0c3c18b99f3d8dfbff625d29 From 84506c64fff3e636443022abd84c82ba6542dae0 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 18 May 2025 05:02:46 +0000 Subject: [PATCH 232/245] fmt --- src/tools/miri/src/helpers.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index d2a735166239..8e7c9edfcc07 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -933,7 +933,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// Check that the calling convention is what we expect. - fn check_callconv<'a>(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, exp_abi: Conv) -> InterpResult<'a, ()> { + fn check_callconv<'a>( + &self, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + exp_abi: Conv, + ) -> InterpResult<'a, ()> { if fn_abi.conv != exp_abi { throw_ub_format!( "calling a function with calling convention {exp_abi} using caller calling convention {}", From cf7caded0bc2d0d84f4c84cc6dc108c16d23f134 Mon Sep 17 00:00:00 2001 From: sayantn Date: Thu, 17 Apr 2025 03:28:16 +0530 Subject: [PATCH 233/245] Stabilize `avx512_target_feature` --- compiler/rustc_feature/src/accepted.rs | 2 + compiler/rustc_feature/src/unstable.rs | 1 - compiler/rustc_target/src/target_features.rs | 44 +++++++++---------- library/core/src/lib.rs | 2 +- library/stdarch | 2 +- .../using-target-feature-unstable.rs | 4 +- tests/ui/target-feature/gate.rs | 3 +- tests/ui/target-feature/gate.stderr | 10 ++--- tests/ui/target-feature/unstable-feature.rs | 4 +- .../ui/target-feature/unstable-feature.stderr | 2 +- 10 files changed, 37 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 809d1630ddec..820af9ac84b2 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -82,6 +82,8 @@ declare_features! ( (accepted, attr_literals, "1.30.0", Some(34981)), /// Allows overloading augmented assignment operations like `a += b`. (accepted, augmented_assignments, "1.8.0", Some(28235)), + /// Allows using `avx512*` target features. + (accepted, avx512_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), /// Allows mixing bind-by-move in patterns and references to those identifiers in guards. (accepted, bind_by_move_pattern_guards, "1.39.0", Some(15287)), /// Allows bindings in the subpattern of a binding pattern. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8fb10736539a..6cdcf451f37e 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -318,7 +318,6 @@ declare_features! ( (unstable, aarch64_ver_target_feature, "1.27.0", Some(44839)), (unstable, apx_target_feature, "1.88.0", Some(139284)), (unstable, arm_target_feature, "1.27.0", Some(44839)), - (unstable, avx512_target_feature, "1.27.0", Some(44839)), (unstable, bpf_target_feature, "1.54.0", Some(44839)), (unstable, csky_target_feature, "1.73.0", Some(44839)), (unstable, ermsb_target_feature, "1.49.0", Some(44839)), diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 5428aa4cf708..99b04ac27200 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -416,25 +416,25 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ), ("avx10.2", Unstable(sym::avx10_target_feature), &["avx10.1"]), ("avx2", Stable, &["avx"]), - ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), - ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avx512bf16", Stable, &["avx512bw"]), + ("avx512bitalg", Stable, &["avx512bw"]), + ("avx512bw", Stable, &["avx512f"]), + ("avx512cd", Stable, &["avx512f"]), + ("avx512dq", Stable, &["avx512f"]), + ("avx512f", Stable, &["avx2", "fma", "f16c"]), + ("avx512fp16", Stable, &["avx512bw"]), + ("avx512ifma", Stable, &["avx512f"]), + ("avx512vbmi", Stable, &["avx512bw"]), + ("avx512vbmi2", Stable, &["avx512bw"]), + ("avx512vl", Stable, &["avx512f"]), + ("avx512vnni", Stable, &["avx512f"]), + ("avx512vp2intersect", Stable, &["avx512f"]), + ("avx512vpopcntdq", Stable, &["avx512f"]), + ("avxifma", Stable, &["avx2"]), + ("avxneconvert", Stable, &["avx2"]), + ("avxvnni", Stable, &["avx2"]), + ("avxvnniint16", Stable, &["avx2"]), + ("avxvnniint8", Stable, &["avx2"]), ("bmi1", Stable, &[]), ("bmi2", Stable, &[]), ("cmpxchg16b", Stable, &[]), @@ -442,7 +442,7 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("f16c", Stable, &["avx"]), ("fma", Stable, &["avx"]), ("fxsr", Stable, &[]), - ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ("gfni", Stable, &["sse2"]), ("kl", Unstable(sym::keylocker_x86), &["sse2"]), ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), ("lzcnt", Stable, &[]), @@ -469,8 +469,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), ("ssse3", Stable, &["sse3"]), ("tbm", Unstable(sym::tbm_target_feature), &[]), - ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), - ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ("vaes", Stable, &["avx2", "aes"]), + ("vpclmulqdq", Stable, &["avx", "pclmulqdq"]), ("widekl", Unstable(sym::keylocker_x86), &["kl"]), ("x87", Unstable(sym::x87_target_feature), &[]), ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 64a7ec8906b6..b3c7e2c1d2da 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -188,9 +188,9 @@ // // Target features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(avx512_target_feature))] #![feature(aarch64_unstable_target_feature)] #![feature(arm_target_feature)] -#![feature(avx512_target_feature)] #![feature(hexagon_target_feature)] #![feature(keylocker_x86)] #![feature(loongarch_target_feature)] diff --git a/library/stdarch b/library/stdarch index f1c1839c0deb..1dfaa4db2479 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit f1c1839c0deb985a9f98cbd6b38a6d43f2df6157 +Subproject commit 1dfaa4db2479753a46a3e90f2c3c89d89d0b21f1 diff --git a/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs b/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs index 2682028936c1..15bcfdd9076e 100644 --- a/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs +++ b/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs @@ -1,5 +1,5 @@ -#![feature(avx512_target_feature)] +#![feature(x87_target_feature)] #[inline] -#[target_feature(enable = "avx512ifma")] +#[target_feature(enable = "x87")] pub unsafe fn foo() {} diff --git a/tests/ui/target-feature/gate.rs b/tests/ui/target-feature/gate.rs index 14fdad02f566..9244a98d82fd 100644 --- a/tests/ui/target-feature/gate.rs +++ b/tests/ui/target-feature/gate.rs @@ -2,7 +2,6 @@ // // gate-test-sse4a_target_feature // gate-test-powerpc_target_feature -// gate-test-avx512_target_feature // gate-test-tbm_target_feature // gate-test-arm_target_feature // gate-test-hexagon_target_feature @@ -27,7 +26,7 @@ // gate-test-x87_target_feature // gate-test-m68k_target_feature -#[target_feature(enable = "avx512bw")] +#[target_feature(enable = "x87")] //~^ ERROR: currently unstable unsafe fn foo() {} diff --git a/tests/ui/target-feature/gate.stderr b/tests/ui/target-feature/gate.stderr index fa876893848f..32d60ce43822 100644 --- a/tests/ui/target-feature/gate.stderr +++ b/tests/ui/target-feature/gate.stderr @@ -1,11 +1,11 @@ -error[E0658]: the target feature `avx512bw` is currently unstable - --> $DIR/gate.rs:30:18 +error[E0658]: the target feature `x87` is currently unstable + --> $DIR/gate.rs:29:18 | -LL | #[target_feature(enable = "avx512bw")] - | ^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "x87")] + | ^^^^^^^^^^^^^^ | = note: see issue #44839 for more information - = help: add `#![feature(avx512_target_feature)]` to the crate attributes to enable + = help: add `#![feature(x87_target_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 1 previous error diff --git a/tests/ui/target-feature/unstable-feature.rs b/tests/ui/target-feature/unstable-feature.rs index f62c4dd938a0..a79ad4696033 100644 --- a/tests/ui/target-feature/unstable-feature.rs +++ b/tests/ui/target-feature/unstable-feature.rs @@ -1,8 +1,8 @@ -//@ compile-flags: -Ctarget-feature=+vaes --crate-type=rlib --target=x86_64-unknown-linux-gnu +//@ compile-flags: -Ctarget-feature=+x87 --crate-type=rlib --target=x86_64-unknown-linux-gnu //@ build-pass //@ needs-llvm-components: x86 #![feature(no_core)] #![no_core] -//~? WARN unstable feature specified for `-Ctarget-feature`: `vaes` +//~? WARN unstable feature specified for `-Ctarget-feature`: `x87` diff --git a/tests/ui/target-feature/unstable-feature.stderr b/tests/ui/target-feature/unstable-feature.stderr index d34544c5c277..309b64afd922 100644 --- a/tests/ui/target-feature/unstable-feature.stderr +++ b/tests/ui/target-feature/unstable-feature.stderr @@ -1,4 +1,4 @@ -warning: unstable feature specified for `-Ctarget-feature`: `vaes` +warning: unstable feature specified for `-Ctarget-feature`: `x87` | = note: this feature is not stably supported; its behavior can change in the future From 2898680ebdb6608deccf40a93c1fa062b588b365 Mon Sep 17 00:00:00 2001 From: sayantn Date: Thu, 17 Apr 2025 03:31:00 +0530 Subject: [PATCH 234/245] Remove uses of `#[feature(avx512_target_feature)]` --- .../pass/shims/x86/intrinsics-x86-aes-vaes.rs | 2 +- .../pass/shims/x86/intrinsics-x86-avx512.rs | 1 - .../pass/shims/x86/intrinsics-x86-gfni.rs | 1 - .../shims/x86/intrinsics-x86-vpclmulqdq.rs | 1 - .../homogenous-floats-target-feature-mixup.rs | 2 -- tests/ui/abi/simd-abi-checks-avx.rs | 1 - tests/ui/abi/simd-abi-checks-avx.stderr | 24 +++++++++---------- .../ui/asm/x86_64/evex512-implicit-feature.rs | 1 - tests/ui/asm/x86_64/target-feature-attr.rs | 2 -- .../ui/asm/x86_64/target-feature-attr.stderr | 8 +++---- tests/ui/simd/target-feature-mixup.rs | 1 - 11 files changed, 17 insertions(+), 27 deletions(-) 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 47f086f7340d..48633c0a7fe6 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 @@ -2,7 +2,7 @@ //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+aes,+vaes,+avx512f -#![feature(avx512_target_feature, stdarch_x86_avx512)] +#![feature(stdarch_x86_avx512)] use core::mem::transmute; #[cfg(target_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 db5930638901..0ec2f679d80b 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 @@ -2,7 +2,6 @@ //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] #[cfg(target_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 882b5e3f7952..b58d68e2ef9e 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 @@ -6,7 +6,6 @@ // be interpreted as integers; signedness does not make sense for them, but // __mXXXi happens to be defined in terms of signed integers. #![allow(overflowing_literals)] -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] #[cfg(target_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 68964728e4ea..c7c9eb5e3951 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 @@ -8,7 +8,6 @@ // be interpreted as integers; signedness does not make sense for them, but // __mXXXi happens to be defined in terms of signed integers. #![allow(overflowing_literals)] -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] #[cfg(target_arch = "x86")] diff --git a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs index 22b9b029a404..2c78b794a8d7 100644 --- a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs +++ b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs @@ -7,8 +7,6 @@ //@ run-pass //@ needs-subprocess -#![feature(avx512_target_feature)] - #![allow(overflowing_literals)] #![allow(unused_variables)] diff --git a/tests/ui/abi/simd-abi-checks-avx.rs b/tests/ui/abi/simd-abi-checks-avx.rs index 772512702ece..7432381d15b7 100644 --- a/tests/ui/abi/simd-abi-checks-avx.rs +++ b/tests/ui/abi/simd-abi-checks-avx.rs @@ -2,7 +2,6 @@ //@ build-fail //@ compile-flags: -C target-feature=-avx -#![feature(avx512_target_feature)] #![feature(portable_simd)] #![feature(simd_ffi)] #![allow(improper_ctypes_definitions)] diff --git a/tests/ui/abi/simd-abi-checks-avx.stderr b/tests/ui/abi/simd-abi-checks-avx.stderr index 48db30bf4537..7489ca019460 100644 --- a/tests/ui/abi/simd-abi-checks-avx.stderr +++ b/tests/ui/abi/simd-abi-checks-avx.stderr @@ -1,5 +1,5 @@ error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:60:11 + --> $DIR/simd-abi-checks-avx.rs:59:11 | LL | f(g()); | ^^^ function called here @@ -7,7 +7,7 @@ LL | f(g()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:60:9 + --> $DIR/simd-abi-checks-avx.rs:59:9 | LL | f(g()); | ^^^^^^ function called here @@ -15,7 +15,7 @@ LL | f(g()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:66:14 + --> $DIR/simd-abi-checks-avx.rs:65:14 | LL | gavx(favx()); | ^^^^^^ function called here @@ -23,7 +23,7 @@ LL | gavx(favx()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:66:9 + --> $DIR/simd-abi-checks-avx.rs:65:9 | LL | gavx(favx()); | ^^^^^^^^^^^^ function called here @@ -31,7 +31,7 @@ LL | gavx(favx()); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:76:19 + --> $DIR/simd-abi-checks-avx.rs:75:19 | LL | w(Wrapper(g())); | ^^^ function called here @@ -39,7 +39,7 @@ LL | w(Wrapper(g())); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:76:9 + --> $DIR/simd-abi-checks-avx.rs:75:9 | LL | w(Wrapper(g())); | ^^^^^^^^^^^^^^^ function called here @@ -47,7 +47,7 @@ LL | w(Wrapper(g())); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:90:9 + --> $DIR/simd-abi-checks-avx.rs:89:9 | LL | some_extern(); | ^^^^^^^^^^^^^ function called here @@ -55,7 +55,7 @@ LL | some_extern(); = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:25:1 + --> $DIR/simd-abi-checks-avx.rs:24:1 | LL | unsafe extern "C" fn g() -> __m256 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -63,7 +63,7 @@ LL | unsafe extern "C" fn g() -> __m256 { = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:20:1 + --> $DIR/simd-abi-checks-avx.rs:19:1 | LL | unsafe extern "C" fn f(_: __m256) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -71,7 +71,7 @@ LL | unsafe extern "C" fn f(_: __m256) { = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:15:1 + --> $DIR/simd-abi-checks-avx.rs:14:1 | LL | unsafe extern "C" fn w(_: Wrapper) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -79,7 +79,7 @@ LL | unsafe extern "C" fn w(_: Wrapper) { = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:54:8 + --> $DIR/simd-abi-checks-avx.rs:53:8 | LL | || g() | ^^^ function called here @@ -87,7 +87,7 @@ LL | || g() = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) note: the above error was encountered while instantiating `fn in_closure::{closure#0}` - --> $DIR/simd-abi-checks-avx.rs:82:9 + --> $DIR/simd-abi-checks-avx.rs:81:9 | LL | in_closure()(); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/x86_64/evex512-implicit-feature.rs b/tests/ui/asm/x86_64/evex512-implicit-feature.rs index ea2acd424e2c..ec5da7c7fa48 100644 --- a/tests/ui/asm/x86_64/evex512-implicit-feature.rs +++ b/tests/ui/asm/x86_64/evex512-implicit-feature.rs @@ -2,7 +2,6 @@ //@ only-x86_64 //@ compile-flags: --crate-type=lib -C target-cpu=skylake -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] use std::arch::x86_64::*; diff --git a/tests/ui/asm/x86_64/target-feature-attr.rs b/tests/ui/asm/x86_64/target-feature-attr.rs index 6bb277ac1659..2193117caeb6 100644 --- a/tests/ui/asm/x86_64/target-feature-attr.rs +++ b/tests/ui/asm/x86_64/target-feature-attr.rs @@ -2,8 +2,6 @@ // Set the base cpu explicitly, in case the default has been changed. //@ compile-flags: -C target-cpu=x86-64 -#![feature(avx512_target_feature)] - use std::arch::asm; #[target_feature(enable = "avx")] diff --git a/tests/ui/asm/x86_64/target-feature-attr.stderr b/tests/ui/asm/x86_64/target-feature-attr.stderr index 0cd571ac8cce..c852726ee7ff 100644 --- a/tests/ui/asm/x86_64/target-feature-attr.stderr +++ b/tests/ui/asm/x86_64/target-feature-attr.stderr @@ -1,23 +1,23 @@ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:40 + --> $DIR/target-feature-attr.rs:18:40 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:55 + --> $DIR/target-feature-attr.rs:18:55 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:70 + --> $DIR/target-feature-attr.rs:18:70 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^^^^^^ error: register class `kreg` requires at least one of the following target features: avx512bw, avx512f - --> $DIR/target-feature-attr.rs:35:23 + --> $DIR/target-feature-attr.rs:33:23 | LL | asm!("/* {0} */", in(kreg) x); | ^^^^^^^^^^ diff --git a/tests/ui/simd/target-feature-mixup.rs b/tests/ui/simd/target-feature-mixup.rs index 2786251c7951..77f186152487 100644 --- a/tests/ui/simd/target-feature-mixup.rs +++ b/tests/ui/simd/target-feature-mixup.rs @@ -7,7 +7,6 @@ //@ ignore-fuchsia must translate zircon signal to SIGILL, FIXME (#58590) #![feature(repr_simd, target_feature, cfg_target_feature)] -#![feature(avx512_target_feature)] use std::process::{Command, ExitStatus}; use std::env; From 48093fd6959835408dc15d878ad7ae0d9c469a42 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 18 May 2025 08:10:09 +0200 Subject: [PATCH 235/245] attempt to make doctests work properly with old and new cargo --- src/tools/miri/cargo-miri/src/phases.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 171e157789d7..4857f62cd3a1 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -176,8 +176,11 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // Set `--target-dir` to `miri` inside the original target directory. let target_dir = get_target_dir(&metadata); cmd.arg("--target-dir").arg(target_dir); - // Enable cross-target doctests (for consistency between different cargo versions). - cmd.arg("-Zdoctest-xcompile"); + // Only when running in x.py (where we are running with beta cargo): set `RUSTC_STAGE`. + // Will have to be removed on next bootstrap bump. tag: cfg(bootstrap). + if env::var_os("RUSTC_STAGE").is_some() { + cmd.arg("-Zdoctest-xcompile"); + } // *After* we set all the flags that need setting, forward everything else. Make sure to skip // `--target-dir` (which would otherwise be set twice). From de8e305ba8feae17e5bc1281647865dc3f1deadf Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 29 Apr 2025 22:43:46 +0200 Subject: [PATCH 236/245] a new parser generating the exact same error messages Co-authored-by: Travis Cross --- compiler/rustc_builtin_macros/src/asm.rs | 390 ++++++++++++++--------- 1 file changed, 233 insertions(+), 157 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 3afa2d3dd8ee..be6f6b601369 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -4,7 +4,7 @@ use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AsmMacro, token}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; -use rustc_errors::PResult; +use rustc_errors::{DiagCtxtHandle, PResult}; use rustc_expand::base::*; use rustc_index::bit_set::GrowableBitSet; use rustc_parse::exp; @@ -18,6 +18,21 @@ use {rustc_ast as ast, rustc_parse_format as parse}; use crate::errors; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; +/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise +/// not validated at all. +pub struct RawAsmArg { + pub kind: RawAsmArgKind, + pub span: Span, +} + +pub enum RawAsmArgKind { + Template(P), + Operand(Option, ast::InlineAsmOperand), + Options(Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>), + ClobberAbi(Vec<(Symbol, Span)>), +} + +/// Validated assembly arguments, ready for macro expansion. pub struct AsmArgs { pub templates: Vec>, pub operands: Vec<(ast::InlineAsmOperand, Span)>, @@ -59,16 +74,6 @@ fn eat_operand_keyword<'a>( } } -fn parse_args<'a>( - ecx: &ExtCtxt<'a>, - sp: Span, - tts: TokenStream, - asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { - let mut p = ecx.new_parser_from_tts(tts); - parse_asm_args(&mut p, sp, asm_macro) -} - fn parse_asm_operand<'a>( p: &mut Parser<'a>, asm_macro: AsmMacro, @@ -139,31 +144,28 @@ fn parse_asm_operand<'a>( })) } -// Primarily public for rustfmt consumption. -// Internal consumers should continue to leverage `expand_asm`/`expand__global_asm` -pub fn parse_asm_args<'a>( +// Public for rustfmt. +pub fn parse_raw_asm_args<'a>( p: &mut Parser<'a>, sp: Span, asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { +) -> PResult<'a, Vec> { let dcx = p.dcx(); if p.token == token::Eof { return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp })); } + let mut args = Vec::new(); + let first_template = p.parse_expr()?; - let mut args = AsmArgs { - templates: vec![first_template], - operands: vec![], - named_args: Default::default(), - reg_args: Default::default(), - clobber_abis: Vec::new(), - options: ast::InlineAsmOptions::empty(), - options_spans: vec![], - }; + args.push(RawAsmArg { + span: first_template.span, + kind: RawAsmArgKind::Template(first_template), + }); let mut allow_templates = true; + while p.token != token::Eof { if !p.eat(exp!(Comma)) { if allow_templates { @@ -174,27 +176,39 @@ pub fn parse_asm_args<'a>( return Err(p.expect(exp!(Comma)).err().unwrap()); } } + + // Accept trailing commas. if p.token == token::Eof { break; - } // accept trailing commas - - // Parse clobber_abi - if p.eat_keyword(exp!(ClobberAbi)) { - parse_clobber_abi(p, &mut args)?; - allow_templates = false; - continue; - } - - // Parse options - if p.eat_keyword(exp!(Options)) { - parse_options(p, &mut args, asm_macro)?; - allow_templates = false; - continue; } let span_start = p.token.span; - // Parse operand names + // Parse `clobber_abi`. + if p.eat_keyword(exp!(ClobberAbi)) { + allow_templates = false; + + args.push(RawAsmArg { + kind: RawAsmArgKind::ClobberAbi(parse_clobber_abi(p)?), + span: span_start.to(p.prev_token.span), + }); + + continue; + } + + // Parse `options`. + if p.eat_keyword(exp!(Options)) { + allow_templates = false; + + args.push(RawAsmArg { + kind: RawAsmArgKind::Options(parse_options(p, asm_macro)?), + span: span_start.to(p.prev_token.span), + }); + + continue; + } + + // Parse operand names. let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) { let (ident, _) = p.token.ident().unwrap(); p.bump(); @@ -205,60 +219,172 @@ pub fn parse_asm_args<'a>( None }; - let Some(op) = parse_asm_operand(p, asm_macro)? else { - if allow_templates { - let template = p.parse_expr()?; - // If it can't possibly expand to a string, provide diagnostics here to include other - // things it could have been. - match template.kind { - ast::ExprKind::Lit(token_lit) - if matches!( - token_lit.kind, - token::LitKind::Str | token::LitKind::StrRaw(_) - ) => {} - ast::ExprKind::MacCall(..) => {} - _ => { - let err = dcx.create_err(errors::AsmExpectedOther { - span: template.span, - is_inline_asm: matches!(asm_macro, AsmMacro::Asm), - }); - return Err(err); + if let Some(op) = parse_asm_operand(p, asm_macro)? { + allow_templates = false; + + args.push(RawAsmArg { + span: span_start.to(p.prev_token.span), + kind: RawAsmArgKind::Operand(name, op), + }); + } else if allow_templates { + let template = p.parse_expr()?; + // If it can't possibly expand to a string, provide diagnostics here to include other + // things it could have been. + match template.kind { + ast::ExprKind::Lit(token_lit) + if matches!( + token_lit.kind, + token::LitKind::Str | token::LitKind::StrRaw(_) + ) => {} + ast::ExprKind::MacCall(..) => {} + _ => { + let err = dcx.create_err(errors::AsmExpectedOther { + span: template.span, + is_inline_asm: matches!(asm_macro, AsmMacro::Asm), + }); + return Err(err); + } + } + + args.push(RawAsmArg { span: template.span, kind: RawAsmArgKind::Template(template) }); + } else { + p.unexpected_any()? + } + } + + Ok(args) +} + +fn parse_args<'a>( + ecx: &ExtCtxt<'a>, + sp: Span, + tts: TokenStream, + asm_macro: AsmMacro, +) -> PResult<'a, AsmArgs> { + let mut p = ecx.new_parser_from_tts(tts); + parse_asm_args(&mut p, sp, asm_macro) +} + +// public for use in rustfmt +// FIXME: use `RawAsmArg` in the formatting code instead. +pub fn parse_asm_args<'a>( + p: &mut Parser<'a>, + sp: Span, + asm_macro: AsmMacro, +) -> PResult<'a, AsmArgs> { + let raw_args = parse_raw_asm_args(p, sp, asm_macro)?; + validate_raw_asm_args(p.dcx(), asm_macro, raw_args) +} + +pub fn validate_raw_asm_args<'a>( + dcx: DiagCtxtHandle<'a>, + asm_macro: AsmMacro, + raw_args: Vec, +) -> PResult<'a, AsmArgs> { + let mut args = AsmArgs { + templates: vec![], + operands: vec![], + named_args: Default::default(), + reg_args: Default::default(), + clobber_abis: Vec::new(), + options: ast::InlineAsmOptions::empty(), + options_spans: vec![], + }; + + let mut allow_templates = true; + + for arg in raw_args { + match arg.kind { + RawAsmArgKind::Template(template) => { + // The error for the first template is delayed. + if !allow_templates { + match template.kind { + ast::ExprKind::Lit(token_lit) + if matches!( + token_lit.kind, + token::LitKind::Str | token::LitKind::StrRaw(_) + ) => {} + ast::ExprKind::MacCall(..) => {} + _ => { + let err = dcx.create_err(errors::AsmExpectedOther { + span: template.span, + is_inline_asm: matches!(asm_macro, AsmMacro::Asm), + }); + return Err(err); + } } } + args.templates.push(template); - continue; - } else { - p.unexpected_any()? } - }; + RawAsmArgKind::Operand(name, op) => { + allow_templates = false; - let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); + let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); + let span = arg.span; + let slot = args.operands.len(); + args.operands.push((op, span)); - allow_templates = false; - let span = span_start.to(p.prev_token.span); - let slot = args.operands.len(); - args.operands.push((op, span)); + // Validate the order of named, positional & explicit register operands and + // clobber_abi/options. We do this at the end once we have the full span + // of the argument available. - // Validate the order of named, positional & explicit register operands and - // clobber_abi/options. We do this at the end once we have the full span - // of the argument available. + if explicit_reg { + if name.is_some() { + dcx.emit_err(errors::AsmExplicitRegisterName { span }); + } + args.reg_args.insert(slot); + } else if let Some(name) = name { + if let Some(&prev) = args.named_args.get(&name) { + dcx.emit_err(errors::AsmDuplicateArg { + span, + name, + prev: args.operands[prev].1, + }); + continue; + } + args.named_args.insert(name, slot); + } else if !args.named_args.is_empty() || !args.reg_args.is_empty() { + let named = args.named_args.values().map(|p| args.operands[*p].1).collect(); + let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect(); - if explicit_reg { - if name.is_some() { - dcx.emit_err(errors::AsmExplicitRegisterName { span }); + dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); + } } - args.reg_args.insert(slot); - } else if let Some(name) = name { - if let Some(&prev) = args.named_args.get(&name) { - dcx.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 }); - continue; - } - args.named_args.insert(name, slot); - } else if !args.named_args.is_empty() || !args.reg_args.is_empty() { - let named = args.named_args.values().map(|p| args.operands[*p].1).collect(); - let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect(); + RawAsmArgKind::Options(new_options) => { + allow_templates = false; - dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); + for (symbol, option, span, full_span) in new_options { + if !asm_macro.is_supported_option(option) { + /* + // Tool-only output + p.dcx().emit_err(errors::AsmUnsupportedOption { + span, + symbol, + full_span, + macro_name: asm_macro.macro_name(), + }); + */ + } else if args.options.contains(option) { + // Tool-only output. + dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); + } else { + args.options |= option; + } + } + + args.options_spans.push(arg.span); + } + RawAsmArgKind::ClobberAbi(new_abis) => { + allow_templates = false; + + match &new_abis[..] { + // This should have errored above during parsing. + [] => unreachable!(), + [(abi, _span)] => args.clobber_abis.push((*abi, arg.span)), + _ => args.clobber_abis.extend(new_abis), + } + } } } @@ -348,61 +474,14 @@ pub fn parse_asm_args<'a>( Ok(args) } -/// Report a duplicate option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the suggestion will be incorrect. -fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) { - // Tool-only output - let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span }; - p.dcx().emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); -} - -/// Report an invalid option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the suggestion will be incorrect. -fn err_unsupported_option(p: &Parser<'_>, asm_macro: AsmMacro, symbol: Symbol, span: Span) { - // Tool-only output - let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span }; - p.dcx().emit_err(errors::AsmUnsupportedOption { - span, - symbol, - full_span, - macro_name: asm_macro.macro_name(), - }); -} - -/// Try to set the provided option in the provided `AsmArgs`. -/// If it is already set, report a duplicate option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the error will not point to the correct spot. -fn try_set_option<'a>( - p: &Parser<'a>, - args: &mut AsmArgs, - asm_macro: AsmMacro, - symbol: Symbol, - option: ast::InlineAsmOptions, -) { - if !asm_macro.is_supported_option(option) { - err_unsupported_option(p, asm_macro, symbol, p.prev_token.span); - } else if args.options.contains(option) { - err_duplicate_option(p, symbol, p.prev_token.span); - } else { - args.options |= option; - } -} - fn parse_options<'a>( p: &mut Parser<'a>, - args: &mut AsmArgs, asm_macro: AsmMacro, -) -> PResult<'a, ()> { - let span_start = p.prev_token.span; - +) -> PResult<'a, Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>> { p.expect(exp!(OpenParen))?; + let mut options = Vec::new(); + while !p.eat(exp!(CloseParen)) { const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ (exp!(Pure), ast::InlineAsmOptions::PURE), @@ -418,6 +497,7 @@ fn parse_options<'a>( 'blk: { for (exp, option) in OPTIONS { + // Gives a more accurate list of expected next tokens. let kw_matched = if asm_macro.is_supported_option(option) { p.eat_keyword(exp) } else { @@ -425,30 +505,39 @@ fn parse_options<'a>( }; if kw_matched { - try_set_option(p, args, asm_macro, exp.kw, option); + let span = p.prev_token.span; + let full_span = + if p.token == token::Comma { span.to(p.token.span) } else { span }; + + if !asm_macro.is_supported_option(option) { + // Tool-only output. + p.dcx().emit_err(errors::AsmUnsupportedOption { + span, + symbol: exp.kw, + full_span, + macro_name: asm_macro.macro_name(), + }); + } + + options.push((exp.kw, option, span, full_span)); break 'blk; } } - return p.unexpected(); + return p.unexpected_any(); } - // Allow trailing commas + // Allow trailing commas. if p.eat(exp!(CloseParen)) { break; } p.expect(exp!(Comma))?; } - let new_span = span_start.to(p.prev_token.span); - args.options_spans.push(new_span); - - Ok(()) + Ok(options) } -fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> { - let span_start = p.prev_token.span; - +fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> { p.expect(exp!(OpenParen))?; if p.eat(exp!(CloseParen)) { @@ -474,20 +563,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, p.expect(exp!(Comma))?; } - let full_span = span_start.to(p.prev_token.span); - - match &new_abis[..] { - // should have errored above during parsing - [] => unreachable!(), - [(abi, _span)] => args.clobber_abis.push((*abi, full_span)), - abis => { - for (abi, span) in abis { - args.clobber_abis.push((*abi, *span)); - } - } - } - - Ok(()) + Ok(new_abis) } fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> { From 7ec06fc3b14933b1937f11ee028e2e8f245e7857 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 30 Apr 2025 01:06:38 +0200 Subject: [PATCH 237/245] attempt to have rustfmt use the new logic apparently it doesn't really use the asm parsing at present, so this may work? --- compiler/rustc_builtin_macros/src/asm.rs | 25 ++++++++--------------- src/tools/rustfmt/src/parse/macros/asm.rs | 9 +++++--- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index be6f6b601369..8298b77f7b1c 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -4,7 +4,7 @@ use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AsmMacro, token}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; -use rustc_errors::{DiagCtxtHandle, PResult}; +use rustc_errors::PResult; use rustc_expand::base::*; use rustc_index::bit_set::GrowableBitSet; use rustc_parse::exp; @@ -33,7 +33,7 @@ pub enum RawAsmArgKind { } /// Validated assembly arguments, ready for macro expansion. -pub struct AsmArgs { +struct AsmArgs { pub templates: Vec>, pub operands: Vec<(ast::InlineAsmOperand, Span)>, named_args: FxIndexMap, @@ -261,26 +261,17 @@ fn parse_args<'a>( tts: TokenStream, asm_macro: AsmMacro, ) -> PResult<'a, AsmArgs> { - let mut p = ecx.new_parser_from_tts(tts); - parse_asm_args(&mut p, sp, asm_macro) + let raw_args = parse_raw_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?; + validate_raw_asm_args(ecx, asm_macro, raw_args) } -// public for use in rustfmt -// FIXME: use `RawAsmArg` in the formatting code instead. -pub fn parse_asm_args<'a>( - p: &mut Parser<'a>, - sp: Span, - asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { - let raw_args = parse_raw_asm_args(p, sp, asm_macro)?; - validate_raw_asm_args(p.dcx(), asm_macro, raw_args) -} - -pub fn validate_raw_asm_args<'a>( - dcx: DiagCtxtHandle<'a>, +fn validate_raw_asm_args<'a>( + ecx: &ExtCtxt<'a>, asm_macro: AsmMacro, raw_args: Vec, ) -> PResult<'a, AsmArgs> { + let dcx = ecx.dcx(); + let mut args = AsmArgs { templates: vec![], operands: vec![], diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 58c8d21bd7a4..18e3386f4f10 100644 --- a/src/tools/rustfmt/src/parse/macros/asm.rs +++ b/src/tools/rustfmt/src/parse/macros/asm.rs @@ -1,11 +1,14 @@ use rustc_ast::ast; -use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args}; +use rustc_builtin_macros::asm::{RawAsmArg, parse_raw_asm_args}; use crate::rewrite::RewriteContext; #[allow(dead_code)] -pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option { +pub(crate) fn parse_asm( + context: &RewriteContext<'_>, + mac: &ast::MacCall, +) -> Option> { let ts = mac.args.tokens.clone(); let mut parser = super::build_parser(context, ts); - parse_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok() + parse_raw_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok() } From e12d6757393035aa2de55d0f1712ef7656c60589 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 29 Apr 2025 23:39:10 +0200 Subject: [PATCH 238/245] delay error for unsupported options --- compiler/rustc_builtin_macros/src/asm.rs | 16 +------ tests/ui/asm/aarch64/parse-error.rs | 2 - tests/ui/asm/aarch64/parse-error.stderr | 40 ++++++------------ tests/ui/asm/parse-error.rs | 6 +-- tests/ui/asm/parse-error.stderr | 54 +++++++++--------------- 5 files changed, 39 insertions(+), 79 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 8298b77f7b1c..d9412a3cb423 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -347,15 +347,13 @@ fn validate_raw_asm_args<'a>( for (symbol, option, span, full_span) in new_options { if !asm_macro.is_supported_option(option) { - /* - // Tool-only output - p.dcx().emit_err(errors::AsmUnsupportedOption { + // Tool-only output. + dcx.emit_err(errors::AsmUnsupportedOption { span, symbol, full_span, macro_name: asm_macro.macro_name(), }); - */ } else if args.options.contains(option) { // Tool-only output. dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); @@ -500,16 +498,6 @@ fn parse_options<'a>( let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span }; - if !asm_macro.is_supported_option(option) { - // Tool-only output. - p.dcx().emit_err(errors::AsmUnsupportedOption { - span, - symbol: exp.kw, - full_span, - macro_name: asm_macro.macro_name(), - }); - } - options.push((exp.kw, option, span, full_span)); break 'blk; } diff --git a/tests/ui/asm/aarch64/parse-error.rs b/tests/ui/asm/aarch64/parse-error.rs index aa731c35dda8..35e1d037f388 100644 --- a/tests/ui/asm/aarch64/parse-error.rs +++ b/tests/ui/asm/aarch64/parse-error.rs @@ -96,10 +96,8 @@ global_asm!("", options(FOO)); //~^ ERROR expected one of global_asm!("", options(nomem FOO)); //~^ ERROR expected one of -//~| ERROR the `nomem` option cannot be used with `global_asm!` global_asm!("", options(nomem, FOO)); //~^ ERROR expected one of -//~| ERROR the `nomem` option cannot be used with `global_asm!` global_asm!("{}", options(), const FOO); global_asm!("", clobber_abi(FOO)); //~^ ERROR expected string literal diff --git a/tests/ui/asm/aarch64/parse-error.stderr b/tests/ui/asm/aarch64/parse-error.stderr index b5e1169e5f6b..45f9e7989c2e 100644 --- a/tests/ui/asm/aarch64/parse-error.stderr +++ b/tests/ui/asm/aarch64/parse-error.stderr @@ -218,68 +218,56 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` LL | global_asm!("", options(FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:97:25 - | -LL | global_asm!("", options(nomem FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)` or `,`, found `FOO` --> $DIR/parse-error.rs:97:31 | LL | global_asm!("", options(nomem FOO)); | ^^^ expected one of `)` or `,` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:100:25 - | -LL | global_asm!("", options(nomem, FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:100:32 + --> $DIR/parse-error.rs:99:32 | LL | global_asm!("", options(nomem, FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected string literal - --> $DIR/parse-error.rs:104:29 + --> $DIR/parse-error.rs:102:29 | LL | global_asm!("", clobber_abi(FOO)); | ^^^ not a string literal error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:106:33 + --> $DIR/parse-error.rs:104:33 | LL | global_asm!("", clobber_abi("C" FOO)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:108:34 + --> $DIR/parse-error.rs:106:34 | LL | global_asm!("", clobber_abi("C", FOO)); | ^^^ not a string literal error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:110:19 + --> $DIR/parse-error.rs:108:19 | LL | global_asm!("{}", clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:112:28 + --> $DIR/parse-error.rs:110:28 | LL | global_asm!("", options(), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:114:30 + --> $DIR/parse-error.rs:112:30 | LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: duplicate argument named `a` - --> $DIR/parse-error.rs:116:35 + --> $DIR/parse-error.rs:114:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -287,7 +275,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | previously here error: argument never used - --> $DIR/parse-error.rs:116:35 + --> $DIR/parse-error.rs:114:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ^^^^^^^^^^^^^ argument never used @@ -295,19 +283,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:119:28 + --> $DIR/parse-error.rs:117:28 | LL | global_asm!("", options(), ""); | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:121:30 + --> $DIR/parse-error.rs:119:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal - --> $DIR/parse-error.rs:123:13 + --> $DIR/parse-error.rs:121:13 | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ @@ -315,7 +303,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:125:20 + --> $DIR/parse-error.rs:123:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ @@ -418,6 +406,6 @@ LL - let mut bar = 0; LL + const bar: /* Type */ = 0; | -error: aborting due to 59 previous errors +error: aborting due to 57 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/asm/parse-error.rs b/tests/ui/asm/parse-error.rs index 4d7b522f5fc5..d135ccae1280 100644 --- a/tests/ui/asm/parse-error.rs +++ b/tests/ui/asm/parse-error.rs @@ -113,11 +113,9 @@ global_asm!("", options(FOO)); global_asm!("", options(FOO,)); //~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` global_asm!("", options(nomem FOO)); -//~^ ERROR the `nomem` option cannot be used with `global_asm!` -//~| ERROR expected one of `)` or `,`, found `FOO` +//~^ ERROR expected one of `)` or `,`, found `FOO` global_asm!("", options(nomem, FOO)); -//~^ ERROR the `nomem` option cannot be used with `global_asm!` -//~| ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` +//~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` global_asm!("{}", options(), const FOO); global_asm!("", clobber_abi(FOO)); //~^ ERROR expected string literal diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index 74647372a355..0bba1fd8d9b6 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -270,74 +270,62 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` LL | global_asm!("", options(FOO,)); | ^^^ expected one of `)`, `att_syntax`, or `raw` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:115:25 - | -LL | global_asm!("", options(nomem FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)` or `,`, found `FOO` --> $DIR/parse-error.rs:115:31 | LL | global_asm!("", options(nomem FOO)); | ^^^ expected one of `)` or `,` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:118:25 - | -LL | global_asm!("", options(nomem, FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:118:32 + --> $DIR/parse-error.rs:117:32 | LL | global_asm!("", options(nomem, FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected string literal - --> $DIR/parse-error.rs:122:29 + --> $DIR/parse-error.rs:120:29 | LL | global_asm!("", clobber_abi(FOO)); | ^^^ not a string literal error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:124:33 + --> $DIR/parse-error.rs:122:33 | LL | global_asm!("", clobber_abi("C" FOO)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:126:34 + --> $DIR/parse-error.rs:124:34 | LL | global_asm!("", clobber_abi("C", FOO)); | ^^^ not a string literal error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:128:19 + --> $DIR/parse-error.rs:126:19 | LL | global_asm!("{}", clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:130:28 + --> $DIR/parse-error.rs:128:28 | LL | global_asm!("", options(), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:132:30 + --> $DIR/parse-error.rs:130:30 | LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:134:17 + --> $DIR/parse-error.rs:132:17 | LL | global_asm!("", clobber_abi("C"), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ error: duplicate argument named `a` - --> $DIR/parse-error.rs:136:35 + --> $DIR/parse-error.rs:134:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -345,7 +333,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | previously here error: argument never used - --> $DIR/parse-error.rs:136:35 + --> $DIR/parse-error.rs:134:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ^^^^^^^^^^^^^ argument never used @@ -353,19 +341,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:139:28 + --> $DIR/parse-error.rs:137:28 | LL | global_asm!("", options(), ""); | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:141:30 + --> $DIR/parse-error.rs:139:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal - --> $DIR/parse-error.rs:143:13 + --> $DIR/parse-error.rs:141:13 | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ @@ -373,7 +361,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:145:20 + --> $DIR/parse-error.rs:143:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ @@ -381,37 +369,37 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: the `in` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:148:19 + --> $DIR/parse-error.rs:146:19 | LL | global_asm!("{}", in(reg)); | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `out` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:150:19 + --> $DIR/parse-error.rs:148:19 | LL | global_asm!("{}", out(reg)); | ^^^ the `out` operand is not meaningful for global-scoped inline assembly, remove it error: the `lateout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:152:19 + --> $DIR/parse-error.rs:150:19 | LL | global_asm!("{}", lateout(reg)); | ^^^^^^^ the `lateout` operand is not meaningful for global-scoped inline assembly, remove it error: the `inout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:154:19 + --> $DIR/parse-error.rs:152:19 | LL | global_asm!("{}", inout(reg)); | ^^^^^ the `inout` operand is not meaningful for global-scoped inline assembly, remove it error: the `inlateout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:156:19 + --> $DIR/parse-error.rs:154:19 | LL | global_asm!("{}", inlateout(reg)); | ^^^^^^^^^ the `inlateout` operand is not meaningful for global-scoped inline assembly, remove it error: the `label` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:158:19 + --> $DIR/parse-error.rs:156:19 | LL | global_asm!("{}", label(reg)); | ^^^^^ the `label` operand is not meaningful for global-scoped inline assembly, remove it @@ -476,6 +464,6 @@ LL - let mut bar = 0; LL + const bar: /* Type */ = 0; | -error: aborting due to 72 previous errors +error: aborting due to 70 previous errors For more information about this error, try `rustc --explain E0435`. From 85053d1cd1e65466e6ceae2a925d7d010d7585ea Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 17 May 2025 23:17:06 +0200 Subject: [PATCH 239/245] rename to get rid of the 'raw' concept --- compiler/rustc_builtin_macros/src/asm.rs | 127 +++++++++++----------- src/tools/rustfmt/src/parse/macros/asm.rs | 9 +- 2 files changed, 67 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index d9412a3cb423..867a5b80f918 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -20,12 +20,12 @@ use crate::util::{ExprToSpannedString, expr_to_spanned_string}; /// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise /// not validated at all. -pub struct RawAsmArg { - pub kind: RawAsmArgKind, +pub struct AsmArg { + pub kind: AsmArgKind, pub span: Span, } -pub enum RawAsmArgKind { +pub enum AsmArgKind { Template(P), Operand(Option, ast::InlineAsmOperand), Options(Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>), @@ -33,7 +33,7 @@ pub enum RawAsmArgKind { } /// Validated assembly arguments, ready for macro expansion. -struct AsmArgs { +struct ValidatedAsmArgs { pub templates: Vec>, pub operands: Vec<(ast::InlineAsmOperand, Span)>, named_args: FxIndexMap, @@ -145,11 +145,11 @@ fn parse_asm_operand<'a>( } // Public for rustfmt. -pub fn parse_raw_asm_args<'a>( +pub fn parse_asm_args<'a>( p: &mut Parser<'a>, sp: Span, asm_macro: AsmMacro, -) -> PResult<'a, Vec> { +) -> PResult<'a, Vec> { let dcx = p.dcx(); if p.token == token::Eof { @@ -159,10 +159,7 @@ pub fn parse_raw_asm_args<'a>( let mut args = Vec::new(); let first_template = p.parse_expr()?; - args.push(RawAsmArg { - span: first_template.span, - kind: RawAsmArgKind::Template(first_template), - }); + args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) }); let mut allow_templates = true; @@ -188,8 +185,8 @@ pub fn parse_raw_asm_args<'a>( if p.eat_keyword(exp!(ClobberAbi)) { allow_templates = false; - args.push(RawAsmArg { - kind: RawAsmArgKind::ClobberAbi(parse_clobber_abi(p)?), + args.push(AsmArg { + kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?), span: span_start.to(p.prev_token.span), }); @@ -200,8 +197,8 @@ pub fn parse_raw_asm_args<'a>( if p.eat_keyword(exp!(Options)) { allow_templates = false; - args.push(RawAsmArg { - kind: RawAsmArgKind::Options(parse_options(p, asm_macro)?), + args.push(AsmArg { + kind: AsmArgKind::Options(parse_options(p, asm_macro)?), span: span_start.to(p.prev_token.span), }); @@ -222,9 +219,9 @@ pub fn parse_raw_asm_args<'a>( if let Some(op) = parse_asm_operand(p, asm_macro)? { allow_templates = false; - args.push(RawAsmArg { + args.push(AsmArg { span: span_start.to(p.prev_token.span), - kind: RawAsmArgKind::Operand(name, op), + kind: AsmArgKind::Operand(name, op), }); } else if allow_templates { let template = p.parse_expr()?; @@ -246,7 +243,7 @@ pub fn parse_raw_asm_args<'a>( } } - args.push(RawAsmArg { span: template.span, kind: RawAsmArgKind::Template(template) }); + args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) }); } else { p.unexpected_any()? } @@ -260,19 +257,19 @@ fn parse_args<'a>( sp: Span, tts: TokenStream, asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { - let raw_args = parse_raw_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?; - validate_raw_asm_args(ecx, asm_macro, raw_args) +) -> PResult<'a, ValidatedAsmArgs> { + let args = parse_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?; + validate_asm_args(ecx, asm_macro, args) } -fn validate_raw_asm_args<'a>( +fn validate_asm_args<'a>( ecx: &ExtCtxt<'a>, asm_macro: AsmMacro, - raw_args: Vec, -) -> PResult<'a, AsmArgs> { + args: Vec, +) -> PResult<'a, ValidatedAsmArgs> { let dcx = ecx.dcx(); - let mut args = AsmArgs { + let mut validated = ValidatedAsmArgs { templates: vec![], operands: vec![], named_args: Default::default(), @@ -284,9 +281,9 @@ fn validate_raw_asm_args<'a>( let mut allow_templates = true; - for arg in raw_args { + for arg in args { match arg.kind { - RawAsmArgKind::Template(template) => { + AsmArgKind::Template(template) => { // The error for the first template is delayed. if !allow_templates { match template.kind { @@ -306,15 +303,15 @@ fn validate_raw_asm_args<'a>( } } - args.templates.push(template); + validated.templates.push(template); } - RawAsmArgKind::Operand(name, op) => { + AsmArgKind::Operand(name, op) => { allow_templates = false; let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); let span = arg.span; - let slot = args.operands.len(); - args.operands.push((op, span)); + let slot = validated.operands.len(); + validated.operands.push((op, span)); // Validate the order of named, positional & explicit register operands and // clobber_abi/options. We do this at the end once we have the full span @@ -324,25 +321,27 @@ fn validate_raw_asm_args<'a>( if name.is_some() { dcx.emit_err(errors::AsmExplicitRegisterName { span }); } - args.reg_args.insert(slot); + validated.reg_args.insert(slot); } else if let Some(name) = name { - if let Some(&prev) = args.named_args.get(&name) { + if let Some(&prev) = validated.named_args.get(&name) { dcx.emit_err(errors::AsmDuplicateArg { span, name, - prev: args.operands[prev].1, + prev: validated.operands[prev].1, }); continue; } - args.named_args.insert(name, slot); - } else if !args.named_args.is_empty() || !args.reg_args.is_empty() { - let named = args.named_args.values().map(|p| args.operands[*p].1).collect(); - let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect(); + validated.named_args.insert(name, slot); + } else if !validated.named_args.is_empty() || !validated.reg_args.is_empty() { + let named = + validated.named_args.values().map(|p| validated.operands[*p].1).collect(); + let explicit = + validated.reg_args.iter().map(|p| validated.operands[p].1).collect(); dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); } } - RawAsmArgKind::Options(new_options) => { + AsmArgKind::Options(new_options) => { allow_templates = false; for (symbol, option, span, full_span) in new_options { @@ -354,45 +353,47 @@ fn validate_raw_asm_args<'a>( full_span, macro_name: asm_macro.macro_name(), }); - } else if args.options.contains(option) { + } else if validated.options.contains(option) { // Tool-only output. dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); } else { - args.options |= option; + validated.options |= option; } } - args.options_spans.push(arg.span); + validated.options_spans.push(arg.span); } - RawAsmArgKind::ClobberAbi(new_abis) => { + AsmArgKind::ClobberAbi(new_abis) => { allow_templates = false; match &new_abis[..] { // This should have errored above during parsing. [] => unreachable!(), - [(abi, _span)] => args.clobber_abis.push((*abi, arg.span)), - _ => args.clobber_abis.extend(new_abis), + [(abi, _span)] => validated.clobber_abis.push((*abi, arg.span)), + _ => validated.clobber_abis.extend(new_abis), } } } } - if args.options.contains(ast::InlineAsmOptions::NOMEM) - && args.options.contains(ast::InlineAsmOptions::READONLY) + if validated.options.contains(ast::InlineAsmOptions::NOMEM) + && validated.options.contains(ast::InlineAsmOptions::READONLY) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" }); } - if args.options.contains(ast::InlineAsmOptions::PURE) - && args.options.contains(ast::InlineAsmOptions::NORETURN) + if validated.options.contains(ast::InlineAsmOptions::PURE) + && validated.options.contains(ast::InlineAsmOptions::NORETURN) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" }); } - if args.options.contains(ast::InlineAsmOptions::PURE) - && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) + if validated.options.contains(ast::InlineAsmOptions::PURE) + && !validated + .options + .intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmPureCombine { spans }); } @@ -400,7 +401,7 @@ fn validate_raw_asm_args<'a>( let mut outputs_sp = vec![]; let mut regclass_outputs = vec![]; let mut labels_sp = vec![]; - for (op, op_sp) in &args.operands { + for (op, op_sp) in &validated.operands { match op { ast::InlineAsmOperand::Out { reg, expr, .. } | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => { @@ -423,10 +424,10 @@ fn validate_raw_asm_args<'a>( _ => {} } } - if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { - dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() }); + if validated.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { + dcx.emit_err(errors::AsmPureNoOutput { spans: validated.options_spans.clone() }); } - if args.options.contains(ast::InlineAsmOptions::NORETURN) + if validated.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() && labels_sp.is_empty() { @@ -434,15 +435,15 @@ fn validate_raw_asm_args<'a>( // Bail out now since this is likely to confuse MIR return Err(err); } - if args.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() { + if validated.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() { dcx.emit_err(errors::AsmMayUnwind { labels_sp }); } - if !args.clobber_abis.is_empty() { + if !validated.clobber_abis.is_empty() { match asm_macro { AsmMacro::GlobalAsm | AsmMacro::NakedAsm => { let err = dcx.create_err(errors::AsmUnsupportedClobberAbi { - spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(), + spans: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), macro_name: asm_macro.macro_name(), }); @@ -453,14 +454,14 @@ fn validate_raw_asm_args<'a>( if !regclass_outputs.is_empty() { dcx.emit_err(errors::AsmClobberNoReg { spans: regclass_outputs, - clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(), + clobbers: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), }); } } } } - Ok(args) + Ok(validated) } fn parse_options<'a>( @@ -566,7 +567,7 @@ fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> fn expand_preparsed_asm( ecx: &mut ExtCtxt<'_>, asm_macro: AsmMacro, - args: AsmArgs, + args: ValidatedAsmArgs, ) -> ExpandResult, ()> { let mut template = vec![]; // Register operands are implicitly used since they are not allowed to be diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 18e3386f4f10..1a9614bacec8 100644 --- a/src/tools/rustfmt/src/parse/macros/asm.rs +++ b/src/tools/rustfmt/src/parse/macros/asm.rs @@ -1,14 +1,11 @@ use rustc_ast::ast; -use rustc_builtin_macros::asm::{RawAsmArg, parse_raw_asm_args}; +use rustc_builtin_macros::asm::{AsmArg, parse_asm_args}; use crate::rewrite::RewriteContext; #[allow(dead_code)] -pub(crate) fn parse_asm( - context: &RewriteContext<'_>, - mac: &ast::MacCall, -) -> Option> { +pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option> { let ts = mac.args.tokens.clone(); let mut parser = super::build_parser(context, ts); - parse_raw_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok() + parse_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok() } From 26e3a5041a3d9bcba04a7b201a47b1b03ccb2b2f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 17 May 2025 23:27:33 +0200 Subject: [PATCH 240/245] add `AsmOptions` with some named fields --- compiler/rustc_builtin_macros/src/asm.rs | 46 +++++++++++++-------- compiler/rustc_builtin_macros/src/errors.rs | 4 +- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 867a5b80f918..62ee71fecc27 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -28,10 +28,19 @@ pub struct AsmArg { pub enum AsmArgKind { Template(P), Operand(Option, ast::InlineAsmOperand), - Options(Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>), + Options(Vec), ClobberAbi(Vec<(Symbol, Span)>), } +pub struct AsmOption { + pub symbol: Symbol, + pub span: Span, + // A bitset, with only the bit for this option's symbol set. + pub options: ast::InlineAsmOptions, + // Used when suggesting to remove an option. + pub span_with_comma: Span, +} + /// Validated assembly arguments, ready for macro expansion. struct ValidatedAsmArgs { pub templates: Vec>, @@ -344,20 +353,26 @@ fn validate_asm_args<'a>( AsmArgKind::Options(new_options) => { allow_templates = false; - for (symbol, option, span, full_span) in new_options { - if !asm_macro.is_supported_option(option) { + for asm_option in new_options { + let AsmOption { span, symbol, span_with_comma, options } = asm_option; + + if !asm_macro.is_supported_option(options) { // Tool-only output. dcx.emit_err(errors::AsmUnsupportedOption { span, symbol, - full_span, + span_with_comma, macro_name: asm_macro.macro_name(), }); - } else if validated.options.contains(option) { + } else if validated.options.contains(options) { // Tool-only output. - dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); + dcx.emit_err(errors::AsmOptAlreadyprovided { + span, + symbol, + span_with_comma, + }); } else { - validated.options |= option; + validated.options |= asm_option.options; } } @@ -464,13 +479,10 @@ fn validate_asm_args<'a>( Ok(validated) } -fn parse_options<'a>( - p: &mut Parser<'a>, - asm_macro: AsmMacro, -) -> PResult<'a, Vec<(Symbol, ast::InlineAsmOptions, Span, Span)>> { +fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec> { p.expect(exp!(OpenParen))?; - let mut options = Vec::new(); + let mut asm_options = Vec::new(); while !p.eat(exp!(CloseParen)) { const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ @@ -486,9 +498,9 @@ fn parse_options<'a>( ]; 'blk: { - for (exp, option) in OPTIONS { + for (exp, options) in OPTIONS { // Gives a more accurate list of expected next tokens. - let kw_matched = if asm_macro.is_supported_option(option) { + let kw_matched = if asm_macro.is_supported_option(options) { p.eat_keyword(exp) } else { p.eat_keyword_noexpect(exp.kw) @@ -496,10 +508,10 @@ fn parse_options<'a>( if kw_matched { let span = p.prev_token.span; - let full_span = + let span_with_comma = if p.token == token::Comma { span.to(p.token.span) } else { span }; - options.push((exp.kw, option, span, full_span)); + asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma }); break 'blk; } } @@ -514,7 +526,7 @@ fn parse_options<'a>( p.expect(exp!(Comma))?; } - Ok(options) + Ok(asm_options) } fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> { diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index d14ad8f40144..b28f7d312d93 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -910,7 +910,7 @@ pub(crate) struct AsmOptAlreadyprovided { pub(crate) span: Span, pub(crate) symbol: Symbol, #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub(crate) full_span: Span, + pub(crate) span_with_comma: Span, } #[derive(Diagnostic)] @@ -921,7 +921,7 @@ pub(crate) struct AsmUnsupportedOption { pub(crate) span: Span, pub(crate) symbol: Symbol, #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub(crate) full_span: Span, + pub(crate) span_with_comma: Span, pub(crate) macro_name: &'static str, } From d2e5a3d131bd5a4ac92c0e6cfd3c49b5b6d44ab6 Mon Sep 17 00:00:00 2001 From: dianqk Date: Sun, 18 May 2025 17:04:49 +0800 Subject: [PATCH 241/245] gvn: avoid creating overlapping assignments --- compiler/rustc_mir_transform/src/gvn.rs | 19 ++++++---- .../gvn_overlapping.overlapping.GVN.diff | 18 ++++++++++ tests/mir-opt/gvn_overlapping.rs | 36 +++++++++++++++++++ 3 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 tests/mir-opt/gvn_overlapping.overlapping.GVN.diff create mode 100644 tests/mir-opt/gvn_overlapping.rs diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 8b8d1efbbd2e..209e818e9e32 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -836,6 +836,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn simplify_rvalue( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { @@ -855,7 +856,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Value::Repeat(op, amount) } Rvalue::NullaryOp(op, ty) => Value::NullaryOp(op, ty), - Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location), + Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location), Rvalue::Ref(_, borrow_kind, ref mut place) => { self.simplify_place_projection(place, location); return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind))); @@ -943,6 +944,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_aggregate_to_copy( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, fields: &[VnIndex], @@ -982,12 +984,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Allow introducing places with non-constant offsets, as those are still better than // reconstructing an aggregate. - if let Some(place) = self.try_as_place(copy_from_local_value, location, true) { - if rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty { + if let Some(place) = self.try_as_place(copy_from_local_value, location, true) + && rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty + { + // Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments. + // FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections. + if lhs.as_local().is_some() { self.reused_locals.insert(place.local); *rvalue = Rvalue::Use(Operand::Copy(place)); - return Some(copy_from_local_value); } + return Some(copy_from_local_value); } None @@ -995,6 +1001,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_aggregate( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { @@ -1090,7 +1097,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let AggregateTy::Def(_, _) = ty && let Some(value) = - self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index) + self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index) { return Some(value); } @@ -1765,7 +1772,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind { self.simplify_place_projection(lhs, location); - let value = self.simplify_rvalue(rvalue, location); + let value = self.simplify_rvalue(lhs, rvalue, location); let value = if let Some(local) = lhs.as_local() && self.ssa.is_ssa(local) // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark diff --git a/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff b/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff new file mode 100644 index 000000000000..fcabcdbcfef2 --- /dev/null +++ b/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff @@ -0,0 +1,18 @@ +- // MIR for `overlapping` before GVN ++ // MIR for `overlapping` after GVN + + fn overlapping(_1: Adt) -> () { + let mut _0: (); + let mut _2: *mut Adt; + let mut _3: u32; + let mut _4: &Adt; + + bb0: { + _2 = &raw mut _1; + _4 = &(*_2); + _3 = copy (((*_4) as variant#1).0: u32); + (*_2) = Adt::Some(copy _3); + return; + } + } + diff --git a/tests/mir-opt/gvn_overlapping.rs b/tests/mir-opt/gvn_overlapping.rs new file mode 100644 index 000000000000..99113445e683 --- /dev/null +++ b/tests/mir-opt/gvn_overlapping.rs @@ -0,0 +1,36 @@ +//@ test-mir-pass: GVN + +#![feature(custom_mir, core_intrinsics)] + +// Check that we do not create overlapping assignments. + +use std::intrinsics::mir::*; + +// EMIT_MIR gvn_overlapping.overlapping.GVN.diff +#[custom_mir(dialect = "runtime")] +fn overlapping(_17: Adt) { + // CHECK-LABEL: fn overlapping( + // CHECK: let mut [[PTR:.*]]: *mut Adt; + // CHECK: (*[[PTR]]) = Adt::Some(copy {{.*}}); + mir! { + let _33: *mut Adt; + let _48: u32; + let _73: &Adt; + { + _33 = core::ptr::addr_of_mut!(_17); + _73 = &(*_33); + _48 = Field(Variant((*_73), 1), 0); + (*_33) = Adt::Some(_48); + Return() + } + } +} + +fn main() { + overlapping(Adt::Some(0)); +} + +enum Adt { + None, + Some(u32), +} From f0b8ec1d71f055cbdb741565eaddabc93bf1ae75 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 7 May 2025 06:59:30 -0700 Subject: [PATCH 242/245] name resolution for guard patterns --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_resolve/src/late.rs | 36 ++++- .../feature-gate-guard-patterns.rs | 2 - .../feature-gate-guard-patterns.stderr | 31 +--- .../name-resolution.rs | 81 +++++++++++ .../name-resolution.stderr | 133 ++++++++++++++++++ 6 files changed, 255 insertions(+), 30 deletions(-) create mode 100644 tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs create mode 100644 tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 114b9835b98c..ab48a2899a75 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -611,7 +611,7 @@ impl Pat { /// Walk top-down and call `it` in each place where a pattern occurs /// starting with the root pattern `walk` is called on. If `it` returns /// false then we will descend no further but siblings will be processed. - pub fn walk(&self, it: &mut impl FnMut(&Pat) -> bool) { + pub fn walk<'ast>(&'ast self, it: &mut impl FnMut(&'ast Pat) -> bool) { if !it(self) { return; } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index aa211a8f3c29..1b682d0cf8ae 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -799,7 +799,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r fn visit_pat(&mut self, p: &'ast Pat) { let prev = self.diag_metadata.current_pat; self.diag_metadata.current_pat = Some(p); - visit::walk_pat(self, p); + + if let PatKind::Guard(subpat, _) = &p.kind { + // We walk the guard expression in `resolve_pattern_inner`. Don't resolve it twice. + self.visit_pat(subpat); + } else { + visit::walk_pat(self, p); + } + self.diag_metadata.current_pat = prev; } fn visit_local(&mut self, local: &'ast Local) { @@ -3922,7 +3929,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { #[tracing::instrument(skip(self, bindings), level = "debug")] fn resolve_pattern_inner( &mut self, - pat: &Pat, + pat: &'ast Pat, pat_src: PatternSource, bindings: &mut PatternBindings, ) { @@ -3982,6 +3989,31 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // Prevent visiting `ps` as we've already done so above. return false; } + PatKind::Guard(ref subpat, ref guard) => { + // Add a new set of bindings to the stack to collect bindings in `subpat`. + bindings.push((PatBoundCtx::Product, Default::default())); + // Resolving `subpat` adds bindings onto the newly-pushed context. After, the + // total number of contexts on the stack should be the same as before. + let binding_ctx_stack_len = bindings.len(); + self.resolve_pattern_inner(subpat, pat_src, bindings); + assert_eq!(bindings.len(), binding_ctx_stack_len); + // These bindings, but none from the surrounding pattern, are visible in the + // guard; put them in scope and resolve `guard`. + let subpat_bindings = bindings.pop().unwrap().1; + self.with_rib(ValueNS, RibKind::Normal, |this| { + *this.innermost_rib_bindings(ValueNS) = subpat_bindings.clone(); + this.resolve_expr(guard, None); + }); + // Propagate the subpattern's bindings upwards. + // FIXME(guard_patterns): For `if let` guards, we'll also need to get the + // bindings introduced by the guard from its rib and propagate them upwards. + // This will require checking the identifiers for overlaps with `bindings`, like + // what `fresh_binding` does (ideally sharing its logic). To keep them separate + // from `subpat_bindings`, we can introduce a fresh rib for the guard. + bindings.last_mut().unwrap().1.extend(subpat_bindings); + // Prevent visiting `subpat` as we've already done so above. + return false; + } _ => {} } true diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs index 74fb5817081c..095f66eeb906 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.rs +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs @@ -22,7 +22,6 @@ fn other_guards_dont() { let ((x if guard(x)) | x) = 0; //~^ ERROR: guard patterns are experimental - //~| ERROR: cannot find value `x` if let (x if guard(x)) = 0 {} //~^ ERROR: guard patterns are experimental @@ -37,7 +36,6 @@ fn other_guards_dont() { fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} //~^ ERROR: guard patterns are experimental -//~| ERROR: cannot find value `x` fn guard(x: T) -> bool { unimplemented!() diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr index 8b85b663889f..b0bf302f3cb6 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr @@ -10,24 +10,6 @@ LL - (0 if guard(0)) => {}, LL + 0 if guard(0) => {}, | -error[E0425]: cannot find value `x` in this scope - --> $DIR/feature-gate-guard-patterns.rs:23:22 - | -LL | let ((x if guard(x)) | x) = 0; - | ^ not found in this scope - -error[E0425]: cannot find value `x` in this scope - --> $DIR/feature-gate-guard-patterns.rs:38:45 - | -LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} - | ^ - | -help: the binding `x` is available in a different scope in the same function - --> $DIR/feature-gate-guard-patterns.rs:23:11 - | -LL | let ((x if guard(x)) | x) = 0; - | ^ - error[E0658]: guard patterns are experimental --> $DIR/feature-gate-guard-patterns.rs:18:15 | @@ -51,7 +33,7 @@ LL | let ((x if guard(x)) | x) = 0; = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:27:18 + --> $DIR/feature-gate-guard-patterns.rs:26:18 | LL | if let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -62,7 +44,7 @@ LL | if let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:30:21 + --> $DIR/feature-gate-guard-patterns.rs:29:21 | LL | while let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -73,7 +55,7 @@ LL | while let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:34:21 + --> $DIR/feature-gate-guard-patterns.rs:33:21 | LL | while let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -84,7 +66,7 @@ LL | while let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:38:39 + --> $DIR/feature-gate-guard-patterns.rs:37:39 | LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} | ^^^^^^^^ @@ -94,7 +76,6 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider using match arm guards -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0425, E0658. -For more information about an error, try `rustc --explain E0425`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs new file mode 100644 index 000000000000..83ad8c76bb1c --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs @@ -0,0 +1,81 @@ +//! Test that guard patterns can see bindings already in scope and bindings introduced in their +//! subpattern, but no other bindings from the containing pattern. Also make sure bindings +//! introduced in guard patterns are visible in fn/arm/loop/etc bodies. + +#![feature(guard_patterns)] +#![expect(incomplete_features)] + +fn good_fn_item(((x if x) | x): bool) -> bool { x } + +fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} +//~^ ERROR cannot find value `x` in this scope +fn bad_fn_item_2(((x if y) | x): bool, y: bool) {} +//~^ ERROR cannot find value `y` in this scope + +fn main() { + let ((local if local) if local) = false; + + match (true, true) { + (x if local, y if good_fn_item(y)) => x && y, + (x, y if x) => x && y, + //~^ ERROR cannot find value `x` in this scope + (x if y, y) => x && y, + //~^ ERROR cannot find value `y` in this scope + }; + + match (true,) { + (x @ y if x && y,) => x && y, + (x @ (y if y),) => x && y, + (x @ (y if x),) => x && y, + //~^ ERROR cannot find value `x` in this scope + }; + + match (Ok(true),) { + ((Ok(x) | Err(x)) if good_fn_item(x),) => x, + ((Ok(x) if local) | (Err(x) if good_fn_item(x)),) => x, + ((Ok(x if x) if x) | (Err(x if x) if x) if x,) if x => x, + ((Ok(x) if y) | (Err(y) if x),) => x && y, + //~^ ERROR variable `x` is not bound in all patterns + //~| ERROR variable `y` is not bound in all patterns + //~| ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + }; + + let (_ if nonexistent) = true; + //~^ ERROR cannot find value `nonexistent` in this scope + if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + + (|(x if x), (y if y)| x && y)(true, true); + (|(x if y), (y if x)| x && y)(true, true); + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + + // FIXME(guard_patterns): mismatched bindings are not yet allowed + match Some(0) { + Some(x if x > 0) | None => {} + //~^ ERROR variable `x` is not bound in all patterns + } +} + +/// Make sure shadowing is handled properly. In particular, if a pattern shadows an identifier, +/// a guard pattern's guard should still see the original binding if the shadowing binding isn't in +/// its subpattern. +fn test_shadowing(local: bool) -> u8 { + match (0, 0) { + // The `local` binding here shadows the `bool` definition, so we get a type error. + //~v ERROR mismatched types + local if local => 0, + // The guards here should see the `bool` definition of `local`, not the new `u8` binding. + // The body should see the new binding. + (local, _ if local) => local, + (_ if local, local) => local, + } +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr new file mode 100644 index 000000000000..d76e60478a14 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr @@ -0,0 +1,133 @@ +error[E0408]: variable `y` is not bound in all patterns + --> $DIR/name-resolution.rs:37:10 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^^^^^^^^^^^^ - variable not in all patterns + | | + | pattern doesn't bind `y` + +error[E0408]: variable `x` is not bound in all patterns + --> $DIR/name-resolution.rs:37:25 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | - ^^^^^^^^^^^^^ pattern doesn't bind `x` + | | + | variable not in all patterns + +error[E0408]: variable `x` is not bound in all patterns + --> $DIR/name-resolution.rs:63:28 + | +LL | Some(x if x > 0) | None => {} + | - ^^^^ pattern doesn't bind `x` + | | + | variable not in all patterns + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:10:34 + | +LL | fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:12:25 + | +LL | fn bad_fn_item_2(((x if y) | x): bool, y: bool) {} + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:20:18 + | +LL | (x, y if x) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:22:15 + | +LL | (x if y, y) => x && y, + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:29:20 + | +LL | (x @ (y if x),) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:37:20 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:37:36 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `nonexistent` in this scope + --> $DIR/name-resolution.rs:44:15 + | +LL | let (_ if nonexistent) = true; + | ^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:46:22 + | +LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:46:33 + | +LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:49:25 + | +LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:49:36 + | +LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:52:19 + | +LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:52:30 + | +LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:57:13 + | +LL | (|(x if y), (y if x)| x && y)(true, true); + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:57:23 + | +LL | (|(x if y), (y if x)| x && y)(true, true); + | ^ help: a local variable with a similar name exists: `y` + +error[E0308]: mismatched types + --> $DIR/name-resolution.rs:75:18 + | +LL | local if local => 0, + | ^^^^^ expected `bool`, found `({integer}, {integer})` + | + = note: expected type `bool` + found tuple `({integer}, {integer})` + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0308, E0408, E0425. +For more information about an error, try `rustc --explain E0308`. From 6fc60b8b0493027cc966d4911688f0735bfb329d Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 24 Aug 2024 22:29:39 -0500 Subject: [PATCH 243/245] float: Add `f16` parsing and printing Use the existing Lemire (decimal -> float) and Dragon / Grisu algorithms (float -> decimal) to add support for `f16`. This allows updating the implementation for `Display` to the expected behavior for `Display` (currently it prints the a hex bitwise representation), matching other floats, and adds a `FromStr` implementation. In order to avoid crashes when compiling with Cranelift or on targets where f16 is not well supported, a fallback is used if `cfg(target_has_reliable_f16)` is not true. --- library/core/src/fmt/float.rs | 36 ++++++++++++++++ library/core/src/lib.rs | 1 + library/core/src/num/dec2flt/float.rs | 57 +++++++++++++++++++++++-- library/core/src/num/dec2flt/mod.rs | 16 +++++++ library/core/src/num/flt2dec/decoder.rs | 7 +++ 5 files changed, 113 insertions(+), 4 deletions(-) diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 870ad9df4fd3..556db239f249 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -20,6 +20,8 @@ macro_rules! impl_general_format { } } +#[cfg(target_has_reliable_f16)] +impl_general_format! { f16 } impl_general_format! { f32 f64 } // Don't inline this so callers don't use the stack space this function @@ -231,6 +233,13 @@ macro_rules! floating { floating! { f32 f64 } +#[cfg(target_has_reliable_f16)] +floating! { f16 } + +// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order +// to avoid ICEs. + +#[cfg(not(target_has_reliable_f16))] #[stable(feature = "rust1", since = "1.0.0")] impl Debug for f16 { #[inline] @@ -239,6 +248,33 @@ impl Debug for f16 { } } +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Display for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl LowerExp for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl UpperExp for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Debug for f128 { #[inline] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 64a7ec8906b6..54555f8beec6 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -101,6 +101,7 @@ #![feature(bstr)] #![feature(bstr_internals)] #![feature(cfg_match)] +#![feature(cfg_target_has_reliable_f16_f128)] #![feature(const_carrying_mul_add)] #![feature(const_eval_select)] #![feature(core_intrinsics)] diff --git a/library/core/src/num/dec2flt/float.rs b/library/core/src/num/dec2flt/float.rs index b8a28a675691..5bf0faf0bc91 100644 --- a/library/core/src/num/dec2flt/float.rs +++ b/library/core/src/num/dec2flt/float.rs @@ -45,7 +45,7 @@ macro_rules! int { } } -int!(u32, u64); +int!(u16, u32, u64); /// A helper trait to avoid duplicating basically all the conversion code for IEEE floats. /// @@ -189,9 +189,14 @@ pub trait RawFloat: /// Returns the mantissa, exponent and sign as integers. /// - /// That is, this returns `(m, p, s)` such that `s * m * 2^p` represents the original float. - /// For 0, the exponent will be `-(EXP_BIAS + SIG_BITS`, which is the - /// minimum subnormal power. + /// This returns `(m, p, s)` such that `s * m * 2^p` represents the original float. For 0, the + /// exponent will be `-(EXP_BIAS + SIG_BITS)`, which is the minimum subnormal power. For + /// infinity or NaN, the exponent will be `EXP_SAT - EXP_BIAS - SIG_BITS`. + /// + /// If subnormal, the mantissa will be shifted one bit to the left. Otherwise, it is returned + /// with the explicit bit set but otherwise unshifted + /// + /// `s` is only ever +/-1. fn integer_decode(self) -> (u64, i16, i8) { let bits = self.to_bits(); let sign: i8 = if bits >> (Self::BITS - 1) == Self::Int::ZERO { 1 } else { -1 }; @@ -213,6 +218,50 @@ const fn pow2_to_pow10(a: i64) -> i64 { res as i64 } +#[cfg(target_has_reliable_f16)] +impl RawFloat for f16 { + type Int = u16; + + const INFINITY: Self = Self::INFINITY; + const NEG_INFINITY: Self = Self::NEG_INFINITY; + const NAN: Self = Self::NAN; + const NEG_NAN: Self = -Self::NAN; + + const BITS: u32 = 16; + const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS; + const EXP_MASK: Self::Int = Self::EXP_MASK; + const SIG_MASK: Self::Int = Self::MAN_MASK; + + const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -22; + const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 5; + const SMALLEST_POWER_OF_TEN: i32 = -27; + + #[inline] + fn from_u64(v: u64) -> Self { + debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); + v as _ + } + + #[inline] + fn from_u64_bits(v: u64) -> Self { + Self::from_bits((v & 0xFFFF) as u16) + } + + fn pow10_fast_path(exponent: usize) -> Self { + #[allow(clippy::use_self)] + const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.]; + TABLE[exponent & 7] + } + + fn to_bits(self) -> Self::Int { + self.to_bits() + } + + fn classify(self) -> FpCategory { + self.classify() + } +} + impl RawFloat for f32 { type Int = u32; diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index d1a0e1db3131..abad7acb1046 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -171,9 +171,25 @@ macro_rules! from_str_float_impl { } }; } + +#[cfg(target_has_reliable_f16)] +from_str_float_impl!(f16); from_str_float_impl!(f32); from_str_float_impl!(f64); +// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order +// to avoid ICEs. + +#[cfg(not(target_has_reliable_f16))] +impl FromStr for f16 { + type Err = ParseFloatError; + + #[inline] + fn from_str(_src: &str) -> Result { + unimplemented!("requires target_has_reliable_f16") + } +} + /// An error which can be returned when parsing a float. /// /// This error is used as the error type for the [`FromStr`] implementation diff --git a/library/core/src/num/flt2dec/decoder.rs b/library/core/src/num/flt2dec/decoder.rs index 40b3aae24a53..bd6e2cdbafec 100644 --- a/library/core/src/num/flt2dec/decoder.rs +++ b/library/core/src/num/flt2dec/decoder.rs @@ -45,6 +45,13 @@ pub trait DecodableFloat: RawFloat + Copy { fn min_pos_norm_value() -> Self; } +#[cfg(target_has_reliable_f16)] +impl DecodableFloat for f16 { + fn min_pos_norm_value() -> Self { + f16::MIN_POSITIVE + } +} + impl DecodableFloat for f32 { fn min_pos_norm_value() -> Self { f32::MIN_POSITIVE From 977d8418696438c8cc5f21082d59cdc6d09d94bf Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 8 Mar 2025 03:39:04 +0000 Subject: [PATCH 244/245] float: Add tests for `f16` conversions to and from decimal Extend the existing tests for `f32` and `f64` with versions that include `f16`'s new printing and parsing implementations. Co-authored-by: Speedy_Lex --- .../coretests/tests/num/dec2flt/decimal.rs | 14 ++ library/coretests/tests/num/dec2flt/float.rs | 40 +++ library/coretests/tests/num/dec2flt/lemire.rs | 133 +++++++--- library/coretests/tests/num/dec2flt/mod.rs | 65 ++++- library/coretests/tests/num/dec2flt/parse.rs | 23 +- library/coretests/tests/num/flt2dec/mod.rs | 228 +++++++++++++++--- library/coretests/tests/num/flt2dec/random.rs | 60 +++++ .../tests/num/flt2dec/strategy/dragon.rs | 5 + .../tests/num/flt2dec/strategy/grisu.rs | 4 + 9 files changed, 498 insertions(+), 74 deletions(-) diff --git a/library/coretests/tests/num/dec2flt/decimal.rs b/library/coretests/tests/num/dec2flt/decimal.rs index 1fa06de692e0..f759e1dbde6c 100644 --- a/library/coretests/tests/num/dec2flt/decimal.rs +++ b/library/coretests/tests/num/dec2flt/decimal.rs @@ -7,6 +7,20 @@ const FPATHS_F32: &[FPath] = const FPATHS_F64: &[FPath] = &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))]; +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn check_fast_path_f16() { + const FPATHS_F16: &[FPath] = + &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))]; + for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F16.iter().copied() { + let dec = Decimal { exponent, mantissa, negative, many_digits }; + let actual = dec.try_fast_path::(); + + assert_eq!(actual, expected); + } +} + #[test] fn check_fast_path_f32() { for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F32.iter().copied() { diff --git a/library/coretests/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs index b5afd3e3b243..264de061be98 100644 --- a/library/coretests/tests/num/dec2flt/float.rs +++ b/library/coretests/tests/num/dec2flt/float.rs @@ -1,5 +1,24 @@ use core::num::dec2flt::float::RawFloat; +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn test_f16_integer_decode() { + assert_eq!(3.14159265359f16.integer_decode(), (1608, -9, 1)); + assert_eq!((-8573.5918555f16).integer_decode(), (1072, 3, -1)); + #[cfg(not(miri))] // miri doesn't have powf16 + assert_eq!(2f16.powf(14.0).integer_decode(), (1 << 10, 4, 1)); + assert_eq!(0f16.integer_decode(), (0, -25, 1)); + assert_eq!((-0f16).integer_decode(), (0, -25, -1)); + assert_eq!(f16::INFINITY.integer_decode(), (1 << 10, 6, 1)); + assert_eq!(f16::NEG_INFINITY.integer_decode(), (1 << 10, 6, -1)); + + // Ignore the "sign" (quiet / signalling flag) of NAN. + // It can vary between runtime operations and LLVM folding. + let (nan_m, nan_p, _nan_s) = f16::NAN.integer_decode(); + assert_eq!((nan_m, nan_p), (1536, 6)); +} + #[test] fn test_f32_integer_decode() { assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1)); @@ -34,6 +53,27 @@ fn test_f64_integer_decode() { /* Sanity checks of computed magic numbers */ +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn test_f16_consts() { + assert_eq!(::INFINITY, f16::INFINITY); + assert_eq!(::NEG_INFINITY, -f16::INFINITY); + assert_eq!(::NAN.to_bits(), f16::NAN.to_bits()); + assert_eq!(::NEG_NAN.to_bits(), (-f16::NAN).to_bits()); + assert_eq!(::SIG_BITS, 10); + assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -22); + assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 5); + assert_eq!(::MIN_EXPONENT_FAST_PATH, -4); + assert_eq!(::MAX_EXPONENT_FAST_PATH, 4); + assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 7); + assert_eq!(::EXP_MIN, -14); + assert_eq!(::EXP_SAT, 0x1f); + assert_eq!(::SMALLEST_POWER_OF_TEN, -27); + assert_eq!(::LARGEST_POWER_OF_TEN, 4); + assert_eq!(::MAX_MANTISSA_FAST_PATH, 2048); +} + #[test] fn test_f32_consts() { assert_eq!(::INFINITY, f32::INFINITY); diff --git a/library/coretests/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs index 0db80fbd5250..6d49d85170e2 100644 --- a/library/coretests/tests/num/dec2flt/lemire.rs +++ b/library/coretests/tests/num/dec2flt/lemire.rs @@ -1,6 +1,12 @@ use core::num::dec2flt::float::RawFloat; use core::num::dec2flt::lemire::compute_float; +#[cfg(target_has_reliable_f16)] +fn compute_float16(q: i64, w: u64) -> (i32, u64) { + let fp = compute_float::(q, w); + (fp.p_biased, fp.m) +} + fn compute_float32(q: i64, w: u64) -> (i32, u64) { let fp = compute_float::(q, w); (fp.p_biased, fp.m) @@ -11,23 +17,73 @@ fn compute_float64(q: i64, w: u64) -> (i32, u64) { (fp.p_biased, fp.m) } +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn compute_float_f16_rounding() { + // The maximum integer that cna be converted to a `f16` without lost precision. + let val = 1 << 11; + let scale = 10_u64.pow(10); + + // These test near-halfway cases for half-precision floats. + assert_eq!(compute_float16(0, val), (26, 0)); + assert_eq!(compute_float16(0, val + 1), (26, 0)); + assert_eq!(compute_float16(0, val + 2), (26, 1)); + assert_eq!(compute_float16(0, val + 3), (26, 2)); + assert_eq!(compute_float16(0, val + 4), (26, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 12; + assert_eq!(compute_float16(0, val2), (27, 0)); + assert_eq!(compute_float16(0, val2 + 2), (27, 0)); + assert_eq!(compute_float16(0, val2 + 4), (27, 1)); + assert_eq!(compute_float16(0, val2 + 6), (27, 2)); + assert_eq!(compute_float16(0, val2 + 8), (27, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float16(-10, val * scale), (26, 0)); + assert_eq!(compute_float16(-10, (val + 1) * scale), (26, 0)); + assert_eq!(compute_float16(-10, (val + 2) * scale), (26, 1)); + // Let's check the lines to see if anything is different in table... + assert_eq!(compute_float16(-10, (val + 3) * scale), (26, 2)); + assert_eq!(compute_float16(-10, (val + 4) * scale), (26, 2)); + + // Check the rounding point between infinity and the next representable number down + assert_eq!(compute_float16(4, 6), (f16::INFINITE_POWER - 1, 851)); + assert_eq!(compute_float16(4, 7), (f16::INFINITE_POWER, 0)); // infinity + assert_eq!(compute_float16(2, 655), (f16::INFINITE_POWER - 1, 1023)); +} + #[test] fn compute_float_f32_rounding() { - // These test near-halfway cases for single-precision floats. - assert_eq!(compute_float32(0, 16777216), (151, 0)); - assert_eq!(compute_float32(0, 16777217), (151, 0)); - assert_eq!(compute_float32(0, 16777218), (151, 1)); - assert_eq!(compute_float32(0, 16777219), (151, 2)); - assert_eq!(compute_float32(0, 16777220), (151, 2)); + // the maximum integer that cna be converted to a `f32` without lost precision. + let val = 1 << 24; + let scale = 10_u64.pow(10); - // These are examples of the above tests, with - // digits from the exponent shifted to the mantissa. - assert_eq!(compute_float32(-10, 167772160000000000), (151, 0)); - assert_eq!(compute_float32(-10, 167772170000000000), (151, 0)); - assert_eq!(compute_float32(-10, 167772180000000000), (151, 1)); + // These test near-halfway cases for single-precision floats. + assert_eq!(compute_float32(0, val), (151, 0)); + assert_eq!(compute_float32(0, val + 1), (151, 0)); + assert_eq!(compute_float32(0, val + 2), (151, 1)); + assert_eq!(compute_float32(0, val + 3), (151, 2)); + assert_eq!(compute_float32(0, val + 4), (151, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 25; + assert_eq!(compute_float32(0, val2), (152, 0)); + assert_eq!(compute_float32(0, val2 + 2), (152, 0)); + assert_eq!(compute_float32(0, val2 + 4), (152, 1)); + assert_eq!(compute_float32(0, val2 + 6), (152, 2)); + assert_eq!(compute_float32(0, val2 + 8), (152, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float32(-10, val * scale), (151, 0)); + assert_eq!(compute_float32(-10, (val + 1) * scale), (151, 0)); + assert_eq!(compute_float32(-10, (val + 2) * scale), (151, 1)); // Let's check the lines to see if anything is different in table... - assert_eq!(compute_float32(-10, 167772190000000000), (151, 2)); - assert_eq!(compute_float32(-10, 167772200000000000), (151, 2)); + assert_eq!(compute_float32(-10, (val + 3) * scale), (151, 2)); + assert_eq!(compute_float32(-10, (val + 4) * scale), (151, 2)); // Check the rounding point between infinity and the next representable number down assert_eq!(compute_float32(38, 3), (f32::INFINITE_POWER - 1, 6402534)); @@ -37,23 +93,38 @@ fn compute_float_f32_rounding() { #[test] fn compute_float_f64_rounding() { - // These test near-halfway cases for double-precision floats. - assert_eq!(compute_float64(0, 9007199254740992), (1076, 0)); - assert_eq!(compute_float64(0, 9007199254740993), (1076, 0)); - assert_eq!(compute_float64(0, 9007199254740994), (1076, 1)); - assert_eq!(compute_float64(0, 9007199254740995), (1076, 2)); - assert_eq!(compute_float64(0, 9007199254740996), (1076, 2)); - assert_eq!(compute_float64(0, 18014398509481984), (1077, 0)); - assert_eq!(compute_float64(0, 18014398509481986), (1077, 0)); - assert_eq!(compute_float64(0, 18014398509481988), (1077, 1)); - assert_eq!(compute_float64(0, 18014398509481990), (1077, 2)); - assert_eq!(compute_float64(0, 18014398509481992), (1077, 2)); + // The maximum integer that cna be converted to a `f64` without lost precision. + let val = 1 << 53; + let scale = 1000; - // These are examples of the above tests, with - // digits from the exponent shifted to the mantissa. - assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0)); - assert_eq!(compute_float64(-3, 9007199254740993000), (1076, 0)); - assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1)); - assert_eq!(compute_float64(-3, 9007199254740995000), (1076, 2)); - assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2)); + // These test near-halfway cases for double-precision floats. + assert_eq!(compute_float64(0, val), (1076, 0)); + assert_eq!(compute_float64(0, val + 1), (1076, 0)); + assert_eq!(compute_float64(0, val + 2), (1076, 1)); + assert_eq!(compute_float64(0, val + 3), (1076, 2)); + assert_eq!(compute_float64(0, val + 4), (1076, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 54; + assert_eq!(compute_float64(0, val2), (1077, 0)); + assert_eq!(compute_float64(0, val2 + 2), (1077, 0)); + assert_eq!(compute_float64(0, val2 + 4), (1077, 1)); + assert_eq!(compute_float64(0, val2 + 6), (1077, 2)); + assert_eq!(compute_float64(0, val2 + 8), (1077, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float64(-3, val * scale), (1076, 0)); + assert_eq!(compute_float64(-3, (val + 1) * scale), (1076, 0)); + assert_eq!(compute_float64(-3, (val + 2) * scale), (1076, 1)); + assert_eq!(compute_float64(-3, (val + 3) * scale), (1076, 2)); + assert_eq!(compute_float64(-3, (val + 4) * scale), (1076, 2)); + + // Check the rounding point between infinity and the next representable number down + assert_eq!(compute_float64(308, 1), (f64::INFINITE_POWER - 1, 506821272651936)); + assert_eq!(compute_float64(308, 2), (f64::INFINITE_POWER, 0)); // infinity + assert_eq!( + compute_float64(292, 17976931348623157), + (f64::INFINITE_POWER - 1, 4503599627370495) + ); } diff --git a/library/coretests/tests/num/dec2flt/mod.rs b/library/coretests/tests/num/dec2flt/mod.rs index a9025be5ca7f..b8ca220847cf 100644 --- a/library/coretests/tests/num/dec2flt/mod.rs +++ b/library/coretests/tests/num/dec2flt/mod.rs @@ -11,15 +11,23 @@ mod parse; // Requires a *polymorphic literal*, i.e., one that can serve as f64 as well as f32. macro_rules! test_literal { ($x: expr) => {{ + #[cfg(target_has_reliable_f16)] + let x16: f16 = $x; let x32: f32 = $x; let x64: f64 = $x; let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)]; + for input in inputs { - assert_eq!(input.parse(), Ok(x64)); - assert_eq!(input.parse(), Ok(x32)); + assert_eq!(input.parse(), Ok(x64), "failed f64 {input}"); + assert_eq!(input.parse(), Ok(x32), "failed f32 {input}"); + #[cfg(target_has_reliable_f16)] + assert_eq!(input.parse(), Ok(x16), "failed f16 {input}"); + let neg_input = format!("-{input}"); - assert_eq!(neg_input.parse(), Ok(-x64)); - assert_eq!(neg_input.parse(), Ok(-x32)); + assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}"); + assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}"); + #[cfg(target_has_reliable_f16)] + assert_eq!(neg_input.parse(), Ok(-x16), "failed f16 {neg_input}"); } }}; } @@ -84,48 +92,87 @@ fn fast_path_correct() { test_literal!(1.448997445238699); } +// FIXME(f16_f128): remove gates once tests work on all targets + #[test] fn lonely_dot() { + #[cfg(target_has_reliable_f16)] + assert!(".".parse::().is_err()); assert!(".".parse::().is_err()); assert!(".".parse::().is_err()); } #[test] fn exponentiated_dot() { + #[cfg(target_has_reliable_f16)] + assert!(".e0".parse::().is_err()); assert!(".e0".parse::().is_err()); assert!(".e0".parse::().is_err()); } #[test] fn lonely_sign() { - assert!("+".parse::().is_err()); - assert!("-".parse::().is_err()); + #[cfg(target_has_reliable_f16)] + assert!("+".parse::().is_err()); + assert!("-".parse::().is_err()); + assert!("+".parse::().is_err()); } #[test] fn whitespace() { + #[cfg(target_has_reliable_f16)] + assert!("1.0 ".parse::().is_err()); assert!(" 1.0".parse::().is_err()); assert!("1.0 ".parse::().is_err()); } #[test] fn nan() { + #[cfg(target_has_reliable_f16)] + { + assert!("NaN".parse::().unwrap().is_nan()); + assert!("-NaN".parse::().unwrap().is_nan()); + } + assert!("NaN".parse::().unwrap().is_nan()); + assert!("-NaN".parse::().unwrap().is_nan()); + assert!("NaN".parse::().unwrap().is_nan()); + assert!("-NaN".parse::().unwrap().is_nan()); } #[test] fn inf() { - assert_eq!("inf".parse(), Ok(f64::INFINITY)); - assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY)); + #[cfg(target_has_reliable_f16)] + { + assert_eq!("inf".parse(), Ok(f16::INFINITY)); + assert_eq!("-inf".parse(), Ok(f16::NEG_INFINITY)); + } + assert_eq!("inf".parse(), Ok(f32::INFINITY)); assert_eq!("-inf".parse(), Ok(f32::NEG_INFINITY)); + + assert_eq!("inf".parse(), Ok(f64::INFINITY)); + assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY)); } #[test] fn massive_exponent() { + #[cfg(target_has_reliable_f16)] + { + let max = i16::MAX; + assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f16)); + assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY)); + } + + let max = i32::MAX; + assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f32)); + assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY)); + let max = i64::MAX; assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY)); - assert_eq!(format!("1e-{max}000").parse(), Ok(0.0)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f64)); assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY)); } diff --git a/library/coretests/tests/num/dec2flt/parse.rs b/library/coretests/tests/num/dec2flt/parse.rs index 59be3915052d..dccb6b5528d4 100644 --- a/library/coretests/tests/num/dec2flt/parse.rs +++ b/library/coretests/tests/num/dec2flt/parse.rs @@ -10,6 +10,9 @@ fn new_dec(e: i64, m: u64) -> Decimal { fn missing_pieces() { let permutations = &[".e", "1e", "e4", "e", ".12e", "321.e", "32.12e+", "12.32e-"]; for &s in permutations { + #[cfg(target_has_reliable_f16)] + assert_eq!(dec2flt::(s), Err(pfe_invalid())); + assert_eq!(dec2flt::(s), Err(pfe_invalid())); assert_eq!(dec2flt::(s), Err(pfe_invalid())); } } @@ -17,15 +20,31 @@ fn missing_pieces() { #[test] fn invalid_chars() { let invalid = "r,?(&input) == error, "did not reject invalid {:?}", input); + + #[cfg(target_has_reliable_f16)] + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f16 did not reject invalid {input:?}", + ); + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f32 did not reject invalid {input:?}", + ); + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f64 did not reject invalid {input:?}", + ); } } } diff --git a/library/coretests/tests/num/flt2dec/mod.rs b/library/coretests/tests/num/flt2dec/mod.rs index c64bb0a30720..ce36db33d05f 100644 --- a/library/coretests/tests/num/flt2dec/mod.rs +++ b/library/coretests/tests/num/flt2dec/mod.rs @@ -16,7 +16,7 @@ mod random; pub fn decode_finite(v: T) -> Decoded { match decode(v).1 { FullDecoded::Finite(decoded) => decoded, - full_decoded => panic!("expected finite, got {full_decoded:?} instead"), + full_decoded => panic!("expected finite, got {full_decoded:?} instead for {v:?}"), } } @@ -75,6 +75,11 @@ macro_rules! try_fixed { }) } +#[cfg(target_has_reliable_f16)] +fn ldexp_f16(a: f16, b: i32) -> f16 { + ldexp_f64(a as f64, b) as f16 +} + fn ldexp_f32(a: f32, b: i32) -> f32 { ldexp_f64(a as f64, b) as f32 } @@ -176,6 +181,13 @@ trait TestableFloat: DecodableFloat + fmt::Display { fn ldexpi(f: i64, exp: isize) -> Self; } +#[cfg(target_has_reliable_f16)] +impl TestableFloat for f16 { + fn ldexpi(f: i64, exp: isize) -> Self { + f as Self * (exp as Self).exp2() + } +} + impl TestableFloat for f32 { fn ldexpi(f: i64, exp: isize) -> Self { f as Self * (exp as Self).exp2() @@ -225,6 +237,76 @@ macro_rules! check_exact_one { // // [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion // ftp://ftp.ee.lbl.gov/testbase-report.ps.Z +// or https://www.icir.org/vern/papers/testbase-report.pdf + +#[cfg(target_has_reliable_f16)] +pub fn f16_shortest_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // 0.0999145507813 + // 0.0999755859375 + // 0.100036621094 + check_shortest!(f(0.1f16) => b"1", 0); + + // 0.3330078125 + // 0.333251953125 (1/3 in the default rounding) + // 0.33349609375 + check_shortest!(f(1.0f16/3.0) => b"3333", 0); + + // 10^1 * 0.3138671875 + // 10^1 * 0.3140625 + // 10^1 * 0.3142578125 + check_shortest!(f(3.14f16) => b"314", 1); + + // 10^18 * 0.31415916243714048 + // 10^18 * 0.314159196796878848 + // 10^18 * 0.314159231156617216 + check_shortest!(f(3.1415e4f16) => b"3141", 5); + + // regression test for decoders + // 10^2 * 0.31984375 + // 10^2 * 0.32 + // 10^2 * 0.3203125 + check_shortest!(f(ldexp_f16(1.0, 5)) => b"32", 2); + + // 10^5 * 0.65472 + // 10^5 * 0.65504 + // 10^5 * 0.65536 + check_shortest!(f(f16::MAX) => b"655", 5); + + // 10^-4 * 0.60975551605224609375 + // 10^-4 * 0.6103515625 + // 10^-4 * 0.61094760894775390625 + check_shortest!(f(f16::MIN_POSITIVE) => b"6104", -4); + + // 10^-9 * 0 + // 10^-9 * 0.59604644775390625 + // 10^-8 * 0.11920928955078125 + let minf16 = ldexp_f16(1.0, -24); + check_shortest!(f(minf16) => b"6", -7); +} + +#[cfg(target_has_reliable_f16)] +pub fn f16_exact_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + let minf16 = ldexp_f16(1.0, -24); + + check_exact!(f(0.1f16) => b"999755859375 ", -1); + check_exact!(f(0.5f16) => b"5 ", 0); + check_exact!(f(1.0f16/3.0) => b"333251953125 ", 0); + check_exact!(f(3.141f16) => b"3140625 ", 1); + check_exact!(f(3.141e4f16) => b"31408 ", 5); + check_exact!(f(f16::MAX) => b"65504 ", 5); + check_exact!(f(f16::MIN_POSITIVE) => b"6103515625 ", -4); + check_exact!(f(minf16) => b"59604644775390625", -7); + + // FIXME(f16_f128): these should gain the check_exact_one tests like `f32` and `f64` have, + // but these values are not easy to generate. The algorithm from the Paxon paper [1] needs + // to be adapted to binary16. +} pub fn f32_shortest_sanity_test(mut f: F) where @@ -553,23 +635,45 @@ where assert_eq!(to_string(f, 1.9971e20, Minus, 1), "199710000000000000000.0"); assert_eq!(to_string(f, 1.9971e20, Minus, 8), "199710000000000000000.00000000"); - assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", "")); + #[cfg(target_has_reliable_f16)] + { + // f16 + assert_eq!(to_string(f, f16::MAX, Minus, 0), "65500"); + assert_eq!(to_string(f, f16::MAX, Minus, 1), "65500.0"); + assert_eq!(to_string(f, f16::MAX, Minus, 8), "65500.00000000"); - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", "")); + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 0), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 9), "0.000000060"); + } - assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 8), format!("17976931348623157{:0>292}.00000000", "")); + { + // f32 + assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", "")); - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", "")); + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", "")); + } + + { + // f64 + assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", "")); + assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", "")); + assert_eq!( + to_string(f, f64::MAX, Minus, 8), + format!("17976931348623157{:0>292}.00000000", "") + ); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", "")); + } if cfg!(miri) { // Miri is too slow @@ -655,27 +759,45 @@ where assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000"); assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23"); - assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); + #[cfg(target_has_reliable_f16)] + { + // f16 + assert_eq!(to_string(f, f16::MAX, Minus, (-2, 2), false), "6.55e4"); + assert_eq!(to_string(f, f16::MAX, Minus, (-4, 4), false), "6.55e4"); + assert_eq!(to_string(f, f16::MAX, Minus, (-5, 5), false), "65500"); - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, (-2, 2), false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, (-7, 7), false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, (-8, 8), false), "0.00000006"); + } - assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308"); - assert_eq!( - to_string(f, f64::MAX, Minus, (-308, 309), false), - format!("17976931348623157{:0>292}", "") - ); - assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308"); + { + // f32 + assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324"); - assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); + } + { + // f64 + assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308"); + assert_eq!( + to_string(f, f64::MAX, Minus, (-308, 309), false), + format!("17976931348623157{:0>292}", "") + ); + assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308"); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324"); + assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); + } assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1"); } @@ -791,6 +913,26 @@ where "9.999999999999999547481118258862586856139387236908078193664550781250000e-7" ); + #[cfg(target_has_reliable_f16)] + { + assert_eq!(to_string(f, f16::MAX, Minus, 1, false), "7e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 2, false), "6.6e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 4, false), "6.550e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 5, false), "6.5504e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 6, false), "6.55040e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 16, false), "6.550400000000000e4"); + + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 1, false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, 2, false), "6.0e-8"); + assert_eq!(to_string(f, minf16, Minus, 4, false), "5.960e-8"); + assert_eq!(to_string(f, minf16, Minus, 8, false), "5.9604645e-8"); + assert_eq!(to_string(f, minf16, Minus, 16, false), "5.960464477539062e-8"); + assert_eq!(to_string(f, minf16, Minus, 17, false), "5.9604644775390625e-8"); + assert_eq!(to_string(f, minf16, Minus, 18, false), "5.96046447753906250e-8"); + assert_eq!(to_string(f, minf16, Minus, 24, false), "5.96046447753906250000000e-8"); + } + assert_eq!(to_string(f, f32::MAX, Minus, 1, false), "3e38"); assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "3.4e38"); assert_eq!(to_string(f, f32::MAX, Minus, 4, false), "3.403e38"); @@ -1069,6 +1211,13 @@ where "0.000000999999999999999954748111825886258685613938723690807819366455078125000" ); + #[cfg(target_has_reliable_f16)] + { + assert_eq!(to_string(f, f16::MAX, Minus, 0), "65504"); + assert_eq!(to_string(f, f16::MAX, Minus, 1), "65504.0"); + assert_eq!(to_string(f, f16::MAX, Minus, 2), "65504.00"); + } + assert_eq!(to_string(f, f32::MAX, Minus, 0), "340282346638528859811704183484516925440"); assert_eq!(to_string(f, f32::MAX, Minus, 1), "340282346638528859811704183484516925440.0"); assert_eq!(to_string(f, f32::MAX, Minus, 2), "340282346638528859811704183484516925440.00"); @@ -1078,6 +1227,21 @@ where return; } + #[cfg(target_has_reliable_f16)] + { + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 0), "0"); + assert_eq!(to_string(f, minf16, Minus, 1), "0.0"); + assert_eq!(to_string(f, minf16, Minus, 2), "0.00"); + assert_eq!(to_string(f, minf16, Minus, 4), "0.0000"); + assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 10), "0.0000000596"); + assert_eq!(to_string(f, minf16, Minus, 15), "0.000000059604645"); + assert_eq!(to_string(f, minf16, Minus, 20), "0.00000005960464477539"); + assert_eq!(to_string(f, minf16, Minus, 24), "0.000000059604644775390625"); + assert_eq!(to_string(f, minf16, Minus, 32), "0.00000005960464477539062500000000"); + } + let minf32 = ldexp_f32(1.0, -149); assert_eq!(to_string(f, minf32, Minus, 0), "0"); assert_eq!(to_string(f, minf32, Minus, 1), "0.0"); diff --git a/library/coretests/tests/num/flt2dec/random.rs b/library/coretests/tests/num/flt2dec/random.rs index 586b49df7d9b..7386139aaced 100644 --- a/library/coretests/tests/num/flt2dec/random.rs +++ b/library/coretests/tests/num/flt2dec/random.rs @@ -79,6 +79,20 @@ where (npassed, nignored) } +#[cfg(target_has_reliable_f16)] +pub fn f16_random_equivalence_test(f: F, g: G, k: usize, n: usize) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + let mut rng = crate::test_rng(); + let f16_range = Uniform::new(0x0001u16, 0x7c00).unwrap(); + iterate("f16_random_equivalence_test", k, n, f, g, |_| { + let x = f16::from_bits(f16_range.sample(&mut rng)); + decode_finite(x) + }); +} + pub fn f32_random_equivalence_test(f: F, g: G, k: usize, n: usize) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, @@ -105,6 +119,24 @@ where }); } +#[cfg(target_has_reliable_f16)] +pub fn f16_exhaustive_equivalence_test(f: F, g: G, k: usize) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // Unlike the other float types, `f16` is small enough that these exhaustive tests + // can run in less than a second so we don't need to ignore it. + + // iterate from 0x0001 to 0x7bff, i.e., all finite ranges + let (npassed, nignored) = + iterate("f16_exhaustive_equivalence_test", k, 0x7bff, f, g, |i: usize| { + let x = f16::from_bits(i as u16 + 1); + decode_finite(x) + }); + assert_eq!((npassed, nignored), (29735, 2008)); +} + pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, @@ -133,6 +165,17 @@ fn shortest_random_equivalence_test() { f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); + #[cfg(target_has_reliable_f16)] + f16_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); +} + +#[test] +#[cfg_attr(miri, ignore)] // Miri is to slow +#[cfg(target_has_reliable_f16)] +fn shortest_f16_exhaustive_equivalence_test() { + // see the f32 version + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f16_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); } #[test] @@ -158,6 +201,23 @@ fn shortest_f64_hard_random_equivalence_test() { f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 100_000_000); } +#[test] +#[cfg(target_has_reliable_f16)] +fn exact_f16_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_exact as fallback; + // Miri is too slow + let n = if cfg!(miri) { 3 } else { 1_000 }; + + for k in 1..21 { + f16_random_equivalence_test( + |d, buf| format_exact_opt(d, buf, i16::MIN), + |d, buf| fallback(d, buf, i16::MIN), + k, + n, + ); + } +} + #[test] fn exact_f32_random_equivalence_test() { use core::num::flt2dec::strategy::dragon::format_exact as fallback; diff --git a/library/coretests/tests/num/flt2dec/strategy/dragon.rs b/library/coretests/tests/num/flt2dec/strategy/dragon.rs index be25fee3f6c7..43bb6024f9ce 100644 --- a/library/coretests/tests/num/flt2dec/strategy/dragon.rs +++ b/library/coretests/tests/num/flt2dec/strategy/dragon.rs @@ -18,6 +18,8 @@ fn test_mul_pow10() { fn shortest_sanity_test() { f64_shortest_sanity_test(format_shortest); f32_shortest_sanity_test(format_shortest); + #[cfg(target_has_reliable_f16)] + f16_shortest_sanity_test(format_shortest); more_shortest_sanity_test(format_shortest); } @@ -41,6 +43,9 @@ fn exact_sanity_test() { f64_exact_sanity_test(format_exact); } f32_exact_sanity_test(format_exact); + + #[cfg(target_has_reliable_f16)] + f16_exact_sanity_test(format_exact); } #[test] diff --git a/library/coretests/tests/num/flt2dec/strategy/grisu.rs b/library/coretests/tests/num/flt2dec/strategy/grisu.rs index 9b2f0453de73..117191e0c8fd 100644 --- a/library/coretests/tests/num/flt2dec/strategy/grisu.rs +++ b/library/coretests/tests/num/flt2dec/strategy/grisu.rs @@ -38,6 +38,8 @@ fn test_max_pow10_no_more_than() { fn shortest_sanity_test() { f64_shortest_sanity_test(format_shortest); f32_shortest_sanity_test(format_shortest); + #[cfg(target_has_reliable_f16)] + f16_shortest_sanity_test(format_shortest); more_shortest_sanity_test(format_shortest); } @@ -50,6 +52,8 @@ fn exact_sanity_test() { f64_exact_sanity_test(format_exact); } f32_exact_sanity_test(format_exact); + #[cfg(target_has_reliable_f16)] + f16_exact_sanity_test(format_exact); } #[test] From 250869e909e643c90582337406e943009569fc6e Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 25 Aug 2024 01:30:17 -0500 Subject: [PATCH 245/245] float: Add `f16` to `test-float-parse` This requires a fix to the subnormal test to cap the maximum allowed value within the maximum mantissa. --- src/bootstrap/src/core/build_steps/test.rs | 4 +++- src/bootstrap/src/core/build_steps/tool.rs | 6 +++++- src/etc/test-float-parse/Cargo.toml | 7 +++++++ src/etc/test-float-parse/src/gen_/subnorm.rs | 9 +++++++-- src/etc/test-float-parse/src/lib.rs | 7 +++++++ src/etc/test-float-parse/src/traits.rs | 5 ++++- 6 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index b2dc509ddca0..27791825aa0f 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3554,7 +3554,7 @@ impl Step for TestFloatParse { builder.ensure(tool::TestFloatParse { host: self.host }); // Run any unit tests in the crate - let cargo_test = tool::prepare_tool_cargo( + let mut cargo_test = tool::prepare_tool_cargo( builder, compiler, Mode::ToolStd, @@ -3564,6 +3564,7 @@ impl Step for TestFloatParse { SourceType::InTree, &[], ); + cargo_test.allow_features(tool::TestFloatParse::ALLOW_FEATURES); run_cargo_test(cargo_test, &[], &[], crate_name, bootstrap_host, builder); @@ -3578,6 +3579,7 @@ impl Step for TestFloatParse { SourceType::InTree, &[], ); + cargo_run.allow_features(tool::TestFloatParse::ALLOW_FEATURES); if !matches!(env::var("FLOAT_PARSE_TESTS_NO_SKIP_HUGE").as_deref(), Ok("1") | Ok("true")) { cargo_run.args(["--", "--skip-huge"]); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index ac568eab2e8a..678aa9b01e4a 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1259,6 +1259,10 @@ pub struct TestFloatParse { pub host: TargetSelection, } +impl TestFloatParse { + pub const ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128"; +} + impl Step for TestFloatParse { type Output = ToolBuildResult; const ONLY_HOSTS: bool = true; @@ -1280,7 +1284,7 @@ impl Step for TestFloatParse { path: "src/etc/test-float-parse", source_type: SourceType::InTree, extra_features: Vec::new(), - allow_features: "", + allow_features: Self::ALLOW_FEATURES, cargo_args: Vec::new(), artifact_kind: ToolArtifactKind::Binary, }) diff --git a/src/etc/test-float-parse/Cargo.toml b/src/etc/test-float-parse/Cargo.toml index 8a9c5322ef7b..e407e322f9e1 100644 --- a/src/etc/test-float-parse/Cargo.toml +++ b/src/etc/test-float-parse/Cargo.toml @@ -13,3 +13,10 @@ rayon = "1" [lib] name = "test_float_parse" + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # Internal features aren't marked known config by default + 'cfg(target_has_reliable_f16)', +] diff --git a/src/etc/test-float-parse/src/gen_/subnorm.rs b/src/etc/test-float-parse/src/gen_/subnorm.rs index 4fe3b90a3ddf..654f324b9b01 100644 --- a/src/etc/test-float-parse/src/gen_/subnorm.rs +++ b/src/etc/test-float-parse/src/gen_/subnorm.rs @@ -1,4 +1,3 @@ -use std::cmp::min; use std::fmt::Write; use std::ops::RangeInclusive; @@ -83,7 +82,13 @@ where } fn new() -> Self { - Self { iter: F::Int::ZERO..=min(F::Int::ONE << 22, F::MAN_BITS.try_into().unwrap()) } + let upper_lim = if F::MAN_BITS >= 22 { + F::Int::ONE << 22 + } else { + (F::Int::ONE << F::MAN_BITS) - F::Int::ONE + }; + + Self { iter: F::Int::ZERO..=upper_lim } } fn write_string(s: &mut String, ctx: Self::WriteCtx) { diff --git a/src/etc/test-float-parse/src/lib.rs b/src/etc/test-float-parse/src/lib.rs index 3c3ef5802b6a..0bd4878f9a62 100644 --- a/src/etc/test-float-parse/src/lib.rs +++ b/src/etc/test-float-parse/src/lib.rs @@ -1,3 +1,7 @@ +#![feature(f16)] +#![feature(cfg_target_has_reliable_f16_f128)] +#![expect(internal_features)] // reliable_f16_f128 + mod traits; mod ui; mod validate; @@ -114,6 +118,9 @@ pub fn register_tests(cfg: &Config) -> Vec { let mut tests = Vec::new(); // Register normal generators for all floats. + + #[cfg(target_has_reliable_f16)] + register_float::(&mut tests, cfg); register_float::(&mut tests, cfg); register_float::(&mut tests, cfg); diff --git a/src/etc/test-float-parse/src/traits.rs b/src/etc/test-float-parse/src/traits.rs index 57e702b7d091..65a8721bfa5c 100644 --- a/src/etc/test-float-parse/src/traits.rs +++ b/src/etc/test-float-parse/src/traits.rs @@ -98,7 +98,7 @@ macro_rules! impl_int { } } -impl_int!(u32, i32; u64, i64); +impl_int!(u16, i16; u32, i32; u64, i64); /// Floating point types. pub trait Float: @@ -170,6 +170,9 @@ macro_rules! impl_float { impl_float!(f32, u32; f64, u64); +#[cfg(target_has_reliable_f16)] +impl_float!(f16, u16); + /// A test generator. Should provide an iterator that produces unique patterns to parse. /// /// The iterator needs to provide a `WriteCtx` (could be anything), which is then used to