From c1de8edfdf1f2aa2e9e616c04e9e0d6c258c8ef5 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 30 Sep 2025 10:15:43 -0400 Subject: [PATCH 001/108] `incompatible_msrv`: Don't check the const version for functions referenced by name, but not called. --- clippy_lints/src/incompatible_msrv.rs | 52 ++++++++++++++++++++------- tests/ui/incompatible_msrv.rs | 10 ++++++ tests/ui/incompatible_msrv.stderr | 14 +++++++- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 89988be587588..13d408bd9434b 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -3,10 +3,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::{is_in_const_context, is_in_test}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::DefKind; use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, QPath, RustcVersion, StabilityLevel, StableSince}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::{ExpnKind, Span, sym}; @@ -83,6 +82,10 @@ pub struct IncompatibleMsrv { availability_cache: FxHashMap<(DefId, bool), Availability>, check_in_tests: bool, core_crate: Option, + + // The most recently called path. Used to skip checking the path after it's + // been checked when visiting the call expression. + called_path: Option, } impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]); @@ -98,6 +101,7 @@ impl IncompatibleMsrv { .iter() .find(|krate| tcx.crate_name(**krate) == sym::core) .copied(), + called_path: None, } } @@ -140,7 +144,14 @@ impl IncompatibleMsrv { } /// Emit lint if `def_id`, associated with `node` and `span`, is below the current MSRV. - fn emit_lint_if_under_msrv(&mut self, cx: &LateContext<'_>, def_id: DefId, node: HirId, span: Span) { + fn emit_lint_if_under_msrv( + &mut self, + cx: &LateContext<'_>, + needs_const: bool, + def_id: DefId, + node: HirId, + span: Span, + ) { if def_id.is_local() { // We don't check local items since their MSRV is supposed to always be valid. return; @@ -158,10 +169,6 @@ impl IncompatibleMsrv { return; } - let needs_const = cx.enclosing_body.is_some() - && is_in_const_context(cx) - && matches!(cx.tcx.def_kind(def_id), DefKind::AssocFn | DefKind::Fn); - if (self.check_in_tests || !is_in_test(cx.tcx, node)) && let Some(current) = self.msrv.current(cx) && let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id, needs_const) @@ -190,16 +197,35 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { match expr.kind { ExprKind::MethodCall(_, _, _, span) => { if let Some(method_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - self.emit_lint_if_under_msrv(cx, method_did, expr.hir_id, span); + self.emit_lint_if_under_msrv(cx, is_in_const_context(cx), method_did, expr.hir_id, span); } }, + ExprKind::Call(callee, _) + if let ExprKind::Path(qpath @ (QPath::Resolved(..) | QPath::TypeRelative(..))) = callee.kind => + { + self.called_path = Some(callee.hir_id); + let needs_const = is_in_const_context(cx); + let def_id = if let Some(def_id) = cx.qpath_res(&qpath, callee.hir_id).opt_def_id() { + def_id + } else if needs_const && let ty::FnDef(def_id, _) = *cx.typeck_results().expr_ty(callee).kind() { + // Edge case where a function is first assigned then called. + // We previously would have warned for the non-const MSRV, when + // checking the path, but now that it's called the const MSRV + // must also be met. + def_id + } else { + return; + }; + self.emit_lint_if_under_msrv(cx, needs_const, def_id, expr.hir_id, callee.span); + }, // Desugaring into function calls by the compiler will use `QPath::LangItem` variants. Those should // not be linted as they will not be generated in older compilers if the function is not available, // and the compiler is allowed to call unstable functions. - ExprKind::Path(qpath @ (QPath::Resolved(..) | QPath::TypeRelative(..))) => { - if let Some(path_def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id() { - self.emit_lint_if_under_msrv(cx, path_def_id, expr.hir_id, expr.span); - } + ExprKind::Path(qpath @ (QPath::Resolved(..) | QPath::TypeRelative(..))) + if let Some(path_def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id() + && self.called_path != Some(expr.hir_id) => + { + self.emit_lint_if_under_msrv(cx, false, path_def_id, expr.hir_id, expr.span); }, _ => {}, } @@ -211,7 +237,7 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { // `CStr` and `CString` have been moved around but have been available since Rust 1.0.0 && !matches!(cx.tcx.get_diagnostic_name(ty_def_id), Some(sym::cstr_type | sym::cstring_type)) { - self.emit_lint_if_under_msrv(cx, ty_def_id, hir_ty.hir_id, hir_ty.span); + self.emit_lint_if_under_msrv(cx, false, ty_def_id, hir_ty.hir_id, hir_ty.span); } } } diff --git a/tests/ui/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs index f7f21e1850d0a..3069c8139abe3 100644 --- a/tests/ui/incompatible_msrv.rs +++ b/tests/ui/incompatible_msrv.rs @@ -168,4 +168,14 @@ fn enum_variant_ok() { let _ = const { std::io::ErrorKind::InvalidFilename }; } +#[clippy::msrv = "1.38.0"] +const fn uncalled_len() { + let _ = Vec::::len; + let x = str::len; + let _ = x(""); + //~^ incompatible_msrv + let _ = "".len(); + //~^ incompatible_msrv +} + fn main() {} diff --git a/tests/ui/incompatible_msrv.stderr b/tests/ui/incompatible_msrv.stderr index e42360d296f58..3c0bb595bd5b0 100644 --- a/tests/ui/incompatible_msrv.stderr +++ b/tests/ui/incompatible_msrv.stderr @@ -110,5 +110,17 @@ error: current MSRV (Minimum Supported Rust Version) is `1.86.0` but this item i LL | let _ = const { std::io::ErrorKind::InvalidFilename }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: current MSRV (Minimum Supported Rust Version) is `1.38.0` but this item is stable in a `const` context since `1.39.0` + --> tests/ui/incompatible_msrv.rs:175:13 + | +LL | let _ = x(""); + | ^ + +error: current MSRV (Minimum Supported Rust Version) is `1.38.0` but this item is stable in a `const` context since `1.39.0` + --> tests/ui/incompatible_msrv.rs:177:16 + | +LL | let _ = "".len(); + | ^^^^^ + +error: aborting due to 19 previous errors From 13ee69ddfbe477a9b515479b61a596bcfcd47abd Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 29 May 2024 04:46:57 +0200 Subject: [PATCH 002/108] Update bundled musl to 1.2.5 --- src/ci/docker/scripts/musl-toolchain.sh | 10 +++--- src/ci/docker/scripts/musl.sh | 2 +- src/doc/rustc/src/platform-support.md | 48 ++++++++++++------------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/ci/docker/scripts/musl-toolchain.sh b/src/ci/docker/scripts/musl-toolchain.sh index bc1b30e2d31ae..c52c5ea7953c8 100644 --- a/src/ci/docker/scripts/musl-toolchain.sh +++ b/src/ci/docker/scripts/musl-toolchain.sh @@ -4,7 +4,7 @@ # # Versions of the toolchain components are configurable in `musl-cross-make/Makefile` and # musl unlike GLIBC is forward compatible so upgrading it shouldn't break old distributions. -# Right now we have: Binutils 2.31.1, GCC 9.2.0, musl 1.2.3. +# Right now we have: Binutils 2.31.1, GCC 9.2.0, musl 1.2.5. # ignore-tidy-linelength @@ -45,11 +45,11 @@ export CFLAGS="-fPIC -g1 $CFLAGS" git clone https://github.com/richfelker/musl-cross-make # -b v0.9.9 cd musl-cross-make -# A version that includes support for building musl 1.2.3 -git checkout fe915821b652a7fa37b34a596f47d8e20bc72338 +# A version that includes support for building musl 1.2.5 +git checkout e149c31c48b4f4a4c9349ddf7bc0027b90245afc -hide_output make -j$(nproc) TARGET=$TARGET MUSL_VER=1.2.3 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER -hide_output make install TARGET=$TARGET MUSL_VER=1.2.3 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER OUTPUT=$OUTPUT +hide_output make -j$(nproc) TARGET=$TARGET MUSL_VER=1.2.5 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER +hide_output make install TARGET=$TARGET MUSL_VER=1.2.5 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER OUTPUT=$OUTPUT cd - diff --git a/src/ci/docker/scripts/musl.sh b/src/ci/docker/scripts/musl.sh index 9878bec6fbe8c..7e293d748ce16 100644 --- a/src/ci/docker/scripts/musl.sh +++ b/src/ci/docker/scripts/musl.sh @@ -25,7 +25,7 @@ shift # Apparently applying `-fPIC` everywhere allows them to link successfully. export CFLAGS="-fPIC $CFLAGS" -MUSL=musl-1.2.3 +MUSL=musl-1.2.5 # may have been downloaded in a previous run if [ ! -d $MUSL ]; then diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 263ea1ddb4250..e56e933da5567 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -89,7 +89,7 @@ so Rustup may install the documentation for a similar tier 1 target instead. target | notes -------|------- [`aarch64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ARM64 MinGW (Windows 10+), LLVM ABI -[`aarch64-unknown-linux-musl`](platform-support/aarch64-unknown-linux-musl.md) | ARM64 Linux with musl 1.2.3 +[`aarch64-unknown-linux-musl`](platform-support/aarch64-unknown-linux-musl.md) | ARM64 Linux with musl 1.2.5 [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17) `arm-unknown-linux-gnueabihf` | Armv6 Linux, hardfloat (kernel 3.2+, glibc 2.17) @@ -101,14 +101,14 @@ target | notes `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2+, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2+, glibc 2.17) [`powerpc64le-unknown-linux-gnu`](platform-support/powerpc64le-unknown-linux-gnu.md) | PPC64LE Linux (kernel 3.10+, glibc 2.17) -[`powerpc64le-unknown-linux-musl`](platform-support/powerpc64le-unknown-linux-musl.md) | PPC64LE Linux (kernel 4.19+, musl 1.2.3) +[`powerpc64le-unknown-linux-musl`](platform-support/powerpc64le-unknown-linux-musl.md) | PPC64LE Linux (kernel 4.19+, musl 1.2.5) [`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20+, glibc 2.29) [`s390x-unknown-linux-gnu`](platform-support/s390x-unknown-linux-gnu.md) | S390x Linux (kernel 3.2+, glibc 2.17) [`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+) [`x86_64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | 64-bit x86 MinGW (Windows 10+), LLVM ABI [`x86_64-unknown-freebsd`](platform-support/freebsd.md) | 64-bit x86 FreeBSD [`x86_64-unknown-illumos`](platform-support/illumos.md) | illumos -`x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3 +`x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.5 [`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | x86_64 OpenHarmony [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64 [`x86_64-pc-solaris`](platform-support/solaris.md) | 64-bit x86 Solaris 11.4 @@ -153,24 +153,24 @@ target | std | notes [`aarch64-unknown-none-softfloat`](platform-support/aarch64-unknown-none.md) | * | Bare ARM64, softfloat [`aarch64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | ARM64 UEFI [`arm-linux-androideabi`](platform-support/android.md) | ✓ | Armv6 Android -`arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.3 -`arm-unknown-linux-musleabihf` | ✓ | Armv6 Linux with musl 1.2.3, hardfloat +`arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.5 +`arm-unknown-linux-musleabihf` | ✓ | Armv6 Linux with musl 1.2.5, hardfloat [`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ✓ | Arm64EC Windows MSVC [`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4+, glibc 2.23) -`armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3 +`armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.5 [`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android [`armv7-unknown-linux-gnueabi`](platform-support/armv7-unknown-linux-gnueabi.md) | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27) -`armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.3 -`armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat +`armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.5 +`armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.5, hardfloat [`armv7a-none-eabi`](platform-support/armv7a-none-eabi.md) | * | Bare Armv7-A [`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R [`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, hardfloat `i586-unknown-linux-gnu` | ✓ | 32-bit Linux (kernel 3.2+, glibc 2.17, original Pentium) [^x86_32-floats-x87] -`i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.3, original Pentium) [^x86_32-floats-x87] +`i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.5, original Pentium) [^x86_32-floats-x87] [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android ([Pentium 4 plus various extensions](https://developer.android.com/ndk/guides/abis.html#x86)) [^x86_32-floats-return-ABI] [`i686-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI] [`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI] -`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 (Pentium 4) [^x86_32-floats-return-ABI] +`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.5 (Pentium 4) [^x86_32-floats-return-ABI] [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) [^win32-msvc-alignment] [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI) @@ -180,7 +180,7 @@ target | std | notes [`riscv32imac-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAC ISA) [`riscv32imafc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAFC ISA) [`riscv32imc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMC ISA) -[`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20+, musl 1.2.3) +[`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20+, musl 1.2.5) `riscv64gc-unknown-none-elf` | * | Bare RISC-V (RV64IMAFDC ISA) `riscv64imac-unknown-none-elf` | * | Bare RISC-V (RV64IMAC ISA) `sparc64-unknown-linux-gnu` | ✓ | SPARC Linux (kernel 4.4+, glibc 2.23) @@ -312,7 +312,7 @@ target | std | host | notes `bpfel-unknown-none` | * | | BPF (little endian) `csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian) `csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) -[`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.3 +[`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.5 [`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX) [`i386-apple-ios`](platform-support/apple-ios.md) | ✓ | | 32-bit x86 iOS (Penryn) [^x86_32-floats-return-ABI] [`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86 (original Pentium) [^x86_32-floats-x87] @@ -334,17 +334,17 @@ target | std | host | notes [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux [`m68k-unknown-none-elf`](platform-support/m68k-unknown-none-elf.md) | | | Motorola 680x0 `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) -`mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.3 +`mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.5 `mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc -[`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux musl 1.2.3 +[`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux musl 1.2.5 `mips64-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 Linux, N64 ABI (kernel 4.4, glibc 2.23) -[`mips64-unknown-linux-muslabi64`](platform-support/mips64-unknown-linux-muslabi64.md) | ✓ | ✓ | MIPS64 Linux, N64 ABI, musl 1.2.3 +[`mips64-unknown-linux-muslabi64`](platform-support/mips64-unknown-linux-muslabi64.md) | ✓ | ✓ | MIPS64 Linux, N64 ABI, musl 1.2.5 `mips64el-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 (little endian) Linux, N64 ABI (kernel 4.4, glibc 2.23) -`mips64el-unknown-linux-muslabi64` | ✓ | | MIPS64 (little endian) Linux, N64 ABI, musl 1.2.3 +`mips64el-unknown-linux-muslabi64` | ✓ | | MIPS64 (little endian) Linux, N64 ABI, musl 1.2.5 `mipsel-sony-psp` | * | | MIPS (LE) Sony PlayStation Portable (PSP) [`mipsel-sony-psx`](platform-support/mipsel-sony-psx.md) | * | | MIPS (LE) Sony PlayStation 1 (PSX) [`mipsel-unknown-linux-gnu`](platform-support/mipsel-unknown-linux-gnu.md) | ✓ | ✓ | MIPS (little endian) Linux (kernel 4.4, glibc 2.23) -`mipsel-unknown-linux-musl` | ✓ | | MIPS (little endian) Linux with musl 1.2.3 +`mipsel-unknown-linux-musl` | ✓ | | MIPS (little endian) Linux with musl 1.2.5 `mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc [`mipsel-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | 32-bit MIPS (LE), requires mips32 cpu support `mipsel-unknown-none` | * | | Bare MIPS (LE) softfloat @@ -357,15 +357,15 @@ target | std | host | notes `msp430-none-elf` | * | | 16-bit MSP430 microcontrollers [`powerpc-unknown-freebsd`](platform-support/freebsd.md) | ? | | PowerPC FreeBSD [`powerpc-unknown-linux-gnuspe`](platform-support/powerpc-unknown-linux-gnuspe.md) | ✓ | | PowerPC SPE Linux -`powerpc-unknown-linux-musl` | ? | | PowerPC Linux with musl 1.2.3 -[`powerpc-unknown-linux-muslspe`](platform-support/powerpc-unknown-linux-muslspe.md) | ? | | PowerPC SPE Linux with musl 1.2.3 +`powerpc-unknown-linux-musl` | ? | | PowerPC Linux with musl 1.2.5 +[`powerpc-unknown-linux-muslspe`](platform-support/powerpc-unknown-linux-muslspe.md) | ? | | PowerPC SPE Linux with musl 1.2.5 [`powerpc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD 32-bit powerpc systems [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * | | [`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | | [`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer) [`powerpc64-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64 FreeBSD (ELFv2) -[`powerpc64-unknown-linux-musl`](platform-support/powerpc64-unknown-linux-musl.md) | ✓ | ✓ | PPC64 Linux (kernel 4.19, musl 1.2.3) +[`powerpc64-unknown-linux-musl`](platform-support/powerpc64-unknown-linux-musl.md) | ✓ | ✓ | PPC64 Linux (kernel 4.19, musl 1.2.5) [`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64 [`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`powerpc64le-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64LE FreeBSD @@ -374,7 +374,7 @@ target | std | host | notes [`riscv32em-unknown-none-elf`](platform-support/riscv32e-unknown-none-elf.md) | * | | Bare RISC-V (RV32EM ISA) [`riscv32emc-unknown-none-elf`](platform-support/riscv32e-unknown-none-elf.md) | * | | Bare RISC-V (RV32EMC ISA) `riscv32gc-unknown-linux-gnu` | ✓ | | RISC-V Linux (kernel 5.4, glibc 2.33) -`riscv32gc-unknown-linux-musl` | ? | | RISC-V Linux (kernel 5.4, musl 1.2.3 + RISCV32 support patches) +`riscv32gc-unknown-linux-musl` | ? | | RISC-V Linux (kernel 5.4, musl 1.2.5 + RISCV32 support patches) [`riscv32im-risc0-zkvm-elf`](platform-support/riscv32im-risc0-zkvm-elf.md) | ? | | RISC Zero's zero-knowledge Virtual Machine (RV32IM ISA) [`riscv32ima-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32IMA ISA) [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF @@ -395,7 +395,7 @@ target | std | host | notes [`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64 [`riscv64imac-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 64bit with NuttX [`riscv64a23-unknown-linux-gnu`](platform-support/riscv64a23-unknown-linux-gnu.md) | ✓ | ✓ | RISC-V Linux (kernel 6.8.0+, glibc 2.39) -[`s390x-unknown-linux-musl`](platform-support/s390x-unknown-linux-musl.md) | ✓ | | S390x Linux (kernel 3.2, musl 1.2.3) +[`s390x-unknown-linux-musl`](platform-support/s390x-unknown-linux-musl.md) | ✓ | | S390x Linux (kernel 3.2, musl 1.2.5) `sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux [`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | | Bare 32-bit SPARC V7+ [`sparc64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/sparc64 @@ -410,7 +410,7 @@ target | std | host | notes [`thumbv7em-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7EM with NuttX [`thumbv7em-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv7EM with NuttX, hardfloat [`thumbv7m-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7M with NuttX -`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode Armv7-A Linux with NEON, musl 1.2.3 +`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode Armv7-A Linux with NEON, musl 1.2.5 [`thumbv8m.base-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv8M Baseline with NuttX [`thumbv8m.main-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv8M Mainline with NuttX [`thumbv8m.main-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv8M Mainline with NuttX, hardfloat @@ -423,7 +423,7 @@ target | std | host | notes [`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS with default network stack (io-pkt) | [`x86_64-pc-nto-qnx710_iosock`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS with new network stack (io-sock) | [`x86_64-pc-nto-qnx800`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 8.0 RTOS | -[`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.3 +[`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.5 `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku [`x86_64-unknown-hermit`](platform-support/hermit.md) | ✓ | | x86_64 Hermit From a014a9b0ed396f1ff5b9937a8d80675f3136e0d7 Mon Sep 17 00:00:00 2001 From: Aria Desires Date: Wed, 18 Jun 2025 11:19:46 -0400 Subject: [PATCH 003/108] update all other references to musl 1.2.3 to 1.2.5 --- .../rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs | 2 +- .../rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs | 2 +- .../src/spec/targets/arm_unknown_linux_musleabihf.rs | 2 +- .../src/spec/targets/armv5te_unknown_linux_musleabi.rs | 2 +- .../src/spec/targets/armv7_unknown_linux_musleabi.rs | 2 +- .../src/spec/targets/armv7_unknown_linux_musleabihf.rs | 2 +- .../rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs | 2 +- .../rustc_target/src/spec/targets/i686_unknown_linux_musl.rs | 2 +- .../rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs | 2 +- .../src/spec/targets/mips64_unknown_linux_muslabi64.rs | 2 +- .../src/spec/targets/mips64el_unknown_linux_muslabi64.rs | 2 +- .../rustc_target/src/spec/targets/mips_unknown_linux_musl.rs | 2 +- .../rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs | 2 +- .../src/spec/targets/powerpc64_unknown_linux_musl.rs | 2 +- .../src/spec/targets/powerpc64le_unknown_linux_musl.rs | 2 +- .../rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs | 2 +- .../src/spec/targets/riscv32gc_unknown_linux_musl.rs | 2 +- .../src/spec/targets/riscv64gc_unknown_linux_musl.rs | 2 +- .../rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs | 2 +- .../src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs | 2 +- .../rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs | 2 +- .../rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs | 2 +- src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs index 478726fbef699..158b38e514016 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs @@ -20,7 +20,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "aarch64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("ARM64 Linux with musl 1.2.3".into()), + description: Some("ARM64 Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(true), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs index 3919a5e0771b7..d05f4c79b01fe 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs @@ -4,7 +4,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "arm-unknown-linux-musleabi".into(), metadata: TargetMetadata { - description: Some("Armv6 Linux with musl 1.2.3".into()), + description: Some("Armv6 Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs index ca52e5b3ca6c7..9ce752efd6aa2 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs @@ -4,7 +4,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "arm-unknown-linux-musleabihf".into(), metadata: TargetMetadata { - description: Some("Armv6 Linux with musl 1.2.3, hardfloat".into()), + description: Some("Armv6 Linux with musl 1.2.5, hardfloat".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs index e675739629b59..0240450b61d30 100644 --- a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs @@ -4,7 +4,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "armv5te-unknown-linux-musleabi".into(), metadata: TargetMetadata { - description: Some("Armv5TE Linux with musl 1.2.3".into()), + description: Some("Armv5TE Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs index 42fbf6f486197..dd8a97bf77f65 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "armv7-unknown-linux-musleabi".into(), metadata: TargetMetadata { - description: Some("Armv7-A Linux with musl 1.2.3".into()), + description: Some("Armv7-A Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs index a3ac0223c84bf..c3b7ac5f994c2 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs @@ -6,7 +6,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "armv7-unknown-linux-musleabihf".into(), metadata: TargetMetadata { - description: Some("Armv7-A Linux with musl 1.2.3, hardfloat".into()), + description: Some("Armv7-A Linux with musl 1.2.5, hardfloat".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs index 1abf0537cda77..74a7eab1e0c48 100644 --- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "hexagon-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("Hexagon Linux with musl 1.2.3".into()), + description: Some("Hexagon Linux with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs index 47a7eb3d597b4..b6b85f5236d80 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs @@ -31,7 +31,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "i686-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("32-bit Linux with musl 1.2.3".into()), + description: Some("32-bit Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs index 508abc0101841..eb1148d5e8fa1 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("MIPS64 for OpenWrt Linux musl 1.2.3".into()), + description: Some("MIPS64 for OpenWrt Linux musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs index 94ecd3590a93a..e54628acb1ad2 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs @@ -11,7 +11,7 @@ pub(crate) fn target() -> Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("MIPS64 Linux, N64 ABI, musl 1.2.3".into()), + description: Some("MIPS64 Linux, N64 ABI, musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs index 38c3c7dfaa1bb..a256733734f7f 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs @@ -9,7 +9,7 @@ pub(crate) fn target() -> Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64el-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("MIPS64 Linux, N64 ABI, musl 1.2.3".into()), + description: Some("MIPS64 Linux, N64 ABI, musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs index 82f2fda7fff0a..2d258d7e438ff 100644 --- a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "mips-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("MIPS Linux with musl 1.2.3".into()), + description: Some("MIPS Linux with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs index d008bb55189be..f4fb6be332166 100644 --- a/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_musl.rs @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "mipsel-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("MIPS (little endian) Linux with musl 1.2.3".into()), + description: Some("MIPS (little endian) Linux with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs index 482b6790dadcd..8fb991d0bd1a7 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "powerpc64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("64-bit PowerPC Linux with musl 1.2.3".into()), + description: Some("64-bit PowerPC Linux with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs index 26ee6a68c6a8b..0f8b78fa7307d 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "powerpc64le-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("64-bit PowerPC Linux with musl 1.2.3, Little Endian".into()), + description: Some("64-bit PowerPC Linux with musl 1.2.5, Little Endian".into()), tier: Some(2), host_tools: Some(true), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs index f39142d01018b..f5c7cb061988a 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "powerpc-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("PowerPC Linux with musl 1.2.3".into()), + description: Some("PowerPC Linux with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs index eb592cca1c81d..7183f0a866280 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target: "riscv32-unknown-linux-musl".into(), metadata: TargetMetadata { description: Some( - "RISC-V Linux (kernel 5.4, musl 1.2.3 + RISCV32 support patches".into(), + "RISC-V Linux (kernel 5.4, musl 1.2.5 + RISCV32 support patches".into(), ), tier: Some(3), host_tools: Some(false), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs index 70c19952af063..83fd4e2cfa9ee 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs @@ -6,7 +6,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "riscv64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("RISC-V Linux (kernel 4.20, musl 1.2.3)".into()), + description: Some("RISC-V Linux (kernel 4.20, musl 1.2.5)".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs index 0cdbb626739d0..509105afedcd2 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "s390x-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("S390x Linux (kernel 3.2, musl 1.2.3)".into()), + description: Some("S390x Linux (kernel 3.2, musl 1.2.5)".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs index e026595439f48..d58339bc38c29 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "armv7-unknown-linux-musleabihf".into(), metadata: TargetMetadata { - description: Some("Thumb2-mode ARMv7-A Linux with NEON, musl 1.2.3".into()), + description: Some("Thumb2-mode ARMv7-A Linux with NEON, musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs b/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs index a5723341fe64c..f5bc3ab707db6 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs @@ -6,7 +6,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "x86_64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("64-bit Unikraft with musl 1.2.3".into()), + description: Some("64-bit Unikraft with musl 1.2.5".into()), tier: Some(3), host_tools: Some(false), std: Some(true), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs index cc5f88862400e..3cb2a962a5624 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs @@ -22,7 +22,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "x86_64-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some("64-bit Linux with musl 1.2.3".into()), + description: Some("64-bit Linux with musl 1.2.5".into()), tier: Some(2), host_tools: Some(true), std: Some(true), diff --git a/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md index c604b487c2c26..b8bee11055fe5 100644 --- a/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md +++ b/src/doc/rustc/src/platform-support/s390x-unknown-linux-musl.md @@ -13,7 +13,7 @@ IBM z/Architecture (s390x) targets (including IBM Z and LinuxONE) running Linux. This target requires: * Linux Kernel version 3.2 or later -* musl 1.2.3 or later +* musl 1.2.5 or later Code generated by the target uses the z/Architecture ISA assuming a minimum architecture level of z10 (Eighth Edition of the z/Architecture Principles From 52513ab435721229c779e64bc6f0f7e6d7814d6a Mon Sep 17 00:00:00 2001 From: Aria Desires Date: Wed, 1 Oct 2025 21:42:18 -0400 Subject: [PATCH 004/108] bump powerpc64le-unknown-linux-musl's musl to 1.2.5 --- .../powerpc64le-unknown-linux-musl.defconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig index c6cde30b2a4e5..d8fa8ec4d5e82 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig @@ -11,6 +11,5 @@ CT_ARCH_ARCH="powerpc64le" CT_KERNEL_LINUX=y CT_LINUX_V_4_19=y CT_LIBC_MUSL=y -CT_MUSL_V_1_2_3=y CT_CC_LANG_CXX=y CT_GETTEXT_NEEDED=y From d86095413ac62272eadcf063b6c4a9e77283be2d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 4 Sep 2025 18:45:32 -0500 Subject: [PATCH 005/108] Remove boxes from ast Pat lists --- clippy_lints/src/unnested_or_patterns.rs | 38 ++++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index f3410c98973f5..5dd67e35eb18a 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -223,7 +223,7 @@ macro_rules! always_pat { /// Focus on `focus_idx` in `alternatives`, /// attempting to extend it with elements of the same constructor `C` /// in `alternatives[focus_idx + 1..]`. -fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: usize) -> bool { +fn transform_with_focus_on_idx(alternatives: &mut ThinVec, focus_idx: usize) -> bool { // Extract the kind; we'll need to make some changes in it. let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, Wild); // We'll focus on `alternatives[focus_idx]`, @@ -251,20 +251,20 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: Box(target) => extend_with_matching( target, start, alternatives, |k| matches!(k, Box(_)), - |k| always_pat!(k, Box(p) => p), + |k| always_pat!(k, Box(p) => *p), ), // Transform `&mut x | ... | &mut y` into `&mut (x | y)`. Ref(target, Mutability::Mut) => extend_with_matching( target, start, alternatives, |k| matches!(k, Ref(_, Mutability::Mut)), - |k| always_pat!(k, Ref(p, _) => p), + |k| always_pat!(k, Ref(p, _) => *p), ), // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`. Ident(b1, i1, Some(target)) => extend_with_matching( target, start, alternatives, // Binding names must match. |k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)), - |k| always_pat!(k, Ident(_, _, Some(p)) => p), + |k| always_pat!(k, Ident(_, _, Some(p)) => *p), ), // Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`. Slice(ps1) => extend_with_matching_product( @@ -309,7 +309,7 @@ fn extend_with_struct_pat( fps1: &mut [ast::PatField], rest1: ast::PatFieldsRest, start: usize, - alternatives: &mut ThinVec>, + alternatives: &mut ThinVec, ) -> bool { (0..fps1.len()).any(|idx| { let pos_in_2 = Cell::new(None); // The element `k`. @@ -339,7 +339,7 @@ fn extend_with_struct_pat( })) }, // Extract `p2_k`. - |k| always_pat!(k, Struct(_, _, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat), + |k| always_pat!(k, Struct(_, _, mut fps, _) => *fps.swap_remove(pos_in_2.take().unwrap()).pat), ); extend_with_tail_or(&mut fps1[idx].pat, tail_or) }) @@ -351,11 +351,11 @@ fn extend_with_struct_pat( /// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post), /// where `~` denotes semantic equality. fn extend_with_matching_product( - targets: &mut [Box], + targets: &mut [Pat], start: usize, - alternatives: &mut ThinVec>, - predicate: impl Fn(&PatKind, &[Box], usize) -> bool, - extract: impl Fn(PatKind) -> ThinVec>, + alternatives: &mut ThinVec, + predicate: impl Fn(&PatKind, &[Pat], usize) -> bool, + extract: impl Fn(PatKind) -> ThinVec, ) -> bool { (0..targets.len()).any(|idx| { let tail_or = drain_matching( @@ -382,14 +382,14 @@ fn take_pat(from: &mut Pat) -> Pat { /// Extend `target` as an or-pattern with the alternatives /// in `tail_or` if there are any and return if there were. -fn extend_with_tail_or(target: &mut Pat, tail_or: ThinVec>) -> bool { - fn extend(target: &mut Pat, mut tail_or: ThinVec>) { +fn extend_with_tail_or(target: &mut Pat, tail_or: ThinVec) -> bool { + fn extend(target: &mut Pat, mut tail_or: ThinVec) { match target { // On an existing or-pattern in the target, append to it. Pat { kind: Or(ps), .. } => ps.append(&mut tail_or), // Otherwise convert the target to an or-pattern. target => { - let mut init_or = thin_vec![Box::new(take_pat(target))]; + let mut init_or = thin_vec![take_pat(target)]; init_or.append(&mut tail_or); target.kind = Or(init_or); }, @@ -408,10 +408,10 @@ fn extend_with_tail_or(target: &mut Pat, tail_or: ThinVec>) -> bool { // Only elements beginning with `start` are considered for extraction. fn drain_matching( start: usize, - alternatives: &mut ThinVec>, + alternatives: &mut ThinVec, predicate: impl Fn(&PatKind) -> bool, - extract: impl Fn(PatKind) -> Box, -) -> ThinVec> { + extract: impl Fn(PatKind) -> Pat, +) -> ThinVec { let mut tail_or = ThinVec::new(); let mut idx = 0; @@ -443,15 +443,15 @@ fn drain_matching( fn extend_with_matching( target: &mut Pat, start: usize, - alternatives: &mut ThinVec>, + alternatives: &mut ThinVec, predicate: impl Fn(&PatKind) -> bool, - extract: impl Fn(PatKind) -> Box, + extract: impl Fn(PatKind) -> Pat, ) -> bool { extend_with_tail_or(target, drain_matching(start, alternatives, predicate, extract)) } /// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`? -fn eq_pre_post(ps1: &[Box], ps2: &[Box], idx: usize) -> bool { +fn eq_pre_post(ps1: &[Pat], ps2: &[Pat], idx: usize) -> bool { ps1.len() == ps2.len() && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r)) From 303a82853355956baa82ac2d1cb77e90b0aaf449 Mon Sep 17 00:00:00 2001 From: Aria Desires Date: Mon, 6 Oct 2025 21:21:07 -0400 Subject: [PATCH 006/108] pin all musl deconfigs to 1.2.5 --- .../host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig | 1 + .../dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig | 1 + .../powerpc64le-unknown-linux-musl.defconfig | 1 + 3 files changed, 3 insertions(+) diff --git a/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig index e7afdbe9d4dea..014930052b4ac 100644 --- a/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig +++ b/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig @@ -11,3 +11,4 @@ CT_BINUTILS_V_2_32=y CT_GLIBC_V_2_17=y CT_GCC_V_8=y CT_CC_LANG_CXX=y +CT_MUSL_V_1_2_5=y diff --git a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig index 01fe493a9fecd..f5f3cfd4b49f3 100644 --- a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/i586-linux-gnu.defconfig @@ -12,3 +12,4 @@ CT_BINUTILS_EXTRA_CONFIG_ARRAY="--enable-compressed-debug-sections=none" CT_GLIBC_V_2_17=y CT_GCC_V_8=y CT_CC_LANG_CXX=y +CT_MUSL_V_1_2_5=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig index d8fa8ec4d5e82..db2b5533947cd 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig @@ -11,5 +11,6 @@ CT_ARCH_ARCH="powerpc64le" CT_KERNEL_LINUX=y CT_LINUX_V_4_19=y CT_LIBC_MUSL=y +CT_MUSL_V_1_2_5=y CT_CC_LANG_CXX=y CT_GETTEXT_NEEDED=y From 22e6656c2108c9af819221e325a797a10ed0d806 Mon Sep 17 00:00:00 2001 From: Aria Desires Date: Mon, 6 Oct 2025 21:21:37 -0400 Subject: [PATCH 007/108] remove references to upstreamed patches --- .../src/spec/targets/riscv32gc_unknown_linux_musl.rs | 2 +- src/doc/rustc/src/platform-support.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs index 7183f0a866280..bd18d80a107b7 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target: "riscv32-unknown-linux-musl".into(), metadata: TargetMetadata { description: Some( - "RISC-V Linux (kernel 5.4, musl 1.2.5 + RISCV32 support patches".into(), + "RISC-V Linux (kernel 5.4, musl 1.2.5)".into(), ), tier: Some(3), host_tools: Some(false), diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index e56e933da5567..b2e9cf7c868ea 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -374,7 +374,7 @@ target | std | host | notes [`riscv32em-unknown-none-elf`](platform-support/riscv32e-unknown-none-elf.md) | * | | Bare RISC-V (RV32EM ISA) [`riscv32emc-unknown-none-elf`](platform-support/riscv32e-unknown-none-elf.md) | * | | Bare RISC-V (RV32EMC ISA) `riscv32gc-unknown-linux-gnu` | ✓ | | RISC-V Linux (kernel 5.4, glibc 2.33) -`riscv32gc-unknown-linux-musl` | ? | | RISC-V Linux (kernel 5.4, musl 1.2.5 + RISCV32 support patches) +`riscv32gc-unknown-linux-musl` | ? | | RISC-V Linux (kernel 5.4, musl 1.2.5) [`riscv32im-risc0-zkvm-elf`](platform-support/riscv32im-risc0-zkvm-elf.md) | ? | | RISC Zero's zero-knowledge Virtual Machine (RV32IM ISA) [`riscv32ima-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32IMA ISA) [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF From b64cdd0f59f4bbc98dc5e3c1a64c09ca4d999906 Mon Sep 17 00:00:00 2001 From: Aria Desires Date: Mon, 6 Oct 2025 21:24:49 -0400 Subject: [PATCH 008/108] bump musl-cross-make commit to get fixes --- src/ci/docker/scripts/musl-toolchain.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/scripts/musl-toolchain.sh b/src/ci/docker/scripts/musl-toolchain.sh index c52c5ea7953c8..511521a2b49da 100644 --- a/src/ci/docker/scripts/musl-toolchain.sh +++ b/src/ci/docker/scripts/musl-toolchain.sh @@ -46,7 +46,7 @@ export CFLAGS="-fPIC -g1 $CFLAGS" git clone https://github.com/richfelker/musl-cross-make # -b v0.9.9 cd musl-cross-make # A version that includes support for building musl 1.2.5 -git checkout e149c31c48b4f4a4c9349ddf7bc0027b90245afc +git checkout 3635262e4524c991552789af6f36211a335a77b3 hide_output make -j$(nproc) TARGET=$TARGET MUSL_VER=1.2.5 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER hide_output make install TARGET=$TARGET MUSL_VER=1.2.5 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER OUTPUT=$OUTPUT From 010b69011761443d1450b6b37d551afe2262b477 Mon Sep 17 00:00:00 2001 From: Aria Desires Date: Mon, 6 Oct 2025 21:59:04 -0400 Subject: [PATCH 009/108] fmt --- .../src/spec/targets/riscv32gc_unknown_linux_musl.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs index bd18d80a107b7..a13bb173e2483 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs @@ -6,9 +6,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "riscv32-unknown-linux-musl".into(), metadata: TargetMetadata { - description: Some( - "RISC-V Linux (kernel 5.4, musl 1.2.5)".into(), - ), + description: Some("RISC-V Linux (kernel 5.4, musl 1.2.5)".into()), tier: Some(3), host_tools: Some(false), std: Some(true), From 813eb082f302a59d1cb66a0db3a5b6df0de8a660 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 31 May 2025 10:26:18 -0400 Subject: [PATCH 010/108] `clippy_dev`: Move parsing code to it's own module. --- clippy_dev/src/deprecate_lint.rs | 3 +- clippy_dev/src/lib.rs | 2 + clippy_dev/src/new_lint.rs | 3 +- clippy_dev/src/parse.rs | 399 +++++++++++++++++++++++++++++++ clippy_dev/src/rename_lint.rs | 7 +- clippy_dev/src/update_lints.rs | 267 +-------------------- clippy_dev/src/utils.rs | 175 -------------- 7 files changed, 412 insertions(+), 444 deletions(-) create mode 100644 clippy_dev/src/parse.rs diff --git a/clippy_dev/src/deprecate_lint.rs b/clippy_dev/src/deprecate_lint.rs index 3bdc5b2772327..9f6d1984eb571 100644 --- a/clippy_dev/src/deprecate_lint.rs +++ b/clippy_dev/src/deprecate_lint.rs @@ -1,4 +1,5 @@ -use crate::update_lints::{DeprecatedLint, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints}; +use crate::parse::{DeprecatedLint, Lint, find_lint_decls, read_deprecated_lints}; +use crate::update_lints::generate_lint_files; use crate::utils::{UpdateMode, Version}; use std::ffi::OsStr; use std::path::{Path, PathBuf}; diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 16f413e0c862f..28f5e4ac30fb9 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -34,3 +34,5 @@ pub mod update_lints; mod utils; pub use utils::{ClippyInfo, UpdateMode}; + +mod parse; diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index a14afd8c5f416..d3d3a547e6eac 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,4 +1,5 @@ -use crate::utils::{RustSearcher, Token, Version}; +use crate::parse::{RustSearcher, Token}; +use crate::utils::Version; use clap::ValueEnum; use indoc::{formatdoc, writedoc}; use std::fmt::{self, Write as _}; diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs new file mode 100644 index 0000000000000..2b2e5fcf91311 --- /dev/null +++ b/clippy_dev/src/parse.rs @@ -0,0 +1,399 @@ +use crate::utils::{ErrAction, File, expect_action}; +use core::ops::Range; +use core::slice; +use rustc_lexer::{self as lexer, FrontmatterAllowed}; +use std::fs; +use std::path::{Path, PathBuf}; +use walkdir::{DirEntry, WalkDir}; + +#[derive(Clone, Copy)] +pub enum Token<'a> { + /// Matches any number of comments / doc comments. + AnyComment, + Ident(&'a 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, + Semi, +} + +pub struct RustSearcher<'txt> { + text: &'txt str, + cursor: lexer::Cursor<'txt>, + pos: u32, + next_token: lexer::Token, +} +impl<'txt> RustSearcher<'txt> { + #[must_use] + #[expect(clippy::inconsistent_struct_constructor)] + pub fn new(text: &'txt str) -> Self { + let mut cursor = lexer::Cursor::new(text, FrontmatterAllowed::Yes); + Self { + text, + pos: 0, + next_token: cursor.advance_token(), + cursor, + } + } + + #[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_len(&self) -> u32 { + self.next_token.len + } + + #[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_token.len` is zero for 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) { + (_, lexer::TokenKind::Whitespace) + | ( + Token::AnyComment, + lexer::TokenKind::BlockComment { terminated: true, .. } | lexer::TokenKind::LineComment { .. }, + ) => self.step(), + (Token::AnyComment, _) => 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::Semi, lexer::TokenKind::Semi) + | ( + 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; + }, + #[rustfmt::skip] + ( + Token::CaptureLitStr, + lexer::TokenKind::Literal { + kind: + lexer::LiteralKind::Str { terminated: true } + | lexer::LiteralKind::RawStr { n_hashes: Some(_) }, + .. + }, + ) + | (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)) + } +} + +pub struct Lint { + pub name: String, + pub group: String, + pub module: String, + pub path: PathBuf, + pub declaration_range: Range, +} + +pub struct DeprecatedLint { + pub name: String, + pub reason: String, + pub version: String, +} + +pub struct RenamedLint { + pub old_name: String, + pub new_name: String, + pub version: String, +} + +/// Finds all lint declarations (`declare_clippy_lint!`) +#[must_use] +pub fn find_lint_decls() -> Vec { + let mut lints = Vec::with_capacity(1000); + let mut contents = String::new(); + for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { + let e = expect_action(e, ErrAction::Read, "."); + if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() { + continue; + } + let Ok(mut name) = e.file_name().into_string() else { + continue; + }; + if name.starts_with("clippy_lints") && name != "clippy_lints_internal" { + name.push_str("/src"); + for (file, module) in read_src_with_module(name.as_ref()) { + parse_clippy_lint_decls( + file.path(), + File::open_read_to_cleared_string(file.path(), &mut contents), + &module, + &mut lints, + ); + } + } + } + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + lints +} + +/// 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 = expect_action(e, ErrAction::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(src_root.as_os_str().len() + 1..) + { + 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 + } + }) +} + +/// Parse a source file looking for `declare_clippy_lint` macro invocations. +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, AnyComment, + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, + // pub NAME, GROUP, + Ident("pub"), CaptureIdent, Comma, AnyComment, 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) = ("", ""); + 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(), + module: module.into(), + path: path.into(), + declaration_range: start..searcher.pos() as usize, + }); + } + } +} + +#[must_use] +pub fn read_deprecated_lints() -> (Vec, Vec) { + #[allow(clippy::enum_glob_use)] + use Token::*; + #[rustfmt::skip] + static DECL_TOKENS: &[Token<'_>] = &[ + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, 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 deprecated = Vec::with_capacity(30); + let mut renamed = Vec::with_capacity(80); + let mut contents = String::new(); + File::open_read_to_cleared_string(path, &mut contents); + + let mut searcher = RustSearcher::new(&contents); + + // First instance is the macro definition. + assert!( + searcher.find_token(Ident("declare_with_version")), + "error reading deprecated lints" + ); + + if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(DEPRECATED_TOKENS, &mut []) { + let mut version = ""; + let mut name = ""; + let mut reason = ""; + while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) { + deprecated.push(DeprecatedLint { + name: parse_str_single_line(path.as_ref(), name), + reason: parse_str_single_line(path.as_ref(), reason), + version: parse_str_single_line(path.as_ref(), version), + }); + } + } else { + panic!("error reading deprecated lints"); + } + + if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(RENAMED_TOKENS, &mut []) { + let mut version = ""; + let mut old_name = ""; + let mut new_name = ""; + while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) { + 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), + version: parse_str_single_line(path.as_ref(), version), + }); + } + } else { + panic!("error reading renamed lints"); + } + + deprecated.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(&rhs.old_name)); + (deprecated, renamed) +} + +/// Removes the line splices and surrounding quotes from a string literal +fn parse_str_lit(s: &str) -> String { + let (s, is_raw) = if let Some(s) = s.strip_prefix("r") { + (s.trim_matches('#'), true) + } else { + (s, false) + }; + let s = s + .strip_prefix('"') + .and_then(|s| s.strip_suffix('"')) + .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); + + if is_raw { + s.into() + } else { + let mut res = String::with_capacity(s.len()); + rustc_literal_escaper::unescape_str(s, &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 +} diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index d62597428e210..a46c03a61feed 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -1,7 +1,8 @@ -use crate::update_lints::{RenamedLint, find_lint_decls, generate_lint_files, read_deprecated_lints}; +use crate::parse::{RenamedLint, RustSearcher, Token, find_lint_decls, read_deprecated_lints}; +use crate::update_lints::generate_lint_files; use crate::utils::{ - ErrAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, - delete_file_if_exists, expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, + ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, + expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, }; use rustc_lexer::TokenKind; use std::ffi::OsString; diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 5f6e874ffe254..87f81ecd6a460 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,13 +1,9 @@ -use crate::utils::{ - ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, expect_action, update_text_region_fn, -}; +use crate::parse::{DeprecatedLint, Lint, RenamedLint, RustSearcher, Token, find_lint_decls, read_deprecated_lints}; +use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; use itertools::Itertools; use std::collections::HashSet; use std::fmt::Write; -use std::fs; -use std::ops::Range; -use std::path::{self, Path, PathBuf}; -use walkdir::{DirEntry, WalkDir}; +use std::path::{self, Path}; const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\ // Use that command to update this file and do not edit by hand.\n\ @@ -200,260 +196,3 @@ pub fn generate_lint_files( fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } - -/// Lint data parsed from the Clippy source code. -#[derive(PartialEq, Eq, Debug)] -pub struct Lint { - pub name: String, - pub group: String, - pub module: String, - pub path: PathBuf, - pub declaration_range: Range, -} - -pub struct DeprecatedLint { - pub name: String, - pub reason: String, - pub version: String, -} - -pub struct RenamedLint { - pub old_name: String, - pub new_name: String, - pub version: String, -} - -/// Finds all lint declarations (`declare_clippy_lint!`) -#[must_use] -pub fn find_lint_decls() -> Vec { - let mut lints = Vec::with_capacity(1000); - let mut contents = String::new(); - for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { - let e = expect_action(e, ErrAction::Read, "."); - if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() { - continue; - } - let Ok(mut name) = e.file_name().into_string() else { - continue; - }; - if name.starts_with("clippy_lints") && name != "clippy_lints_internal" { - name.push_str("/src"); - for (file, module) in read_src_with_module(name.as_ref()) { - parse_clippy_lint_decls( - file.path(), - File::open_read_to_cleared_string(file.path(), &mut contents), - &module, - &mut lints, - ); - } - } - } - lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); - lints -} - -/// 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 = expect_action(e, ErrAction::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(src_root.as_os_str().len() + 1..) - { - 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 - } - }) -} - -/// Parse a source file looking for `declare_clippy_lint` macro invocations. -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, AnyComment, - // #[clippy::version = "version"] - Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, - // pub NAME, GROUP, - Ident("pub"), CaptureIdent, Comma, AnyComment, 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) = ("", ""); - 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(), - module: module.into(), - path: path.into(), - declaration_range: start..searcher.pos() as usize, - }); - } - } -} - -#[must_use] -pub fn read_deprecated_lints() -> (Vec, Vec) { - #[allow(clippy::enum_glob_use)] - use Token::*; - #[rustfmt::skip] - static DECL_TOKENS: &[Token<'_>] = &[ - // #[clippy::version = "version"] - Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, 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 deprecated = Vec::with_capacity(30); - let mut renamed = Vec::with_capacity(80); - let mut contents = String::new(); - File::open_read_to_cleared_string(path, &mut contents); - - let mut searcher = RustSearcher::new(&contents); - - // First instance is the macro definition. - assert!( - searcher.find_token(Ident("declare_with_version")), - "error reading deprecated lints" - ); - - if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(DEPRECATED_TOKENS, &mut []) { - let mut version = ""; - let mut name = ""; - let mut reason = ""; - while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) { - deprecated.push(DeprecatedLint { - name: parse_str_single_line(path.as_ref(), name), - reason: parse_str_single_line(path.as_ref(), reason), - version: parse_str_single_line(path.as_ref(), version), - }); - } - } else { - panic!("error reading deprecated lints"); - } - - if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(RENAMED_TOKENS, &mut []) { - let mut version = ""; - let mut old_name = ""; - let mut new_name = ""; - while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) { - 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), - version: parse_str_single_line(path.as_ref(), version), - }); - } - } else { - panic!("error reading renamed lints"); - } - - deprecated.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); - renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(&rhs.old_name)); - (deprecated, renamed) -} - -/// Removes the line splices and surrounding quotes from a string literal -fn parse_str_lit(s: &str) -> String { - let s = s.strip_prefix("r").unwrap_or(s).trim_matches('#'); - let s = s - .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()); - rustc_literal_escaper::unescape_str(s, &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::*; - - #[test] - fn test_parse_clippy_lint_decls() { - static CONTENTS: &str = r#" - declare_clippy_lint! { - #[clippy::version = "Hello Clippy!"] - pub PTR_ARG, - style, - "really long \ - text" - } - - declare_clippy_lint!{ - #[clippy::version = "Test version"] - pub DOC_MARKDOWN, - pedantic, - "single line" - } - "#; - let mut result = Vec::new(); - parse_clippy_lint_decls("".as_ref(), CONTENTS, "module_name", &mut result); - for r in &mut result { - r.declaration_range = Range::default(); - } - - let expected = vec![ - Lint { - name: "ptr_arg".into(), - group: "style".into(), - module: "module_name".into(), - path: PathBuf::new(), - declaration_range: Range::default(), - }, - Lint { - name: "doc_markdown".into(), - group: "pedantic".into(), - module: "module_name".into(), - path: PathBuf::new(), - declaration_range: Range::default(), - }, - ]; - assert_eq!(expected, result); - } -} diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 057951d0e33bd..387cbe2c3ed7b 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -1,9 +1,7 @@ use core::fmt::{self, Display}; use core::num::NonZero; use core::ops::Range; -use core::slice; use core::str::FromStr; -use rustc_lexer::{self as lexer, FrontmatterAllowed}; use std::ffi::OsStr; use std::fs::{self, OpenOptions}; use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; @@ -410,179 +408,6 @@ pub fn update_text_region_fn( move |path, src, dst| update_text_region(path, start, end, src, dst, &mut insert) } -#[derive(Clone, Copy)] -pub enum Token<'a> { - /// Matches any number of comments / doc comments. - AnyComment, - Ident(&'a 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, - Semi, -} - -pub struct RustSearcher<'txt> { - text: &'txt str, - cursor: lexer::Cursor<'txt>, - pos: u32, - next_token: lexer::Token, -} -impl<'txt> RustSearcher<'txt> { - #[must_use] - #[expect(clippy::inconsistent_struct_constructor)] - pub fn new(text: &'txt str) -> Self { - let mut cursor = lexer::Cursor::new(text, FrontmatterAllowed::Yes); - Self { - text, - pos: 0, - next_token: cursor.advance_token(), - cursor, - } - } - - #[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_len(&self) -> u32 { - self.next_token.len - } - - #[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) { - (_, lexer::TokenKind::Whitespace) - | ( - Token::AnyComment, - lexer::TokenKind::BlockComment { terminated: true, .. } | lexer::TokenKind::LineComment { .. }, - ) => self.step(), - (Token::AnyComment, _) => 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::Semi, lexer::TokenKind::Semi) - | ( - 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)) - } -} - #[track_caller] pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { match OpenOptions::new().create_new(true).write(true).open(new_name) { From 4f403f39b586db78bddf4c2c33c164e6afa2fe01 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 10 Oct 2025 17:04:01 -0400 Subject: [PATCH 011/108] `clippy_dev`: Validate lint name format during argument parsing. --- clippy_dev/src/deprecate_lint.rs | 4 ---- clippy_dev/src/main.rs | 24 +++++++++++++++++++++--- clippy_dev/src/rename_lint.rs | 7 ------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/clippy_dev/src/deprecate_lint.rs b/clippy_dev/src/deprecate_lint.rs index 9f6d1984eb571..e6e8b4a5184c8 100644 --- a/clippy_dev/src/deprecate_lint.rs +++ b/clippy_dev/src/deprecate_lint.rs @@ -15,10 +15,6 @@ use std::{fs, io}; /// /// If a file path could not read from or written to pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { - if let Some((prefix, _)) = name.split_once("::") { - panic!("`{name}` should not contain the `{prefix}` prefix"); - } - let mut lints = find_lint_decls(); let (mut deprecated_lints, renamed_lints) = read_deprecated_lints(); diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 1b6a590b896f4..78fb44d7ad1b5 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -7,7 +7,6 @@ use clippy_dev::{ ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, update_lints, }; -use std::convert::Infallible; use std::env; fn main() { @@ -95,6 +94,20 @@ fn main() { } } +fn lint_name(name: &str) -> Result { + let name = name.replace('-', "_"); + if let Some((pre, _)) = name.split_once("::") { + Err(format!("lint name should not contain the `{pre}` prefix")) + } else if name + .bytes() + .any(|x| !matches!(x, b'_' | b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z')) + { + Err("lint name contains invalid characters".to_owned()) + } else { + Ok(name) + } +} + #[derive(Parser)] #[command(name = "dev", about)] struct Dev { @@ -150,7 +163,7 @@ enum DevCommand { #[arg( short, long, - value_parser = |name: &str| Ok::<_, Infallible>(name.replace('-', "_")), + value_parser = lint_name, )] /// Name of the new lint in snake case, ex: `fn_too_long` name: String, @@ -223,8 +236,12 @@ enum DevCommand { /// Rename a lint RenameLint { /// The name of the lint to rename + #[arg(value_parser = lint_name)] old_name: String, - #[arg(required_unless_present = "uplift")] + #[arg( + required_unless_present = "uplift", + value_parser = lint_name, + )] /// The new name of the lint new_name: Option, #[arg(long)] @@ -234,6 +251,7 @@ enum DevCommand { /// Deprecate the given lint Deprecate { /// The name of the lint to deprecate + #[arg(value_parser = lint_name)] name: String, #[arg(long, short)] /// The reason for deprecation diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index a46c03a61feed..52ae888c6b0f3 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -26,13 +26,6 @@ use std::path::Path; /// * If `old_name` names a deprecated or renamed lint. #[expect(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 updater = FileUpdater::default(); let mut lints = find_lint_decls(); let (deprecated_lints, mut renamed_lints) = read_deprecated_lints(); From 88c0674bc85ce59bfdaab711cc1312f6a95fe80d Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 10 Oct 2025 17:49:50 -0400 Subject: [PATCH 012/108] `clippy_dev`: Rename `RustSearcher` to `Cursor` and move it to its own module. --- clippy_dev/src/new_lint.rs | 14 +-- clippy_dev/src/parse.rs | 215 +++------------------------------ clippy_dev/src/parse/cursor.rs | 212 ++++++++++++++++++++++++++++++++ clippy_dev/src/rename_lint.rs | 59 ++++----- clippy_dev/src/update_lints.rs | 11 +- 5 files changed, 277 insertions(+), 234 deletions(-) create mode 100644 clippy_dev/src/parse/cursor.rs diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index d3d3a547e6eac..3930e7a5a9383 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,4 +1,4 @@ -use crate::parse::{RustSearcher, Token}; +use crate::parse::cursor::{self, Cursor}; use crate::utils::Version; use clap::ValueEnum; use indoc::{formatdoc, writedoc}; @@ -517,21 +517,21 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> // 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::*; + use cursor::Pat::*; let mut context = None; let mut decl_end = None; - let mut searcher = RustSearcher::new(contents); - while let Some(name) = searcher.find_capture_token(CaptureIdent) { + let mut cursor = Cursor::new(contents); + while let Some(name) = cursor.find_capture_pat(CaptureIdent) { match name { "declare_clippy_lint" => { - if searcher.match_tokens(&[Bang, OpenBrace], &mut []) && searcher.find_token(CloseBrace) { - decl_end = Some(searcher.pos()); + if cursor.match_all(&[Bang, OpenBrace], &mut []) && cursor.find_pat(CloseBrace) { + decl_end = Some(cursor.pos()); } }, "impl" => { let mut capture = ""; - if searcher.match_tokens(&[Lt, Lifetime, Gt, CaptureIdent], &mut [&mut capture]) { + if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut [&mut capture]) { match capture { "LateLintPass" => context = Some("LateContext"), "EarlyLintPass" => context = Some("EarlyContext"), diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index 2b2e5fcf91311..b81c3da73bc51 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -1,187 +1,12 @@ +pub mod cursor; + +use self::cursor::Cursor; use crate::utils::{ErrAction, File, expect_action}; use core::ops::Range; -use core::slice; -use rustc_lexer::{self as lexer, FrontmatterAllowed}; use std::fs; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; -#[derive(Clone, Copy)] -pub enum Token<'a> { - /// Matches any number of comments / doc comments. - AnyComment, - Ident(&'a 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, - Semi, -} - -pub struct RustSearcher<'txt> { - text: &'txt str, - cursor: lexer::Cursor<'txt>, - pos: u32, - next_token: lexer::Token, -} -impl<'txt> RustSearcher<'txt> { - #[must_use] - #[expect(clippy::inconsistent_struct_constructor)] - pub fn new(text: &'txt str) -> Self { - let mut cursor = lexer::Cursor::new(text, FrontmatterAllowed::Yes); - Self { - text, - pos: 0, - next_token: cursor.advance_token(), - cursor, - } - } - - #[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_len(&self) -> u32 { - self.next_token.len - } - - #[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_token.len` is zero for 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) { - (_, lexer::TokenKind::Whitespace) - | ( - Token::AnyComment, - lexer::TokenKind::BlockComment { terminated: true, .. } | lexer::TokenKind::LineComment { .. }, - ) => self.step(), - (Token::AnyComment, _) => 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::Semi, lexer::TokenKind::Semi) - | ( - 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; - }, - #[rustfmt::skip] - ( - Token::CaptureLitStr, - lexer::TokenKind::Literal { - kind: - lexer::LiteralKind::Str { terminated: true } - | lexer::LiteralKind::RawStr { n_hashes: Some(_) }, - .. - }, - ) - | (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)) - } -} - pub struct Lint { pub name: String, pub group: String, @@ -265,9 +90,9 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator) { #[allow(clippy::enum_glob_use)] - use Token::*; + use cursor::Pat::*; #[rustfmt::skip] - static DECL_TOKENS: &[Token<'_>] = &[ + static DECL_TOKENS: &[cursor::Pat<'_>] = &[ // !{ /// docs Bang, OpenBrace, AnyComment, // #[clippy::version = "version"] @@ -276,17 +101,17 @@ fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mu Ident("pub"), CaptureIdent, Comma, AnyComment, 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 cursor = Cursor::new(contents); + while cursor.find_pat(Ident("declare_clippy_lint")) { + let start = cursor.pos() as usize - "declare_clippy_lint".len(); let (mut name, mut group) = ("", ""); - if searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut group]) && searcher.find_token(CloseBrace) { + if cursor.match_all(DECL_TOKENS, &mut [&mut name, &mut group]) && cursor.find_pat(CloseBrace) { lints.push(Lint { name: name.to_lowercase(), group: group.into(), module: module.into(), path: path.into(), - declaration_range: start..searcher.pos() as usize, + declaration_range: start..cursor.pos() as usize, }); } } @@ -295,21 +120,21 @@ fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mu #[must_use] pub fn read_deprecated_lints() -> (Vec, Vec) { #[allow(clippy::enum_glob_use)] - use Token::*; + use cursor::Pat::*; #[rustfmt::skip] - static DECL_TOKENS: &[Token<'_>] = &[ + static DECL_TOKENS: &[cursor::Pat<'_>] = &[ // #[clippy::version = "version"] Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, CloseBracket, // ("first", "second"), OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma, ]; #[rustfmt::skip] - static DEPRECATED_TOKENS: &[Token<'_>] = &[ + static DEPRECATED_TOKENS: &[cursor::Pat<'_>] = &[ // !{ DEPRECATED(DEPRECATED_VERSION) = [ Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket, ]; #[rustfmt::skip] - static RENAMED_TOKENS: &[Token<'_>] = &[ + static RENAMED_TOKENS: &[cursor::Pat<'_>] = &[ // !{ RENAMED(RENAMED_VERSION) = [ Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket, ]; @@ -320,19 +145,19 @@ pub fn read_deprecated_lints() -> (Vec, Vec) { let mut contents = String::new(); File::open_read_to_cleared_string(path, &mut contents); - let mut searcher = RustSearcher::new(&contents); + let mut cursor = Cursor::new(&contents); // First instance is the macro definition. assert!( - searcher.find_token(Ident("declare_with_version")), + cursor.find_pat(Ident("declare_with_version")), "error reading deprecated lints" ); - if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(DEPRECATED_TOKENS, &mut []) { + if cursor.find_pat(Ident("declare_with_version")) && cursor.match_all(DEPRECATED_TOKENS, &mut []) { let mut version = ""; let mut name = ""; let mut reason = ""; - while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) { + while cursor.match_all(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) { deprecated.push(DeprecatedLint { name: parse_str_single_line(path.as_ref(), name), reason: parse_str_single_line(path.as_ref(), reason), @@ -343,11 +168,11 @@ pub fn read_deprecated_lints() -> (Vec, Vec) { panic!("error reading deprecated lints"); } - if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(RENAMED_TOKENS, &mut []) { + if cursor.find_pat(Ident("declare_with_version")) && cursor.match_all(RENAMED_TOKENS, &mut []) { let mut version = ""; let mut old_name = ""; let mut new_name = ""; - while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) { + while cursor.match_all(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) { 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), diff --git a/clippy_dev/src/parse/cursor.rs b/clippy_dev/src/parse/cursor.rs new file mode 100644 index 0000000000000..c731d8deb973c --- /dev/null +++ b/clippy_dev/src/parse/cursor.rs @@ -0,0 +1,212 @@ +use core::slice; +use rustc_lexer::{self as lex, LiteralKind, Token, TokenKind}; + +/// A token pattern used for searching and matching by the [`Cursor`]. +/// +/// In the event that a pattern is a multi-token sequence, earlier tokens will be consumed +/// even if the pattern ultimately isn't matched. e.g. With the sequence `:*` matching +/// `DoubleColon` will consume the first `:` and then fail to match, leaving the cursor at +/// the `*`. +#[derive(Clone, Copy)] +pub enum Pat<'a> { + /// Matches any number of comments and doc comments. + AnyComment, + Ident(&'a str), + CaptureIdent, + LitStr, + CaptureLitStr, + Bang, + CloseBrace, + CloseBracket, + CloseParen, + Comma, + DoubleColon, + Eq, + Lifetime, + Lt, + Gt, + OpenBrace, + OpenBracket, + OpenParen, + Pound, + Semi, +} + +/// A unidirectional cursor over a token stream that is lexed on demand. +pub struct Cursor<'txt> { + next_token: Token, + pos: u32, + inner: lex::Cursor<'txt>, + text: &'txt str, +} +impl<'txt> Cursor<'txt> { + #[must_use] + pub fn new(text: &'txt str) -> Self { + let mut inner = lex::Cursor::new(text, lex::FrontmatterAllowed::Yes); + Self { + next_token: inner.advance_token(), + pos: 0, + inner, + text, + } + } + + /// Gets the text that makes up the next token in the stream, or the empty string if + /// stream is exhausted. + #[must_use] + pub fn peek_text(&self) -> &'txt str { + &self.text[self.pos as usize..(self.pos + self.next_token.len) as usize] + } + + /// Gets the length of the next token in bytes, or zero if the stream is exhausted. + #[must_use] + pub fn peek_len(&self) -> u32 { + self.next_token.len + } + + /// Gets the next token in the stream, or [`TokenKind::Eof`] if the stream is + /// exhausted. + #[must_use] + pub fn peek(&self) -> TokenKind { + self.next_token.kind + } + + /// Gets the offset of the next token in the source string, or the string's length if + /// the stream is exhausted. + #[must_use] + pub fn pos(&self) -> u32 { + self.pos + } + + /// Gets whether the cursor has exhausted its input. + #[must_use] + pub fn at_end(&self) -> bool { + self.next_token.kind == TokenKind::Eof + } + + /// Advances the cursor to the next token. If the stream is exhausted this will set + /// the next token to [`TokenKind::Eof`]. + pub fn step(&mut self) { + // `next_token.len` is zero for the eof marker. + self.pos += self.next_token.len; + self.next_token = self.inner.advance_token(); + } + + /// Consumes tokens until the given pattern is either fully matched of fails to match. + /// Returns whether the pattern was fully matched. + /// + /// For each capture made by the pattern one item will be taken from the capture + /// sequence with the result placed inside. + fn match_pat(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool { + loop { + match (pat, self.next_token.kind) { + #[rustfmt::skip] // rustfmt bug: https://github.com/rust-lang/rustfmt/issues/6697 + (_, TokenKind::Whitespace) + | ( + Pat::AnyComment, + TokenKind::BlockComment { terminated: true, .. } | TokenKind::LineComment { .. }, + ) => self.step(), + (Pat::AnyComment, _) => return true, + (Pat::Bang, TokenKind::Bang) + | (Pat::CloseBrace, TokenKind::CloseBrace) + | (Pat::CloseBracket, TokenKind::CloseBracket) + | (Pat::CloseParen, TokenKind::CloseParen) + | (Pat::Comma, TokenKind::Comma) + | (Pat::Eq, TokenKind::Eq) + | (Pat::Lifetime, TokenKind::Lifetime { .. }) + | (Pat::Lt, TokenKind::Lt) + | (Pat::Gt, TokenKind::Gt) + | (Pat::OpenBrace, TokenKind::OpenBrace) + | (Pat::OpenBracket, TokenKind::OpenBracket) + | (Pat::OpenParen, TokenKind::OpenParen) + | (Pat::Pound, TokenKind::Pound) + | (Pat::Semi, TokenKind::Semi) + | ( + Pat::LitStr, + TokenKind::Literal { + kind: LiteralKind::Str { terminated: true } | LiteralKind::RawStr { .. }, + .. + }, + ) => { + self.step(); + return true; + }, + (Pat::Ident(x), TokenKind::Ident) if x == self.peek_text() => { + self.step(); + return true; + }, + (Pat::DoubleColon, TokenKind::Colon) => { + self.step(); + if !self.at_end() && matches!(self.next_token.kind, TokenKind::Colon) { + self.step(); + return true; + } + return false; + }, + #[rustfmt::skip] + ( + Pat::CaptureLitStr, + TokenKind::Literal { + kind: + LiteralKind::Str { terminated: true } + | LiteralKind::RawStr { n_hashes: Some(_) }, + .. + }, + ) + | (Pat::CaptureIdent, TokenKind::Ident) => { + **captures.next().unwrap() = self.peek_text(); + self.step(); + return true; + }, + _ => return false, + } + } + } + + /// Continually attempt to match the pattern on subsequent tokens until a match is + /// found. Returns whether the pattern was successfully matched. + /// + /// Not generally suitable for multi-token patterns or patterns that can match + /// nothing. + #[must_use] + pub fn find_pat(&mut self, token: Pat<'_>) -> bool { + let mut capture = [].iter_mut(); + while !self.match_pat(token, &mut capture) { + self.step(); + if self.at_end() { + return false; + } + } + true + } + + /// The same as [`Self::find_pat`], but returns a capture as well. + #[must_use] + pub fn find_capture_pat(&mut self, token: Pat<'_>) -> Option<&'txt str> { + let mut res = ""; + let mut capture = &mut res; + let mut capture = slice::from_mut(&mut capture).iter_mut(); + while !self.match_pat(token, &mut capture) { + self.step(); + if self.at_end() { + return None; + } + } + Some(res) + } + + /// Attempts to match a sequence of patterns at the current position. Returns whether + /// the match is successful. + /// + /// Captures will be written to the given slice in the order they're matched. If a + /// capture is matched, but there are no more capture slots this will panic. If the + /// match is completed without filling all the capture slots they will be left + /// unmodified. + /// + /// If the match fails the cursor will be positioned at the first failing token. + #[must_use] + pub fn match_all(&mut self, tokens: &[Pat<'_>], captures: &mut [&mut &'txt str]) -> bool { + let mut captures = captures.iter_mut(); + tokens.iter().all(|&t| self.match_pat(t, &mut captures)) + } +} diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index 52ae888c6b0f3..1096eed7aabf5 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -1,4 +1,5 @@ -use crate::parse::{RenamedLint, RustSearcher, Token, find_lint_decls, read_deprecated_lints}; +use crate::parse::cursor::{self, Cursor}; +use crate::parse::{RenamedLint, find_lint_decls, read_deprecated_lints}; use crate::update_lints::generate_lint_files; use crate::utils::{ ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, @@ -279,47 +280,49 @@ fn file_update_fn<'a, 'b>( move |_, src, dst| { let mut copy_pos = 0u32; let mut changed = false; - let mut searcher = RustSearcher::new(src); + let mut cursor = Cursor::new(src); let mut capture = ""; loop { - match searcher.peek() { + match cursor.peek() { TokenKind::Eof => break, TokenKind::Ident => { - let match_start = searcher.pos(); - let text = searcher.peek_text(); - searcher.step(); + let match_start = cursor.pos(); + let text = cursor.peek_text(); + cursor.step(); match text { // clippy::line_name or clippy::lint-name "clippy" => { - if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) - && capture == old_name + if cursor.match_all( + &[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], + &mut [&mut capture], + ) && capture == old_name { - dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(&src[copy_pos as usize..cursor.pos() as usize - capture.len()]); dst.push_str(new_name); - copy_pos = searcher.pos(); + copy_pos = cursor.pos(); changed = true; } }, // mod lint_name "mod" => { if !matches!(mod_edit, ModEdit::None) - && searcher.match_tokens(&[Token::CaptureIdent], &mut [&mut capture]) + && cursor.match_all(&[cursor::Pat::CaptureIdent], &mut [&mut capture]) && capture == old_name { match mod_edit { ModEdit::Rename => { - dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(&src[copy_pos as usize..cursor.pos() as usize - capture.len()]); dst.push_str(new_name); - copy_pos = searcher.pos(); + copy_pos = cursor.pos(); changed = true; }, - ModEdit::Delete if searcher.match_tokens(&[Token::Semi], &mut []) => { + ModEdit::Delete if cursor.match_all(&[cursor::Pat::Semi], &mut []) => { let mut start = &src[copy_pos as usize..match_start as usize]; if start.ends_with("\n\n") { start = &start[..start.len() - 1]; } dst.push_str(start); - copy_pos = searcher.pos(); + copy_pos = cursor.pos(); if src[copy_pos as usize..].starts_with("\n\n") { copy_pos += 1; } @@ -331,8 +334,8 @@ fn file_update_fn<'a, 'b>( }, // lint_name:: name if matches!(mod_edit, ModEdit::Rename) && name == old_name => { - let name_end = searcher.pos(); - if searcher.match_tokens(&[Token::DoubleColon], &mut []) { + let name_end = cursor.pos(); + if cursor.match_all(&[cursor::Pat::DoubleColon], &mut []) { dst.push_str(&src[copy_pos as usize..match_start as usize]); dst.push_str(new_name); copy_pos = name_end; @@ -350,36 +353,38 @@ fn file_update_fn<'a, 'b>( }; dst.push_str(&src[copy_pos as usize..match_start as usize]); dst.push_str(replacement); - copy_pos = searcher.pos(); + copy_pos = cursor.pos(); changed = true; }, } }, // //~ lint_name TokenKind::LineComment { doc_style: None } => { - let text = searcher.peek_text(); + let text = cursor.peek_text(); if text.starts_with("//~") && let Some(text) = text.strip_suffix(old_name) && !text.ends_with(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')) { - dst.push_str(&src[copy_pos as usize..searcher.pos() as usize + text.len()]); + dst.push_str(&src[copy_pos as usize..cursor.pos() as usize + text.len()]); dst.push_str(new_name); - copy_pos = searcher.pos() + searcher.peek_len(); + copy_pos = cursor.pos() + cursor.peek_len(); changed = true; } - searcher.step(); + cursor.step(); }, // ::lint_name TokenKind::Colon - if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) - && capture == old_name => + if cursor.match_all( + &[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], + &mut [&mut capture], + ) && capture == old_name => { - dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(&src[copy_pos as usize..cursor.pos() as usize - capture.len()]); dst.push_str(new_name); - copy_pos = searcher.pos(); + copy_pos = cursor.pos(); changed = true; }, - _ => searcher.step(), + _ => cursor.step(), } } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 87f81ecd6a460..6457979d233a2 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,4 +1,5 @@ -use crate::parse::{DeprecatedLint, Lint, RenamedLint, RustSearcher, Token, find_lint_decls, read_deprecated_lints}; +use crate::parse::cursor::{self, Cursor}; +use crate::parse::{DeprecatedLint, Lint, RenamedLint, find_lint_decls, read_deprecated_lints}; use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; use itertools::Itertools; use std::collections::HashSet; @@ -75,13 +76,13 @@ pub fn generate_lint_files( update_mode, "clippy_lints/src/deprecated_lints.rs", &mut |_, src, dst| { - let mut searcher = RustSearcher::new(src); + let mut cursor = Cursor::new(src); assert!( - searcher.find_token(Token::Ident("declare_with_version")) - && searcher.find_token(Token::Ident("declare_with_version")), + cursor.find_pat(cursor::Pat::Ident("declare_with_version")) + && cursor.find_pat(cursor::Pat::Ident("declare_with_version")), "error reading deprecated lints" ); - dst.push_str(&src[..searcher.pos() as usize]); + dst.push_str(&src[..cursor.pos() as usize]); dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); for lint in deprecated { write!( From 5e36990de014931fcc98fb9aa392f2ffa67b8a94 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 10 Oct 2025 19:46:52 -0400 Subject: [PATCH 013/108] `clippy_dev`: Use the new ranges. --- clippy_dev/src/deprecate_lint.rs | 4 ++-- clippy_dev/src/lib.rs | 4 +++- clippy_dev/src/parse.rs | 2 +- clippy_dev/src/release.rs | 2 +- clippy_dev/src/utils.rs | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clippy_dev/src/deprecate_lint.rs b/clippy_dev/src/deprecate_lint.rs index e6e8b4a5184c8..4d99eb91e6f94 100644 --- a/clippy_dev/src/deprecate_lint.rs +++ b/clippy_dev/src/deprecate_lint.rs @@ -132,14 +132,14 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io ); assert!( - content[lint.declaration_range.clone()].contains(&name.to_uppercase()), + content[lint.declaration_range].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(), ""); + content.replace_range(lint.declaration_range, ""); // Remove the module declaration (mod xyz;) let mod_decl = format!("\nmod {name};"); diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 28f5e4ac30fb9..fb8b2e1c91c1f 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,9 +1,11 @@ #![feature( - rustc_private, exit_status_error, if_let_guard, + new_range, + new_range_api, os_str_slice, os_string_truncate, + rustc_private, slice_split_once )] #![warn( diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index b81c3da73bc51..882a2dfde1f00 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -2,7 +2,7 @@ pub mod cursor; use self::cursor::Cursor; use crate::utils::{ErrAction, File, expect_action}; -use core::ops::Range; +use core::range::Range; use std::fs; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; diff --git a/clippy_dev/src/release.rs b/clippy_dev/src/release.rs index 15392dd1d2927..d11070bab85bf 100644 --- a/clippy_dev/src/release.rs +++ b/clippy_dev/src/release.rs @@ -23,7 +23,7 @@ pub fn bump_version(mut version: Version) { dst.push_str(&src[..package.version_range.start]); write!(dst, "\"{}\"", version.toml_display()).unwrap(); dst.push_str(&src[package.version_range.end..]); - UpdateStatus::from_changed(src.get(package.version_range.clone()) != dst.get(package.version_range)) + UpdateStatus::from_changed(src.get(package.version_range) != dst.get(package.version_range)) } }); } diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 387cbe2c3ed7b..526613a53c77b 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -1,6 +1,6 @@ use core::fmt::{self, Display}; use core::num::NonZero; -use core::ops::Range; +use core::range::Range; use core::str::FromStr; use std::ffi::OsStr; use std::fs::{self, OpenOptions}; From 422d4593081ab0a094f29ff00ed3016266613608 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 10 Oct 2025 19:49:13 -0400 Subject: [PATCH 014/108] `clippy_dev`: Capture token patterns by position and length. --- clippy_dev/src/new_lint.rs | 8 +++---- clippy_dev/src/parse.rs | 33 ++++++++++++---------------- clippy_dev/src/parse/cursor.rs | 40 +++++++++++++++++++++++----------- clippy_dev/src/rename_lint.rs | 26 ++++++++++------------ 4 files changed, 56 insertions(+), 51 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 3930e7a5a9383..eae4727e970c8 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,4 +1,4 @@ -use crate::parse::cursor::{self, Cursor}; +use crate::parse::cursor::{self, Capture, Cursor}; use crate::utils::Version; use clap::ValueEnum; use indoc::{formatdoc, writedoc}; @@ -522,6 +522,7 @@ fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) { let mut context = None; let mut decl_end = None; let mut cursor = Cursor::new(contents); + let mut captures = [Capture::EMPTY]; while let Some(name) = cursor.find_capture_pat(CaptureIdent) { match name { "declare_clippy_lint" => { @@ -530,9 +531,8 @@ fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) { } }, "impl" => { - let mut capture = ""; - if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut [&mut capture]) { - match capture { + if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut captures) { + match cursor.get_text(captures[0]) { "LateLintPass" => context = Some("LateContext"), "EarlyLintPass" => context = Some("EarlyContext"), _ => {}, diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index 882a2dfde1f00..f679be3a6b788 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -1,6 +1,6 @@ pub mod cursor; -use self::cursor::Cursor; +use self::cursor::{Capture, Cursor}; use crate::utils::{ErrAction, File, expect_action}; use core::range::Range; use std::fs; @@ -102,13 +102,13 @@ fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mu ]; let mut cursor = Cursor::new(contents); + let mut captures = [Capture::EMPTY; 2]; while cursor.find_pat(Ident("declare_clippy_lint")) { let start = cursor.pos() as usize - "declare_clippy_lint".len(); - let (mut name, mut group) = ("", ""); - if cursor.match_all(DECL_TOKENS, &mut [&mut name, &mut group]) && cursor.find_pat(CloseBrace) { + if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) { lints.push(Lint { - name: name.to_lowercase(), - group: group.into(), + name: cursor.get_text(captures[0]).to_lowercase(), + group: cursor.get_text(captures[1]).into(), module: module.into(), path: path.into(), declaration_range: start..cursor.pos() as usize, @@ -146,6 +146,7 @@ pub fn read_deprecated_lints() -> (Vec, Vec) { File::open_read_to_cleared_string(path, &mut contents); let mut cursor = Cursor::new(&contents); + let mut captures = [Capture::EMPTY; 3]; // First instance is the macro definition. assert!( @@ -154,14 +155,11 @@ pub fn read_deprecated_lints() -> (Vec, Vec) { ); if cursor.find_pat(Ident("declare_with_version")) && cursor.match_all(DEPRECATED_TOKENS, &mut []) { - let mut version = ""; - let mut name = ""; - let mut reason = ""; - while cursor.match_all(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) { + while cursor.match_all(DECL_TOKENS, &mut captures) { deprecated.push(DeprecatedLint { - name: parse_str_single_line(path.as_ref(), name), - reason: parse_str_single_line(path.as_ref(), reason), - version: parse_str_single_line(path.as_ref(), version), + name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), + reason: parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), }); } } else { @@ -169,14 +167,11 @@ pub fn read_deprecated_lints() -> (Vec, Vec) { } if cursor.find_pat(Ident("declare_with_version")) && cursor.match_all(RENAMED_TOKENS, &mut []) { - let mut version = ""; - let mut old_name = ""; - let mut new_name = ""; - while cursor.match_all(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) { + while cursor.match_all(DECL_TOKENS, &mut captures) { 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), - version: parse_str_single_line(path.as_ref(), version), + old_name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), + new_name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), }); } } else { diff --git a/clippy_dev/src/parse/cursor.rs b/clippy_dev/src/parse/cursor.rs index c731d8deb973c..d4ab868a8e735 100644 --- a/clippy_dev/src/parse/cursor.rs +++ b/clippy_dev/src/parse/cursor.rs @@ -32,6 +32,15 @@ pub enum Pat<'a> { Semi, } +#[derive(Clone, Copy)] +pub struct Capture { + pub pos: u32, + pub len: u32, +} +impl Capture { + pub const EMPTY: Self = Self { pos: 0, len: 0 }; +} + /// A unidirectional cursor over a token stream that is lexed on demand. pub struct Cursor<'txt> { next_token: Token, @@ -51,6 +60,12 @@ impl<'txt> Cursor<'txt> { } } + /// Gets the text of the captured token assuming it came from this cursor. + #[must_use] + pub fn get_text(&self, capture: Capture) -> &'txt str { + &self.text[capture.pos as usize..(capture.pos + capture.len) as usize] + } + /// Gets the text that makes up the next token in the stream, or the empty string if /// stream is exhausted. #[must_use] @@ -97,7 +112,7 @@ impl<'txt> Cursor<'txt> { /// /// For each capture made by the pattern one item will be taken from the capture /// sequence with the result placed inside. - fn match_pat(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool { + fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture>) -> bool { loop { match (pat, self.next_token.kind) { #[rustfmt::skip] // rustfmt bug: https://github.com/rust-lang/rustfmt/issues/6697 @@ -154,7 +169,7 @@ impl<'txt> Cursor<'txt> { }, ) | (Pat::CaptureIdent, TokenKind::Ident) => { - **captures.next().unwrap() = self.peek_text(); + *captures.next().unwrap() = Capture { pos: self.pos, len: self.next_token.len }; self.step(); return true; }, @@ -169,9 +184,9 @@ impl<'txt> Cursor<'txt> { /// Not generally suitable for multi-token patterns or patterns that can match /// nothing. #[must_use] - pub fn find_pat(&mut self, token: Pat<'_>) -> bool { + pub fn find_pat(&mut self, pat: Pat<'_>) -> bool { let mut capture = [].iter_mut(); - while !self.match_pat(token, &mut capture) { + while !self.match_impl(pat, &mut capture) { self.step(); if self.at_end() { return false; @@ -182,21 +197,20 @@ impl<'txt> Cursor<'txt> { /// The same as [`Self::find_pat`], but returns a capture as well. #[must_use] - pub fn find_capture_pat(&mut self, token: Pat<'_>) -> Option<&'txt str> { - let mut res = ""; - let mut capture = &mut res; - let mut capture = slice::from_mut(&mut capture).iter_mut(); - while !self.match_pat(token, &mut capture) { + pub fn find_capture_pat(&mut self, pat: Pat<'_>) -> Option<&'txt str> { + let mut capture = Capture::EMPTY; + let mut captures = slice::from_mut(&mut capture).iter_mut(); + while !self.match_impl(pat, &mut captures) { self.step(); if self.at_end() { return None; } } - Some(res) + Some(self.get_text(capture)) } /// Attempts to match a sequence of patterns at the current position. Returns whether - /// the match is successful. + /// all patterns were successfully matched. /// /// Captures will be written to the given slice in the order they're matched. If a /// capture is matched, but there are no more capture slots this will panic. If the @@ -205,8 +219,8 @@ impl<'txt> Cursor<'txt> { /// /// If the match fails the cursor will be positioned at the first failing token. #[must_use] - pub fn match_all(&mut self, tokens: &[Pat<'_>], captures: &mut [&mut &'txt str]) -> bool { + pub fn match_all(&mut self, pats: &[Pat<'_>], captures: &mut [Capture]) -> bool { let mut captures = captures.iter_mut(); - tokens.iter().all(|&t| self.match_pat(t, &mut captures)) + pats.iter().all(|&t| self.match_impl(t, &mut captures)) } } diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index 1096eed7aabf5..42ae61c74f1c2 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -1,4 +1,4 @@ -use crate::parse::cursor::{self, Cursor}; +use crate::parse::cursor::{self, Capture, Cursor}; use crate::parse::{RenamedLint, find_lint_decls, read_deprecated_lints}; use crate::update_lints::generate_lint_files; use crate::utils::{ @@ -281,7 +281,7 @@ fn file_update_fn<'a, 'b>( let mut copy_pos = 0u32; let mut changed = false; let mut cursor = Cursor::new(src); - let mut capture = ""; + let mut captures = [Capture::EMPTY]; loop { match cursor.peek() { TokenKind::Eof => break, @@ -292,12 +292,10 @@ fn file_update_fn<'a, 'b>( match text { // clippy::line_name or clippy::lint-name "clippy" => { - if cursor.match_all( - &[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], - &mut [&mut capture], - ) && capture == old_name + if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures) + && cursor.get_text(captures[0]) == old_name { - dst.push_str(&src[copy_pos as usize..cursor.pos() as usize - capture.len()]); + dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]); dst.push_str(new_name); copy_pos = cursor.pos(); changed = true; @@ -306,12 +304,12 @@ fn file_update_fn<'a, 'b>( // mod lint_name "mod" => { if !matches!(mod_edit, ModEdit::None) - && cursor.match_all(&[cursor::Pat::CaptureIdent], &mut [&mut capture]) - && capture == old_name + && cursor.match_all(&[cursor::Pat::CaptureIdent], &mut captures) + && cursor.get_text(captures[0]) == old_name { match mod_edit { ModEdit::Rename => { - dst.push_str(&src[copy_pos as usize..cursor.pos() as usize - capture.len()]); + dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]); dst.push_str(new_name); copy_pos = cursor.pos(); changed = true; @@ -374,12 +372,10 @@ fn file_update_fn<'a, 'b>( }, // ::lint_name TokenKind::Colon - if cursor.match_all( - &[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], - &mut [&mut capture], - ) && capture == old_name => + if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures) + && cursor.get_text(captures[0]) == old_name => { - dst.push_str(&src[copy_pos as usize..cursor.pos() as usize - capture.len()]); + dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]); dst.push_str(new_name); copy_pos = cursor.pos(); changed = true; From 1b31b09c898355651dad8c1017a0a0d9736b07c9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 10 Oct 2025 21:16:23 -0400 Subject: [PATCH 015/108] `clippy_dev`: Add specialized helpers for finding identifiers to the token cursor. --- clippy_dev/src/new_lint.rs | 4 +- clippy_dev/src/parse.rs | 11 +++--- clippy_dev/src/parse/cursor.rs | 67 ++++++++++++++++++++++++++-------- clippy_dev/src/rename_lint.rs | 9 ++--- clippy_dev/src/update_lints.rs | 6 +-- 5 files changed, 66 insertions(+), 31 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index eae4727e970c8..a180db6ad0629 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -523,8 +523,8 @@ fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) { let mut decl_end = None; let mut cursor = Cursor::new(contents); let mut captures = [Capture::EMPTY]; - while let Some(name) = cursor.find_capture_pat(CaptureIdent) { - match name { + while let Some(name) = cursor.find_any_ident() { + match cursor.get_text(name) { "declare_clippy_lint" => { if cursor.match_all(&[Bang, OpenBrace], &mut []) && cursor.find_pat(CloseBrace) { decl_end = Some(cursor.pos()); diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index f679be3a6b788..5cea73d34af55 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -103,15 +103,14 @@ fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mu let mut cursor = Cursor::new(contents); let mut captures = [Capture::EMPTY; 2]; - while cursor.find_pat(Ident("declare_clippy_lint")) { - let start = cursor.pos() as usize - "declare_clippy_lint".len(); + while let Some(start) = cursor.find_ident("declare_clippy_lint") { if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) { lints.push(Lint { name: cursor.get_text(captures[0]).to_lowercase(), group: cursor.get_text(captures[1]).into(), module: module.into(), path: path.into(), - declaration_range: start..cursor.pos() as usize, + declaration_range: start as usize..cursor.pos() as usize, }); } } @@ -150,11 +149,11 @@ pub fn read_deprecated_lints() -> (Vec, Vec) { // First instance is the macro definition. assert!( - cursor.find_pat(Ident("declare_with_version")), + cursor.find_ident("declare_with_version").is_some(), "error reading deprecated lints" ); - if cursor.find_pat(Ident("declare_with_version")) && cursor.match_all(DEPRECATED_TOKENS, &mut []) { + if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(DEPRECATED_TOKENS, &mut []) { while cursor.match_all(DECL_TOKENS, &mut captures) { deprecated.push(DeprecatedLint { name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), @@ -166,7 +165,7 @@ pub fn read_deprecated_lints() -> (Vec, Vec) { panic!("error reading deprecated lints"); } - if cursor.find_pat(Ident("declare_with_version")) && cursor.match_all(RENAMED_TOKENS, &mut []) { + if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(RENAMED_TOKENS, &mut []) { while cursor.match_all(DECL_TOKENS, &mut captures) { renamed.push(RenamedLint { old_name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), diff --git a/clippy_dev/src/parse/cursor.rs b/clippy_dev/src/parse/cursor.rs index d4ab868a8e735..6dc003f326de7 100644 --- a/clippy_dev/src/parse/cursor.rs +++ b/clippy_dev/src/parse/cursor.rs @@ -178,6 +178,47 @@ impl<'txt> Cursor<'txt> { } } + /// Consumes all tokens until the specified identifier is found and returns its + /// position. Returns `None` if the identifier could not be found. + /// + /// The cursor will be positioned immediately after the identifier, or at the end if + /// it is not. + pub fn find_ident(&mut self, ident: &str) -> Option { + loop { + match self.next_token.kind { + TokenKind::Ident if self.peek_text() == ident => { + let pos = self.pos; + self.step(); + return Some(pos); + }, + TokenKind::Eof => return None, + _ => self.step(), + } + } + } + + /// Consumes all tokens until the next identifier is found and captures it. Returns + /// `None` if no identifier could be found. + /// + /// The cursor will be positioned immediately after the identifier, or at the end if + /// it is not. + pub fn find_any_ident(&mut self) -> Option { + loop { + match self.next_token.kind { + TokenKind::Ident => { + let res = Capture { + pos: self.pos, + len: self.next_token.len, + }; + self.step(); + return Some(res); + }, + TokenKind::Eof => return None, + _ => self.step(), + } + } + } + /// Continually attempt to match the pattern on subsequent tokens until a match is /// found. Returns whether the pattern was successfully matched. /// @@ -195,20 +236,6 @@ impl<'txt> Cursor<'txt> { true } - /// The same as [`Self::find_pat`], but returns a capture as well. - #[must_use] - pub fn find_capture_pat(&mut self, pat: Pat<'_>) -> Option<&'txt str> { - let mut capture = Capture::EMPTY; - let mut captures = slice::from_mut(&mut capture).iter_mut(); - while !self.match_impl(pat, &mut captures) { - self.step(); - if self.at_end() { - return None; - } - } - Some(self.get_text(capture)) - } - /// Attempts to match a sequence of patterns at the current position. Returns whether /// all patterns were successfully matched. /// @@ -221,6 +248,16 @@ impl<'txt> Cursor<'txt> { #[must_use] pub fn match_all(&mut self, pats: &[Pat<'_>], captures: &mut [Capture]) -> bool { let mut captures = captures.iter_mut(); - pats.iter().all(|&t| self.match_impl(t, &mut captures)) + pats.iter().all(|&p| self.match_impl(p, &mut captures)) + } + + /// Attempts to match a single pattern at the current position. Returns whether the + /// pattern was successfully matched. + /// + /// If the pattern attempts to capture anything this will panic. If the match fails + /// the cursor will be positioned at the first failing token. + #[must_use] + pub fn match_pat(&mut self, pat: Pat<'_>) -> bool { + self.match_impl(pat, &mut [].iter_mut()) } } diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index 42ae61c74f1c2..207c9b2ff5966 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -304,17 +304,16 @@ fn file_update_fn<'a, 'b>( // mod lint_name "mod" => { if !matches!(mod_edit, ModEdit::None) - && cursor.match_all(&[cursor::Pat::CaptureIdent], &mut captures) - && cursor.get_text(captures[0]) == old_name + && let Some(pos) = cursor.find_ident(old_name) { match mod_edit { ModEdit::Rename => { - dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]); + dst.push_str(&src[copy_pos as usize..pos as usize]); dst.push_str(new_name); copy_pos = cursor.pos(); changed = true; }, - ModEdit::Delete if cursor.match_all(&[cursor::Pat::Semi], &mut []) => { + ModEdit::Delete if cursor.match_pat(cursor::Pat::Semi) => { let mut start = &src[copy_pos as usize..match_start as usize]; if start.ends_with("\n\n") { start = &start[..start.len() - 1]; @@ -333,7 +332,7 @@ fn file_update_fn<'a, 'b>( // lint_name:: name if matches!(mod_edit, ModEdit::Rename) && name == old_name => { let name_end = cursor.pos(); - if cursor.match_all(&[cursor::Pat::DoubleColon], &mut []) { + if cursor.match_pat(cursor::Pat::DoubleColon) { dst.push_str(&src[copy_pos as usize..match_start as usize]); dst.push_str(new_name); copy_pos = name_end; diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 6457979d233a2..ef841891721d7 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,4 +1,4 @@ -use crate::parse::cursor::{self, Cursor}; +use crate::parse::cursor::Cursor; use crate::parse::{DeprecatedLint, Lint, RenamedLint, find_lint_decls, read_deprecated_lints}; use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; use itertools::Itertools; @@ -78,8 +78,8 @@ pub fn generate_lint_files( &mut |_, src, dst| { let mut cursor = Cursor::new(src); assert!( - cursor.find_pat(cursor::Pat::Ident("declare_with_version")) - && cursor.find_pat(cursor::Pat::Ident("declare_with_version")), + cursor.find_ident("declare_with_version").is_some() + && cursor.find_ident("declare_with_version").is_some(), "error reading deprecated lints" ); dst.push_str(&src[..cursor.pos() as usize]); From 3e1f8623f75efee9d7f82b2bb720d49e716c3c5d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 12 Oct 2025 13:52:54 +0200 Subject: [PATCH 016/108] clean-up tests --- tests/ui/option_option.rs | 28 ++++++++++++++-------------- tests/ui/option_option.stderr | 7 ++----- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 42f03aae7bb82..136db902a9758 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,52 +1,52 @@ -#![deny(clippy::option_option)] -#![allow(clippy::unnecessary_wraps, clippy::manual_unwrap_or_default)] +#![warn(clippy::option_option)] +#![expect(clippy::unnecessary_wraps)] const C: Option> = None; -//~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if +//~^ option_option static S: Option> = None; -//~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if +//~^ option_option fn input(_: Option>) {} -//~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if +//~^ option_option fn output() -> Option> { - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if + //~^ option_option None } fn output_nested() -> Vec>> { - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if + //~^ option_option vec![None] } // The lint only generates one warning for this fn output_nested_nested() -> Option>> { - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum if + //~^ option_option None } struct Struct { x: Option>, - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum + //~^ option_option } impl Struct { fn struct_fn() -> Option> { - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum + //~^ option_option None } } trait Trait { fn trait_fn() -> Option>; - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum + //~^ option_option } enum Enum { Tuple(Option>), - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum + //~^ option_option Struct { x: Option> }, - //~^ ERROR: consider using `Option` instead of `Option>` or a custom enum + //~^ option_option } // The lint allows this @@ -88,7 +88,7 @@ mod issue_4298 { #[serde(default)] #[serde(borrow)] foo: Option>>, - //~^ ERROR: consider using `Option` instead of `Option>` or a custom + //~^ option_option } #[allow(clippy::option_option)] diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index 0cd048e400e40..c60cff91e8f19 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -4,11 +4,8 @@ error: consider using `Option` instead of `Option>` or a custom enu LL | const C: Option> = None; | ^^^^^^^^^^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui/option_option.rs:1:9 - | -LL | #![deny(clippy::option_option)] - | ^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::option-option` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::option_option)]` error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases --> tests/ui/option_option.rs:6:11 From 2fe9d4bfa6a29b6253752f0285a8fc814fdc843a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 12 Oct 2025 13:02:52 +0200 Subject: [PATCH 017/108] option_option: split part of diagnostic message into help message --- clippy_lints/src/types/option_option.rs | 18 +++++++--- tests/ui/option_option.stderr | 47 ++++++++++++++++++------- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/types/option_option.rs b/clippy_lints/src/types/option_option.rs index 10df007f2a13d..6d57bb6ef3a0d 100644 --- a/clippy_lints/src/types/option_option.rs +++ b/clippy_lints/src/types/option_option.rs @@ -1,6 +1,7 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::qpath_generic_tys; use clippy_utils::res::MaybeResPath; +use clippy_utils::source::snippet; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -13,12 +14,21 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ && let Some(arg) = qpath_generic_tys(qpath).next() && arg.basic_res().opt_def_id() == Some(def_id) { - span_lint( + span_lint_and_then( cx, OPTION_OPTION, hir_ty.span, - "consider using `Option` instead of `Option>` or a custom \ - enum if you need to distinguish all 3 cases", + // use just `T` here, as the inner type is not what's problematic + "use of `Option>`", + |diag| { + // but use the specific type here, as: + // - this is kind of a suggestion + // - it's printed right after the linted type + let inner_opt = snippet(cx, arg.span, "_"); + diag.help(format!( + "consider using `{inner_opt}`, or a custom enum if you need to distinguish all 3 cases" + )); + }, ); true } else { diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index c60cff91e8f19..1c39f9acae8e3 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -1,77 +1,100 @@ -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:4:10 | LL | const C: Option> = None; | ^^^^^^^^^^^^^^^^^^^ | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases = note: `-D clippy::option-option` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_option)]` -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:6:11 | LL | static S: Option> = None; | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:9:13 | LL | fn input(_: Option>) {} | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:12:16 | LL | fn output() -> Option> { | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:17:27 | LL | fn output_nested() -> Vec>> { | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:23:30 | LL | fn output_nested_nested() -> Option>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option>`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:29:8 | LL | x: Option>, | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:34:23 | LL | fn struct_fn() -> Option> { | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:41:22 | LL | fn trait_fn() -> Option>; | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:46:11 | LL | Tuple(Option>), | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:48:17 | LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option`, or a custom enum if you need to distinguish all 3 cases -error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases +error: use of `Option>` --> tests/ui/option_option.rs:90:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `Option>`, or a custom enum if you need to distinguish all 3 cases error: aborting due to 12 previous errors From 5f0f7d8543dd0f963543903cc1e5ab7423119de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 14 Oct 2025 15:21:27 +0200 Subject: [PATCH 018/108] return spans out of is_doc_comment to reduce reliance on .span() on attrs --- clippy_lints/src/four_forward_slashes.rs | 2 +- clippy_lints/src/undocumented_unsafe_blocks.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/four_forward_slashes.rs b/clippy_lints/src/four_forward_slashes.rs index a7b0edeb7991f..5a0cee40a1551 100644 --- a/clippy_lints/src/four_forward_slashes.rs +++ b/clippy_lints/src/four_forward_slashes.rs @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { .tcx .hir_attrs(item.hir_id()) .iter() - .filter(|i| i.is_doc_comment()) + .filter(|i| i.is_doc_comment().is_some()) .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span())); let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else { return; diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index ba0d4de5f3b3b..751e9b0034277 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -475,7 +475,7 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span { span.to(cx.tcx.hir_attrs(hir_id).iter().fold(span, |acc, attr| { - if attr.is_doc_comment() { + if attr.is_doc_comment().is_some() { return acc; } acc.to(attr.span()) From 1e1c1f381e10c1b34b8a03badb395b64417655a5 Mon Sep 17 00:00:00 2001 From: Nick Drozd Date: Tue, 14 Oct 2025 16:04:52 -0400 Subject: [PATCH 019/108] Add multiple-inherent-impl tests --- tests/ui/impl.rs | 22 +++++ .../multiple_inherent_impl_cfg.normal.stderr | 31 +++++++ tests/ui/multiple_inherent_impl_cfg.rs | 46 ++++++++++ tests/ui/multiple_inherent_impl_cfg.stderr | 91 +++++++++++++++++++ ...multiple_inherent_impl_cfg.withtest.stderr | 91 +++++++++++++++++++ 5 files changed, 281 insertions(+) create mode 100644 tests/ui/multiple_inherent_impl_cfg.normal.stderr create mode 100644 tests/ui/multiple_inherent_impl_cfg.rs create mode 100644 tests/ui/multiple_inherent_impl_cfg.stderr create mode 100644 tests/ui/multiple_inherent_impl_cfg.withtest.stderr diff --git a/tests/ui/impl.rs b/tests/ui/impl.rs index 15cb61c6eebd9..e6044cc507812 100644 --- a/tests/ui/impl.rs +++ b/tests/ui/impl.rs @@ -84,4 +84,26 @@ impl OneExpected {} impl OneExpected {} //~^ multiple_inherent_impl +// issue #8714 +struct Lifetime<'s> { + s: &'s str, +} + +impl Lifetime<'_> {} +impl Lifetime<'_> {} // false negative + +impl<'a> Lifetime<'a> {} +impl<'a> Lifetime<'a> {} // false negative + +impl<'b> Lifetime<'b> {} // false negative? + +impl Lifetime<'static> {} + +struct Generic { + g: Vec, +} + +impl Generic {} +impl Generic {} // false negative + fn main() {} diff --git a/tests/ui/multiple_inherent_impl_cfg.normal.stderr b/tests/ui/multiple_inherent_impl_cfg.normal.stderr new file mode 100644 index 0000000000000..b7d95fbfa8f58 --- /dev/null +++ b/tests/ui/multiple_inherent_impl_cfg.normal.stderr @@ -0,0 +1,31 @@ +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:11:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui/multiple_inherent_impl_cfg.rs:3:9 + | +LL | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:21:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/multiple_inherent_impl_cfg.rs b/tests/ui/multiple_inherent_impl_cfg.rs new file mode 100644 index 0000000000000..15c8b7c508781 --- /dev/null +++ b/tests/ui/multiple_inherent_impl_cfg.rs @@ -0,0 +1,46 @@ +//@compile-flags: --cfg test +#![deny(clippy::multiple_inherent_impl)] + +// issue #13040 + +fn main() {} + +struct A; + +impl A {} + +impl A {} +//~^ multiple_inherent_impl + +#[cfg(test)] +impl A {} // false positive +//~^ multiple_inherent_impl + +#[cfg(test)] +impl A {} +//~^ multiple_inherent_impl + +struct B; + +impl B {} + +#[cfg(test)] +impl B {} // false positive +//~^ multiple_inherent_impl + +impl B {} +//~^ multiple_inherent_impl + +#[cfg(test)] +impl B {} +//~^ multiple_inherent_impl + +#[cfg(test)] +struct C; + +#[cfg(test)] +impl C {} + +#[cfg(test)] +impl C {} +//~^ multiple_inherent_impl diff --git a/tests/ui/multiple_inherent_impl_cfg.stderr b/tests/ui/multiple_inherent_impl_cfg.stderr new file mode 100644 index 0000000000000..9d408ce3dec38 --- /dev/null +++ b/tests/ui/multiple_inherent_impl_cfg.stderr @@ -0,0 +1,91 @@ +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:12:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui/multiple_inherent_impl_cfg.rs:2:9 + | +LL | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:16:1 + | +LL | impl A {} // false positive + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:20:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:28:1 + | +LL | impl B {} // false positive + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:31:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:35:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:45:1 + | +LL | impl C {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:42:1 + | +LL | impl C {} + | ^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/multiple_inherent_impl_cfg.withtest.stderr b/tests/ui/multiple_inherent_impl_cfg.withtest.stderr new file mode 100644 index 0000000000000..1e98b1f18801e --- /dev/null +++ b/tests/ui/multiple_inherent_impl_cfg.withtest.stderr @@ -0,0 +1,91 @@ +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:11:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui/multiple_inherent_impl_cfg.rs:3:9 + | +LL | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:14:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:17:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 + | +LL | impl A {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:23:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:21:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:21:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:28:1 + | +LL | impl B {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:21:1 + | +LL | impl B {} + | ^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:36:1 + | +LL | impl C {} + | ^^^^^^^^^ + | +note: first implementation here + --> tests/ui/multiple_inherent_impl_cfg.rs:34:1 + | +LL | impl C {} + | ^^^^^^^^^ + +error: aborting due to 7 previous errors + From d449806ad044dd5ad00b38bb62e659bba6fc943a Mon Sep 17 00:00:00 2001 From: Zihan Date: Sat, 4 Oct 2025 12:56:27 -0400 Subject: [PATCH 020/108] `manual_unwrap_or(_default)`: don't lint if not safe to move scrutinee When matched against `Result` with copyable `Ok` variant, uncopyable scrutinee won't actually be moved. But `manual_unwrap_or`/`manual_unwrap_or_default` will force it to move, which can cause problem if scrutinee is used later. changelog: [`manual_unwrap_or`]: don't lint if not safe to move scrutinee changelog: [`manual_unwrap_or_default`]: don't lint if not safe to move scrutinee Signed-off-by: Zihan --- clippy_lints/src/matches/manual_unwrap_or.rs | 32 ++++++++++++++++++-- tests/ui/manual_unwrap_or.fixed | 14 +++++++++ tests/ui/manual_unwrap_or.rs | 14 +++++++++ tests/ui/manual_unwrap_or.stderr | 14 ++++++++- tests/ui/manual_unwrap_or_default.fixed | 14 +++++++++ tests/ui/manual_unwrap_or_default.rs | 14 +++++++++ tests/ui/manual_unwrap_or_default.stderr | 14 ++++++++- 7 files changed, 111 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index e00d0c7f3d6c1..abbc43d8e9b04 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,5 +1,5 @@ use clippy_utils::consts::ConstEvalCtxt; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; @@ -11,7 +11,8 @@ use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{expr_type_is_certain, implements_trait}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_copy}; +use clippy_utils::usage::local_used_after_expr; use clippy_utils::{is_default_equivalent, is_lint_allowed, peel_blocks, span_contains_comment}; use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; @@ -87,7 +88,9 @@ fn handle( binding_id: HirId, ) { // Only deal with situations where both alternatives return the same non-adjusted type. - if cx.typeck_results().expr_ty(body_some) != cx.typeck_results().expr_ty(body_none) { + if cx.typeck_results().expr_ty(body_some) != cx.typeck_results().expr_ty(body_none) + || !safe_to_move_scrutinee(cx, expr, condition) + { return; } @@ -185,6 +188,29 @@ fn find_type_name<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'static } } +/// Checks whether it is safe to move scrutinee. +/// It is not safe to move if: +/// 1. `scrutinee` is a `Result` that doesn't implemenet `Copy`, mainly because the `Err` +/// variant is not copyable. +/// 2. `expr` is a local variable that is used after the if-let-else expression. +/// ```rust,ignore +/// let foo: Result = Ok(0); +/// let v = if let Ok(v) = foo { v } else { 1 }; +/// let bar = foo; +/// ``` +fn safe_to_move_scrutinee(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>) -> bool { + if let Some(hir_id) = scrutinee.res_local_id() + && let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee) + && scrutinee_ty.is_diag_item(cx, sym::Result) + && !is_copy(cx, scrutinee_ty) + && local_used_after_expr(cx, hir_id, expr) + { + false + } else { + true + } +} + pub fn check_match<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index e12287a709395..8dd9e7a8a6fae 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -250,4 +250,18 @@ fn allowed_manual_unwrap_or_zero() -> u32 { Some(42).unwrap_or(0) } +fn issue_15807() { + let uncopyable_res: Result = Ok(1); + let _ = if let Ok(v) = uncopyable_res { v } else { 2 }; + + let x = uncopyable_res; + let _ = x.unwrap_or(2); + //~^ manual_unwrap_or + + let copyable_res: Result = Ok(1); + let _ = copyable_res.unwrap_or(2); + //~^ manual_unwrap_or + let _ = copyable_res; +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 53cffcab5b56c..f8e2c5f43745a 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -329,4 +329,18 @@ fn allowed_manual_unwrap_or_zero() -> u32 { } } +fn issue_15807() { + let uncopyable_res: Result = Ok(1); + let _ = if let Ok(v) = uncopyable_res { v } else { 2 }; + + let x = uncopyable_res; + let _ = if let Ok(v) = x { v } else { 2 }; + //~^ manual_unwrap_or + + let copyable_res: Result = Ok(1); + let _ = if let Ok(v) = copyable_res { v } else { 2 }; + //~^ manual_unwrap_or + let _ = copyable_res; +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 320e895fb8237..18764d1501d89 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -201,5 +201,17 @@ LL | | 0 LL | | } | |_____^ help: replace with: `Some(42).unwrap_or(0)` -error: aborting due to 18 previous errors +error: this pattern reimplements `Result::unwrap_or` + --> tests/ui/manual_unwrap_or.rs:337:13 + | +LL | let _ = if let Ok(v) = x { v } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `x.unwrap_or(2)` + +error: this pattern reimplements `Result::unwrap_or` + --> tests/ui/manual_unwrap_or.rs:341:13 + | +LL | let _ = if let Ok(v) = copyable_res { v } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `copyable_res.unwrap_or(2)` + +error: aborting due to 20 previous errors diff --git a/tests/ui/manual_unwrap_or_default.fixed b/tests/ui/manual_unwrap_or_default.fixed index 11023ac1142a7..0c64ed1826b26 100644 --- a/tests/ui/manual_unwrap_or_default.fixed +++ b/tests/ui/manual_unwrap_or_default.fixed @@ -128,3 +128,17 @@ mod issue14716 { }; } } + +fn issue_15807() { + let uncopyable_res: Result = Ok(1); + let _ = if let Ok(v) = uncopyable_res { v } else { 0 }; + + let x = uncopyable_res; + let _ = x.unwrap_or_default(); + //~^ manual_unwrap_or_default + + let copyable_res: Result = Ok(1); + let _ = copyable_res.unwrap_or_default(); + //~^ manual_unwrap_or_default + let _ = copyable_res; +} diff --git a/tests/ui/manual_unwrap_or_default.rs b/tests/ui/manual_unwrap_or_default.rs index bf06d9af7d219..e99f7d44dde19 100644 --- a/tests/ui/manual_unwrap_or_default.rs +++ b/tests/ui/manual_unwrap_or_default.rs @@ -169,3 +169,17 @@ mod issue14716 { }; } } + +fn issue_15807() { + let uncopyable_res: Result = Ok(1); + let _ = if let Ok(v) = uncopyable_res { v } else { 0 }; + + let x = uncopyable_res; + let _ = if let Ok(v) = x { v } else { 0 }; + //~^ manual_unwrap_or_default + + let copyable_res: Result = Ok(1); + let _ = if let Ok(v) = copyable_res { v } else { 0 }; + //~^ manual_unwrap_or_default + let _ = copyable_res; +} diff --git a/tests/ui/manual_unwrap_or_default.stderr b/tests/ui/manual_unwrap_or_default.stderr index 031100832b16f..a9e78afe6f065 100644 --- a/tests/ui/manual_unwrap_or_default.stderr +++ b/tests/ui/manual_unwrap_or_default.stderr @@ -97,5 +97,17 @@ LL | | 0 LL | | } | |_____^ help: replace it with: `Some(42).unwrap_or_default()` -error: aborting due to 9 previous errors +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:178:13 + | +LL | let _ = if let Ok(v) = x { v } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x.unwrap_or_default()` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:182:13 + | +LL | let _ = if let Ok(v) = copyable_res { v } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `copyable_res.unwrap_or_default()` + +error: aborting due to 11 previous errors From 8f613057221a94b3573c635e3bb08fa293c83984 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 16 Oct 2025 16:37:37 +0200 Subject: [PATCH 021/108] Merge commit 'd9fb15c4b1ebe9e7dc419e07f53af681d7860cbe' into clippy-subtree-update --- .github/workflows/remark.yml | 4 +- CHANGELOG.md | 377 +++++++--- book/src/development/adding_lints.md | 4 +- .../development/common_tools_writing_lints.md | 45 +- book/src/development/macro_expansions.md | 4 +- book/src/development/method_checking.md | 19 +- book/src/development/trait_checking.md | 9 +- book/src/lint_configuration.md | 20 + clippy_config/src/conf.rs | 18 +- clippy_config/src/types.rs | 10 +- clippy_dev/src/dogfood.rs | 2 +- clippy_dev/src/main.rs | 2 +- clippy_dev/src/new_lint.rs | 1 - .../src/arbitrary_source_item_ordering.rs | 2 +- clippy_lints/src/arc_with_non_send_sync.rs | 5 +- .../src/assertions_on_result_states.rs | 9 +- clippy_lints/src/assigning_clones.rs | 21 +- clippy_lints/src/booleans.rs | 7 +- clippy_lints/src/box_default.rs | 7 +- clippy_lints/src/casts/manual_dangling_ptr.rs | 5 +- clippy_lints/src/casts/unnecessary_cast.rs | 7 +- clippy_lints/src/cloned_ref_to_slice_refs.rs | 5 +- clippy_lints/src/cognitive_complexity.rs | 4 +- clippy_lints/src/collection_is_never_read.rs | 14 +- clippy_lints/src/declared_lints.rs | 3 + clippy_lints/src/deprecated_lints.rs | 1 - clippy_lints/src/dereference.rs | 4 +- .../src/derive/expl_impl_clone_on_copy.rs | 15 +- clippy_lints/src/derive/mod.rs | 4 +- clippy_lints/src/doc/missing_headers.rs | 12 +- clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/drop_forget_ref.rs | 5 +- clippy_lints/src/error_impl_error.rs | 4 +- clippy_lints/src/eta_reduction.rs | 14 +- clippy_lints/src/explicit_write.rs | 10 +- clippy_lints/src/fallible_impl_from.rs | 6 +- clippy_lints/src/floating_point_arithmetic.rs | 7 +- clippy_lints/src/format.rs | 1 - clippy_lints/src/format_args.rs | 16 +- clippy_lints/src/format_impl.rs | 13 +- clippy_lints/src/format_push_string.rs | 7 +- clippy_lints/src/from_over_into.rs | 9 +- clippy_lints/src/from_raw_with_void_ptr.rs | 5 +- clippy_lints/src/from_str_radix_10.rs | 4 +- clippy_lints/src/functions/must_use.rs | 2 +- .../src/functions/not_unsafe_ptr_arg_deref.rs | 5 +- clippy_lints/src/functions/ref_option.rs | 2 +- clippy_lints/src/functions/result.rs | 5 +- clippy_lints/src/if_let_mutex.rs | 4 +- clippy_lints/src/if_then_some_else_none.rs | 8 +- clippy_lints/src/ifs/branches_sharing_code.rs | 6 +- clippy_lints/src/ifs/ifs_same_cond.rs | 5 +- clippy_lints/src/implicit_hasher.rs | 6 +- clippy_lints/src/implicit_saturating_sub.rs | 6 +- clippy_lints/src/index_refutable_slice.rs | 5 +- clippy_lints/src/ineffective_open_options.rs | 8 +- clippy_lints/src/infinite_iter.rs | 5 +- clippy_lints/src/inherent_impl.rs | 55 +- clippy_lints/src/inherent_to_string.rs | 5 +- clippy_lints/src/iter_over_hash_type.rs | 6 +- clippy_lints/src/legacy_numeric_constants.rs | 30 +- clippy_lints/src/len_zero.rs | 7 +- clippy_lints/src/let_if_seq.rs | 4 +- clippy_lints/src/lib.rs | 10 +- clippy_lints/src/lifetimes.rs | 4 +- clippy_lints/src/lines_filter_map_ok.rs | 22 +- .../src/loops/char_indices_as_byte_indices.rs | 10 +- .../src/loops/explicit_into_iter_loop.rs | 8 +- clippy_lints/src/loops/explicit_iter_loop.rs | 6 +- clippy_lints/src/loops/for_kv_map.rs | 4 +- clippy_lints/src/loops/iter_next_loop.rs | 4 +- clippy_lints/src/loops/manual_find.rs | 10 +- clippy_lints/src/loops/manual_flatten.rs | 5 +- clippy_lints/src/loops/manual_memcpy.rs | 11 +- clippy_lints/src/loops/missing_spin_loop.rs | 4 +- clippy_lints/src/loops/mod.rs | 8 +- clippy_lints/src/loops/mut_range_bound.rs | 5 +- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/loops/same_item_push.rs | 11 +- .../src/loops/unused_enumerate_index.rs | 4 +- clippy_lints/src/loops/utils.rs | 15 +- clippy_lints/src/loops/while_let_loop.rs | 30 +- .../src/loops/while_let_on_iterator.rs | 7 +- clippy_lints/src/manual_abs_diff.rs | 5 +- clippy_lints/src/manual_assert.rs | 37 +- clippy_lints/src/manual_clamp.rs | 30 +- clippy_lints/src/manual_div_ceil.rs | 13 +- clippy_lints/src/manual_float_methods.rs | 7 +- clippy_lints/src/manual_hash_one.rs | 7 +- clippy_lints/src/manual_ignore_case_cmp.rs | 8 +- clippy_lints/src/manual_is_ascii_check.rs | 5 +- clippy_lints/src/manual_let_else.rs | 62 +- clippy_lints/src/manual_main_separator_str.rs | 5 +- clippy_lints/src/manual_option_as_slice.rs | 7 +- clippy_lints/src/manual_rem_euclid.rs | 5 +- clippy_lints/src/manual_retain.rs | 8 +- clippy_lints/src/manual_rotate.rs | 86 ++- clippy_lints/src/map_unit_fn.rs | 6 +- clippy_lints/src/match_result_ok.rs | 8 +- clippy_lints/src/matches/collapsible_match.rs | 58 +- .../matches/infallible_destructuring_match.rs | 5 +- clippy_lints/src/matches/manual_filter.rs | 11 +- clippy_lints/src/matches/manual_map.rs | 5 +- clippy_lints/src/matches/manual_ok_err.rs | 12 +- clippy_lints/src/matches/manual_unwrap_or.rs | 22 +- clippy_lints/src/matches/manual_utils.rs | 30 +- clippy_lints/src/matches/match_as_ref.rs | 10 +- .../src/matches/match_like_matches.rs | 196 ++++-- clippy_lints/src/matches/match_same_arms.rs | 7 +- .../src/matches/match_str_case_mismatch.rs | 4 +- clippy_lints/src/matches/match_wild_enum.rs | 5 +- .../src/matches/match_wild_err_arm.rs | 4 +- clippy_lints/src/matches/needless_match.rs | 10 +- clippy_lints/src/matches/redundant_guards.rs | 5 +- .../src/matches/redundant_pattern_match.rs | 9 +- clippy_lints/src/matches/single_match.rs | 2 +- clippy_lints/src/matches/try_err.rs | 5 +- clippy_lints/src/mem_replace.rs | 9 +- clippy_lints/src/methods/bytecount.rs | 12 +- .../src/methods/bytes_count_to_len.rs | 4 +- clippy_lints/src/methods/bytes_nth.rs | 4 +- ...se_sensitive_file_extension_comparisons.rs | 4 +- clippy_lints/src/methods/chars_cmp.rs | 5 +- clippy_lints/src/methods/clear_with_drain.rs | 6 +- clippy_lints/src/methods/clone_on_copy.rs | 23 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 37 +- .../src/methods/cloned_instead_of_copied.rs | 6 +- .../src/methods/double_ended_iterator_last.rs | 5 +- clippy_lints/src/methods/drain_collect.rs | 6 +- clippy_lints/src/methods/err_expect.rs | 7 +- clippy_lints/src/methods/expect_fun_call.rs | 25 +- clippy_lints/src/methods/extend_with_drain.rs | 8 +- clippy_lints/src/methods/filetype_is_file.rs | 4 +- clippy_lints/src/methods/filter_map.rs | 28 +- .../src/methods/filter_map_bool_then.rs | 7 +- .../src/methods/filter_map_identity.rs | 5 +- clippy_lints/src/methods/filter_map_next.rs | 4 +- clippy_lints/src/methods/flat_map_identity.rs | 7 +- clippy_lints/src/methods/flat_map_option.rs | 7 +- clippy_lints/src/methods/format_collect.rs | 4 +- .../methods/from_iter_instead_of_collect.rs | 5 +- clippy_lints/src/methods/get_first.rs | 4 +- clippy_lints/src/methods/get_unwrap.rs | 20 +- clippy_lints/src/methods/implicit_clone.rs | 29 +- .../src/methods/inefficient_to_string.rs | 21 +- clippy_lints/src/methods/inspect_for_each.rs | 4 +- clippy_lints/src/methods/into_iter_on_ref.rs | 13 +- clippy_lints/src/methods/io_other_error.rs | 5 +- clippy_lints/src/methods/is_empty.rs | 6 +- .../src/methods/iter_cloned_collect.rs | 5 +- clippy_lints/src/methods/iter_count.rs | 18 +- clippy_lints/src/methods/iter_filter.rs | 7 +- clippy_lints/src/methods/iter_kv_map.rs | 4 +- clippy_lints/src/methods/iter_next_slice.rs | 4 +- clippy_lints/src/methods/iter_nth.rs | 4 +- clippy_lints/src/methods/iter_nth_zero.rs | 5 +- .../iter_on_single_or_empty_collections.rs | 14 +- .../src/methods/iter_out_of_bounds.rs | 5 +- clippy_lints/src/methods/iter_skip_next.rs | 12 +- clippy_lints/src/methods/iter_skip_zero.rs | 5 +- .../src/methods/iterator_step_by_zero.rs | 4 +- .../src/methods/join_absolute_paths.rs | 4 +- clippy_lints/src/methods/manual_inspect.rs | 16 +- .../src/methods/manual_is_variant_and.rs | 8 +- clippy_lints/src/methods/manual_next_back.rs | 6 +- clippy_lints/src/methods/manual_ok_or.rs | 24 +- clippy_lints/src/methods/manual_repeat_n.rs | 5 +- .../methods/manual_saturating_arithmetic.rs | 5 +- clippy_lints/src/methods/manual_str_repeat.rs | 18 +- clippy_lints/src/methods/manual_try_fold.rs | 5 +- .../src/methods/map_all_any_identity.rs | 9 +- clippy_lints/src/methods/map_clone.rs | 18 +- .../src/methods/map_collect_result_unit.rs | 4 +- clippy_lints/src/methods/map_err_ignore.rs | 8 +- clippy_lints/src/methods/map_flatten.rs | 8 +- clippy_lints/src/methods/map_identity.rs | 11 +- clippy_lints/src/methods/map_unwrap_or.rs | 6 +- clippy_lints/src/methods/mod.rs | 127 ++-- clippy_lints/src/methods/mut_mutex_lock.rs | 5 +- clippy_lints/src/methods/needless_as_bytes.rs | 4 +- .../methods/needless_character_iteration.rs | 9 +- clippy_lints/src/methods/needless_collect.rs | 36 +- .../src/methods/needless_option_as_deref.rs | 8 +- .../src/methods/needless_option_take.rs | 4 +- clippy_lints/src/methods/no_effect_replace.rs | 4 +- .../src/methods/obfuscated_if_else.rs | 51 +- clippy_lints/src/methods/ok_expect.rs | 7 +- clippy_lints/src/methods/open_options.rs | 4 +- .../src/methods/option_as_ref_cloned.rs | 8 +- .../src/methods/option_as_ref_deref.rs | 10 +- .../src/methods/option_map_or_none.rs | 13 +- .../src/methods/option_map_unwrap_or.rs | 5 +- clippy_lints/src/methods/or_fun_call.rs | 7 +- clippy_lints/src/methods/or_then_unwrap.rs | 9 +- .../src/methods/path_buf_push_overwrite.rs | 8 +- .../src/methods/path_ends_with_ext.rs | 8 +- .../src/methods/range_zip_with_len.rs | 7 +- .../src/methods/read_line_without_trim.rs | 6 +- .../src/methods/readonly_write_lock.rs | 9 +- clippy_lints/src/methods/repeat_once.rs | 4 +- .../src/methods/result_map_or_else_none.rs | 10 +- clippy_lints/src/methods/return_and_then.rs | 4 +- clippy_lints/src/methods/search_is_some.rs | 14 +- clippy_lints/src/methods/skip_while_next.rs | 4 +- .../src/methods/sliced_string_as_bytes.rs | 4 +- clippy_lints/src/methods/str_splitn.rs | 10 +- .../src/methods/string_extend_chars.rs | 6 +- .../src/methods/string_lit_chars_any.rs | 9 +- .../methods/suspicious_command_arg_space.rs | 4 +- clippy_lints/src/methods/suspicious_map.rs | 8 +- .../src/methods/suspicious_to_owned.rs | 12 +- clippy_lints/src/methods/unbuffered_bytes.rs | 4 +- .../src/methods/uninit_assumed_init.rs | 4 +- clippy_lints/src/methods/unit_hash.rs | 4 +- .../src/methods/unnecessary_filter_map.rs | 20 +- clippy_lints/src/methods/unnecessary_fold.rs | 9 +- .../src/methods/unnecessary_get_then_check.rs | 6 +- .../src/methods/unnecessary_iter_cloned.rs | 5 +- clippy_lints/src/methods/unnecessary_join.rs | 6 +- .../src/methods/unnecessary_lazy_eval.rs | 6 +- .../src/methods/unnecessary_literal_unwrap.rs | 17 +- .../src/methods/unnecessary_map_or.rs | 13 +- .../methods/unnecessary_option_map_or_else.rs | 111 +++ .../methods/unnecessary_result_map_or_else.rs | 4 +- .../src/methods/unnecessary_sort_by.rs | 8 +- .../src/methods/unnecessary_to_owned.rs | 58 +- .../src/methods/unused_enumerate_index.rs | 8 +- .../src/methods/unwrap_expect_used.rs | 7 +- clippy_lints/src/methods/useless_asref.rs | 19 +- .../methods/useless_nonzero_new_unchecked.rs | 4 +- clippy_lints/src/methods/utils.rs | 8 +- .../src/methods/vec_resize_to_zero.rs | 8 +- .../src/methods/verbose_file_reads.rs | 11 +- clippy_lints/src/methods/waker_clone_wake.rs | 4 +- .../src/methods/wrong_self_convention.rs | 1 - clippy_lints/src/minmax.rs | 7 +- clippy_lints/src/missing_fields_in_debug.rs | 16 +- .../src/mixed_read_write_in_expression.rs | 7 +- clippy_lints/src/mutex_atomic.rs | 103 ++- clippy_lints/src/needless_continue.rs | 251 ++++--- clippy_lints/src/needless_for_each.rs | 5 +- clippy_lints/src/needless_late_init.rs | 4 +- clippy_lints/src/needless_maybe_sized.rs | 2 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 2 - clippy_lints/src/needless_pass_by_value.rs | 13 +- clippy_lints/src/needless_question_mark.rs | 4 +- clippy_lints/src/no_effect.rs | 7 +- clippy_lints/src/non_canonical_impls.rs | 165 +++-- clippy_lints/src/non_std_lazy_statics.rs | 11 +- clippy_lints/src/only_used_in_recursion.rs | 11 +- .../src/operators/arithmetic_side_effects.rs | 6 +- clippy_lints/src/operators/bit_mask.rs | 1 - clippy_lints/src/operators/cmp_owned.rs | 15 +- clippy_lints/src/operators/duration_subsec.rs | 8 +- .../src/operators/integer_division.rs | 4 +- clippy_lints/src/option_if_let_else.rs | 16 +- clippy_lints/src/panic_in_result_fn.rs | 4 +- clippy_lints/src/partialeq_to_none.rs | 18 +- clippy_lints/src/pathbuf_init_then_push.rs | 10 +- .../src/permissions_set_readonly_false.rs | 7 +- clippy_lints/src/ptr.rs | 13 +- clippy_lints/src/pub_underscore_fields.rs | 4 +- clippy_lints/src/question_mark.rs | 59 +- clippy_lints/src/ranges.rs | 12 +- clippy_lints/src/redundant_clone.rs | 5 +- clippy_lints/src/redundant_slicing.rs | 8 +- clippy_lints/src/regex.rs | 5 +- clippy_lints/src/replace_box.rs | 111 +++ .../src/reserve_after_initialization.rs | 5 +- clippy_lints/src/returns/let_and_return.rs | 5 +- .../needless_return_with_question_mark.rs | 5 +- clippy_lints/src/set_contains_or_insert.rs | 4 +- clippy_lints/src/shadow.rs | 4 +- .../src/significant_drop_tightening.rs | 13 +- .../src/single_char_lifetime_names.rs | 6 +- clippy_lints/src/single_option_map.rs | 11 +- clippy_lints/src/size_of_ref.rs | 5 +- .../src/slow_vector_initialization.rs | 18 +- clippy_lints/src/string_patterns.rs | 9 +- clippy_lints/src/strings.rs | 14 +- clippy_lints/src/strlen_on_c_strings.rs | 6 +- clippy_lints/src/swap.rs | 13 +- clippy_lints/src/swap_ptr_to_ref.rs | 5 +- clippy_lints/src/time_subtraction.rs | 18 +- clippy_lints/src/to_digit_is_some.rs | 5 +- clippy_lints/src/trait_bounds.rs | 2 +- .../src/transmute/transmute_null_to_fn.rs | 5 +- .../src/transmute/transmuting_null.rs | 5 +- clippy_lints/src/tuple_array_conversions.rs | 5 +- clippy_lints/src/types/box_collection.rs | 5 +- clippy_lints/src/types/mod.rs | 2 +- clippy_lints/src/types/option_option.rs | 5 +- clippy_lints/src/types/owned_cow.rs | 9 +- clippy_lints/src/types/rc_buffer.rs | 11 +- clippy_lints/src/types/rc_mutex.rs | 6 +- .../src/types/redundant_allocation.rs | 7 +- clippy_lints/src/unconditional_recursion.rs | 5 +- .../src/undocumented_unsafe_blocks.rs | 229 ++++--- clippy_lints/src/uninit_vec.rs | 17 +- clippy_lints/src/unnecessary_literal_bound.rs | 4 +- .../src/unnecessary_map_on_constructor.rs | 4 +- .../src/unnecessary_owned_empty_strings.rs | 4 +- .../src/unnecessary_struct_initialization.rs | 5 +- clippy_lints/src/unnecessary_wraps.rs | 5 +- clippy_lints/src/unused_io_amount.rs | 10 +- clippy_lints/src/unused_peekable.rs | 19 +- clippy_lints/src/unused_result_ok.rs | 4 +- clippy_lints/src/unwrap.rs | 10 +- clippy_lints/src/use_self.rs | 19 +- clippy_lints/src/useless_conversion.rs | 30 +- clippy_lints/src/utils/author.rs | 26 +- clippy_lints/src/vec.rs | 8 +- clippy_lints/src/vec_init_then_push.rs | 5 +- clippy_lints/src/volatile_composites.rs | 180 +++++ clippy_lints/src/zero_repeat_side_effects.rs | 43 +- clippy_lints/src/zero_sized_map_values.rs | 5 +- clippy_lints/src/zombie_processes.rs | 8 +- clippy_lints_internal/Cargo.toml | 1 + clippy_lints_internal/src/internal_paths.rs | 5 + clippy_lints_internal/src/lib.rs | 3 + clippy_lints_internal/src/produce_ice.rs | 4 +- .../src/unnecessary_def_path.rs | 7 +- clippy_lints_internal/src/unusual_names.rs | 99 +++ clippy_utils/README.md | 2 +- clippy_utils/src/ast_utils/mod.rs | 4 +- clippy_utils/src/check_proc_macro.rs | 19 +- clippy_utils/src/consts.rs | 21 +- clippy_utils/src/higher.rs | 19 +- clippy_utils/src/lib.rs | 212 ++---- clippy_utils/src/mir/mod.rs | 2 +- clippy_utils/src/mir/possible_borrower.rs | 2 - clippy_utils/src/mir/possible_origin.rs | 1 - clippy_utils/src/paths.rs | 15 +- clippy_utils/src/qualify_min_const_fn.rs | 4 +- clippy_utils/src/res.rs | 643 ++++++++++++++++++ clippy_utils/src/source.rs | 1 - clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/sym.rs | 14 +- clippy_utils/src/ty/mod.rs | 99 +-- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- clippy_utils/src/usage.rs | 5 +- clippy_utils/src/visitors.rs | 13 +- lintcheck/src/config.rs | 2 +- lintcheck/src/input.rs | 2 +- lintcheck/src/main.rs | 4 +- rust-toolchain.toml | 2 +- src/driver.rs | 7 +- src/main.rs | 2 - tests/compile-test.rs | 3 +- tests/symbols-used.rs | 1 - .../config_fail/Cargo.stderr | 7 + .../config_fail/Cargo.toml | 7 + .../config_fail/clippy.toml | 1 + .../config_fail/src/main.rs | 3 + .../crate_fail/Cargo.stderr | 57 ++ .../crate_fail/Cargo.toml | 7 + .../crate_fail/clippy.toml | 1 + .../crate_fail/src/b.rs | 6 + .../crate_fail/src/main.rs | 41 ++ .../file_fail/Cargo.stderr | 58 ++ .../file_fail/Cargo.toml | 7 + .../file_fail/clippy.toml | 1 + .../multiple_inherent_impl/file_fail/src/c.rs | 21 + .../file_fail/src/main.rs | 40 ++ .../module_fail/Cargo.stderr | 41 ++ .../module_fail/Cargo.toml | 7 + .../module_fail/clippy.toml | 1 + .../module_fail/src/c.rs | 15 + .../module_fail/src/main.rs | 40 ++ .../fail/Cargo.stderr | 8 +- .../toml_unknown_key/conf_unknown_key.stderr | 6 + .../undocumented_unsafe_blocks.default.stderr | 136 +++- ...undocumented_unsafe_blocks.disabled.stderr | 98 ++- .../undocumented_unsafe_blocks.rs | 62 ++ ...mented_unsafe_blocks_fixable.default.fixed | 20 + ...ented_unsafe_blocks_fixable.default.stderr | 22 + ...ented_unsafe_blocks_fixable.disabled.fixed | 20 + ...nted_unsafe_blocks_fixable.disabled.stderr | 22 + .../undocumented_unsafe_blocks_fixable.rs | 20 + tests/ui-toml/use_self/default/clippy.toml | 0 tests/ui-toml/use_self/disabled/clippy.toml | 1 + tests/ui-toml/use_self/use_self.default.fixed | 17 + .../ui-toml/use_self/use_self.default.stderr | 17 + .../ui-toml/use_self/use_self.disabled.fixed | 17 + .../ui-toml/use_self/use_self.disabled.stderr | 11 + tests/ui-toml/use_self/use_self.rs | 17 + tests/ui/author/blocks.stdout | 4 +- tests/ui/author/call.stdout | 2 +- tests/ui/author/issue_3849.stdout | 2 +- tests/ui/char_lit_as_u8_unfixable.rs | 1 - tests/ui/char_lit_as_u8_unfixable.stderr | 2 +- tests/ui/clone_on_ref_ptr.fixed | 30 + tests/ui/clone_on_ref_ptr.rs | 30 + tests/ui/clone_on_ref_ptr.stderr | 8 +- tests/ui/collapsible_match.rs | 41 +- tests/ui/collapsible_match.stderr | 33 +- tests/ui/crashes/ice-15684.rs | 10 + tests/ui/crashes/ice-15684.stderr | 16 + tests/ui/derive.rs | 15 +- tests/ui/derive.stderr | 55 +- tests/ui/explicit_write_in_test.rs | 9 + tests/ui/explicit_write_in_test.stderr | 0 tests/ui/impl.rs | 15 +- tests/ui/impl.stderr | 16 +- tests/ui/infinite_loops.rs | 2 +- .../ui/legacy_numeric_constants_unfixable.rs | 11 + tests/ui/manual_assert.edition2018.fixed | 98 +++ tests/ui/manual_assert.edition2018.stderr | 114 ++-- tests/ui/manual_assert.edition2021.fixed | 98 +++ tests/ui/manual_assert.edition2021.stderr | 114 ++-- tests/ui/manual_assert.rs | 2 - tests/ui/manual_div_ceil.fixed | 5 + tests/ui/manual_div_ceil.rs | 5 + tests/ui/manual_div_ceil.stderr | 8 +- tests/ui/manual_rotate.fixed | 21 + tests/ui/manual_rotate.rs | 21 + tests/ui/manual_rotate.stderr | 48 +- tests/ui/manual_unwrap_or_default.fixed | 9 + tests/ui/manual_unwrap_or_default.rs | 9 + tests/ui/manual_unwrap_or_default.stderr | 4 +- ...o.fixed => match_like_matches_macro.fixed} | 5 +- ...s_macro.rs => match_like_matches_macro.rs} | 5 +- ...stderr => match_like_matches_macro.stderr} | 28 +- tests/ui/must_use_unit_unfixable.rs | 2 - tests/ui/must_use_unit_unfixable.stderr | 8 +- tests/ui/mutex_atomic.fixed | 67 ++ tests/ui/mutex_atomic.rs | 47 +- tests/ui/mutex_atomic.stderr | 148 ++-- tests/ui/mutex_atomic_unfixable.rs | 13 + tests/ui/mutex_atomic_unfixable.stderr | 17 + tests/ui/needless_borrow_pat.fixed | 2 - tests/ui/needless_borrow_pat.rs | 2 - tests/ui/needless_borrow_pat.stderr | 24 +- tests/ui/needless_continue.rs | 98 +++ tests/ui/needless_continue.stderr | 18 +- tests/ui/non_canonical_clone_impl.fixed | 53 +- tests/ui/non_canonical_clone_impl.rs | 56 +- tests/ui/non_canonical_clone_impl.stderr | 14 +- tests/ui/non_canonical_partial_ord_impl.fixed | 71 ++ tests/ui/non_canonical_partial_ord_impl.rs | 73 ++ .../ui/non_canonical_partial_ord_impl.stderr | 25 +- tests/ui/option_if_let_else.fixed | 3 +- tests/ui/option_if_let_else.rs | 3 +- tests/ui/option_if_let_else.stderr | 58 +- tests/ui/or_fun_call.fixed | 1 + tests/ui/or_fun_call.rs | 1 + tests/ui/or_fun_call.stderr | 98 +-- tests/ui/replace_box.fixed | 72 ++ tests/ui/replace_box.rs | 72 ++ tests/ui/replace_box.stderr | 52 ++ tests/ui/unnecessary_option_map_or_else.fixed | 75 ++ tests/ui/unnecessary_option_map_or_else.rs | 82 +++ .../ui/unnecessary_option_map_or_else.stderr | 47 ++ tests/ui/unnecessary_safety_comment.stderr | 36 +- tests/ui/use_self_structs.fixed | 134 ++++ tests/ui/use_self_structs.rs | 134 ++++ tests/ui/use_self_structs.stderr | 77 +++ tests/ui/volatile_composites.rs | 221 ++++++ tests/ui/volatile_composites.stderr | 89 +++ tests/ui/while_let_loop.rs | 13 + tests/ui/while_let_loop.stderr | 29 +- tests/ui/zero_repeat_side_effects.fixed | 40 +- tests/ui/zero_repeat_side_effects.rs | 1 + tests/ui/zero_repeat_side_effects.stderr | 72 +- ...ro_repeat_side_effects_never_pattern.fixed | 10 + .../zero_repeat_side_effects_never_pattern.rs | 9 + ...o_repeat_side_effects_never_pattern.stderr | 16 + .../ui/zero_repeat_side_effects_unfixable.rs | 13 + .../zero_repeat_side_effects_unfixable.stderr | 20 + triagebot.toml | 4 + 470 files changed, 7416 insertions(+), 2694 deletions(-) create mode 100644 clippy_lints/src/methods/unnecessary_option_map_or_else.rs create mode 100644 clippy_lints/src/replace_box.rs create mode 100644 clippy_lints/src/volatile_composites.rs create mode 100644 clippy_lints_internal/src/unusual_names.rs create mode 100644 clippy_utils/src/res.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.stderr create mode 100644 tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/config_fail/src/main.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.stderr create mode 100644 tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/crate_fail/clippy.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/crate_fail/src/b.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/crate_fail/src/main.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.stderr create mode 100644 tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/file_fail/clippy.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/file_fail/src/c.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/file_fail/src/main.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.stderr create mode 100644 tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/module_fail/clippy.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/module_fail/src/c.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/module_fail/src/main.rs create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.fixed create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.stderr create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.fixed create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.stderr create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs create mode 100644 tests/ui-toml/use_self/default/clippy.toml create mode 100644 tests/ui-toml/use_self/disabled/clippy.toml create mode 100644 tests/ui-toml/use_self/use_self.default.fixed create mode 100644 tests/ui-toml/use_self/use_self.default.stderr create mode 100644 tests/ui-toml/use_self/use_self.disabled.fixed create mode 100644 tests/ui-toml/use_self/use_self.disabled.stderr create mode 100644 tests/ui-toml/use_self/use_self.rs create mode 100644 tests/ui/crashes/ice-15684.rs create mode 100644 tests/ui/crashes/ice-15684.stderr create mode 100644 tests/ui/explicit_write_in_test.rs create mode 100644 tests/ui/explicit_write_in_test.stderr create mode 100644 tests/ui/manual_assert.edition2018.fixed create mode 100644 tests/ui/manual_assert.edition2021.fixed rename tests/ui/{match_expr_like_matches_macro.fixed => match_like_matches_macro.fixed} (97%) rename tests/ui/{match_expr_like_matches_macro.rs => match_like_matches_macro.rs} (97%) rename tests/ui/{match_expr_like_matches_macro.stderr => match_like_matches_macro.stderr} (84%) create mode 100644 tests/ui/mutex_atomic.fixed create mode 100644 tests/ui/mutex_atomic_unfixable.rs create mode 100644 tests/ui/mutex_atomic_unfixable.stderr create mode 100644 tests/ui/replace_box.fixed create mode 100644 tests/ui/replace_box.rs create mode 100644 tests/ui/replace_box.stderr create mode 100644 tests/ui/unnecessary_option_map_or_else.fixed create mode 100644 tests/ui/unnecessary_option_map_or_else.rs create mode 100644 tests/ui/unnecessary_option_map_or_else.stderr create mode 100644 tests/ui/use_self_structs.fixed create mode 100644 tests/ui/use_self_structs.rs create mode 100644 tests/ui/use_self_structs.stderr create mode 100644 tests/ui/volatile_composites.rs create mode 100644 tests/ui/volatile_composites.stderr create mode 100644 tests/ui/zero_repeat_side_effects_never_pattern.fixed create mode 100644 tests/ui/zero_repeat_side_effects_never_pattern.rs create mode 100644 tests/ui/zero_repeat_side_effects_never_pattern.stderr create mode 100644 tests/ui/zero_repeat_side_effects_unfixable.rs create mode 100644 tests/ui/zero_repeat_side_effects_unfixable.stderr diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index c9d350ee0b304..c2cc48ab95116 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -17,9 +17,9 @@ jobs: persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: - node-version: '18.x' + node-version: '20.x' - name: Install remark run: npm install remark-cli remark-lint remark-lint-maximum-line-length@^3.1.3 remark-preset-lint-recommended remark-gfm diff --git a/CHANGELOG.md b/CHANGELOG.md index 30781d3d33fba..37d46d3496677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,19 +28,19 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### Enhancements -* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods - [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) - [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) +* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods + [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) + [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) [#15074](https://github.com/rust-lang/rust-clippy/pull/15074) -* [`incompatible_msrv`] now recognizes types exceeding MSRV +* [`incompatible_msrv`] now recognizes types exceeding MSRV [#15296](https://github.com/rust-lang/rust-clippy/pull/15296) -* [`incompatible_msrv`] now checks the right MSRV when in a `const` context +* [`incompatible_msrv`] now checks the right MSRV when in a `const` context [#15297](https://github.com/rust-lang/rust-clippy/pull/15297) -* [`zero_ptr`] now lints in `const` context as well +* [`zero_ptr`] now lints in `const` context as well [#15152](https://github.com/rust-lang/rust-clippy/pull/15152) * [`map_identity`],[`flat_map_identity`] now recognizes `|[x, y]| [x, y]` as an identity function [#15229](https://github.com/rust-lang/rust-clippy/pull/15229) -* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag +* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag [#15222](https://github.com/rust-lang/rust-clippy/pull/15222) ### False Positive Fixes @@ -51,7 +51,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15319](https://github.com/rust-lang/rust-clippy/pull/15319) * [`unused_async`] fixed FP on function with `todo!` [#15308](https://github.com/rust-lang/rust-clippy/pull/15308) -* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes +* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes on `use` statements [#15318](https://github.com/rust-lang/rust-clippy/pull/15318) * [`pattern_type_mismatch`] fixed FP in external macro @@ -64,7 +64,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15314](https://github.com/rust-lang/rust-clippy/pull/15314) * [`ptr_as_ptr`] fixed wrong suggestions with turbo fish [#15289](https://github.com/rust-lang/rust-clippy/pull/15289) -* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe +* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe to switch the range type [#14432](https://github.com/rust-lang/rust-clippy/pull/14432) * [`ptr_arg`] fixed FP with underscore binding to `&T` or `&mut T` argument @@ -129,7 +129,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15022](https://github.com/rust-lang/rust-clippy/pull/15022) * [`collapsible_else_if`] fixed FP on conditionally compiled stmt [#14906](https://github.com/rust-lang/rust-clippy/pull/14906) -* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing +* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing non-`Sized` trait bounds [#15080](https://github.com/rust-lang/rust-clippy/pull/15080) * [`question_mark`] fixed FP when else branch of let-else contains `#[cfg]` @@ -137,7 +137,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### ICE Fixes -* [`single_match`] fixed ICE with deref patterns and string literals +* [`single_match`] fixed ICE with deref patterns and string literals [#15124](https://github.com/rust-lang/rust-clippy/pull/15124) * [`needless_doctest_main`] fixed panic when doctest is invalid [#15052](https://github.com/rust-lang/rust-clippy/pull/15052) @@ -146,11 +146,11 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### Documentation Improvements -* [`manual_is_variant_and`] improved documentation to include equality comparison patterns +* [`manual_is_variant_and`] improved documentation to include equality comparison patterns [#15239](https://github.com/rust-lang/rust-clippy/pull/15239) * [`uninlined_format_args`] improved documentation with example of how to fix a `{:?}` parameter [#15228](https://github.com/rust-lang/rust-clippy/pull/15228) -* [`undocumented_unsafe_blocks`] improved documentation wording +* [`undocumented_unsafe_blocks`] improved documentation wording [#15213](https://github.com/rust-lang/rust-clippy/pull/15213) ## Rust 1.89 @@ -292,7 +292,7 @@ Current stable, released 2025-06-26 [#14408](https://github.com/rust-lang/rust-clippy/pull/14408) * [`iter_kv_map`] now recognizes references on maps [#14596](https://github.com/rust-lang/rust-clippy/pull/14596) -* [`empty_enum_variants_with_brackets`] no longer lints reachable enums or enums used +* [`empty_enum_variants_with_brackets`] no longer lints reachable enums or enums used as functions within same crate [#12971](https://github.com/rust-lang/rust-clippy/pull/12971) * [`needless_lifetimes`] now checks for lifetime uses in closures [#14608](https://github.com/rust-lang/rust-clippy/pull/14608) @@ -928,50 +928,50 @@ Released 2024-02-08 ### New Lints -- [`infinite_loop`] +* [`infinite_loop`] [#11829](https://github.com/rust-lang/rust-clippy/pull/11829) -- [`ineffective_open_options`] +* [`ineffective_open_options`] [#11902](https://github.com/rust-lang/rust-clippy/pull/11902) -- [`uninhabited_references`] +* [`uninhabited_references`] [#11878](https://github.com/rust-lang/rust-clippy/pull/11878) -- [`repeat_vec_with_capacity`] +* [`repeat_vec_with_capacity`] [#11597](https://github.com/rust-lang/rust-clippy/pull/11597) -- [`test_attr_in_doctest`] +* [`test_attr_in_doctest`] [#11872](https://github.com/rust-lang/rust-clippy/pull/11872) -- [`option_map_or_err_ok`] +* [`option_map_or_err_ok`] [#11864](https://github.com/rust-lang/rust-clippy/pull/11864) -- [`join_absolute_paths`] +* [`join_absolute_paths`] [#11453](https://github.com/rust-lang/rust-clippy/pull/11453) -- [`impl_hash_borrow_with_str_and_bytes`] +* [`impl_hash_borrow_with_str_and_bytes`] [#11781](https://github.com/rust-lang/rust-clippy/pull/11781) -- [`iter_over_hash_type`] +* [`iter_over_hash_type`] [#11791](https://github.com/rust-lang/rust-clippy/pull/11791) ### Moves and Deprecations -- Renamed `blocks_in_if_conditions` to [`blocks_in_conditions`] +* Renamed `blocks_in_if_conditions` to [`blocks_in_conditions`] [#11853](https://github.com/rust-lang/rust-clippy/pull/11853) -- Moved [`implied_bounds_in_impls`] to `complexity` (Now warn-by-default) +* Moved [`implied_bounds_in_impls`] to `complexity` (Now warn-by-default) [#11867](https://github.com/rust-lang/rust-clippy/pull/11867) -- Moved [`if_same_then_else`] to `style` (Now warn-by-default) +* Moved [`if_same_then_else`] to `style` (Now warn-by-default) [#11809](https://github.com/rust-lang/rust-clippy/pull/11809) ### Enhancements -- [`missing_safety_doc`], [`unnecessary_safety_doc`], [`missing_panics_doc`], [`missing_errors_doc`]: +* [`missing_safety_doc`], [`unnecessary_safety_doc`], [`missing_panics_doc`], [`missing_errors_doc`]: Added the [`check-private-items`] configuration to enable lints on private items [#11842](https://github.com/rust-lang/rust-clippy/pull/11842) ### ICE Fixes -- [`impl_trait_in_params`]: No longer crashes when a function has generics but no function parameters +* [`impl_trait_in_params`]: No longer crashes when a function has generics but no function parameters [#11804](https://github.com/rust-lang/rust-clippy/pull/11804) -- [`unused_enumerate_index`]: No longer crashes on empty tuples +* [`unused_enumerate_index`]: No longer crashes on empty tuples [#11756](https://github.com/rust-lang/rust-clippy/pull/11756) ### Others -- Clippy now respects the `CARGO` environment value +* Clippy now respects the `CARGO` environment value [#11944](https://github.com/rust-lang/rust-clippy/pull/11944) ## Rust 1.75 @@ -997,7 +997,6 @@ Released 2023-12-28 * [`manual_hash_one`] [#11556](https://github.com/rust-lang/rust-clippy/pull/11556) - ### Moves and Deprecations * Moved [`read_zero_byte_vec`] to `nursery` (Now allow-by-default) @@ -1073,7 +1072,7 @@ Released 2023-11-16 ### Enhancements -* [`undocumented_unsafe_blocks`]: The config values [`accept-comment-above-statement`] and +* [`undocumented_unsafe_blocks`]: The config values [`accept-comment-above-statement`] and [`accept-comment-above-attributes`] are now `true` by default [#11170](https://github.com/rust-lang/rust-clippy/pull/11170) * [`explicit_iter_loop`]: Added [`enforce-iter-loop-reborrow`] to disable reborrow linting by default @@ -2254,7 +2253,6 @@ Released 2022-09-22 * [`explicit_auto_deref`] [#8355](https://github.com/rust-lang/rust-clippy/pull/8355) - ### Moves and Deprecations * Moved [`format_push_string`] to `restriction` (now allow-by-default) @@ -2455,10 +2453,10 @@ Released 2022-08-11 * [`redundant_allocation`]: No longer lints on fat pointers that would become thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813) * [`derive_partial_eq_without_eq`]: - * Handle differing predicates applied by `#[derive(PartialEq)]` and + * Handle differing predicates applied by `#[derive(PartialEq)]` and `#[derive(Eq)]` [#8869](https://github.com/rust-lang/rust-clippy/pull/8869) - * No longer lints on non-public types and better handles generics + * No longer lints on non-public types and better handles generics [#8950](https://github.com/rust-lang/rust-clippy/pull/8950) * [`empty_line_after_outer_attr`]: No longer lints empty lines in inner string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892) @@ -2952,12 +2950,12 @@ Released 2022-02-24 [#7957](https://github.com/rust-lang/rust-clippy/pull/7957) * [`needless_borrow`] [#7977](https://github.com/rust-lang/rust-clippy/pull/7977) - * Lint when a borrow is auto-dereffed more than once - * Lint in the trailing expression of a block for a match arm + * Lint when a borrow is auto-dereffed more than once + * Lint in the trailing expression of a block for a match arm * [`strlen_on_c_strings`] [8001](https://github.com/rust-lang/rust-clippy/pull/8001) - * Lint when used without a fully-qualified path - * Suggest removing the surrounding unsafe block when possible + * Lint when used without a fully-qualified path + * Suggest removing the surrounding unsafe block when possible * [`non_ascii_literal`]: Now also lints on `char`s, not just `string`s [#8034](https://github.com/rust-lang/rust-clippy/pull/8034) * [`single_char_pattern`]: Now also lints on `split_inclusive`, `split_once`, @@ -3068,7 +3066,7 @@ Released 2022-02-24 [#7813](https://github.com/rust-lang/rust-clippy/pull/7813) * New and improved issue templates [#8032](https://github.com/rust-lang/rust-clippy/pull/8032) -* _Dev:_ Add `cargo dev lint` command, to run your modified Clippy version on a +* *Dev:* Add `cargo dev lint` command, to run your modified Clippy version on a file [#7917](https://github.com/rust-lang/rust-clippy/pull/7917) ## Rust 1.58 @@ -3278,15 +3276,15 @@ Released 2021-12-02 [#7566](https://github.com/rust-lang/rust-clippy/pull/7566) * [`option_if_let_else`]: Multiple fixes [#7573](https://github.com/rust-lang/rust-clippy/pull/7573) - * `break` and `continue` statements local to the would-be closure are + * `break` and `continue` statements local to the would-be closure are allowed - * Don't lint in const contexts - * Don't lint when yield expressions are used - * Don't lint when the captures made by the would-be closure conflict with + * Don't lint in const contexts + * Don't lint when yield expressions are used + * Don't lint when the captures made by the would-be closure conflict with the other branch - * Don't lint when a field of a local is used when the type could be + * Don't lint when a field of a local is used when the type could be potentially moved from - * In some cases, don't lint when scrutinee expression conflicts with the + * In some cases, don't lint when scrutinee expression conflicts with the captures of the would-be closure * [`redundant_allocation`]: No longer lints on `Box>` which replaces wide pointers with thin pointers @@ -3535,124 +3533,124 @@ Released 2021-07-29 ### New Lints -- [`ref_binding_to_reference`] +* [`ref_binding_to_reference`] [#7105](https://github.com/rust-lang/rust-clippy/pull/7105) -- [`needless_bitwise_bool`] +* [`needless_bitwise_bool`] [#7133](https://github.com/rust-lang/rust-clippy/pull/7133) -- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225) -- [`manual_str_repeat`] +* [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225) +* [`manual_str_repeat`] [#7265](https://github.com/rust-lang/rust-clippy/pull/7265) -- [`suspicious_splitn`] +* [`suspicious_splitn`] [#7292](https://github.com/rust-lang/rust-clippy/pull/7292) ### Moves and Deprecations -- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of +* Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of the new `avoid-breaking-exported-api` config option (see [Enhancements](#1-54-enhancements)) [#7187](https://github.com/rust-lang/rust-clippy/pull/7187) -- Move [`inconsistent_struct_constructor`] to `pedantic` +* Move [`inconsistent_struct_constructor`] to `pedantic` [#7193](https://github.com/rust-lang/rust-clippy/pull/7193) -- Move [`needless_borrow`] to `style` (now warn-by-default) +* Move [`needless_borrow`] to `style` (now warn-by-default) [#7254](https://github.com/rust-lang/rust-clippy/pull/7254) -- Move [`suspicious_operation_groupings`] to `nursery` +* Move [`suspicious_operation_groupings`] to `nursery` [#7266](https://github.com/rust-lang/rust-clippy/pull/7266) -- Move [`semicolon_if_nothing_returned`] to `pedantic` +* Move [`semicolon_if_nothing_returned`] to `pedantic` [#7268](https://github.com/rust-lang/rust-clippy/pull/7268) ### Enhancements -- [`while_let_on_iterator`]: Now also lints in nested loops +* [`while_let_on_iterator`]: Now also lints in nested loops [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix` +* [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix` [#7156](https://github.com/rust-lang/rust-clippy/pull/7156) -- [`needless_collect`]: Now also lints on assignments with type annotations +* [`needless_collect`]: Now also lints on assignments with type annotations [#7163](https://github.com/rust-lang/rust-clippy/pull/7163) -- [`if_then_some_else_none`]: Now works with the MSRV config +* [`if_then_some_else_none`]: Now works with the MSRV config [#7177](https://github.com/rust-lang/rust-clippy/pull/7177) -- Add `avoid-breaking-exported-api` config option for the lints +* Add `avoid-breaking-exported-api` config option for the lints [`enum_variant_names`], [`large_types_passed_by_value`], [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`], [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set this configuration option to `false` before a major release (1.0/2.0/...) to clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187) -- [`needless_collect`]: Now lints on even more data structures +* [`needless_collect`]: Now lints on even more data structures [#7188](https://github.com/rust-lang/rust-clippy/pull/7188) -- [`missing_docs_in_private_items`]: No longer sees `#[ = ""]` like +* [`missing_docs_in_private_items`]: No longer sees `#[ = ""]` like attributes as sufficient documentation [#7281](https://github.com/rust-lang/rust-clippy/pull/7281) -- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]: +* [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]: Now work as expected when used with `allow` [#7282](https://github.com/rust-lang/rust-clippy/pull/7282) ### False Positive Fixes -- [`implicit_return`]: Now takes all diverging functions in account to avoid +* [`implicit_return`]: Now takes all diverging functions in account to avoid false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951) -- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field +* [`while_let_on_iterator`]: No longer lints when the iterator is a struct field and the struct is used in the loop [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`multiple_inherent_impl`]: No longer lints with generic arguments +* [`multiple_inherent_impl`]: No longer lints with generic arguments [#7089](https://github.com/rust-lang/rust-clippy/pull/7089) -- [`comparison_chain`]: No longer lints in a `const` context +* [`comparison_chain`]: No longer lints in a `const` context [#7118](https://github.com/rust-lang/rust-clippy/pull/7118) -- [`while_immutable_condition`]: Fix false positive where mutation in the loop +* [`while_immutable_condition`]: Fix false positive where mutation in the loop variable wasn't picked up [#7144](https://github.com/rust-lang/rust-clippy/pull/7144) -- [`default_trait_access`]: No longer lints in macros +* [`default_trait_access`]: No longer lints in macros [#7150](https://github.com/rust-lang/rust-clippy/pull/7150) -- [`needless_question_mark`]: No longer lints when the inner value is implicitly +* [`needless_question_mark`]: No longer lints when the inner value is implicitly dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165) -- [`unused_unit`]: No longer lints when multiple macro contexts are involved +* [`unused_unit`]: No longer lints when multiple macro contexts are involved [#7167](https://github.com/rust-lang/rust-clippy/pull/7167) -- [`eval_order_dependence`]: Fix false positive in async context +* [`eval_order_dependence`]: Fix false positive in async context [#7174](https://github.com/rust-lang/rust-clippy/pull/7174) -- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the +* [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175) -- [`wrong_self_convention`]: No longer lints in trait implementations of +* [`wrong_self_convention`]: No longer lints in trait implementations of non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182) -- [`suboptimal_flops`]: No longer lints on `powi(2)` +* [`suboptimal_flops`]: No longer lints on `powi(2)` [#7201](https://github.com/rust-lang/rust-clippy/pull/7201) -- [`wrong_self_convention`]: No longer lints if there is no implicit `self` +* [`wrong_self_convention`]: No longer lints if there is no implicit `self` [#7215](https://github.com/rust-lang/rust-clippy/pull/7215) -- [`option_if_let_else`]: No longer lints on `else if let` pattern +* [`option_if_let_else`]: No longer lints on `else if let` pattern [#7216](https://github.com/rust-lang/rust-clippy/pull/7216) -- [`use_self`], [`useless_conversion`]: Fix false positives when generic +* [`use_self`], [`useless_conversion`]: Fix false positives when generic arguments are involved [#7223](https://github.com/rust-lang/rust-clippy/pull/7223) -- [`manual_unwrap_or`]: Fix false positive with deref coercion +* [`manual_unwrap_or`]: Fix false positive with deref coercion [#7233](https://github.com/rust-lang/rust-clippy/pull/7233) -- [`similar_names`]: No longer lints on `wparam`/`lparam` +* [`similar_names`]: No longer lints on `wparam`/`lparam` [#7255](https://github.com/rust-lang/rust-clippy/pull/7255) -- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a +* [`redundant_closure`]: No longer lints on using the `vec![]` macro in a closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263) ### Suggestion Fixes/Improvements -- [`implicit_return`] +* [`implicit_return`] [#6951](https://github.com/rust-lang/rust-clippy/pull/6951) - - Fix suggestion for async functions - - Improve suggestion with macros - - Suggest to change `break` to `return` when appropriate -- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary + * Fix suggestion for async functions + * Improve suggestion with macros + * Suggest to change `break` to `return` when appropriate +* [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`match_single_binding`]: Improve suggestion when match scrutinee has side +* [`match_single_binding`]: Improve suggestion when match scrutinee has side effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095) -- [`needless_borrow`]: Now suggests to also change usage sites as needed +* [`needless_borrow`]: Now suggests to also change usage sites as needed [#7105](https://github.com/rust-lang/rust-clippy/pull/7105) -- [`write_with_newline`]: Improve suggestion when only `\n` is written to the +* [`write_with_newline`]: Improve suggestion when only `\n` is written to the buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183) -- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also +* [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also when a `<_ as Trait>::_` is involved [#7264](https://github.com/rust-lang/rust-clippy/pull/7264) -- [`not_unsafe_ptr_arg_deref`]: Improved error message +* [`not_unsafe_ptr_arg_deref`]: Improved error message [#7294](https://github.com/rust-lang/rust-clippy/pull/7294) ### ICE Fixes -- Fix ICE when running Clippy on `libstd` +* Fix ICE when running Clippy on `libstd` [#7140](https://github.com/rust-lang/rust-clippy/pull/7140) -- [`implicit_return`] +* [`implicit_return`] [#7242](https://github.com/rust-lang/rust-clippy/pull/7242) ## Rust 1.53 @@ -3697,9 +3695,9 @@ Released 2021-06-17 [#6828](https://github.com/rust-lang/rust-clippy/pull/6828) * [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: [#6863](https://github.com/rust-lang/rust-clippy/pull/6863) - * Attempt to find a common path prefix in suggestion - * Don't lint on `Option` and `Result` - * Consider `Self` prefix + * Attempt to find a common path prefix in suggestion + * Don't lint on `Option` and `Result` + * Consider `Self` prefix * [`explicit_deref_methods`]: Also lint on chained `deref` calls [#6865](https://github.com/rust-lang/rust-clippy/pull/6865) * [`or_fun_call`]: Also lint on `unsafe` blocks @@ -3959,6 +3957,7 @@ Released 2021-05-06 [#6782](https://github.com/rust-lang/rust-clippy/pull/6782) ### Others + * Running `cargo clippy` after `cargo check` now works as expected (`cargo clippy` and `cargo check` no longer shares the same build cache) [#6687](https://github.com/rust-lang/rust-clippy/pull/6687) @@ -4174,7 +4173,6 @@ Released 2021-02-11 * [`field_reassign_with_default`] No longer lint for private fields [#6537](https://github.com/rust-lang/rust-clippy/pull/6537) - ### Suggestion Fixes/Improvements * [`vec_box`]: Provide correct type scope suggestion @@ -4319,8 +4317,8 @@ Released 2020-12-31 ### Documentation Improvements * Some doc improvements: - * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090) - * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162) + * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090) + * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162) * [`doc_markdown`]: Document problematic link text style [#6107](https://github.com/rust-lang/rust-clippy/pull/6107) @@ -4703,7 +4701,6 @@ Released 2020-06-04 * [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294) * [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294) - ### Moves and Deprecations * Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380) @@ -4718,7 +4715,7 @@ Released 2020-06-04 ### Enhancements -* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to +* On *nightly* you can now use `cargo clippy --fix -Z unstable-options` to auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363) * Make [`redundant_clone`] also trigger on cases where the cloned value is not consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304) @@ -4745,7 +4742,6 @@ Released 2020-06-04 * [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287) * [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451) - ### Suggestion Improvements * Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481) @@ -4823,7 +4819,6 @@ Released 2020-04-23 * Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190) - ## Rust 1.42 Released 2020-03-12 @@ -4890,7 +4885,6 @@ Released 2020-03-12 * Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`] - ## Rust 1.41 Released 2020-01-30 @@ -5106,7 +5100,6 @@ Released 2019-07-04 * Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960) * Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931) - ## Rust 1.35 Released 2019-05-20 @@ -5129,7 +5122,7 @@ Released 2019-05-20 * Fix false positive in [`needless_continue`] pertaining to loop labels * Fix false positive for [`boxed_local`] pertaining to arguments moved into closures * Fix false positive for [`use_self`] in nested functions -* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846) +* Fix suggestion for [`expect_fun_call`] () * Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables * Fix suggestion for [`single_char_pattern`] to correctly escape single quotes * Avoid triggering [`redundant_closure`] in macros @@ -5273,6 +5266,7 @@ Released 2018-12-06 Released 2018-10-25 [View all 88 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2018-08-02T16%3A54%3A12Z..2018-09-17T09%3A44%3A06Z+base%3Amaster) + * Deprecate `assign_ops` lint * New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`], [`needless_collect`], [`copy_iterator`] @@ -5322,255 +5316,326 @@ Released 2018-09-13 * Improve website header ## 0.0.212 (2018-07-10) + * Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)* ## 0.0.211 + * Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)* ## 0.0.210 + * Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)* ## 0.0.209 + * Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)* ## 0.0.208 + * Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)* ## 0.0.207 + * Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)* ## 0.0.206 + * Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)* ## 0.0.205 + * Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)* * Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint ## 0.0.204 + * Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)* ## 0.0.203 + * Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)* * Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)` ## 0.0.202 + * Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)* ## 0.0.201 + * Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)* ## 0.0.200 + * Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)* ## 0.0.199 + * Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)* ## 0.0.198 + * Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)* ## 0.0.197 + * Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)* ## 0.0.196 + * Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)* ## 0.0.195 + * Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)* ## 0.0.194 + * Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)* * New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`] ## 0.0.193 + * Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)* ## 0.0.192 + * Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)* * New lint: [`print_literal`] ## 0.0.191 + * Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)* * Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction. ## 0.0.190 + * Fix a bunch of intermittent cargo bugs ## 0.0.189 + * Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)* ## 0.0.188 + * Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)* * New lint: [`while_immutable_condition`] ## 0.0.187 + * Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)* * New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`] ## 0.0.186 + * Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)* * Various false positive fixes ## 0.0.185 + * Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)* * New lint: [`question_mark`] ## 0.0.184 + * Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)* * New lints: [`double_comparisons`], [`empty_line_after_outer_attr`] ## 0.0.183 + * Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)* * New lint: [`misaligned_transmute`] ## 0.0.182 + * Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)* * New lint: [`decimal_literal_representation`] ## 0.0.181 + * Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)* * New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`] * Removed `unit_expr` * Various false positive fixes for [`needless_pass_by_value`] ## 0.0.180 + * Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)* ## 0.0.179 + * Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)* ## 0.0.178 + * Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)* ## 0.0.177 + * Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)* * New lint: [`match_as_ref`] ## 0.0.176 + * Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)* ## 0.0.175 + * Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)* ## 0.0.174 + * Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)* ## 0.0.173 + * Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)* ## 0.0.172 + * Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)* ## 0.0.171 + * Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)* ## 0.0.170 + * Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)* ## 0.0.169 + * Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)* * New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`] ## 0.0.168 + * Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)* ## 0.0.167 + * Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)* * New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`] ## 0.0.166 + * Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)* * New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`], [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`], [`transmute_int_to_float`] ## 0.0.165 + * Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27) * New lint: [`mut_range_bound`] ## 0.0.164 + * Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)* * New lint: [`int_plus_one`] ## 0.0.163 + * Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)* ## 0.0.162 + * Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)* * New lint: [`chars_last_cmp`] * Improved suggestions for [`needless_borrow`], [`ptr_arg`], ## 0.0.161 + * Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)* ## 0.0.160 + * Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)* ## 0.0.159 + * Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)* * New lint: [`clone_on_ref_ptr`] ## 0.0.158 + * New lint: [`manual_memcpy`] * [`cast_lossless`] no longer has redundant parentheses in its suggestions * Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)* ## 0.0.157 - 2017-09-04 + * Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)* * New lint: `unit_expr` ## 0.0.156 - 2017-09-03 + * Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)* ## 0.0.155 + * Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)* * New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`] ## 0.0.154 + * Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)* * Fix [`use_self`] triggering inside derives * Add support for linting an entire workspace with `cargo clippy --all` * New lint: [`naive_bytecount`] ## 0.0.153 + * Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)* * New lint: [`use_self`] ## 0.0.152 + * Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)* ## 0.0.151 + * Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)* ## 0.0.150 + * Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)* ## 0.0.148 + * Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)* * New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`] ## 0.0.147 + * Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)* ## 0.0.146 + * Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)* * Fixes false positives in `inline_always` * Fixes false negatives in `panic_params` ## 0.0.145 + * Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)* ## 0.0.144 + * Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)* ## 0.0.143 + * Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)* * Fix `cargo clippy` crashing on `dylib` projects * Fix false positives around `nested_while_let` and `never_loop` ## 0.0.142 + * Update to *rustc 1.20.0-nightly (067971139 2017-07-02)* ## 0.0.141 + * Rewrite of the `doc_markdown` lint. * Deprecated [`range_step_by_zero`] * New lint: [`iterator_step_by_zero`] @@ -5578,151 +5643,195 @@ Released 2018-09-13 * Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)* ## 0.0.140 - 2017-06-16 + * Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)* ## 0.0.139 — 2017-06-10 + * Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)* * Fix bugs with for loop desugaring * Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`] ## 0.0.138 — 2017-06-05 + * Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)* ## 0.0.137 — 2017-06-05 + * Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)* ## 0.0.136 — 2017—05—26 + * Update to *rustc 1.19.0-nightly (557967766 2017-05-26)* ## 0.0.135 — 2017—05—24 + * Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)* ## 0.0.134 — 2017—05—19 + * Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)* ## 0.0.133 — 2017—05—14 + * Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)* ## 0.0.132 — 2017—05—05 + * Fix various bugs and some ices ## 0.0.131 — 2017—05—04 + * Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)* ## 0.0.130 — 2017—05—03 + * Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)* ## 0.0.129 — 2017-05-01 + * Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)* ## 0.0.128 — 2017-04-28 + * Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)* ## 0.0.127 — 2017-04-27 + * Update to *rustc 1.18.0-nightly (036983201 2017-04-26)* * New lint: [`needless_continue`] ## 0.0.126 — 2017-04-24 + * Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)* ## 0.0.125 — 2017-04-19 + * Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)* ## 0.0.124 — 2017-04-16 + * Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)* ## 0.0.123 — 2017-04-07 + * Fix various false positives ## 0.0.122 — 2017-04-07 + * Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)* * New lint: [`op_ref`] ## 0.0.121 — 2017-03-21 + * Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)* ## 0.0.120 — 2017-03-17 + * Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)* ## 0.0.119 — 2017-03-13 + * Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)* ## 0.0.118 — 2017-03-05 + * Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)* ## 0.0.117 — 2017-03-01 + * Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)* ## 0.0.116 — 2017-02-28 + * Fix `cargo clippy` on 64 bit windows systems ## 0.0.115 — 2017-02-27 + * Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)* * New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`] ## 0.0.114 — 2017-02-08 + * Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)* * Tests are now ui tests (testing the exact output of rustc) ## 0.0.113 — 2017-02-04 + * Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)* * New lint: [`large_enum_variant`] * `explicit_into_iter_loop` provides suggestions ## 0.0.112 — 2017-01-27 + * Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)* ## 0.0.111 — 2017-01-21 + * Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)* ## 0.0.110 — 2017-01-20 + * Add badges and categories to `Cargo.toml` ## 0.0.109 — 2017-01-19 + * Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)* ## 0.0.108 — 2017-01-12 + * Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)* ## 0.0.107 — 2017-01-11 + * Update regex dependency * Fix FP when matching `&&mut` by `&ref` * Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()` * New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`] ## 0.0.106 — 2017-01-04 + * Fix FP introduced by rustup in [`wrong_self_convention`] ## 0.0.105 — 2017-01-04 + * Update to *rustc 1.16.0-nightly (468227129 2017-01-03)* * New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`] * Fix suggestion in [`new_without_default`] * FP fix in [`absurd_extreme_comparisons`] ## 0.0.104 — 2016-12-15 + * Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)* ## 0.0.103 — 2016-11-25 + * Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)* ## 0.0.102 — 2016-11-24 + * Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)* ## 0.0.101 — 2016-11-23 + * Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)* * New lint: [`string_extend_chars`] ## 0.0.100 — 2016-11-20 + * Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)* ## 0.0.99 — 2016-11-18 + * Update to rustc 1.15.0-nightly (0ed951993 2016-11-14) * New lint: [`get_unwrap`] ## 0.0.98 — 2016-11-08 + * Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy` ## 0.0.97 — 2016-11-03 + * For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was previously added for a short time under the name `clippy` but removed for compatibility. @@ -5731,34 +5840,43 @@ Released 2018-09-13 * New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`] ## 0.0.96 — 2016-10-22 + * Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)* * New lint: [`iter_skip_next`] ## 0.0.95 — 2016-10-06 + * Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)* ## 0.0.94 — 2016-10-04 + * Fixes bustage on Windows due to forbidden directory name ## 0.0.93 — 2016-10-03 + * Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)* * `option_map_unwrap_or` and `option_map_unwrap_or_else` are now allowed by default. * New lint: [`explicit_into_iter_loop`] ## 0.0.92 — 2016-09-30 + * Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)* ## 0.0.91 — 2016-09-28 + * Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)* ## 0.0.90 — 2016-09-09 + * Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)* ## 0.0.89 — 2016-09-06 + * Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)* ## 0.0.88 — 2016-09-04 + * Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)* * The following lints are not new but were only usable through the `clippy` lint groups: [`filter_next`], `for_loop_over_option`, @@ -5767,30 +5885,37 @@ Released 2018-09-13 through `cargo clippy`. ## 0.0.87 — 2016-08-31 + * Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)* * New lints: [`builtin_type_shadow`] * Fix FP in [`zero_prefixed_literal`] and `0b`/`0o` ## 0.0.86 — 2016-08-28 + * Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)* * New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`] ## 0.0.85 — 2016-08-19 + * Fix ICE with [`useless_attribute`] * [`useless_attribute`] ignores `unused_imports` on `use` statements ## 0.0.84 — 2016-08-18 + * Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)* ## 0.0.83 — 2016-08-17 + * Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)* * New lints: [`print_with_newline`], [`useless_attribute`] ## 0.0.82 — 2016-08-17 + * Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)* * New lint: [`module_inception`] ## 0.0.81 — 2016-08-14 + * Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)* * New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`] * False positive fix in [`too_many_arguments`] @@ -5800,14 +5925,17 @@ Released 2018-09-13 * Doc improvements ## 0.0.80 — 2016-07-31 + * Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)* * New lints: [`misrefactored_assign_op`], [`serde_api_misuse`] ## 0.0.79 — 2016-07-10 + * Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)* * Major suggestions refactoring ## 0.0.78 — 2016-07-02 + * Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)* * New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`] * For compatibility, `cargo clippy` does not defines the `clippy` feature @@ -5815,118 +5943,148 @@ Released 2018-09-13 * [`collapsible_if`] now considers `if let` ## 0.0.77 — 2016-06-21 + * Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)* * New lints: `stutter` and [`iter_nth`] ## 0.0.76 — 2016-06-10 + * Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)* * `cargo clippy` now automatically defines the `clippy` feature * New lint: [`not_unsafe_ptr_arg_deref`] ## 0.0.75 — 2016-06-08 + * Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)* ## 0.0.74 — 2016-06-07 + * Fix bug with `cargo-clippy` JSON parsing * Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the “for further information visit *lint-link*” message. ## 0.0.73 — 2016-06-05 + * Fix false positives in [`useless_let_if_seq`] ## 0.0.72 — 2016-06-04 + * Fix false positives in [`useless_let_if_seq`] ## 0.0.71 — 2016-05-31 + * Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)* * New lint: [`useless_let_if_seq`] ## 0.0.70 — 2016-05-28 + * Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)* * [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`, `RegexBuilder::new` and byte regexes ## 0.0.69 — 2016-05-20 + * Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)* * [`used_underscore_binding`] has been made `Allow` temporarily ## 0.0.68 — 2016-05-17 + * Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)* * New lint: [`unnecessary_operation`] ## 0.0.67 — 2016-05-12 + * Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)* ## 0.0.66 — 2016-05-11 + * New `cargo clippy` subcommand * New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`] ## 0.0.65 — 2016-05-08 + * Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)* * New lints: [`float_arithmetic`], [`integer_arithmetic`] ## 0.0.64 — 2016-04-26 + * Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)* * New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`] ## 0.0.63 — 2016-04-08 + * Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)* ## 0.0.62 — 2016-04-07 + * Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)* ## 0.0.61 — 2016-04-03 + * Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)* * New lint: [`invalid_upcast_comparisons`] ## 0.0.60 — 2016-04-01 + * Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)* ## 0.0.59 — 2016-03-31 + * Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)* * New lints: [`logic_bug`], [`nonminimal_bool`] * Fixed: [`match_same_arms`] now ignores arms with guards * Improved: [`useless_vec`] now warns on `for … in vec![…]` ## 0.0.58 — 2016-03-27 + * Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)* * New lint: [`doc_markdown`] ## 0.0.57 — 2016-03-27 + * Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)* * Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`] * New lint: [`crosspointer_transmute`] ## 0.0.56 — 2016-03-23 + * Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)* * New lints: [`many_single_char_names`] and [`similar_names`] ## 0.0.55 — 2016-03-21 + * Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)* ## 0.0.54 — 2016-03-16 + * Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)* ## 0.0.53 — 2016-03-15 + * Add a [configuration file] ## ~~0.0.52~~ ## 0.0.51 — 2016-03-13 + * Add `str` to types considered by [`len_zero`] * New lints: [`indexing_slicing`] ## 0.0.50 — 2016-03-11 + * Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)* ## 0.0.49 — 2016-03-09 + * Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)* * New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`] ## 0.0.48 — 2016-03-07 + * Fixed: ICE in [`needless_range_loop`] with globals ## 0.0.47 — 2016-03-07 + * Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)* * New lint: [`redundant_closure_call`] @@ -6573,6 +6731,7 @@ Released 2018-09-13 [`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity +[`replace_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_box [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`repr_packed_without_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#repr_packed_without_abi [`reserve_after_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#reserve_after_initialization @@ -6738,6 +6897,7 @@ Released 2018-09-13 [`unnecessary_min_or_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min_or_max [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation +[`unnecessary_option_map_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_option_map_or_else [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings [`unnecessary_result_map_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_result_map_or_else [`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment @@ -6798,6 +6958,7 @@ Released 2018-09-13 [`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads +[`volatile_composites`]: https://rust-lang.github.io/rust-clippy/master/index.html#volatile_composites [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons [`waker_clone_wake`]: https://rust-lang.github.io/rust-clippy/master/index.html#waker_clone_wake [`while_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_float @@ -6874,6 +7035,7 @@ Released 2018-09-13 [`excessive-nesting-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#excessive-nesting-threshold [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability +[`inherent-impl-lint-scope`]: https://doc.rust-lang.org/clippy/lint_configuration.html#inherent-impl-lint-scope [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold [`lint-commented-code`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-commented-code [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold @@ -6891,6 +7053,7 @@ Released 2018-09-13 [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior +[`recursive-self-in-type-definitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#recursive-self-in-type-definitions [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline [`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline [`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 2b89e94cf8f4f..d9a5f04c3e1c0 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -759,8 +759,7 @@ for some users. Adding a configuration is done in the following steps: Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need - is already in here ([`is_type_diagnostic_item`], [`implements_trait`], - [`snippet`], etc) + is already in here ([`implements_trait`], [`snippet`], etc) * [Clippy diagnostics][diagnostics] * [Let chains][let-chains] * [`from_expansion`][from_expansion] and @@ -790,7 +789,6 @@ get away with copying things from existing similar lints. If you are stuck, don't hesitate to ask on [Zulip] or in the issue/PR. [utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html -[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html [`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html [`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html [let-chains]: https://github.com/rust-lang/rust/pull/94927 diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 3bec3ce33af36..b5958f802e384 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for MyStructLint { // Check our expr is calling a method if let hir::ExprKind::MethodCall(path, _, _self_arg, ..) = &expr.kind // Check the name of this method is `some_method` - && path.ident.name.as_str() == "some_method" + && path.ident.name == sym::some_method // Optionally, check the type of the self argument. // - See "Checking for a specific type" { @@ -85,9 +85,8 @@ to check for. All of these methods only check for the base type, generic arguments have to be checked separately. ```rust -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::paths; -use rustc_span::symbol::sym; +use clippy_utils::{paths, sym}; +use clippy_utils::res::MaybeDef; use rustc_hir::LangItem; impl LateLintPass<'_> for MyStructLint { @@ -97,12 +96,12 @@ impl LateLintPass<'_> for MyStructLint { // 1. Using diagnostic items // The last argument is the diagnostic item to check for - if is_type_diagnostic_item(cx, ty, sym::Option) { + if ty.is_diag_item(cx, sym::Option) { // The type is an `Option` } // 2. Using lang items - if is_type_lang_item(cx, ty, LangItem::RangeFull) { + if ty.is_lang_item(cx, LangItem::RangeFull) { // The type is a full range like `.drain(..)` } @@ -123,27 +122,29 @@ There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. ```rust +use clippy_utils::sym; use clippy_utils::ty::implements_trait; -use clippy_utils::is_trait_method; -use rustc_span::symbol::sym; impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - // 1. Using diagnostic items with the expression - // we use `is_trait_method` function from Clippy's utils - if is_trait_method(cx, expr, sym::Iterator) { - // method call in `expr` belongs to `Iterator` trait - } - // 2. Using lang items with the expression type + // 1. Get the `DefId` of the trait. + // via lang items + let trait_id = cx.tcx.lang_items().drop_trait(); + // via diagnostic items + let trait_id = cx.tcx.get_diagnostic_item(sym::Eq); + + // 2. Check for the trait implementation via the `implements_trait` util. let ty = cx.typeck_results().expr_ty(expr); - if cx.tcx.lang_items() - // we are looking for the `DefId` of `Drop` trait in lang items - .drop_trait() - // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils - .is_some_and(|id| implements_trait(cx, ty, id, &[])) { - // `expr` implements `Drop` trait - } + if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[])) { + // `ty` implements the trait. + } + + // 3. If the trait requires additional generic arguments + let trait_id = cx.tcx.lang_items().eq_trait(); + if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[ty])) { + // `ty` implements `PartialEq` + } } } ``` @@ -173,7 +174,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go further and even check if its return type is `String` - && is_type_lang_item(cx, return_ty(cx, impl_item.hir_id), LangItem::String) + && return_ty(cx, impl_item.hir_id).is_lang_item(cx, LangItem::String) { // ... } diff --git a/book/src/development/macro_expansions.md b/book/src/development/macro_expansions.md index ed547130b358e..63a96dc373f31 100644 --- a/book/src/development/macro_expansions.md +++ b/book/src/development/macro_expansions.md @@ -37,7 +37,7 @@ before emitting suggestions to the end user to avoid false positives. Several functions are available for working with macros. -### The `Span.from_expansion` method +### The `Span::from_expansion` method We could utilize a `span`'s [`from_expansion`] method, which detects if the `span` is from a macro expansion / desugaring. @@ -50,7 +50,7 @@ if expr.span.from_expansion() { } ``` -### `Span.ctxt` method +### `Span::ctxt` method The `span`'s context, given by the method [`ctxt`] and returning [SyntaxContext], represents if the span is from a macro expansion and, if it is, which diff --git a/book/src/development/method_checking.md b/book/src/development/method_checking.md index b3126024b990d..cca6d6ae7bf34 100644 --- a/book/src/development/method_checking.md +++ b/book/src/development/method_checking.md @@ -15,20 +15,20 @@ the [`ExprKind`] that we can access from `expr.kind`: ```rust use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::sym; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::sym; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // Check our expr is calling a method with pattern matching if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind // Check if the name of this method is `our_fancy_method` - && path.ident.name.as_str() == "our_fancy_method" + && path.ident.name == sym::our_fancy_method // We can check the type of the self argument whenever necessary. // (It's necessary if we want to check that method is specifically belonging to a specific trait, // for example, a `map` method could belong to user-defined trait instead of to `Iterator`) // See the next section for more information. - && is_trait_method(cx, self_arg, sym::OurFancyTrait) + && cx.ty_based_def(self_arg).opt_parent(cx).is_diag_item(cx, sym::OurFancyTrait) { println!("`expr` is a method call for `our_fancy_method`"); } @@ -41,6 +41,10 @@ information on the pattern matching. As mentioned in [Define Lints](defining_lints.md#lint-types), the `methods` lint type is full of pattern matching with `MethodCall` in case the reader wishes to explore more. +New symbols such as `our_fancy_method` need to be added to the `clippy_utils::sym` module. +This module extends the list of symbols already provided by the compiler crates +in `rustc_span::sym`. + ## Checking if a `impl` block implements a method While sometimes we want to check whether a method is being called or not, other @@ -56,11 +60,10 @@ Let us take a look at how we might check for the implementation of `our_fancy_method` on a type: ```rust -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::return_ty; +use clippy_utils::{return_ty, sym}; +use clippy_utils::res::MaybeDef; use rustc_hir::{ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::symbol::sym; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { @@ -71,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go even further and even check if its return type is `String` - && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::String) + && return_ty(cx, impl_item.hir_id).is_diag_item(cx, sym::String) { println!("`our_fancy_method` is implemented!"); } diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index c6f6f6bd2f99e..714607ef25e5f 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -17,10 +17,10 @@ providing the `LateContext` (`cx`), our expression at hand, and the symbol of the trait in question: ```rust +use clippy_utils::sym; use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::symbol::sym; impl LateLintPass<'_> for CheckIteratorTraitLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -53,7 +53,7 @@ For instance, if we want to examine whether an expression `expr` implements we can check that the `Ty` of the `expr` implements the trait: ```rust -use clippy_utils::implements_trait; +use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; @@ -79,7 +79,8 @@ If neither diagnostic item nor a language item is available, we can use 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::{implements_trait, paths}; +use clippy_utils::paths; +use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; @@ -124,8 +125,8 @@ The following code demonstrates how to do this: ```rust use rustc_middle::ty::Ty; +use clippy_utils::sym; use clippy_utils::ty::implements_trait; -use rustc_span::symbol::sym; let ty = todo!("Get the `Foo` type to check for a trait implementation"); let borrow_id = cx.tcx.get_diagnostic_item(sym::Borrow).unwrap(); // avoid unwrap in real code diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index b2ba19631f133..b9ecff1fa3643 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -671,6 +671,16 @@ A list of paths to types that should be treated as if they do not contain interi * [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) +## `inherent-impl-lint-scope` +Sets the scope ("crate", "file", or "module") in which duplicate inherent `impl` blocks for the same type are linted. + +**Default Value:** `"crate"` + +--- +**Affected lints:** +* [`multiple_inherent_impl`](https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl) + + ## `large-error-threshold` The maximum size of the `Err`-variant in a `Result` returned from a function @@ -927,6 +937,16 @@ exported visibility, or whether they are marked as "pub". * [`pub_underscore_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields) +## `recursive-self-in-type-definitions` +Whether the type itself in a struct or enum should be replaced with `Self` when encountering recursive types. + +**Default Value:** `true` + +--- +**Affected lints:** +* [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) + + ## `semicolon-inside-block-ignore-singleline` Whether to lint only if it's multiline. diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 9ad434604dfcf..9993765b4bdc4 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -1,9 +1,9 @@ use crate::ClippyConfiguration; use crate::types::{ - DisallowedPath, DisallowedPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, - Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, - SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, - SourceItemOrderingWithinModuleItemGroupings, + DisallowedPath, DisallowedPathWithoutReplacement, InherentImplLintScope, MacroMatcher, MatchLintBehaviour, + PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, + SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, + SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; use itertools::Itertools; @@ -248,7 +248,7 @@ macro_rules! define_Conf { #[derive(Deserialize)] #[serde(field_identifier, rename_all = "kebab-case")] - #[allow(non_camel_case_types)] + #[expect(non_camel_case_types)] enum Field { $($name,)* third_party, } struct ConfVisitor<'a>(&'a SourceFile); @@ -663,6 +663,9 @@ define_Conf! { /// A list of paths to types that should be treated as if they do not contain interior mutability #[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)] ignore_interior_mutability: Vec = Vec::from(["bytes::Bytes".into()]), + /// Sets the scope ("crate", "file", or "module") in which duplicate inherent `impl` blocks for the same type are linted. + #[lints(multiple_inherent_impl)] + inherent_impl_lint_scope: InherentImplLintScope = InherentImplLintScope::Crate, /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, @@ -809,6 +812,9 @@ define_Conf! { /// exported visibility, or whether they are marked as "pub". #[lints(pub_underscore_fields)] pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported, + /// Whether the type itself in a struct or enum should be replaced with `Self` when encountering recursive types. + #[lints(use_self)] + recursive_self_in_type_definitions: bool = true, /// Whether to lint only if it's multiline. #[lints(semicolon_inside_block)] semicolon_inside_block_ignore_singleline: bool = false, @@ -1213,7 +1219,7 @@ mod tests { for entry in toml_files { let file = fs::read_to_string(entry.path()).unwrap(); - #[allow(clippy::zero_sized_map_values)] + #[expect(clippy::zero_sized_map_values)] if let Ok(map) = toml::from_str::>(&file) { for name in map.keys() { names.remove(name.as_str()); diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index f64eefa0c232d..8d9326a904b1e 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -131,7 +131,7 @@ impl DisallowedPathEnum { } /// Creates a map of disallowed items to the reason they were disallowed. -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] pub fn create_disallowed_map( tcx: TyCtxt<'_>, disallowed_paths: &'static [DisallowedPath], @@ -698,3 +698,11 @@ pub enum PubUnderscoreFieldsBehaviour { PubliclyExported, AllPubFields, } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum InherentImplLintScope { + Crate, + File, + Module, +} diff --git a/clippy_dev/src/dogfood.rs b/clippy_dev/src/dogfood.rs index d0fca952b9322..9eb323eaef5aa 100644 --- a/clippy_dev/src/dogfood.rs +++ b/clippy_dev/src/dogfood.rs @@ -4,7 +4,7 @@ use itertools::Itertools; /// # Panics /// /// Panics if unable to run the dogfood test -#[allow(clippy::fn_params_excessive_bools)] +#[expect(clippy::fn_params_excessive_bools)] pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { run_exit_on_err( "cargo test", diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 5fef231f6ca1c..1b6a590b896f4 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -192,7 +192,7 @@ enum DevCommand { /// Which lint's page to load initially (optional) lint: Option, }, - #[allow(clippy::doc_markdown)] + #[expect(clippy::doc_markdown)] /// Manually run clippy on a file or package /// /// ## Examples diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 4121daa85e6d7..a14afd8c5f416 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -443,7 +443,6 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R Ok(()) } -#[allow(clippy::too_many_lines)] fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> { let lint_name_upper = lint.name.to_uppercase(); diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index 36498adff502e..b8bf8b25b3233 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -167,7 +167,7 @@ declare_clippy_lint! { impl_lint_pass!(ArbitrarySourceItemOrdering => [ARBITRARY_SOURCE_ITEM_ORDERING]); #[derive(Debug)] -#[allow(clippy::struct_excessive_bools)] // Bools are cached feature flags. +#[expect(clippy::struct_excessive_bools, reason = "Bools are cached feature flags")] pub struct ArbitrarySourceItemOrdering { assoc_types_order: SourceItemOrderingTraitAssocItemKinds, enable_ordering_for_enum: bool, diff --git a/clippy_lints/src/arc_with_non_send_sync.rs b/clippy_lints/src/arc_with_non_send_sync.rs index 085029a744bec..acfdfa65baeda 100644 --- a/clippy_lints/src/arc_with_non_send_sync.rs +++ b/clippy_lints/src/arc_with_non_send_sync.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -46,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { && let ExprKind::Path(QPath::TypeRelative(func_ty, func_name)) = func.kind && func_name.ident.name == sym::new && !expr.span.from_expansion() - && is_type_diagnostic_item(cx, cx.typeck_results().node_type(func_ty.hir_id), sym::Arc) + && cx.typeck_results().node_type(func_ty.hir_id).is_diag_item(cx, sym::Arc) && let arg_ty = cx.typeck_results().expr_ty(arg) // make sure that the type is not and does not contain any type parameters && arg_ty.walk().all(|arg| { diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index 08253b0c4995e..cc62306b33b53 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; +use clippy_utils::sym; +use clippy_utils::ty::{has_debug_impl, is_copy}; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, Node}; @@ -55,13 +56,13 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { && let ExprKind::MethodCall(method_segment, recv, [], _) = condition.kind && let result_type_with_refs = cx.typeck_results().expr_ty(recv) && let result_type = result_type_with_refs.peel_refs() - && is_type_diagnostic_item(cx, result_type, sym::Result) + && result_type.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = result_type.kind() { if !is_copy(cx, result_type) { if result_type_with_refs != result_type { return; - } else if let Res::Local(binding_id) = path_res(cx, recv) + } else if let Res::Local(binding_id) = *recv.basic_res() && local_used_after_expr(cx, binding_id, recv) { return; diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 52287be34c784..efce23d13a382 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local, sym}; +use clippy_utils::{is_in_test, last_path_segment, local_is_initialized, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -68,15 +69,15 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Assign(lhs, rhs, _) = e.kind && let typeck = cx.typeck_results() - && let (call_kind, fn_name, fn_id, fn_arg, fn_gen_args) = match rhs.kind { + && let (call_kind, fn_name, fn_def, fn_arg, fn_gen_args) = match rhs.kind { ExprKind::Call(f, [arg]) if let ExprKind::Path(fn_path) = &f.kind - && let Some(id) = typeck.qpath_res(fn_path, f.hir_id).opt_def_id() => + && let Some(def) = typeck.qpath_res(fn_path, f.hir_id).opt_def(cx) => { - (CallKind::Ufcs, last_path_segment(fn_path).ident.name, id, arg, typeck.node_args(f.hir_id)) + (CallKind::Ufcs, last_path_segment(fn_path).ident.name, def, arg, typeck.node_args(f.hir_id)) }, - ExprKind::MethodCall(name, recv, [], _) if let Some(id) = typeck.type_dependent_def_id(rhs.hir_id) => { - (CallKind::Method, name.ident.name, id, recv, typeck.node_args(rhs.hir_id)) + ExprKind::MethodCall(name, recv, [], _) if let Some(def) = typeck.type_dependent_def(rhs.hir_id) => { + (CallKind::Method, name.ident.name, def, recv, typeck.node_args(rhs.hir_id)) }, _ => return, } @@ -84,20 +85,20 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { // Don't lint in macros. && ctxt.is_root() && let which_trait = match fn_name { - sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone, + sym::clone if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Clone) => CloneTrait::Clone, sym::to_owned - if is_diag_trait_item(cx, fn_id, sym::ToOwned) + if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::ToOwned) && self.msrv.meets(cx, msrvs::CLONE_INTO) => { CloneTrait::ToOwned }, _ => return, } - && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_id, fn_gen_args) + && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_def.1, fn_gen_args) // TODO: This check currently bails if the local variable has no initializer. // That is overly conservative - the lint should fire even if there was no initializer, // but the variable has been initialized before `lhs` was evaluated. - && path_to_local(lhs).is_none_or(|lhs| local_is_initialized(cx, lhs)) + && lhs.res_local_id().is_none_or(|lhs| local_is_initialized(cx, lhs)) && let Some(resolved_impl) = cx.tcx.impl_of_assoc(resolved_fn.def_id()) // Derived forms don't implement `clone_from`/`clone_into`. // See https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305 diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 64aeb27df693f..f3985603c4d2e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher::has_let_expr; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::implements_trait; use clippy_utils::{eq_expr_value, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -431,9 +432,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio }, ExprKind::MethodCall(path, receiver, args, _) => { let type_of_receiver = cx.typeck_results().expr_ty(receiver); - if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option) - && !is_type_diagnostic_item(cx, type_of_receiver, sym::Result) - { + if !type_of_receiver.is_diag_item(cx, sym::Option) && !type_of_receiver.is_diag_item(cx, sym::Result) { return None; } METHODS_WITH_NEGATION diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 3b861848f94a6..6f51f2343ab5b 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_default_equivalent; use clippy_utils::macros::macro_backtrace; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::expr_sig; -use clippy_utils::{is_default_equivalent, path_def_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; -use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LetStmt, Node, QPath, Ty, TyKind}; +use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LangItem, LetStmt, Node, QPath, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::{Span, sym}; @@ -44,7 +45,7 @@ impl LateLintPass<'_> for BoxDefault { // And that method is `new` && seg.ident.name == sym::new // And the call is that of a `Box` method - && path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box()) + && ty.basic_res().is_lang_item(cx, LangItem::OwnedBox) // And the single argument to the call is another function call // This is the `T::default()` (or default equivalent) of `Box::new(T::default())` && let ExprKind::Call(arg_path, _) = arg.kind diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index ff5320719aa28..be1f406770ce6 100644 --- a/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{expr_or_init, is_path_diagnostic_item, std_or_core, sym}; +use clippy_utils::{expr_or_init, std_or_core, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind}; @@ -53,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 - && is_path_diagnostic_item(cx, fun, sym::mem_align_of) + && fun.basic_res().is_diag_item(cx, sym::mem_align_of) && let Some(args) = path.segments.last().and_then(|seg| seg.args) && let [GenericArg::Type(generic_ty)] = args.args { diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index c88a0539d70e1..7bfe9201d812a 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{SpanRangeExt, snippet_opt}; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; -use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -167,11 +168,11 @@ pub(super) fn check<'tcx>( sym::assert_ne_macro, sym::debug_assert_ne_macro, ]; - matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if + 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) + if let Some(id) = cast_expr.res_local_id() && !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span) { // Binding context is different than the identifiers context. diff --git a/clippy_lints/src/cloned_ref_to_slice_refs.rs b/clippy_lints/src/cloned_ref_to_slice_refs.rs index 72ab292ee3c66..35b799aefb04c 100644 --- a/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; 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 clippy_utils::{is_in_const_context, is_mutable}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { // check for clones && let ExprKind::MethodCall(_, val, _, _) = item.kind - && is_trait_method(cx, item, sym::Clone) + && cx.ty_based_def(item).opt_parent(cx).is_diag_item(cx, sym::Clone) // check for immutability or purity && (!is_mutable(cx, val) || is_const_evaluatable(cx, val)) diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index c0f30e456d8db..595625c08bef9 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{IntoSpan, SpanRangeExt}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; @@ -93,7 +93,7 @@ impl CognitiveComplexity { }); let ret_ty = cx.typeck_results().node_type(expr.hir_id); - let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) { + let ret_adjust = if ret_ty.is_diag_item(cx, sym::Result) { returns } else { #[expect(clippy::integer_division)] diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index 1279be34ed8f2..fd84ce70bd717 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; +use clippy_utils::get_enclosing_block; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::visitors::{Visitable, for_each_expr}; -use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { let ty = cx.typeck_results().pat_ty(local.pat); matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some( sym::BTreeMap | sym::BTreeSet @@ -71,7 +71,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { | sym::Vec | sym::VecDeque ) - ) || is_type_lang_item(cx, ty, LangItem::String) + ) || ty.is_lang_item(cx, LangItem::String) } fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool { @@ -81,7 +81,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // Inspect all expressions and sub-expressions in the block. for_each_expr(cx, block, |expr| { // Ignore expressions that are not simply `id`. - if !path_to_local_id(expr, id) { + if expr.res_local_id() != Some(id) { return ControlFlow::Continue(()); } @@ -93,7 +93,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // id = ...; // Not reading `id`. if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) && let ExprKind::Assign(lhs, ..) = parent.kind - && path_to_local_id(lhs, id) + && lhs.res_local_id() == Some(id) { return ControlFlow::Continue(()); } @@ -107,7 +107,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // have side effects, so consider them a read. if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) && let ExprKind::MethodCall(_, receiver, args, _) = parent.kind - && path_to_local_id(receiver, id) + && receiver.res_local_id() == Some(id) && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) && !method_def_id.is_local() { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 0ec0aaaad4536..375d179681da3 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -488,6 +488,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO, crate::methods::UNNECESSARY_MAP_OR_INFO, crate::methods::UNNECESSARY_MIN_OR_MAX_INFO, + crate::methods::UNNECESSARY_OPTION_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_SORT_BY_INFO, crate::methods::UNNECESSARY_TO_OWNED_INFO, @@ -655,6 +656,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::regex::REGEX_CREATION_IN_LOOPS_INFO, crate::regex::TRIVIAL_REGEX_INFO, crate::repeat_vec_with_capacity::REPEAT_VEC_WITH_CAPACITY_INFO, + crate::replace_box::REPLACE_BOX_INFO, crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO, crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO, crate::returns::LET_AND_RETURN_INFO, @@ -780,6 +782,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::visibility::NEEDLESS_PUB_SELF_INFO, crate::visibility::PUB_WITHOUT_SHORTHAND_INFO, crate::visibility::PUB_WITH_SHORTHAND_INFO, + crate::volatile_composites::VOLATILE_COMPOSITES_INFO, crate::wildcard_imports::ENUM_GLOB_USE_INFO, crate::wildcard_imports::WILDCARD_IMPORTS_INFO, crate::write::PRINTLN_EMPTY_STRING_INFO, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 2147f72889093..f087a894d76ae 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -7,7 +7,6 @@ macro_rules! declare_with_version { $e:expr, )*]) => { pub static $name: &[(&str, &str)] = &[$($e),*]; - #[allow(unused)] pub static $name_version: &[&str] = &[$($version),*]; }; } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9ebb8e6e15d9e..de1362081323a 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, - path_to_local, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -239,7 +239,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { return; } - if let Some(local) = path_to_local(expr) { + if let Some(local) = expr.res_local_id() { self.check_local_usage(cx, expr, local); } diff --git a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs index dfb723b86eb93..b2bc6402561f5 100644 --- a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs +++ b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::fulfill_or_allowed; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_hir::{self as hir, HirId, Item}; use rustc_lint::LateContext; @@ -60,14 +61,16 @@ pub(super) fn check<'tcx>( return; } - span_lint_hir_and_then( + if fulfill_or_allowed(cx, EXPL_IMPL_CLONE_ON_COPY, [adt_hir_id]) { + return; + } + + span_lint_and_help( cx, EXPL_IMPL_CLONE_ON_COPY, - adt_hir_id, item.span, "you are implementing `Clone` explicitly on a `Copy` type", - |diag| { - diag.span_help(item.span, "consider deriving `Clone` or removing `Copy`"); - }, + None, + "consider deriving `Clone` or removing `Copy`", ); } diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs index 06efc2709faa6..eafe7c4bb9f23 100644 --- a/clippy_lints/src/derive/mod.rs +++ b/clippy_lints/src/derive/mod.rs @@ -1,4 +1,4 @@ -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use rustc_hir::def::Res; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { self_ty, .. }) = item.kind - && let Res::Def(_, def_id) = path_res(cx, self_ty) + && let Res::Def(_, def_id) = *self_ty.basic_res() && let Some(local_def_id) = def_id.as_local() { let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index 3033ac0d0b0b7..b164a9a99782b 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -1,7 +1,8 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait_with_env; use clippy_utils::visitors::for_each_expr; use clippy_utils::{fulfill_or_allowed, is_doc_hidden, is_inside_always_const_context, method_chain_args, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; @@ -62,7 +63,7 @@ pub fn check( ); } if !headers.errors { - if is_type_diagnostic_item(cx, return_ty(cx, owner_id), sym::Result) { + if return_ty(cx, owner_id).is_diag_item(cx, sym::Result) { span_lint( cx, MISSING_ERRORS_DOC, @@ -83,7 +84,7 @@ pub fn check( &[], ) && let ty::Coroutine(_, subs) = ret_ty.kind() - && is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result) + && subs.as_coroutine().return_ty().is_diag_item(cx, sym::Result) { span_lint( cx, @@ -119,10 +120,7 @@ fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]).or_else(|| method_chain_args(expr, &[sym::expect])) && let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs() - && matches!( - get_type_diagnostic_name(cx, receiver_ty), - Some(sym::Option | sym::Result) - ) + && matches!(receiver_ty.opt_diag_name(cx), Some(sym::Option | sym::Result)) && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) && panic_span.is_none() { diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index cfdf9ca623688..2a3fb82946117 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -970,7 +970,7 @@ fn check_for_code_clusters<'a, Events: Iterator, Range)>>( cx: &LateContext<'_>, valid_idents: &FxHashSet, diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 5c360ce6a5f7e..3bb8c484ceec3 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_must_use_func_call; -use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::{is_copy, is_must_use_ty}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -97,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { sym::mem_forget if arg_ty.is_ref() => return, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return, sym::mem_forget if is_copy => return, - sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => return, + sym::mem_drop if arg_ty.is_lang_item(cx, LangItem::ManuallyDrop) => return, sym::mem_drop if !(arg_ty.needs_drop(cx.tcx, cx.typing_env()) || is_must_use_func_call(cx, arg) diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs index 3018e1f127347..3d8650f05168d 100644 --- a/clippy_lints/src/error_impl_error.rs +++ b/clippy_lints/src/error_impl_error.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::implements_trait; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{Item, ItemKind}; @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_ref.trait_def_id()) && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) && error_def_id == trait_def_id - && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local) + && let Some(def_id) = imp.self_ty.basic_res().opt_def_id().and_then(DefId::as_local) && let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id()) && ident.name == sym::Error && is_visible_outside_module(cx, def_id) => diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 752f39b4e6dc1..21385ee4fdc7f 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,11 +1,9 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::VecArgs; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; -use clippy_utils::{ - get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, -}; +use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate}; use rustc_abi::ExternAbi; use rustc_errors::Applicability; use rustc_hir::attrs::AttributeKind; @@ -86,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) { let body = if let ExprKind::Closure(c) = expr.kind && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) @@ -144,7 +142,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx { let callee_ty_raw = typeck.expr_ty(callee); let callee_ty = callee_ty_raw.peel_refs(); - if matches!(get_type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) + if matches!(callee_ty.opt_diag_name(cx), Some(sym::Arc | sym::Rc)) || !check_inputs(typeck, body.params, None, args) { return; @@ -218,7 +216,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if path_to_local(callee).is_some_and(|l| { + if callee.res_local_id().is_some_and(|l| { // FIXME: Do we really need this `local_used_in` check? // Isn't it checking something like... `callee(callee)`? // If somehow this check is needed, add some test for it, @@ -307,7 +305,7 @@ fn check_inputs( matches!( p.pat.kind, PatKind::Binding(BindingMode::NONE, id, _, None) - if path_to_local_id(arg, id) + if arg.res_local_id() == Some(id) ) // Only allow adjustments which change regions (i.e. re-borrowing). && typeck diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 085ee4448a4ee..c59ffa14a5fee 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, path_def_id, sym}; +use clippy_utils::{is_expn_of, is_in_test, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; @@ -59,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind) && let ExprKind::Call(write_recv_path, []) = write_recv.kind && write_fun.ident.name == sym::write_fmt - && let Some(def_id) = path_def_id(cx, write_recv_path) + && let Some(def_id) = write_recv_path.basic_res().opt_def_id() { // match calls to std::io::stdout() / std::io::stderr () let (dest_name, prefix) = match cx.tcx.get_diagnostic_name(def_id) { @@ -71,6 +72,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { return; }; + // Performing an explicit write in a test circumvent's libtest's capture of stdio and stdout. + if is_in_test(cx.tcx, expr.hir_id) { + return; + } + // ordering is important here, since `writeln!` uses `write!` internally let calling_macro = if is_expn_of(write_call.span, sym::writeln).is_some() { Some("writeln") diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index fdfcbb540bce6..4446b912bf7ee 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::method_chain_args; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -84,9 +84,7 @@ fn lint_impl_body(cx: &LateContext<'_>, item_def_id: hir::OwnerId, impl_span: Sp // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]) { let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result) - { + if receiver_ty.is_diag_item(self.lcx, sym::Option) || receiver_ty.is_diag_item(self.lcx, sym::Result) { self.result.push(expr.span); } } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 407a3f1306739..5f022ba307ff9 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -1,9 +1,10 @@ use clippy_utils::consts::Constant::{F32, F64, Int}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::{ - eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, - is_inherent_method_call, is_no_std_crate, numeric_literal, peel_blocks, sugg, sym, + eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, is_no_std_crate, + numeric_literal, peel_blocks, sugg, sym, }; use rustc_ast::ast; use rustc_errors::Applicability; @@ -737,7 +738,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind { 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) { + if recv_ty.is_floating_point() && !is_no_std_crate(cx) && cx.ty_based_def(expr).opt_parent(cx).is_impl(cx) { match path.ident.name { sym::ln => check_ln1p(cx, expr, receiver), sym::log => check_log_base(cx, expr, receiver, args), diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 94e66769eb265..098bf4ba42f93 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -39,7 +39,6 @@ declare_clippy_lint! { "useless use of `format!`" } -#[allow(clippy::module_name_repetitions)] pub struct UselessFormat { format_args: FormatArgsStorage, } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 3359aa603239e..011cbf8c5d41c 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -9,9 +9,10 @@ use clippy_utils::macros::{ root_macro_call_first_node, }; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{implements_trait, is_type_lang_item}; -use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test, trait_ref_of_method}; +use clippy_utils::ty::implements_trait; +use clippy_utils::{is_from_proc_macro, is_in_test, trait_ref_of_method}; use itertools::Itertools; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, @@ -237,7 +238,7 @@ impl_lint_pass!(FormatArgs<'_> => [ POINTER_FORMAT, ]); -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] pub struct FormatArgs<'tcx> { format_args: FormatArgsStorage, msrv: Msrv, @@ -344,7 +345,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { if let Some(placeholder_span) = placeholder.span && *options != FormatOptions::default() && let ty = self.cx.typeck_results().expr_ty(arg).peel_refs() - && is_type_lang_item(self.cx, ty, LangItem::FormatArguments) + && ty.is_lang_item(self.cx, LangItem::FormatArguments) { span_lint_and_then( self.cx, @@ -497,8 +498,11 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { let cx = self.cx; if !value.span.from_expansion() && let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind - && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) - && is_diag_trait_item(cx, method_def_id, sym::ToString) + && cx + .typeck_results() + .type_dependent_def_id(value.hir_id) + .opt_parent(cx) + .is_diag_item(cx, sym::ToString) && let receiver_ty = cx.typeck_results().expr_ty(receiver) && let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display) && let (n_needed_derefs, target) = diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 416aea51ea195..903d43e56c4b2 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; -use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{get_parent_as_impl, peel_ref_operators, sym}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; @@ -157,8 +158,12 @@ impl FormatImplExpr<'_, '_> { && path.ident.name == sym::to_string // Is the method a part of the ToString trait? (i.e. not to_string() implemented // separately) - && let Some(expr_def_id) = self.cx.typeck_results().type_dependent_def_id(self.expr.hir_id) - && is_diag_trait_item(self.cx, expr_def_id, sym::ToString) + && self + .cx + .typeck_results() + .type_dependent_def_id(self.expr.hir_id) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::ToString) // Is the method is called on self && let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind && let [segment] = path.segments @@ -210,7 +215,7 @@ impl FormatImplExpr<'_, '_> { // Since the argument to fmt is itself a reference: &self let reference = peel_ref_operators(self.cx, arg); // Is the reference self? - if path_to_local(reference).map(|x| self.cx.tcx.hir_name(x)) == Some(kw::SelfLower) { + if reference.res_local_id().map(|x| self.cx.tcx.hir_name(x)) == Some(kw::SelfLower) { let FormatTraitNames { name, .. } = self.format_trait_impl; span_lint( self.cx, diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs index b64d608c0c709..a23ba9ab837ae 100644 --- a/clippy_lints/src/format_push_string.rs +++ b/clippy_lints/src/format_push_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{AssignOpKind, Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -41,7 +41,10 @@ declare_clippy_lint! { declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) + cx.typeck_results() + .expr_ty(e) + .peel_refs() + .is_lang_item(cx, LangItem::String) } fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { let e = e.peel_blocks().peel_borrows(); diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index e3bb5ee10db7c..fd807290dc090 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -4,7 +4,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::path_def_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_path}; @@ -90,7 +90,12 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { |diag| { // If the target type is likely foreign mention the orphan rules as it's a common source of // confusion - if path_def_id(cx, target_ty.peel_refs()).is_none_or(|id| !id.is_local()) { + if target_ty + .peel_refs() + .basic_res() + .opt_def_id() + .is_none_or(|id| !id.is_local()) + { diag.help( "`impl From for Foreign` is allowed by the orphan rules, for more information see\n\ https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence" diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs index 5e2e2c9dbf725..0e6eeb75ec573 100644 --- a/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::res::MaybeResPath; +use clippy_utils::sym; 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}; @@ -41,7 +42,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr { if let ExprKind::Call(box_from_raw, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind && 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 Some(type_str) = ty.basic_res().opt_def_id().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 && is_c_void(cx, *ty) diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index d5873b3f85aa1..df8a35d9658b0 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def}; @@ -89,5 +89,5 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { /// Checks if a Ty is `String` or `&str` fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_lang_item(cx, ty, LangItem::String) || ty.peel_refs().is_str() + ty.is_lang_item(cx, LangItem::String) || ty.peel_refs().is_str() } diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 8de68bfcb511b..68532de0368f8 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -132,7 +132,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr } // FIXME: needs to be an EARLY LINT. all attribute lints should be -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_needless_must_use( cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 72f8795304055..c6b0e7c54c06f 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -1,12 +1,13 @@ +use clippy_utils::res::MaybeResPath; use rustc_hir::{self as hir, HirId, HirIdSet, intravisit}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; +use clippy_utils::iter_input_pats; use clippy_utils::ty::is_unsafe_fn; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{iter_input_pats, path_to_local}; use core::ops::ControlFlow; @@ -87,7 +88,7 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option { } fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) { - if path_to_local(arg).is_some_and(|id| raw_ptrs.contains(&id)) { + if arg.res_local_id().is_some_and(|id| raw_ptrs.contains(&id)) { span_lint( cx, NOT_UNSAFE_PTR_ARG_DEREF, diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs index 5dc1b7269b761..cc9dc47e15f2b 100644 --- a/clippy_lints/src/functions/ref_option.rs +++ b/clippy_lints/src/functions/ref_option.rs @@ -62,7 +62,7 @@ fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(crate) fn check_fn<'a>( cx: &LateContext<'a>, kind: FnKind<'a>, diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 1f2fce687ed1b..fb80cc1a63a30 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -1,4 +1,5 @@ use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use rustc_errors::Diag; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; @@ -6,7 +7,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_type_diagnostic_item}; +use clippy_utils::ty::{AdtVariantInfo, approx_ty_size}; use clippy_utils::{is_no_std_crate, trait_ref_of_method}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; @@ -24,7 +25,7 @@ fn result_err_ty<'tcx>( && let ty = cx .tcx .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(id).instantiate_identity().output()) - && is_type_diagnostic_item(cx, ty, sym::Result) + && ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = ty.kind() { let err_ty = args.type_at(1); diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index a99118f90f88a..eed2d5d885353 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, higher, sym}; use core::ops::ControlFlow; @@ -95,7 +95,7 @@ fn mutex_lock_call<'tcx>( if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind && path.ident.name == sym::lock && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() - && is_type_diagnostic_item(cx, ty, sym::Mutex) + && ty.is_diag_item(cx, sym::Mutex) && op_mutex.is_none_or(|op| eq_expr_value(cx, self_arg, op)) { ControlFlow::Break(self_arg) diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index f9fee292837ea..dfc8411baa005 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -2,11 +2,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, - path_res, peel_blocks, sym, + contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -73,8 +73,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind && !expr.span.from_expansion() && !then_expr.span.from_expansion() - && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) - && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) + && then_call.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) + && peel_blocks(els).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) && !is_else_clause(cx.tcx, expr) && !is_in_const_context(cx) && self.msrv.meets(cx, msrvs::BOOL_THEN) diff --git a/clippy_lints/src/ifs/branches_sharing_code.rs b/clippy_lints/src/ifs/branches_sharing_code.rs index eb1025f71498e..b3f597cc8736e 100644 --- a/clippy_lints/src/ifs/branches_sharing_code.rs +++ b/clippy_lints/src/ifs/branches_sharing_code.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, get_enclosing_block, hash_expr, hash_stmt, - path_to_local, }; use core::iter; use core::ops::ControlFlow; @@ -149,7 +149,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { /// Checks if the statement modifies or moves any of the given locals. fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool { for_each_expr_without_closures(s, |e| { - if let Some(id) = path_to_local(e) + if let Some(id) = e.res_local_id() && locals.contains(&id) && !capture_local_usage(cx, e).is_imm_ref() { @@ -198,7 +198,7 @@ fn scan_block_for_eq<'tcx>( let mut cond_locals = HirIdSet::default(); for &cond in conds { let _: Option = for_each_expr_without_closures(cond, |e| { - if let Some(id) = path_to_local(e) { + if let Some(id) = e.res_local_id() { cond_locals.insert(id); } ControlFlow::Continue(()) diff --git a/clippy_lints/src/ifs/ifs_same_cond.rs b/clippy_lints/src/ifs/ifs_same_cond.rs index ca76fc2587db9..3ea941320a086 100644 --- a/clippy_lints/src/ifs/ifs_same_cond.rs +++ b/clippy_lints/src/ifs/ifs_same_cond.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::InteriorMut; -use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, search_same}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -16,7 +17,7 @@ fn method_caller_is_mutable<'tcx>( interior_mut.is_interior_mut_ty(cx, caller_ty) || caller_ty.is_mutable_ptr() // `find_binding_init` will return the binding iff its not mutable - || path_to_local(caller_expr) + || caller_expr.res_local_id() .and_then(|hid| find_binding_init(cx, hid)) .is_none() } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index b3c90f364e834..d2bc0b6d99354 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; +use clippy_utils::res::MaybeDef; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::{Visitor, VisitorExt, walk_body, walk_expr, walk_ty}; use rustc_hir::{self as hir, AmbigArg, Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; @@ -14,7 +15,6 @@ use rustc_span::Span; 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! { /// ### What it does @@ -227,14 +227,14 @@ impl<'tcx> ImplicitHasherType<'tcx> { let ty = lower_ty(cx.tcx, hir_ty); - if is_type_diagnostic_item(cx, ty, sym::HashMap) && params_len == 2 { + if ty.is_diag_item(cx, sym::HashMap) && params_len == 2 { Some(ImplicitHasherType::HashMap( hir_ty.span, ty, snippet(cx, params[0].span, "K"), snippet(cx, params[1].span, "V"), )) - } else if is_type_diagnostic_item(cx, ty, sym::HashSet) && params_len == 1 { + } else if ty.is_diag_item(cx, sym::HashSet) && params_len == 1 { Some(ImplicitHasherType::HashSet( hir_ty.span, ty, diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index c634c12e1877b..678a29924e528 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_manual_check<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, @@ -165,7 +165,7 @@ fn check_manual_check<'tcx>( } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_gt( cx: &LateContext<'_>, condition_span: Span, @@ -196,7 +196,7 @@ fn is_side_effect_free(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { eq_expr_value(cx, expr, expr) } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_subtraction( cx: &LateContext<'_>, condition_span: Span, diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 8f9f71a147694..919702c5714ad 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; +use clippy_utils::is_lint_allowed; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::is_copy; -use clippy_utils::{is_lint_allowed, path_to_local}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -225,7 +226,7 @@ impl<'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'_, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if let Some(local_id) = path_to_local(expr) { + if let Some(local_id) = expr.res_local_id() { let Self { cx, ref mut slice_lint_info, diff --git a/clippy_lints/src/ineffective_open_options.rs b/clippy_lints/src/ineffective_open_options.rs index a159f61571839..bc57d9e85478a 100644 --- a/clippy_lints/src/ineffective_open_options.rs +++ b/clippy_lints/src/ineffective_open_options.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{peel_blocks, peel_hir_expr_while, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -47,7 +47,11 @@ impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions { if let ExprKind::MethodCall(name, recv, [_], _) = expr.kind && name.ident.name == sym::open && !expr.span.from_expansion() - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::FsOpenOptions) + && cx + .typeck_results() + .expr_ty(recv) + .peel_refs() + .is_diag_item(cx, sym::FsOpenOptions) { let mut append = false; let mut write = None; diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index bf3eafe09b3d0..f193f065e68d5 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use clippy_utils::{higher, sym}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -235,7 +236,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } else if method.ident.name == sym::collect { let ty = cx.typeck_results().expr_ty(expr); if matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some( sym::BinaryHeap | sym::BTreeMap diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 309d2dfb28b87..a08efbc52d457 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -1,17 +1,23 @@ +use clippy_config::Conf; +use clippy_config::types::InherentImplLintScope; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_lint_allowed; +use clippy_utils::fulfill_or_allowed; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::{Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use rustc_span::Span; +use rustc_session::impl_lint_pass; +use rustc_span::{FileName, Span}; use std::collections::hash_map::Entry; declare_clippy_lint! { /// ### What it does /// Checks for multiple inherent implementations of a struct /// + /// The config option controls the scope in which multiple inherent `impl` blocks for the same + /// struct are linted, allowing values of `module` (only within the same module), `file` + /// (within the same file), or `crate` (anywhere in the crate, default). + /// /// ### Why restrict this? /// Splitting the implementation of a type makes the code harder to navigate. /// @@ -41,7 +47,26 @@ declare_clippy_lint! { "Multiple inherent impl that could be grouped" } -declare_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); +impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); + +pub struct MultipleInherentImpl { + scope: InherentImplLintScope, +} + +impl MultipleInherentImpl { + pub fn new(conf: &'static Conf) -> Self { + Self { + scope: conf.inherent_impl_lint_scope, + } + } +} + +#[derive(Hash, Eq, PartialEq, Clone)] +enum Criterion { + Module(LocalModDefId), + File(FileName), + Crate, +} impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { @@ -55,18 +80,27 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { for (&id, impl_ids) in &impls.inherent_impls { if impl_ids.len() < 2 - // Check for `#[allow]` on the type definition - || is_lint_allowed( + // Check for `#[expect]` or `#[allow]` on the type definition + || fulfill_or_allowed( cx, MULTIPLE_INHERENT_IMPL, - cx.tcx.local_def_id_to_hir_id(id), + [cx.tcx.local_def_id_to_hir_id(id)], ) { continue; } for impl_id in impl_ids.iter().map(|id| id.expect_local()) { let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity(); - match type_map.entry(impl_ty) { + let hir_id = cx.tcx.local_def_id_to_hir_id(impl_id); + let criterion = match self.scope { + InherentImplLintScope::Module => Criterion::Module(cx.tcx.parent_module(hir_id)), + InherentImplLintScope::File => { + let span = cx.tcx.hir_span(hir_id); + Criterion::File(cx.tcx.sess.source_map().lookup_source_file(span.lo()).name.clone()) + }, + InherentImplLintScope::Crate => Criterion::Crate, + }; + match type_map.entry((impl_ty, criterion)) { Entry::Vacant(e) => { // Store the id for the first impl block of this type. The span is retrieved lazily. e.insert(IdOrSpan::Id(impl_id)); @@ -97,7 +131,6 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // Switching to the next type definition, no need to keep the current entries around. type_map.clear(); } - // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first. lint_spans.sort_by_key(|x| x.0.lo()); for (span, first_span) in lint_spans { @@ -125,7 +158,7 @@ fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option { { (!span.from_expansion() && impl_item.generics.params.is_empty() - && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id)) + && !fulfill_or_allowed(cx, MULTIPLE_INHERENT_IMPL, [id])) .then_some(span) } else { None diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 7f2e25367a6a4..e569a5c7b6126 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{implements_trait, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use clippy_utils::{return_ty, trait_ref_of_method}; use rustc_abi::ExternAbi; use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem}; @@ -104,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { && impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) && !impl_item.span.from_expansion() // Check if return type is String - && is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String) + && return_ty(cx, impl_item.owner_id).is_lang_item(cx, LangItem::String) // Filters instances of to_string which are required by a trait && trait_ref_of_method(cx, impl_item.owner_id).is_none() { diff --git a/clippy_lints/src/iter_over_hash_type.rs b/clippy_lints/src/iter_over_hash_type.rs index b1cb6da9475ba..6bb46ac3b5542 100644 --- a/clippy_lints/src/iter_over_hash_type.rs +++ b/clippy_lints/src/iter_over_hash_type.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::higher::ForLoop; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -55,9 +55,7 @@ impl LateLintPass<'_> for IterOverHashType { if let Some(for_loop) = ForLoop::hir(expr) && !for_loop.body.span.from_expansion() && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs() - && hash_iter_tys - .into_iter() - .any(|sym| is_type_diagnostic_item(cx, ty, sym)) + && hash_iter_tys.into_iter().any(|sym| ty.is_diag_item(cx, sym)) { span_lint( cx, diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index 42c636505c015..8a5d97294d2bd 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -113,35 +113,18 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { // since this would only require removing a `use` import (which is already linted). && !is_numeric_const_path_canonical(path, [*mod_name, *name]) { - ( - vec![(expr.span, format!("{mod_name}::{name}"))], - "usage of a legacy numeric constant", - ) + (format!("{mod_name}::{name}"), "usage of a legacy numeric constant") // `::xxx_value` check } else if let ExprKind::Call(func, []) = &expr.kind && let ExprKind::Path(qpath) = &func.kind && let QPath::TypeRelative(ty, last_segment) = qpath && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && is_integer_method(cx, def_id) + && let Some(mod_name) = ty.span.get_source_text(cx) + && ty.span.eq_ctxt(last_segment.ident.span) { - let mut sugg = vec![ - // Replace the function name up to the end by the constant name - ( - last_segment.ident.span.to(expr.span.shrink_to_hi()), - last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(), - ), - ]; - let before_span = expr.span.shrink_to_lo().until(ty.span); - if !before_span.is_empty() { - // Remove everything before the type name - sugg.push((before_span, String::new())); - } - // Use `::` between the type name and the constant - let between_span = ty.span.shrink_to_hi().until(last_segment.ident.span); - if !between_span.check_source_text(cx, |s| s == "::") { - sugg.push((between_span, String::from("::"))); - } - (sugg, "usage of a legacy numeric method") + let name = last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(); + (format!("{mod_name}::{name}"), "usage of a legacy numeric method") } else { return; }; @@ -151,7 +134,8 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { && !is_from_proc_macro(cx, expr) { span_lint_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.span, msg, |diag| { - diag.multipart_suggestion_verbose( + diag.span_suggestion_verbose( + expr.span, "use the associated constant instead", sugg, Applicability::MaybeIncorrect, diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 28a0fbc051158..04a8e4739b853 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; 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_parent_as_impl, is_trait_method, parent_item_name, peel_ref_operators, sym, -}; +use clippy_utils::{fulfill_or_allowed, get_parent_as_impl, parent_item_name, peel_ref_operators, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -204,7 +203,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { } if let ExprKind::MethodCall(method, lhs_expr, [rhs_expr], _) = expr.kind - && is_trait_method(cx, expr, sym::PartialEq) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::PartialEq) && !expr.span.from_expansion() { check_empty_expr( diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index e480c8fbed53c..2dbf55a8540ba 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::path_to_local_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use rustc_errors::Applicability; @@ -145,7 +145,7 @@ fn check_assign<'tcx>( && let Some(expr) = block.stmts.iter().last() && let hir::StmtKind::Semi(expr) = expr.kind && let hir::ExprKind::Assign(var, value, _) = expr.kind - && path_to_local_id(var, decl) + && var.res_local_id() == Some(decl) { if block .stmts diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dcc2d985f3fe9..a4ad9424b3eb1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -318,6 +318,7 @@ mod ref_patterns; mod reference; mod regex; mod repeat_vec_with_capacity; +mod replace_box; mod reserve_after_initialization; mod return_self_not_must_use; mod returns; @@ -394,6 +395,7 @@ mod useless_conversion; mod vec; mod vec_init_then_push; mod visibility; +mod volatile_composites; mod wildcard_imports; mod write; mod zero_div_zero; @@ -581,7 +583,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)); store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)); store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit)); - store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); + store.register_late_pass(move |_| Box::new(inherent_impl::MultipleInherentImpl::new(conf))); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); store.register_late_pass(move |_| Box::new(unwrap::Unwrap::new(conf))); store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); @@ -612,7 +614,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co 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)); - store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue)); + store.register_late_pass(|_| Box::new(needless_continue::NeedlessContinue)); store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|_| Box::new(create_dir::CreateDir)); store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)); @@ -758,7 +760,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))); store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)); store.register_late_pass(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))); - store.register_late_pass(|_| Box::new(non_canonical_impls::NonCanonicalImpls)); + store.register_late_pass(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx))); store.register_late_pass(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))); store.register_early_pass(move || Box::new(raw_strings::RawStrings::new(conf))); store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))); @@ -825,5 +827,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom)); store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)); + store.register_late_pass(|_| Box::new(volatile_composites::VolatileComposites)); + store.register_late_pass(|_| Box::new(replace_box::ReplaceBox)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index d8b186b6787d1..519ec228c8840 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -184,7 +184,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, sig: &'tcx FnSig<'_>, @@ -540,7 +540,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } -#[allow(clippy::struct_excessive_bools)] +#[expect(clippy::struct_excessive_bools)] struct Usage { lifetime: Lifetime, in_where_predicate: bool, diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index 14ccb6fce2239..65e922ac07d36 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,10 +73,13 @@ impl_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]); impl LateLintPass<'_> for LinesFilterMapOk { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let fm_method_name = fm_method.ident.name && matches!(fm_method_name, sym::filter_map | sym::flat_map | sym::flatten) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines) + && cx + .typeck_results() + .expr_ty_adjusted(fm_receiver) + .is_diag_item(cx, sym::IoLines) && should_lint(cx, fm_args, fm_method_name) && self.msrv.meets(cx, msrvs::MAP_WHILE) { @@ -117,10 +120,15 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> params: [param], value, .. } = cx.tcx.hir_body(*body) && let ExprKind::MethodCall(method, receiver, [], _) = value.kind - && path_to_local_id(receiver, param.pat.hir_id) - && let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id) { - is_diag_item_method(cx, method_did, sym::Result) && method.ident.name == sym::ok + method.ident.name == sym::ok + && receiver.res_local_id() == Some(param.pat.hir_id) + && cx + .typeck_results() + .type_dependent_def_id(value.hir_id) + .opt_parent(cx) + .opt_impl_ty(cx) + .is_diag_item(cx, sym::Result) } else { false } diff --git a/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/clippy_lints/src/loops/char_indices_as_byte_indices.rs index a702e60f1c276..7acf2a5462220 100644 --- a/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -1,9 +1,9 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{eq_expr_value, higher, path_to_local_id, sym}; +use clippy_utils::{eq_expr_value, higher, sym}; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{Expr, ExprKind, LangItem, Node, Pat, PatKind}; use rustc_lint::LateContext; @@ -49,7 +49,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr { // Destructured iterator element `(idx, _)`, look for uses of the binding for_each_expr(cx, body, |expr| { - if path_to_local_id(expr, binding_id) { + if expr.res_local_id() == Some(binding_id) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); } CONTINUE @@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr // Bound as a tuple, look for `tup.0` for_each_expr(cx, body, |expr| { if let ExprKind::Field(e, field) = expr.kind - && path_to_local_id(e, binding_id) + && e.res_local_id() == Some(binding_id) && field.name == sym::integer(0) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); @@ -81,7 +81,7 @@ fn check_index_usage<'tcx>( return; }; - let is_string_like = |ty: Ty<'_>| ty.is_str() || is_type_lang_item(cx, ty, LangItem::String); + let is_string_like = |ty: Ty<'_>| ty.is_str() || ty.is_lang_item(cx, LangItem::String); let message = match parent_expr.kind { ExprKind::MethodCall(segment, recv, ..) // We currently only lint `str` methods (which `String` can deref to), so a `.is_str()` check is sufficient here diff --git a/clippy_lints/src/loops/explicit_into_iter_loop.rs b/clippy_lints/src/loops/explicit_into_iter_loop.rs index 4aa1c2e211d33..daca78e344748 100644 --- a/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,6 +1,6 @@ use super::EXPLICIT_INTO_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -43,7 +43,11 @@ impl AdjustKind { } pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) { - if !is_trait_method(cx, call_expr, sym::IntoIterator) { + if !cx + .ty_based_def(call_expr) + .opt_parent(cx) + .is_diag_item(cx, sym::IntoIterator) + { return; } diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index af475c40586fe..40d1d36bd162c 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -1,10 +1,11 @@ use super::EXPLICIT_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; use clippy_utils::sym; use clippy_utils::ty::{ - implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection, + implements_trait, implements_trait_with_env, is_copy, make_normalized_projection, make_normalized_projection_with_regions, normalize_with_regions, }; use rustc_errors::Applicability; @@ -127,8 +128,7 @@ fn is_ref_iterable<'tcx>( let self_ty = typeck.expr_ty(self_arg); let self_is_copy = is_copy(cx, self_ty); - if is_type_lang_item(cx, self_ty.peel_refs(), rustc_hir::LangItem::OwnedBox) - && !msrv.meets(cx, msrvs::BOX_INTO_ITER) + if self_ty.peel_refs().is_lang_item(cx, rustc_hir::LangItem::OwnedBox) && !msrv.meets(cx, msrvs::BOX_INTO_ITER) { return None; } diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index e314bc2068b30..c6b650a1a88be 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -1,7 +1,7 @@ use super::FOR_KV_MAP; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx _ => arg, }; - if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) { + if ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap) { span_lint_and_then( cx, FOR_KV_MAP, diff --git a/clippy_lints/src/loops/iter_next_loop.rs b/clippy_lints/src/loops/iter_next_loop.rs index b8a263817d297..8a4644cdf5efd 100644 --- a/clippy_lints/src/loops/iter_next_loop.rs +++ b/clippy_lints/src/loops/iter_next_loop.rs @@ -1,12 +1,12 @@ use super::ITER_NEXT_LOOP; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) { - if is_trait_method(cx, arg, sym::Iterator) { + if cx.ty_based_def(arg).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint( cx, ITER_NEXT_LOOP, diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index f99989ec6ba4b..c38cf83f44100 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -1,12 +1,12 @@ use super::MANUAL_FIND; use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}; +use clippy_utils::{higher, peel_blocks_with_stmt}; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::lang_items::LangItem; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; @@ -34,8 +34,8 @@ pub(super) fn check<'tcx>( && let StmtKind::Semi(semi) = stmt.kind && let ExprKind::Ret(Some(ret_value)) = semi.kind && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind - && is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome) - && path_res(cx, inner_ret) == Res::Local(binding_id) + && ctor.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) + && inner_ret.res_local_id() == Some(binding_id) && !contains_return_break_continue_macro(cond) && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) { @@ -150,7 +150,7 @@ fn last_stmt_and_ret<'tcx>( && let Some((_, Node::Block(block))) = parent_iter.next() && let Some((last_stmt, last_ret)) = extract(block) && last_stmt.hir_id == node_hir - && is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone) + && last_ret.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionNone) && let Some((_, Node::Expr(_block))) = parent_iter.next() // This includes the function header && let Some((_, func)) = parent_iter.next() diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index ddb8bb536c04e..96de118b5233b 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -2,9 +2,10 @@ use super::MANUAL_FLATTEN; use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{higher, is_refutable, path_to_local_id, peel_blocks_with_stmt, span_contains_comment}; +use clippy_utils::{higher, is_refutable, peel_blocks_with_stmt, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, Pat, PatKind}; @@ -27,7 +28,7 @@ pub(super) fn check<'tcx>( = higher::IfLet::hir(cx, inner_expr) // Ensure match_expr in `if let` statement is the same as the pat from the for-loop && let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind - && path_to_local_id(let_expr, pat_hir_id) + && let_expr.res_local_id() == Some(pat_hir_id) // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` && let PatKind::TupleStruct(ref qpath, [inner_pat], _) = let_pat.kind && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id) diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index d9c4b526da99e..a2da43c2ca245 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -1,10 +1,11 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::usage::local_used_in; -use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; +use clippy_utils::{get_enclosing_block, higher, sugg}; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir::intravisit::walk_block; @@ -67,7 +68,7 @@ pub(super) fn check<'tcx>( && !local_used_in(cx, canonical_id, base_left) && !local_used_in(cx, canonical_id, base_right) // Source and destination must be different - && path_to_local(base_left) != path_to_local(base_right) + && base_left.res_local_id() != base_right.res_local_id() { Some(( ty, @@ -128,7 +129,7 @@ fn build_manual_memcpy_suggestion<'tcx>( let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { if let ExprKind::MethodCall(method, recv, [], _) = end.kind && method.ident.name == sym::len - && path_to_local(recv) == path_to_local(base) + && recv.res_local_id() == base.res_local_id() { if sugg.to_string() == end_str { sugg::EMPTY.into() @@ -364,7 +365,7 @@ fn get_details_from_idx<'tcx>( starts: &[Start<'tcx>], ) -> Option<(StartKind<'tcx>, Offset)> { fn get_start<'tcx>(e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option> { - let id = path_to_local(e)?; + let id = e.res_local_id()?; starts.iter().find(|start| start.id == id).map(|start| start.kind) } @@ -425,7 +426,7 @@ fn get_assignments<'a, 'tcx>( .chain(*expr) .filter(move |e| { if let ExprKind::AssignOp(_, place, _) = e.kind { - path_to_local(place).is_some_and(|id| { + place.res_local_id().is_some_and(|id| { !loop_counters .iter() // skip the first item which should be `StartKind::Range` diff --git a/clippy_lints/src/loops/missing_spin_loop.rs b/clippy_lints/src/loops/missing_spin_loop.rs index 8a2d0036203a2..d5dbcbe9dc701 100644 --- a/clippy_lints/src/loops/missing_spin_loop.rs +++ b/clippy_lints/src/loops/missing_spin_loop.rs @@ -1,7 +1,7 @@ use super::MISSING_SPIN_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::std_or_core; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::LateContext; @@ -40,7 +40,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' && let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name) && let callee_ty = cx.typeck_results().expr_ty(callee) - && is_type_diagnostic_item(cx, callee_ty, sym::AtomicBool) + && callee_ty.is_diag_item(cx, sym::AtomicBool) && let Some(std_or_core) = std_or_core(cx) { span_lint_and_sugg( diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 01c36b8cb12fc..a064a5910ef98 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -861,6 +861,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // check for `loop { if let {} else break }` that could be `while let` // (also matches an explicit "match" instead of "if let") // (even if the "match" or "if let" is used for declaration) + // (also matches on `let {} else break`) if let ExprKind::Loop(block, label, LoopSource::Loop, _) = expr.kind { // also check for empty `loop {}` statements, skipping those in #[panic_handler] empty_loop::check(cx, expr, block); @@ -870,7 +871,10 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_let_on_iterator::check(cx, expr); - if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) { + if let Some(higher::While { + condition, body, span, .. + }) = higher::While::hir(expr) + { while_immutable_condition::check(cx, condition, body); while_float::check(cx, condition); missing_spin_loop::check(cx, condition, body); @@ -880,7 +884,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { } impl Loops { - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments)] fn check_for_loop<'tcx>( &self, cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 70ca452013f91..daeda227f670c 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -1,6 +1,7 @@ use super::MUT_RANGE_BOUND; use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::{get_enclosing_block, higher, path_to_local}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{get_enclosing_block, higher}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Node, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -39,7 +40,7 @@ fn mut_warn_with_span(cx: &LateContext<'_>, span: Option) { } fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option { - if let Some(hir_id) = path_to_local(bound) + if let Some(hir_id) = bound.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) && let PatKind::Binding(BindingMode::MUT, ..) = pat.kind { diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 544c3c34d029c..528cc64fa7bc5 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -240,7 +240,7 @@ fn is_label_for_block(cx: &LateContext<'_>, dest: &Destination) -> bool { .is_ok_and(|hir_id| matches!(cx.tcx.hir_node(hir_id), Node::Block(_))) } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn never_loop_expr<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index e792edbe23e0b..4135c63d4d7ff 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,9 +1,10 @@ use super::SAME_ITEM_PUSH; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::{MaybeDef, MaybeResPath}; 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, sym}; +use clippy_utils::ty::implements_trait; +use clippy_utils::{msrvs, std_or_core, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -125,7 +126,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { if !self.non_deterministic_expr && !self.multiple_pushes && let Some((vec, _, _)) = self.vec_push - && let Some(hir_id) = path_to_local(vec) + && let Some(hir_id) = vec.res_local_id() { !self.used_locals.contains(&hir_id) } else { @@ -141,7 +142,7 @@ impl<'tcx> Visitor<'tcx> for SameItemPushVisitor<'_, 'tcx> { ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::If(..) => self.non_deterministic_expr = true, ExprKind::Block(block, _) => self.visit_block(block), _ => { - if let Some(hir_id) = path_to_local(expr) { + if let Some(hir_id) = expr.res_local_id() { self.used_locals.insert(hir_id); } walk_expr(self, expr); @@ -186,7 +187,7 @@ fn get_vec_push<'tcx>( && 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) + && cx.typeck_results().expr_ty(self_expr).is_diag_item(cx, sym::Vec) { return Some((self_expr, pushed_item, semi_stmt.span.ctxt())); } diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs index 13b93d2c0097b..b893b0baad490 100644 --- a/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_ && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind && let ty = cx.typeck_results().expr_ty(arg) && pat_is_wild(cx, &index.kind, body) - && is_type_diagnostic_item(cx, ty, sym::Enumerate) + && ty.is_diag_item(cx, sym::Enumerate) && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id) { diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 2f6950b4380c3..56d535c4f2625 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -1,5 +1,6 @@ +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::{has_iter_method, implements_trait}; -use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; +use clippy_utils::{get_parent_expr, is_integer_const, sugg}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; @@ -47,7 +48,7 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // If node is a variable - if let Some(def_id) = path_to_local(expr) { + if let Some(def_id) = expr.res_local_id() { if let Some(parent) = get_parent_expr(self.cx, expr) { let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial); if *state == IncrementVisitorVarState::IncrOnce { @@ -175,7 +176,7 @@ impl<'tcx> Visitor<'tcx> for InitializeVisitor<'_, 'tcx> { } // If node is the desired variable, see how it's used - if path_to_local_id(expr, self.var_id) { + if expr.res_local_id() == Some(self.var_id) { if self.past_loop { self.state = InitializeVisitorState::DontWarn; return; @@ -255,7 +256,7 @@ fn is_conditional(expr: &Expr<'_>) -> bool { /// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the /// actual `Iterator` that the loop uses. -pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String { +pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applicability: &mut Applicability) -> String { let impls_iterator = cx .tcx .get_diagnostic_item(sym::Iterator) @@ -263,7 +264,7 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic if impls_iterator { format!( "{}", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applicability).maybe_paren() ) } else { // (&x).into_iter() ==> x.iter() @@ -281,12 +282,12 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic }; format!( "{}.{method_name}()", - sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_paren(), + sugg::Sugg::hir_with_applicability(cx, caller, "_", applicability).maybe_paren(), ) }, _ => format!( "{}.into_iter()", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applicability).maybe_paren() ), } } diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index 845edb9cae158..d4285db0abfcd 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -10,19 +10,19 @@ use rustc_hir::{Block, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind, Path, use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { - let (init, let_info) = match (loop_block.stmts, loop_block.expr) { + let (init, let_info, els) = match (loop_block.stmts, loop_block.expr) { ([stmt, ..], _) => match stmt.kind { StmtKind::Let(LetStmt { init: Some(e), - els: None, + els, pat, ty, .. - }) => (*e, Some((*pat, *ty))), - StmtKind::Semi(e) | StmtKind::Expr(e) => (e, None), + }) => (*e, Some((*pat, *ty)), *els), + StmtKind::Semi(e) | StmtKind::Expr(e) => (e, None, None), _ => return, }, - ([], Some(e)) => (e, None), + ([], Some(e)) => (e, None, None), _ => return, }; let has_trailing_exprs = loop_block.stmts.len() + usize::from(loop_block.expr.is_some()) > 1; @@ -38,14 +38,26 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_blo if_let.let_expr, has_trailing_exprs, let_info, - if_let.if_then, + Some(if_let.if_then), ); + } else if els.and_then(|x| x.expr).is_some_and(is_simple_break_expr) + && let Some((pat, _)) = let_info + { + could_be_while_let(cx, expr, pat, init, has_trailing_exprs, let_info, None); } else if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal) = init.kind && arm1.guard.is_none() && arm2.guard.is_none() && is_simple_break_expr(arm2.body) { - could_be_while_let(cx, expr, arm1.pat, scrutinee, has_trailing_exprs, let_info, arm1.body); + could_be_while_let( + cx, + expr, + arm1.pat, + scrutinee, + has_trailing_exprs, + let_info, + Some(arm1.body), + ); } } @@ -70,7 +82,7 @@ fn could_be_while_let<'tcx>( let_expr: &'tcx Expr<'_>, has_trailing_exprs: bool, let_info: Option<(&Pat<'_>, Option<&Ty<'_>>)>, - inner_expr: &Expr<'_>, + inner_expr: Option<&Expr<'_>>, ) { if has_trailing_exprs && (needs_ordered_drop(cx, cx.typeck_results().expr_ty(let_expr)) @@ -85,7 +97,7 @@ fn could_be_while_let<'tcx>( // 1) it was ugly with big bodies; // 2) it was not indented properly; // 3) it wasn’t very smart (see #675). - let inner_content = if let Some((pat, ty)) = let_info + let inner_content = if let Some(((pat, ty), inner_expr)) = let_info.zip(inner_expr) // Prevent trivial reassignments such as `let x = x;` or `let _ = …;`, but // keep them if the type has been explicitly specified. && (!is_trivial_assignment(pat, peel_blocks(inner_expr)) || ty.is_some()) diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 6000ff7a36099..3ea6ba341bedb 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -2,9 +2,10 @@ use std::ops::ControlFlow; use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_res_used; -use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method}; +use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -19,11 +20,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr) // check for `Some(..)` pattern && let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) + && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) // check for call to `Iterator::next` && let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind && method_name.ident.name == sym::next - && is_trait_method(cx, let_expr, sym::Iterator) + && cx.ty_based_def(let_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr) // get the loop containing the match expression && !uses_iter(cx, &iter_expr_struct, if_then) diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index 5814b6815a1ea..22de5e8268bd9 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::HasSession as _; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -104,7 +105,7 @@ impl ManualAbsDiff { fn are_ty_eligible<'tcx>(&self, cx: &LateContext<'tcx>, a: &Expr<'_>, b: &Expr<'_>) -> Option<(Ty<'tcx>, usize)> { let is_int = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(_) | ty::Int(_)) && self.msrv.meets(cx, msrvs::ABS_DIFF); let is_duration = - |ty| is_type_diagnostic_item(cx, ty, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); + |ty: Ty<'_>| ty.is_diag_item(cx, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); let a_ty = cx.typeck_results().expr_ty(a).peel_refs(); let (b_ty, b_n_refs, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(b)); diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 76cb22864779f..c34e0d33e713e 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call}; +use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::{higher, is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -50,32 +51,32 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { // Should this have a config value? && !is_else_clause(cx.tcx, expr) { - let mut applicability = Applicability::MachineApplicable; - let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); - if !comments.is_empty() { - comments += "\n"; - } - let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); - let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; - let sugg = format!("assert!({cond_sugg}, {format_args_snip}){semicolon}"); - // we show to the user the suggestion without the comments, but when applying the fix, include the - // comments in the block span_lint_and_then( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", |diag| { - // comments can be noisy, do not show them to the user + let mut applicability = Applicability::MachineApplicable; + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); if !comments.is_empty() { - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability, - ); + comments += "\n"; } - diag.span_suggestion(expr.span, "try instead", sugg, applicability); + let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); + let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; + + let indent = indent_of(cx, expr.span); + let full_sugg = reindent_multiline( + format!("{comments}assert!({cond_sugg}, {format_args_snip}){semicolon}").as_str(), + true, + indent, + ); + diag.span_suggestion_verbose( + expr.span, + "replace `if`-then-`panic!` with `assert!`", + full_sugg, + applicability, + ); }, ); } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 42fe386d2c3c4..54387e36d6c84 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -3,13 +3,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; -use clippy_utils::{ - MaybePath, eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, - peel_blocks, peel_blocks_with_stmt, sym, -}; +use clippy_utils::{eq_expr_value, is_in_const_context, peel_blocks, peel_blocks_with_stmt, sym}; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::Res; @@ -292,10 +290,12 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx /// # ; /// ``` fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { - if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = &expr.kind - && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord)) + if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = expr.kind + && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() + || cx.ty_based_def(expr).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind - && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || is_trait_method(cx, receiver, sym::Ord)) + && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() + || cx.ty_based_def(receiver).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) { let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); let (min, max) = match (seg_first.ident.name, seg_second.ident.name) { @@ -331,18 +331,18 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) fn segment<'tcx>(cx: &LateContext<'_>, func: &Expr<'tcx>) -> Option> { match func.kind { ExprKind::Path(QPath::Resolved(None, path)) => { - let id = path.res.opt_def_id()?; - match cx.tcx.get_diagnostic_name(id) { + let def = path.res.opt_def(cx)?; + match cx.tcx.get_diagnostic_name(def.1) { Some(sym::cmp_min) => Some(FunctionType::CmpMin), Some(sym::cmp_max) => Some(FunctionType::CmpMax), - _ if is_diag_trait_item(cx, id, sym::Ord) => { + _ if def.assoc_fn_parent(cx).is_diag_item(cx, sym::Ord) => { Some(FunctionType::OrdOrFloat(path.segments.last().expect("infallible"))) }, _ => None, } }, ExprKind::Path(QPath::TypeRelative(ty, seg)) => { - matches!(path_res(cx, ty), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg)) + matches!(ty.basic_res(), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg)) }, _ => None, } @@ -435,7 +435,7 @@ fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt let first = BinaryOp::new(first)?; let second = BinaryOp::new(second)?; if let PatKind::Binding(_, binding, _, None) = &last_arm.pat.kind - && path_to_local_id(peel_blocks_with_stmt(last_arm.body), *binding) + && peel_blocks_with_stmt(last_arm.body).res_local_id() == Some(*binding) && last_arm.guard.is_none() { // Proceed as normal @@ -516,7 +516,7 @@ fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> }, span: first_expr.span.to(second_expr.span), make_assignment: Some(maybe_input_first_path), - hir_with_ignore_attr: Some(first_expr.hir_id()), + hir_with_ignore_attr: Some(first_expr.hir_id), }) } else { None @@ -655,8 +655,8 @@ fn is_clamp_meta_pattern<'tcx>( let (min, max) = (second_expr, first_expr); let refers_to_input = match input_hir_ids { Some((first_hir_id, second_hir_id)) => { - path_to_local_id(peel_blocks(first_bin.left), first_hir_id) - && path_to_local_id(peel_blocks(second_bin.left), second_hir_id) + peel_blocks(first_bin.left).res_local_id() == Some(first_hir_id) + && peel_blocks(second_bin.left).res_local_id() == Some(second_hir_id) }, None => eq_expr_value(cx, first_bin.left, second_bin.left), }; diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index ed0cce754b954..ee531741a5153 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -1,7 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -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}; @@ -165,6 +164,7 @@ fn build_suggestion( applicability: &mut Applicability, ) { let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_paren(); + let rhs_ty = cx.typeck_results().expr_ty(rhs); let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric() && matches!( lhs.kind, @@ -182,7 +182,7 @@ fn build_suggestion( } ) ) { - format!("_{}", cx.typeck_results().expr_ty(rhs)) + format!("_{rhs_ty}") } else { String::new() }; @@ -199,9 +199,12 @@ fn build_suggestion( } else { format!("{dividend_sugg_str}{type_suffix}") }; - let divisor_snippet = snippet_with_context(cx, rhs.span, expr.span.ctxt(), "..", applicability); - let sugg = format!("{suggestion_before_div_ceil}.div_ceil({})", divisor_snippet.0); + // Dereference the RHS if it is a reference type + let divisor_snippet = match Sugg::hir_with_context(cx, rhs, expr.span.ctxt(), "_", applicability) { + sugg if rhs_ty.is_ref() => sugg.deref(), + sugg => sugg, + }; span_lint_and_sugg( cx, @@ -209,7 +212,7 @@ fn build_suggestion( expr.span, "manually reimplementing `div_ceil`", "consider using `.div_ceil()`", - sugg, + format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"), *applicability, ); } diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 60782f445ab91..a81c4dc6a7931 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_from_proc_macro, path_to_local}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -138,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { // Checking all possible scenarios using a function would be a hopeless task, as we have // 16 possible alignments of constants/operands. For now, let's use `partition`. && let mut exprs = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs] - && exprs.iter_mut().partition_in_place(|i| path_to_local(i).is_some()) == 2 + && exprs.iter_mut().partition_in_place(|i| i.res_local_id().is_some()) == 2 && !expr.span.in_external_macro(cx.sess().source_map()) && ( is_not_const(cx.tcx, cx.tcx.hir_enclosing_body_owner(expr.hir_id).into()) @@ -149,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { && let ctxt = expr.span.ctxt() && let Some(const_1) = ecx.eval_local(const_1, ctxt) && let Some(const_2) = ecx.eval_local(const_2, ctxt) - && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) + && first.res_local_id().is_some_and(|f| second.res_local_id().is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason && (const_1.is_pos_infinity() && const_2.is_neg_infinity() diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index b3ee45cc02098..ccb8d4272bfac 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; use clippy_utils::visitors::{is_local_used, local_used_once}; -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}; @@ -81,8 +82,8 @@ impl LateLintPass<'_> for ManualHashOne { && !hash_expr.span.from_expansion() && let ExprKind::MethodCall(seg, hashed_value, [ref_to_hasher], _) = hash_expr.kind && seg.ident.name == sym::hash - && is_trait_method(cx, hash_expr, sym::Hash) - && path_to_local_id(ref_to_hasher.peel_borrows(), hasher) + && cx.ty_based_def(hash_expr).opt_parent(cx).is_diag_item(cx, sym::Hash) + && ref_to_hasher.peel_borrows().res_local_id() == Some(hasher) && let maybe_finish_stmt = stmts.next() // There should be no more statements referencing `hasher` diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index f7d9ec1fae8e4..25057b4aeaa27 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -1,8 +1,8 @@ use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; 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; use rustc_hir::ExprKind::{Binary, Lit, MethodCall}; @@ -58,7 +58,7 @@ fn get_ascii_type<'a>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'_>) -> Op if needs_ref_to_cmp(cx, ty) || ty.is_str() || ty.is_slice() - || matches!(get_type_diagnostic_name(cx, ty), Some(sym::OsStr | sym::OsString)) + || matches!(ty.opt_diag_name(cx), Some(sym::OsStr | sym::OsString)) { return Some((expr.span, ToAscii(is_lower, ty_raw))); } @@ -72,8 +72,8 @@ fn get_ascii_type<'a>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'_>) -> Op fn needs_ref_to_cmp(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { ty.is_char() || *ty.kind() == ty::Uint(UintTy::U8) - || is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_lang_item(cx, ty, LangItem::String) + || ty.is_diag_item(cx, sym::Vec) + || ty.is_lang_item(cx, LangItem::String) } impl LateLintPass<'_> for ManualIgnoreCaseCmp { diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 2eebb2430fd94..8c6abbeac4489 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; 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::res::MaybeResPath; use clippy_utils::sugg::Sugg; -use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators, sym}; +use clippy_utils::{higher, is_in_const_context, peel_ref_operators, sym}; use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -125,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { } fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> { - let local_hid = path_to_local(arg)?; + let local_hid = arg.res_local_id()?; if let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given && ty_span == span diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 2705ef20b795d..298bf10754897 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -2,16 +2,14 @@ use crate::question_mark::{QUESTION_MARK, QuestionMark}; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; -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 clippy_utils::{is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; use rustc_ast::BindingMode; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; @@ -131,39 +129,25 @@ fn is_arms_disjointed(cx: &LateContext<'_>, arm1: &Arm<'_>, arm2: &Arm<'_>) -> b /// 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)); + let path = match pat.kind { + PatKind::Struct(ref qpath, fields, _) + if fields + .iter() + .all(|field| is_wild(field.pat) || matches!(field.pat.kind, PatKind::Binding(..))) => + { + (qpath, pat.hir_id) + }, + PatKind::TupleStruct(ref qpath, pats, _) + if pats + .iter() + .all(|pat| is_wild(pat) || matches!(pat.kind, PatKind::Binding(..))) => + { + (qpath, pat.hir_id) + }, + PatKind::Expr(e) if let Some((qpath, id)) = e.opt_qpath() => (qpath, id), + _ => return false, + }; + let res = path.res(cx); matches!( res, Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) @@ -384,7 +368,7 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo } let ty = typeck_results.pat_ty(pat); // Option and Result are allowed, everything else isn't. - if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) { + if !(ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result)) { has_disallowed = true; } }); diff --git a/clippy_lints/src/manual_main_separator_str.rs b/clippy_lints/src/manual_main_separator_str.rs index f54ccf2c87b0f..e78f6affda2a3 100644 --- a/clippy_lints/src/manual_main_separator_str.rs +++ b/clippy_lints/src/manual_main_separator_str.rs @@ -1,7 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{is_trait_method, peel_hir_expr_refs}; +use clippy_utils::peel_hir_expr_refs; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Mutability, QPath}; @@ -52,7 +53,7 @@ impl LateLintPass<'_> for ManualMainSeparatorStr { && path.ident.name == sym::to_string && let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind && let Res::Def(DefKind::Const, receiver_def_id) = path.res - && is_trait_method(cx, target, sym::ToString) + && cx.ty_based_def(target).opt_parent(cx).is_diag_item(cx, sym::ToString) && cx.tcx.is_diagnostic_item(sym::path_main_separator, receiver_def_id) && let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() && ty.is_str() diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index b036e78cdedc5..63f6d89f2ad74 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -189,7 +190,7 @@ fn check_arms(cx: &LateContext<'_>, none_arm: &Arm<'_>, some_arm: &Arm<'_>) -> b fn returns_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { - ExprKind::Path(_) => clippy_utils::is_path_diagnostic_item(cx, expr, sym::default_fn), + ExprKind::Path(_) => expr.res(cx).is_diag_item(cx, sym::default_fn), ExprKind::Closure(cl) => is_empty_slice(cx, cx.tcx.hir_body(cl.body).value), _ => false, } @@ -214,11 +215,11 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => false, }, ExprKind::Array([]) => true, - ExprKind::Call(def, []) => clippy_utils::is_path_diagnostic_item(cx, def, sym::default_fn), + ExprKind::Call(def, []) => def.res(cx).is_diag_item(cx, sym::default_fn), _ => false, } } fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - clippy_utils::is_path_diagnostic_item(cx, expr, sym::slice_from_ref) + expr.basic_res().is_diag_item(cx, sym::slice_from_ref) } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 1e91a429fe45c..d993ed48eac4a 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_in_const_context; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_context; -use clippy_utils::{is_in_const_context, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -64,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind && rem2_op.node == BinOpKind::Rem && const1 == const2 - && let Some(hir_id) = path_to_local(rem2_lhs) + && let Some(hir_id) = rem2_lhs.res_local_id() && let Some(const3) = check_for_unsigned_int_constant(cx, ctxt, rem2_rhs) // Also ensures the const is nonzero since zero can't be a divisor && const2 == const3 diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 7fb88763e640f..674f0da818f5f 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -2,8 +2,8 @@ use clippy_config::Conf; use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind::Assign; @@ -189,7 +189,7 @@ fn check_to_owned( && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id) && cx.tcx.is_diagnostic_item(sym::str_chars, chars_expr_def_id) && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs() - && is_type_lang_item(cx, ty, hir::LangItem::String) + && ty.is_lang_item(cx, hir::LangItem::String) && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind && let hir::ExprKind::Closure(closure) = closure_expr.kind @@ -250,7 +250,7 @@ fn match_acceptable_sym(cx: &LateContext<'_>, collect_def_id: DefId) -> bool { fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) -> bool { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - let required = match get_type_diagnostic_name(cx, ty) { + let required = match ty.opt_diag_name(cx) { Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN, Some(sym::BTreeSet) => msrvs::BTREE_SET_RETAIN, Some(sym::BTreeMap) => msrvs::BTREE_MAP_RETAIN, @@ -264,7 +264,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - matches!(get_type_diagnostic_name(cx, ty), Some(sym::BTreeMap | sym::HashMap)) + matches!(ty.opt_diag_name(cx), Some(sym::BTreeMap | sym::HashMap)) } fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) { diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index 22e3407303f07..e8db44698d9ce 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -56,20 +56,14 @@ impl Display for ShiftDirection { } } -fn parse_shift<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, -) -> Option<(ShiftDirection, u128, &'tcx Expr<'tcx>)> { +fn parse_shift<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(ShiftDirection, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { let dir = match op.node { BinOpKind::Shl => ShiftDirection::Left, BinOpKind::Shr => ShiftDirection::Right, _ => return None, }; - let const_expr = ConstEvalCtxt::new(cx).eval_local(r, expr.span.ctxt())?; - if let Constant::Int(shift) = const_expr { - return Some((dir, shift, l)); - } + return Some((dir, l, r)); } None } @@ -78,40 +72,62 @@ impl LateLintPass<'_> for ManualRotate { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::Binary(op, l, r) = expr.kind && let BinOpKind::Add | BinOpKind::BitOr = op.node - && let Some((l_shift_dir, l_amount, l_expr)) = parse_shift(cx, l) - && let Some((r_shift_dir, r_amount, r_expr)) = parse_shift(cx, r) - { - if l_shift_dir == r_shift_dir { - return; - } - if !clippy_utils::eq_expr_value(cx, l_expr, r_expr) { - return; - } - let Some(bit_width) = (match cx.typeck_results().expr_ty(expr).kind() { + && let Some((l_shift_dir, l_expr, l_amount)) = parse_shift(l) + && let Some((r_shift_dir, r_expr, r_amount)) = parse_shift(r) + && l_shift_dir != r_shift_dir + && clippy_utils::eq_expr_value(cx, l_expr, r_expr) + && let Some(bit_width) = match cx.typeck_results().expr_ty(expr).kind() { ty::Int(itype) => itype.bit_width(), ty::Uint(itype) => itype.bit_width(), _ => return, - }) else { - return; - }; - if l_amount + r_amount == u128::from(bit_width) { - let (shift_function, amount) = if l_amount < r_amount { + } + { + let const_eval = ConstEvalCtxt::new(cx); + + let ctxt = expr.span.ctxt(); + let (shift_function, amount) = if let Some(Constant::Int(l_amount_val)) = + const_eval.eval_local(l_amount, ctxt) + && let Some(Constant::Int(r_amount_val)) = const_eval.eval_local(r_amount, ctxt) + && l_amount_val + r_amount_val == u128::from(bit_width) + { + if l_amount_val < r_amount_val { (l_shift_dir, l_amount) } else { (r_shift_dir, r_amount) + } + } else { + let (amount1, binop, minuend, amount2, shift_direction) = match (l_amount.kind, r_amount.kind) { + (_, ExprKind::Binary(binop, minuend, other)) => (l_amount, binop, minuend, other, l_shift_dir), + (ExprKind::Binary(binop, minuend, other), _) => (r_amount, binop, minuend, other, r_shift_dir), + _ => return, }; - let mut applicability = Applicability::MachineApplicable; - let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); - span_lint_and_sugg( - cx, - MANUAL_ROTATE, - expr.span, - "there is no need to manually implement bit rotation", - "this expression can be rewritten as", - format!("{expr_sugg}.{shift_function}({amount})"), - Applicability::MachineApplicable, - ); - } + + if let Some(Constant::Int(minuend)) = const_eval.eval_local(minuend, ctxt) + && clippy_utils::eq_expr_value(cx, amount1, amount2) + // (x << s) | (x >> bit_width - s) + && ((binop.node == BinOpKind::Sub && u128::from(bit_width) == minuend) + // (x << s) | (x >> (bit_width - 1) ^ s) + || (binop.node == BinOpKind::BitXor && u128::from(bit_width).checked_sub(minuend) == Some(1))) + { + // NOTE: we take these from the side that _doesn't_ have the binop, since it's probably simpler + (shift_direction, amount1) + } else { + return; + } + }; + + let mut applicability = Applicability::MachineApplicable; + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); + let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_ROTATE, + expr.span, + "there is no need to manually implement bit rotation", + "this expression can be rewritten as", + format!("{expr_sugg}.{shift_function}({amount})"), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 39e5289c62ae4..681dc2ab5bc0c 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{iter_input_pats, method_chain_args}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -205,9 +205,9 @@ fn lint_map_unit_fn( ) { let var_arg = &map_args.0; - let (map_type, variant, lint) = if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Option) { + let (map_type, variant, lint) = if cx.typeck_results().expr_ty(var_arg).is_diag_item(cx, sym::Option) { ("Option", "Some", OPTION_MAP_UNIT_FN) - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Result) { + } else if cx.typeck_results().expr_ty(var_arg).is_diag_item(cx, sym::Result) { ("Result", "Ok", RESULT_MAP_UNIT_FN) } else { return; diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index e0cb5d14d3c93..fb83f7cf65ddb 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{higher, is_res_lang_ctor, sym}; +use clippy_utils::{higher, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -57,8 +57,8 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result.ok(, _) && let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation && ok_path.ident.name == sym::ok - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) - && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) + && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && let ctxt = expr.span.ctxt() && let_expr.span.ctxt() == ctxt && let_pat.span.ctxt() == ctxt diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index aaf559fc4439e..79f737f07eb1e 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,18 +1,17 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; -use clippy_utils::{ - SpanlessEq, get_ref_operators, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, - peel_ref_operators, -}; +use clippy_utils::{SpanlessEq, get_ref_operators, is_unit_expr, peel_blocks_with_stmt, peel_ref_operators}; use rustc_ast::BorrowKind; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; +use rustc_span::symbol::Ident; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; @@ -35,7 +34,7 @@ pub(super) fn check_if_let<'tcx>( check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv); } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, @@ -50,26 +49,28 @@ fn check_arm<'tcx>( if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr) && let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner { IfLetOrMatch::IfLet(scrutinee, pat, _, els, _) => Some((scrutinee, pat, els)), - IfLetOrMatch::Match(scrutinee, arms, ..) => if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()) - // if there are more than two arms, collapsing would be non-trivial - // one of the arms must be "wild-like" - && let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)) - { - let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); - Some((scrutinee, then.pat, Some(els.body))) - } else { - None + IfLetOrMatch::Match(scrutinee, arms, ..) => { + if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()) + // if there are more than two arms, collapsing would be non-trivial + // one of the arms must be "wild-like" + && let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)) + { + let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); + Some((scrutinee, then.pat, Some(els.body))) + } else { + None + } }, } && outer_pat.span.eq_ctxt(inner_scrutinee.span) // match expression must be a local binding // match { .. } - && let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)) + && let Some(binding_id) = peel_ref_operators(cx, inner_scrutinee).res_local_id() && !pat_contains_disallowed_or(cx, inner_then_pat, msrv) // the binding must come from the pattern of the containing match arm // .... => match { .. } - && let (Some(binding_span), is_innermost_parent_pat_struct) - = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) + && let (Some((binding_ident, binding_span)), is_innermost_parent_pat_struct) = + find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) // the "else" branches must be equal && match (outer_else_body, inner_else_body) { (None, None) => true, @@ -77,9 +78,7 @@ fn check_arm<'tcx>( (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), } // the binding must not be used in the if guard - && outer_guard.is_none_or( - |e| !is_local_used(cx, e, binding_id) - ) + && outer_guard.is_none_or(|e| !is_local_used(cx, e, binding_id)) // ...or anywhere in the inner expression && match inner { IfLetOrMatch::IfLet(_, _, body, els, _) => { @@ -103,7 +102,7 @@ fn check_arm<'tcx>( // collapsing patterns need an explicit field name in struct pattern matching // ex: Struct {x: Some(1)} let replace_msg = if is_innermost_parent_pat_struct { - format!(", prefixed by `{}`:", snippet(cx, binding_span, "their field name")) + format!(", prefixed by `{binding_ident}: `") } else { String::new() }; @@ -135,21 +134,24 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { kind: PatExprKind::Path(qpath), hir_id, .. - }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), + }) => cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), _ => false, } } -fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option, bool) { - let mut span = None; +fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option<(Ident, Span)>, bool) { + let mut binding = None; let mut is_innermost_parent_pat_struct = false; - pat.walk_short(|p| match &p.kind { + pat.walk_short(|p| match p.kind { // ignore OR patterns PatKind::Or(_) => false, - PatKind::Binding(_bm, _, _ident, _) => { + PatKind::Binding(_bm, _, ident, _) => { let found = p.hir_id == hir_id; if found { - span = Some(p.span); + binding = Some((ident, p.span)); } !found }, @@ -158,7 +160,7 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi true }, }); - (span, is_innermost_parent_pat_struct) + (binding, is_innermost_parent_pat_struct) } /// Builds a chain of reference-manipulation method calls (e.g., `.as_ref()`, `.as_mut()`, diff --git a/clippy_lints/src/matches/infallible_destructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs index 93d7683d2af81..be4b4f346dbcf 100644 --- a/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{ExprKind, LetStmt, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; @@ -17,7 +18,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { && args.len() == 1 && let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind && let body = peel_blocks(arms[0].body) - && path_to_local_id(body, arg) + && body.res_local_id() == Some(arg) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index abf723fa6f4ca..d7224052ebc58 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; @@ -66,15 +65,15 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: && let ExprKind::Call(callee, [arg]) = inner_expr.kind { return ctxt == expr.span.ctxt() - && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) - && path_to_local_id(arg, target); + && callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) + && arg.res_local_id() == Some(target); } false } fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { - return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone); + return inner_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone); } false } @@ -98,7 +97,7 @@ pub(super) fn check_match<'tcx>( expr: &'tcx Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, ty, sym::Option) + if ty.is_diag_item(cx, sym::Option) && let [first_arm, second_arm] = arms && first_arm.guard.is_none() && second_arm.guard.is_none() diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index de57d1eee9249..f111da60bbd5b 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -2,8 +2,7 @@ use super::MANUAL_MAP; use super::manual_utils::{SomeExpr, check_with}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_res_lang_ctor, path_res}; - +use clippy_utils::res::{MaybeDef, MaybeQPath}; use rustc_hir::LangItem::OptionSome; use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource}; use rustc_lint::LateContext; @@ -91,7 +90,7 @@ fn get_some_expr<'tcx>( // TODO: Allow more complex expressions. match expr.kind { ExprKind::Call(callee, [arg]) - if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => + if ctxt == expr.span.ctxt() && callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => { Some(SomeExpr::new_no_negated(arg, needs_unsafe_block)) }, diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index a8490d6aa7d82..c9293412fba82 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs}; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; +use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; @@ -72,7 +73,10 @@ fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool true }, PatKind::TupleStruct(qpath, ..) => { - is_res_lang_ctor(cx, cx.qpath_res(&qpath, pat.hir_id), ResultErr) == must_match_err + cx.qpath_res(&qpath, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) + == must_match_err }, PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) => { is_variant_or_wildcard(cx, pat, can_be_wild, must_match_err) @@ -103,7 +107,7 @@ fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &' /// Check if `expr` contains `Some(ident)`, possibly as a block fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool { if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind - && is_res_lang_ctor(cx, path_res(cx, body_callee), OptionSome) + && body_callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && cx.typeck_results().expr_ty(body_arg) == ty && let ExprKind::Path(QPath::Resolved( _, @@ -120,7 +124,7 @@ fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, t /// Check if `expr` is `None`, possibly as a block fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) + peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) } /// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index ac9e51890362b..e00d0c7f3d6c1 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,4 +1,5 @@ use clippy_utils::consts::ConstEvalCtxt; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; @@ -10,8 +11,8 @@ use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{expr_type_is_certain, get_type_diagnostic_name, implements_trait}; -use clippy_utils::{is_default_equivalent, is_lint_allowed, path_res, peel_blocks, span_contains_comment}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait}; +use clippy_utils::{is_default_equivalent, is_lint_allowed, peel_blocks, span_contains_comment}; use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; @@ -31,7 +32,7 @@ fn get_some(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { } } -fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { +fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>, allow_wildcard: bool) -> Option<&'tcx Expr<'tcx>> { if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.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 @@ -48,7 +49,9 @@ fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'t && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) { Some(arm.body) - } else if let PatKind::Wild = arm.pat.kind { + } else if let PatKind::Wild = arm.pat.kind + && allow_wildcard + { // We consider that the `Some` check will filter it out if it's not right. Some(arm.body) } else { @@ -62,11 +65,11 @@ fn get_some_and_none_bodies<'tcx>( arm2: &'tcx Arm<'tcx>, ) -> Option<((&'tcx Expr<'tcx>, HirId), &'tcx Expr<'tcx>)> { if let Some(binding_id) = get_some(cx, arm1.pat) - && let Some(body_none) = get_none(cx, arm2) + && let Some(body_none) = get_none(cx, arm2, true) { Some(((arm1.body, binding_id), body_none)) - } else if let Some(binding_id) = get_some(cx, arm2.pat) - && let Some(body_none) = get_none(cx, arm1) + } else if let Some(body_none) = get_none(cx, arm1, false) + && let Some(binding_id) = get_some(cx, arm2.pat) { Some(((arm2.body, binding_id), body_none)) } else { @@ -114,7 +117,8 @@ fn handle( && is_default_equivalent(cx, peel_blocks(body_none)) { // We now check if the condition is a None variant, in which case we need to specify the type - if path_res(cx, condition) + if condition + .res(cx) .opt_def_id() .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) { @@ -174,7 +178,7 @@ fn handle( } fn find_type_name<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'static str> { - match get_type_diagnostic_name(cx, ty)? { + match ty.opt_diag_name(cx)? { sym::Option => Some("Option"), sym::Result => Some("Result"), _ => None, diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index d4bfdb7e440d8..235cb9e4ecce8 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -1,11 +1,12 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_and_count_ty_refs}; +use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, - path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, + CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, peel_blocks, + peel_hir_expr_refs, peel_hir_expr_while, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; @@ -33,8 +34,7 @@ where let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(scrutinee)); let ty_mutability = ty_mutability.unwrap_or(Mutability::Mut); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + if !(scrutinee_ty.is_diag_item(cx, sym::Option) && cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Option)) { return None; } @@ -138,7 +138,7 @@ where { snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() } else { - if path_to_local_id(some_expr.expr, id) + if some_expr.expr.res_local_id() == Some(id) && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) && binding_ref.is_some() { @@ -190,7 +190,7 @@ pub struct SuggInfo<'a> { fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) - if path_to_local_id(arg, binding) + if arg.res_local_id() == Some(binding) && cx.typeck_results().expr_adjustments(arg).is_empty() && !is_unsafe_fn(cx, cx.typeck_results().expr_ty(func).peel_refs()) => { @@ -259,9 +259,19 @@ pub(super) fn try_parse_pattern<'tcx>( kind: PatExprKind::Path(qpath), hir_id, .. - }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone) => Some(OptionPat::None), + }) if cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { + Some(OptionPat::None) + }, PatKind::TupleStruct(ref qpath, [pattern], _) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => + if cx + .qpath_res(qpath, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionSome) + && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, @@ -273,5 +283,5 @@ pub(super) fn try_parse_pattern<'tcx>( // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) + peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) } diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 1cb4b512a30e5..156118be347f9 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_none_arm, is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{is_none_arm, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; use rustc_lint::LateContext; @@ -58,10 +59,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome) + && cx + .qpath_res(qpath, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, LangItem::OptionSome) && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind - && is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome) + && e.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind && path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 5816da5695eb6..b5f631e8fea33 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -1,17 +1,18 @@ +//! Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` + use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment}; use rustc_ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Arm, Attribute, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath}; +use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; use rustc_span::source_map::Spanned; use super::MATCH_LIKE_MATCHES_MACRO; -/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` pub(crate) fn check_if_let<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -20,16 +21,42 @@ pub(crate) fn check_if_let<'tcx>( then_expr: &'tcx Expr<'_>, else_expr: &'tcx Expr<'_>, ) { - find_matches_sugg( - cx, - let_expr, - IntoIterator::into_iter([ - (&[][..], Some(let_pat), then_expr, None), - (&[][..], None, else_expr, None), - ]), - expr, - true, - ); + if !span_contains_comment(cx.sess().source_map(), expr.span) + && cx.typeck_results().expr_ty(expr).is_bool() + && let Some(b0) = find_bool_lit(then_expr) + && let Some(b1) = find_bool_lit(else_expr) + && b0 != b1 + { + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, let_pat.hir_id) && is_some_wild(let_pat.kind) { + return; + } + + // The suggestion may be incorrect, because some arms can have `cfg` attributes + // evaluated into `false` and so such arms will be stripped before. + let mut applicability = Applicability::MaybeIncorrect; + let pat = snippet_with_applicability(cx, let_pat.span, "..", &mut applicability); + + // strip potential borrows (#6503), but only if the type is a reference + let mut ex_new = let_expr; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = let_expr.kind + && let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() + { + ex_new = ex_inner; + } + span_lint_and_sugg( + cx, + MATCH_LIKE_MATCHES_MACRO, + expr.span, + "if let .. else expression looks like `matches!` macro", + "try", + format!( + "{}matches!({}, {pat})", + if b0 { "" } else { "!" }, + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), + ), + applicability, + ); + } } pub(super) fn check_match<'tcx>( @@ -38,55 +65,80 @@ pub(super) fn check_match<'tcx>( scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'tcx>], ) -> bool { - find_matches_sugg( - cx, - scrutinee, - arms.iter() - .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)), - e, - false, - ) -} - -/// Lint a `match` or `if let` for replacement by `matches!` -fn find_matches_sugg<'a, 'b, I>( - cx: &LateContext<'_>, - ex: &Expr<'_>, - mut iter: I, - expr: &Expr<'_>, - is_if_let: bool, -) -> bool -where - 'b: 'a, - I: Clone - + DoubleEndedIterator - + ExactSizeIterator - + Iterator>, &'a Expr<'b>, Option<&'a Expr<'b>>)>, -{ - if !span_contains_comment(cx.sess().source_map(), expr.span) - && iter.len() >= 2 - && cx.typeck_results().expr_ty(expr).is_bool() - && let Some((_, last_pat_opt, last_expr, _)) = iter.next_back() - && let iter_without_last = iter.clone() - && let Some((first_attrs, _, first_expr, first_guard)) = iter.next() - && let Some(b0) = find_bool_lit(&first_expr.kind) - && let Some(b1) = find_bool_lit(&last_expr.kind) + if let Some((last_arm, arms_without_last)) = arms.split_last() + && let Some((first_arm, middle_arms)) = arms_without_last.split_first() + && !span_contains_comment(cx.sess().source_map(), e.span) + && cx.typeck_results().expr_ty(e).is_bool() + && let Some(b0) = find_bool_lit(first_arm.body) + && let Some(b1) = find_bool_lit(last_arm.body) && b0 != b1 - && (first_guard.is_none() || iter.len() == 0) - && first_attrs.is_empty() - && iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|b| b == b0) && arm.3.is_none() && arm.0.is_empty()) + // We handle two cases: + && ( + // - There are no middle arms, i.e., 2 arms in total + // + // In that case, the first arm may or may not have a guard, because this: + // ```rs + // match e { + // Either::Left $(if $guard)|+ => true, // or `false`, but then we'll need `!matches!(..)` + // _ => false, + // } + // ``` + // can always become this: + // ```rs + // matches!(e, Either::Left $(if $guard)|+) + // ``` + middle_arms.is_empty() + + // - (added in #6216) There are middle arms + // + // In that case, neither they nor the first arm may have guards + // -- otherwise, they couldn't be combined into an or-pattern in `matches!` + // + // This: + // ```rs + // match e { + // Either3::First => true, + // Either3::Second => true, + // _ /* matches `Either3::Third` */ => false, + // } + // ``` + // can become this: + // ```rs + // matches!(e, Either3::First | Either3::Second) + // ``` + // + // But this: + // ```rs + // match e { + // Either3::First if X => true, + // Either3::Second => true, + // _ => false, + // } + // ``` + // cannot be transformed. + // + // We set an additional constraint of all of them needing to return the same bool, + // so we don't lint things like: + // ```rs + // match e { + // Either3::First => true, + // Either3::Second => false, + // _ => false, + // } + // ``` + // This is not *strictly* necessary, but it simplifies the logic a bit + || arms_without_last.iter().all(|arm| { + cx.tcx.hir_attrs(arm.hir_id).is_empty() && arm.guard.is_none() && find_bool_lit(arm.body) == Some(b0) + }) + ) { - if let Some(last_pat) = last_pat_opt - && !is_wild(last_pat) - { + if !is_wild(last_arm.pat) { return false; } - for arm in iter_without_last.clone() { - if let Some(pat) = arm.1 - && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) - && is_some(pat.kind) - { + for arm in arms_without_last { + let pat = arm.pat; + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some_wild(pat.kind) { return false; } } @@ -96,14 +148,12 @@ where let mut applicability = Applicability::MaybeIncorrect; let pat = { use itertools::Itertools as _; - iter_without_last - .filter_map(|arm| { - let pat_span = arm.1?.span; - Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability)) - }) + arms_without_last + .iter() + .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability)) .join(" | ") }; - let pat_and_guard = if let Some(g) = first_guard { + let pat_and_guard = if let Some(g) = first_arm.guard { format!( "{pat} if {}", snippet_with_applicability(cx, g.span, "..", &mut applicability) @@ -113,8 +163,8 @@ where }; // strip potential borrows (#6503), but only if the type is a reference - let mut ex_new = ex; - if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind + let mut ex_new = scrutinee; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = scrutinee.kind && let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { ex_new = ex_inner; @@ -122,11 +172,8 @@ where span_lint_and_sugg( cx, MATCH_LIKE_MATCHES_MACRO, - expr.span, - format!( - "{} expression looks like `matches!` macro", - if is_if_let { "if let .. else" } else { "match" } - ), + e.span, + "match expression looks like `matches!` macro", "try", format!( "{}matches!({}, {pat_and_guard})", @@ -142,11 +189,11 @@ where } /// Extract a `bool` or `{ bool }` -fn find_bool_lit(ex: &ExprKind<'_>) -> Option { - match ex { +fn find_bool_lit(ex: &Expr<'_>) -> Option { + match ex.kind { ExprKind::Lit(Spanned { node: LitKind::Bool(b), .. - }) => Some(*b), + }) => Some(b), ExprKind::Block( rustc_hir::Block { stmts: [], @@ -168,8 +215,9 @@ fn find_bool_lit(ex: &ExprKind<'_>) -> Option { } } -fn is_some(path_kind: PatKind<'_>) -> bool { - match path_kind { +/// Checks whether a pattern is `Some(_)` +fn is_some_wild(pat_kind: PatKind<'_>) -> bool { + match pat_kind { PatKind::TupleStruct(QPath::Resolved(_, path), [first, ..], _) if is_wild(first) => { let name = path.segments[0].ident; name.name == rustc_span::sym::Some diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 818e504245549..be914429edb44 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, search_same}; use core::cmp::Ordering; use core::{iter, slice}; use itertools::Itertools; @@ -61,8 +62,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let check_eq_with_pat = |expr_a: &Expr<'_>, expr_b: &Expr<'_>| { let mut local_map: HirIdMap = HirIdMap::default(); let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - if let Some(a_id) = path_to_local(a) - && let Some(b_id) = path_to_local(b) + if let Some(a_id) = a.res_local_id() + && let Some(b_id) = b.res_local_id() && let entry = match local_map.entry(a_id) { HirIdMapEntry::Vacant(entry) => entry, // check if using the same bindings as before diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index eb8b16e1561bd..3f8f2dc0e132d 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -1,8 +1,8 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -58,7 +58,7 @@ impl MatchExprVisitor<'_, '_> { if let Some(case_method) = get_case_method(segment_ident) { let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); - if is_type_lang_item(self.cx, ty, LangItem::String) || ty.kind() == &ty::Str { + if ty.is_lang_item(self.cx, LangItem::String) || ty.kind() == &ty::Str { return ControlFlow::Break(case_method); } } diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 70a03ff937625..ac5da320bdffb 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -16,8 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ty = cx.typeck_results().expr_ty(ex).peel_refs(); let adt_def = match ty.kind() { ty::Adt(adt_def, _) - if adt_def.is_enum() - && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) => + if adt_def.is_enum() && !(ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result)) => { adt_def }, diff --git a/clippy_lints/src/matches/match_wild_err_arm.rs b/clippy_lints/src/matches/match_wild_err_arm.rs index 8ce8453360f78..e38ba801c0bf7 100644 --- a/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/clippy_lints/src/matches/match_wild_err_arm.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::macros::{is_panic, root_macro_call}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::is_local_used; use clippy_utils::{is_in_const_context, is_wild, peel_blocks_with_stmt}; use rustc_hir::{Arm, Expr, PatKind}; @@ -16,7 +16,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' } let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); - if is_type_diagnostic_item(cx, ex_ty, sym::Result) { + if ex_ty.is_diag_item(cx, sym::Result) { for arm in arms { if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind { let path_str = rustc_hir_pretty::qpath_to_string(&cx.tcx, path); diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 3a2097c3df261..c9b6821ad98fd 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -1,10 +1,10 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_modulo_regions}; +use clippy_utils::ty::same_type_modulo_regions; use clippy_utils::{ - SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, - peel_blocks_with_stmt, + SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, over, peel_blocks_with_stmt, }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; @@ -104,8 +104,8 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool return false; } let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); - if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { - return is_res_lang_ctor(cx, path_res(cx, else_expr), OptionNone) + if let_expr_ty.is_diag_item(cx, sym::Option) { + return else_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) || eq_expr_value(cx, if_let.let_expr, else_expr); } return eq_expr_value(cx, if_let.let_expr, else_expr); diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index 7c6d45e424006..d39e315cae1f2 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used}; -use clippy_utils::{is_in_const_context, path_to_local, sym}; +use clippy_utils::{is_in_const_context, sym}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -164,7 +165,7 @@ fn get_pat_binding<'tcx>( guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>, ) -> Option { - if let Some(local) = path_to_local(guard_expr) + if let Some(local) = guard_expr.res_local_id() && !is_local_used(cx, outer_arm.body, local) { let mut span = None; diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 9d0115791838c..2ca76a1fe6945 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,10 +1,11 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; -use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; +use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; -use clippy_utils::{higher, is_expn_of, is_trait_method, sym}; +use clippy_utils::{higher, is_expn_of, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; @@ -216,7 +217,7 @@ fn find_method_sugg_for_if_let<'tcx>( if keyword == "while" && let ExprKind::MethodCall(method_path, _, [], _) = let_expr.kind && method_path.ident.name == sym::next - && is_trait_method(cx, let_expr, sym::Iterator) + && cx.ty_based_def(let_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } @@ -460,7 +461,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte Item::Diag(expected_ty, expected_variant) => { let ty = cx.typeck_results().pat_ty(pat); - if is_type_diagnostic_item(cx, ty, expected_ty) { + if ty.is_diag_item(cx, expected_ty) { let variant = ty .ty_adt_def() .expect("struct pattern type is not an ADT") diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 02f87512966ba..44c4d7a31ff31 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -224,7 +224,7 @@ enum PatState<'a> { /// A std enum we know won't be extended. Tracks the states of each variant separately. /// /// This is not used for `Option` since it uses the current pattern to track its state. - StdEnum(&'a mut [PatState<'a>]), + StdEnum(&'a mut [Self]), /// Either the initial state for a pattern or a non-std enum. There is currently no need to /// distinguish these cases. /// diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index af90cb5e67334..7358628f0f7e6 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_parent_expr; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::option_arg_ty; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -25,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine && let ExprKind::Path(ref match_fun_path) = match_fun.kind && matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)) && let ExprKind::Call(err_fun, [err_arg]) = try_arg.kind - && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr) + && err_fun.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && let Some(return_ty) = find_return_type(cx, &expr.kind) { let (prefix, suffix, err_ty) = if let Some(ty) = result_error_type(cx, return_ty) { diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 95ecef5987007..ac3cbaec55f30 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,12 +1,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{ - is_default_equivalent, is_expr_used_or_unified, is_res_lang_ctor, path_res, peel_ref_operators, std_or_core, -}; +use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, peel_ref_operators, std_or_core}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -129,7 +128,7 @@ impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool { - if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { + if src.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { // Since this is a late pass (already type-checked), // and we already know that the second argument is an // `Option`, we do not need to check the first @@ -163,7 +162,7 @@ fn check_replace_option_with_some( msrv: Msrv, ) -> bool { if let ExprKind::Call(src_func, [src_arg]) = src.kind - && is_res_lang_ctor(cx, path_res(cx, src_func), OptionSome) + && src_func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && msrv.meets(cx, msrvs::OPTION_REPLACE) { // We do not have to check for a `const` context here, because `core::mem::replace()` and diff --git a/clippy_lints/src/methods/bytecount.rs b/clippy_lints/src/methods/bytecount.rs index 0498f317442ac..77ac181ee633a 100644 --- a/clippy_lints/src/methods/bytecount.rs +++ b/clippy_lints/src/methods/bytecount.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; -use clippy_utils::{path_to_local_id, peel_blocks, peel_ref_operators, strip_pat_refs}; +use clippy_utils::{peel_blocks, peel_ref_operators, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -23,10 +23,14 @@ pub(super) fn check<'tcx>( && let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind && let ExprKind::Binary(ref op, l, r) = body.value.kind && op.node == BinOpKind::Eq - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv).peel_refs(), sym::SliceIter) + && cx + .typeck_results() + .expr_ty(filter_recv) + .peel_refs() + .is_diag_item(cx, sym::SliceIter) && let operand_is_arg = (|expr| { let expr = peel_ref_operators(cx, peel_blocks(expr)); - path_to_local_id(expr, arg_id) + expr.res_local_id() == Some(arg_id) }) && let needle = if operand_is_arg(l) { r diff --git a/clippy_lints/src/methods/bytes_count_to_len.rs b/clippy_lints/src/methods/bytes_count_to_len.rs index b8cc5ddd845c9..baea49296cd7a 100644 --- a/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/clippy_lints/src/methods/bytes_count_to_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>( && let Some(impl_id) = cx.tcx.impl_of_assoc(bytes_id) && cx.tcx.type_of(impl_id).instantiate_identity().is_str() && let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs() - && (ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String)) + && (ty.is_str() || ty.is_lang_item(cx, hir::LangItem::String)) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 02fc09170e59c..40d521d61c118 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let caller_type = if ty.is_str() { "str" - } else if is_type_lang_item(cx, ty, LangItem::String) { + } else if ty.is_lang_item(cx, LangItem::String) { "String" } else { return; 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 6f9702f6c6c33..b4b10e972f6d4 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>( || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit())) && !ext_str.chars().skip(1).all(|c| c.is_ascii_digit()) && let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs() - && (recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String)) + && (recv_ty.is_str() || recv_ty.is_lang_item(cx, LangItem::String)) { span_lint_and_then( cx, diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index de27a45ba4d92..4b0fc7eba8e34 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::method_chain_args; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{method_chain_args, path_def_id}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; @@ -17,7 +18,7 @@ pub(super) fn check( ) -> bool { if let Some(args) = method_chain_args(info.chain, chain_methods) && let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind - && let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id)) + && let Some(id) = fun.res(cx).opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) && Some(id) == cx.tcx.lang_items().option_some_variant() { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/clear_with_drain.rs b/clippy_lints/src/methods/clear_with_drain.rs index 6e24cabca8bba..67def8767bb0a 100644 --- a/clippy_lints/src/methods/clear_with_drain.rs +++ b/clippy_lints/src/methods/clear_with_drain.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_range_full; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_lint::LateContext; @@ -29,9 +29,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span fn match_acceptable_type(cx: &LateContext<'_>, expr: &Expr<'_>, types: &[rustc_span::Symbol]) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); - types.iter().any(|&ty| is_type_diagnostic_item(cx, expr_ty, ty)) + types.iter().any(|&ty| expr_ty.is_diag_item(cx, ty)) // String type is a lang item but not a diagnostic item for now so we need a separate check - || is_type_lang_item(cx, expr_ty, LangItem::String) + || expr_ty.is_lang_item(cx, LangItem::String) } fn suggest(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) { diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 0a456d1057ad9..2a0ae14a4b088 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -4,27 +4,14 @@ use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::print::with_forced_trimmed_paths; -use rustc_middle::ty::{self}; -use rustc_span::symbol::{Symbol, sym}; use super::CLONE_ON_COPY; /// Checks for the `CLONE_ON_COPY` lint. -#[allow(clippy::too_many_lines)] -pub(super) fn check( - cx: &LateContext<'_>, - expr: &Expr<'_>, - method_name: Symbol, - receiver: &Expr<'_>, - args: &[Expr<'_>], -) { - let arg = if method_name == sym::clone && args.is_empty() { - receiver - } else { - return; - }; +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { if cx .typeck_results() .type_dependent_def_id(expr.hir_id) @@ -34,10 +21,10 @@ pub(super) fn check( { return; } - let arg_adjustments = cx.typeck_results().expr_adjustments(arg); + let arg_adjustments = cx.typeck_results().expr_adjustments(receiver); let arg_ty = arg_adjustments .last() - .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target); + .map_or_else(|| cx.typeck_results().expr_ty(receiver), |a| a.target); let ty = cx.typeck_results().expr_ty(expr); if let ty::Ref(_, inner, _) = arg_ty.kind() @@ -76,7 +63,7 @@ pub(super) fn check( }; let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0; + let snip = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "_", &mut app).0; let deref_count = arg_adjustments .iter() diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 65583c6a9811e..238e1fe988b39 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -3,21 +3,12 @@ use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty; -use rustc_span::symbol::{Symbol, sym}; +use rustc_middle::ty::{self, IsSuggestable}; +use rustc_span::symbol::sym; use super::CLONE_ON_REF_PTR; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_name: Symbol, - receiver: &hir::Expr<'_>, - args: &[hir::Expr<'_>], -) { - if !(args.is_empty() && method_name == sym::clone) { - return; - } +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>) { let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); if let ty::Adt(adt, subst) = obj_ty.kind() @@ -39,12 +30,22 @@ pub(super) fn check( // Sometimes unnecessary ::<_> after Rc/Arc/Weak let mut app = Applicability::Unspecified; let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0; - diag.span_suggestion( - expr.span, - "try", - format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), - app, - ); + let generic = subst.type_at(0); + if generic.is_suggestable(cx.tcx, true) { + diag.span_suggestion( + expr.span, + "try", + format!("{caller_type}::<{generic}>::clone(&{snippet})"), + app, + ); + } else { + diag.span_suggestion( + expr.span, + "try", + format!("{caller_type}::::clone(&{snippet})"), + Applicability::HasPlaceholders, + ); + } }, ); } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index f50fb627b89a0..d80d6f7810f53 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::{get_iterator_item_ty, is_copy}; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -19,7 +19,9 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(cx, msrvs::ITERATOR_COPIED) => { + _ if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && msrv.meets(cx, msrvs::ITERATOR_COPIED) => + { match get_iterator_item_ty(cx, recv_ty) { // ::Item Some(ty) => ty, diff --git a/clippy_lints/src/methods/double_ended_iterator_last.rs b/clippy_lints/src/methods/double_ended_iterator_last.rs index 578865c329180..8ba8264b713c4 100644 --- a/clippy_lints/src/methods/double_ended_iterator_last.rs +++ b/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::{has_non_owning_mutable_access, implements_trait}; -use clippy_utils::{is_mutable, is_trait_method, path_to_local_with_projections, sym}; +use clippy_utils::{is_mutable, path_to_local_with_projections, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; @@ -13,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp let typeck = cx.typeck_results(); // if the "last" method is that of Iterator - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // if self implements DoubleEndedIterator && let Some(deiter_id) = cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator) && let self_type = cx.typeck_results().expr_ty(self_expr) diff --git a/clippy_lints/src/methods/drain_collect.rs b/clippy_lints/src/methods/drain_collect.rs index cbf713a3b17c4..9b0c290577405 100644 --- a/clippy_lints/src/methods/drain_collect.rs +++ b/clippy_lints/src/methods/drain_collect.rs @@ -1,7 +1,7 @@ use crate::methods::DRAIN_COLLECT; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_range_full, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; @@ -35,8 +35,8 @@ fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_> /// Checks `std::string::String` fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - is_type_lang_item(cx, expr, LangItem::String) - && is_type_lang_item(cx, recv, LangItem::String) + expr.is_lang_item(cx, LangItem::String) + && recv.is_lang_item(cx, LangItem::String) && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) } diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index 91ddaca07d8bb..6e9aebcf18ae4 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -1,7 +1,8 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::has_debug_impl; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; @@ -16,7 +17,7 @@ pub(super) fn check( err_span: Span, msrv: Msrv, ) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // Grabs the `Result` type && let result_type = cx.typeck_results().expr_ty(recv) // Tests if the T type in a `Result` is not None @@ -40,7 +41,7 @@ pub(super) fn check( /// Given a `Result` type, return its data (`T`). fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().next(), + ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().next(), _ => None, } } diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 818e26f8aa1df..288f966991acb 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span, root_macro_call_first_node}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{contains_return, is_inside_always_const_context, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::Span; use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; use std::borrow::Cow; use std::ops::ControlFlow; @@ -20,20 +20,15 @@ pub(super) fn check<'tcx>( format_args_storage: &FormatArgsStorage, expr: &hir::Expr<'_>, method_span: Span, - name: Symbol, receiver: &'tcx hir::Expr<'tcx>, - args: &'tcx [hir::Expr<'tcx>], + arg: &'tcx hir::Expr<'tcx>, ) { - if name == sym::expect - && let [arg] = args - && let arg_root = get_arg_root(cx, arg) - && contains_call(cx, arg_root) - && !contains_return(arg_root) - { + let arg_root = get_arg_root(cx, arg); + if contains_call(cx, arg_root) && !contains_return(arg_root) { let receiver_type = cx.typeck_results().expr_ty_adjusted(receiver); - let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::Option) { + let closure_args = if receiver_type.is_diag_item(cx, sym::Option) { "||" - } else if is_type_diagnostic_item(cx, receiver_type, sym::Result) { + } else if receiver_type.is_diag_item(cx, sym::Result) { "|_|" } else { return; @@ -54,7 +49,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - format!("function call inside of `{name}`"), + "function call inside of `expect`", "try", format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, @@ -69,7 +64,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - format!("function call inside of `{name}`"), + "function call inside of `expect`", "try", format!("unwrap_or_else({closure_args} panic!(\"{{}}\", {arg_root_snippet}))"), applicability, @@ -88,7 +83,7 @@ fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Ex if (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref) && { let arg_type = cx.typeck_results().expr_ty(receiver); let base_type = arg_type.peel_refs(); - base_type.is_str() || is_type_lang_item(cx, base_type, hir::LangItem::String) + base_type.is_str() || base_type.is_lang_item(cx, hir::LangItem::String) } { receiver } else { diff --git a/clippy_lints/src/methods/extend_with_drain.rs b/clippy_lints/src/methods/extend_with_drain.rs index db60061904f6f..829c1226a859e 100644 --- a/clippy_lints/src/methods/extend_with_drain.rs +++ b/clippy_lints/src/methods/extend_with_drain.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::EXTEND_WITH_DRAIN; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if is_type_diagnostic_item(cx, ty, sym::Vec) + if ty.is_diag_item(cx, sym::Vec) //check source object && let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind && src_method.ident.name == sym::drain @@ -18,10 +18,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: //check if actual src type is mutable for code suggestion && let immutable = src_ty.is_mutable_ptr() && let src_ty = src_ty.peel_refs() - && is_type_diagnostic_item(cx, src_ty, sym::Vec) + && src_ty.is_diag_item(cx, sym::Vec) //check drain range && let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs() - && is_type_lang_item(cx, src_ty_range, LangItem::RangeFull) + && src_ty_range.is_lang_item(cx, LangItem::RangeFull) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index 35008c39c084d..a964cbf657b4d 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -10,7 +10,7 @@ use super::FILETYPE_IS_FILE; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv); - if !is_type_diagnostic_item(cx, ty, sym::FileType) { + if !ty.is_diag_item(cx, sym::FileType) { return; } diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 2da0f8341b17b..26b19848fe1b6 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks, sym}; +use clippy_utils::{SpanlessEq, higher, peel_blocks, sym}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -134,16 +134,16 @@ impl<'tcx> OffendingFilterExpr<'tcx> { _ => map_arg, } // .map(|y| y[.acceptable_method()].unwrap()) - && let simple_equal = (path_to_local_id(receiver, filter_param_id) - && path_to_local_id(map_arg_peeled, map_param_id)) + && let simple_equal = (receiver.res_local_id() == Some(filter_param_id) + && map_arg_peeled.res_local_id() == Some(map_param_id)) && let eq_fallback = (|a: &Expr<'_>, b: &Expr<'_>| { // in `filter(|x| ..)`, replace `*x` with `x` let a_path = if !is_filter_param_ref && let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind { expr_path } else { a }; // let the filter closure arg and the map closure arg be equal - path_to_local_id(a_path, filter_param_id) - && path_to_local_id(b, map_param_id) + a_path.res_local_id() == Some(filter_param_id) + && b.res_local_id() == Some(map_param_id) && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) }) && (simple_equal @@ -166,7 +166,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { let expr_uses_local = |pat: &Pat<'_>, expr: &Expr<'_>| { if let PatKind::TupleStruct(QPath::Resolved(_, path), [subpat], _) = pat.kind && let PatKind::Binding(_, local_id, ident, _) = subpat.kind - && path_to_local_id(expr.peel_blocks(), local_id) + && expr.peel_blocks().res_local_id() == Some(local_id) && let Some(local_variant_def_id) = path.res.opt_def_id() && local_variant_def_id == variant_def_id { @@ -204,7 +204,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { _ => return None, }; - if path_to_local_id(scrutinee, map_param_id) + if scrutinee.res_local_id() == Some(map_param_id) // else branch should be a `panic!` or `unreachable!` macro call && let Some(mac) = root_macro_call(else_.peel_blocks().span) && (is_panic(cx, mac.def_id) || cx.tcx.opt_item_name(mac.def_id) == Some(sym::unreachable)) @@ -247,7 +247,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { } else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some() // we know for a fact that the wildcard pattern is the second arm && let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind - && path_to_local_id(scrutinee, filter_param_id) + && scrutinee.res_local_id() == Some(filter_param_id) && let PatKind::TupleStruct(QPath::Resolved(_, path), ..) = arm.pat.kind && let Some(variant_def_id) = path.res.opt_def_id() { @@ -266,8 +266,8 @@ fn is_filter_some_map_unwrap( filter_arg: &Expr<'_>, map_arg: &Expr<'_>, ) -> bool { - let iterator = is_trait_method(cx, expr, sym::Iterator); - let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::Option); + let iterator = cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator); + let option = cx.typeck_results().expr_ty(filter_recv).is_diag_item(cx, sym::Option); (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) } @@ -275,12 +275,12 @@ fn is_filter_some_map_unwrap( /// is `filter(|x| x.is_ok()).map(|x| x.unwrap())` fn is_filter_ok_map_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { // result has no filter, so we only check for iterators - let iterator = is_trait_method(cx, expr, sym::Iterator); + let iterator = cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator); iterator && is_ok_filter_map(cx, filter_arg, map_arg) } /// lint use of `filter().map()` or `find().map()` for `Iterators` -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, @@ -398,7 +398,7 @@ fn is_find_or_filter<'a>( filter_arg: &Expr<'_>, map_arg: &Expr<'_>, ) -> Option<(Ident, CheckResult<'a>)> { - if is_trait_method(cx, map_recv, sym::Iterator) + if cx.ty_based_def(map_recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) // filter(|x| ...is_some())... && let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind && let filter_body = cx.tcx.hir_body(filter_body_id) diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index 94944bd9445b8..b803d1a3309da 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,10 +1,9 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::ty::is_copy; -use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, is_trait_method, peel_blocks, -}; +use clippy_utils::{CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, peel_blocks}; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -16,7 +15,7 @@ use rustc_span::{Span, sym}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { if !expr.span.in_external_macro(cx.sess().source_map()) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::Closure(closure) = arg.kind && let body = cx.tcx.hir_body(closure.body) && let value = peel_blocks(body.value) diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index b04d761d4860e..c6e30710eef90 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind; @@ -19,7 +20,7 @@ fn is_identity(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(applicability) = is_identity(cx, filter_map_arg) { // check if the iterator is from an empty array, see issue #12653 diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index 9f3c346042ff9..698025d58e543 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::is_trait_method; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, msrv: Msrv, ) { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { if !msrv.meets(cx, msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 0c2ecfbc8ffdf..043adb8434a0d 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::is_expr_untyped_identity_function; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +15,9 @@ pub(super) fn check<'tcx>( flat_map_arg: &'tcx hir::Expr<'_>, flat_map_span: Span, ) { - if is_trait_method(cx, expr, sym::Iterator) && is_expr_untyped_identity_function(cx, flat_map_arg) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && is_expr_untyped_identity_function(cx, flat_map_arg) + { span_lint_and_sugg( cx, FLAT_MAP_IDENTITY, diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs index 3242dcadb7013..fb0e8f9d91b5a 100644 --- a/clippy_lints/src/methods/flat_map_option.rs +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -7,10 +7,9 @@ use rustc_middle::ty; use rustc_span::{Span, sym}; use super::FLAT_MAP_OPTION; -use clippy_utils::ty::is_type_diagnostic_item; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } let arg_ty = cx.typeck_results().expr_ty_adjusted(arg); @@ -19,7 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg _ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx), _ => return, }; - if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::Option) { + if !sig.output().skip_binder().is_diag_item(cx, sym::Option) { return; } span_lint_and_sugg( diff --git a/clippy_lints/src/methods/format_collect.rs b/clippy_lints/src/methods/format_collect.rs index 1b28596d50da6..7b2fe00d32188 100644 --- a/clippy_lints/src/methods/format_collect.rs +++ b/clippy_lints/src/methods/format_collect.rs @@ -1,7 +1,7 @@ use super::FORMAT_COLLECT; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_format_macro, root_macro_call_first_node}; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use rustc_span::Span; @@ -17,7 +17,7 @@ fn peel_non_expn_blocks<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx> } pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) { - if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) + if cx.typeck_results().expr_ty(expr).is_lang_item(cx, LangItem::String) && let ExprKind::Closure(closure) = map_arg.kind && let body = cx.tcx.hir_body(closure.body) && let Some(value) = peel_non_expn_blocks(body.value) diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index d664eaaac704f..11671f377e668 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,9 +1,10 @@ use std::fmt::Write as _; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_path_diagnostic_item, sugg}; use rustc_ast::join_path_idents; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -15,7 +16,7 @@ use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>], func: &Expr<'_>) { - if is_path_diagnostic_item(cx, func, sym::from_iter_fn) + if func.res(cx).is_diag_item(cx, sym::from_iter_fn) && let arg_ty = cx.typeck_results().expr_ty(&args[0]) && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) && implements_trait(cx, arg_ty, iter_id, &[]) diff --git a/clippy_lints/src/methods/get_first.rs b/clippy_lints/src/methods/get_first.rs index 2e1d71ce284d6..88e9f2fdaee3e 100644 --- a/clippy_lints/src/methods/get_first.rs +++ b/clippy_lints/src/methods/get_first.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( format!("{slice_name}.first()"), app, ); - } else if is_type_diagnostic_item(cx, identity, sym::VecDeque) { + } else if identity.is_diag_item(cx, sym::VecDeque) { let mut app = Applicability::MachineApplicable; let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); span_lint_and_sugg( diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index 9daad1a8a949e..d273558a7006e 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -2,7 +2,6 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -22,16 +21,17 @@ pub(super) fn check<'tcx>( let expr_ty = cx.typeck_results().expr_ty(recv); let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() { "slice" - } else if is_type_diagnostic_item(cx, expr_ty, sym::Vec) { - "Vec" - } else if is_type_diagnostic_item(cx, expr_ty, sym::VecDeque) { - "VecDeque" - } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::HashMap) { - "HashMap" - } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::BTreeMap) { - "BTreeMap" } else { - return; // caller is not a type that we want to lint + match expr_ty + .ty_adt_def() + .and_then(|def| cx.tcx.get_diagnostic_name(def.did())) + { + Some(sym::Vec) => "Vec", + Some(sym::VecDeque) => "VecDeque", + Some(sym::HashMap) if !is_mut => "HashMap", + Some(sym::BTreeMap) if !is_mut => "BTreeMap", + _ => return, // caller is not a type that we want to lint + } }; let mut span = expr.span; diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 0ba84919395cc..57c0ba25ebbf0 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; +use clippy_utils::sym; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; -use clippy_utils::{is_diag_item_method, is_diag_trait_item, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,12 +11,12 @@ use rustc_span::Symbol; use super::IMPLICIT_CLONE; pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && is_clone_like(cx, method_name, method_def_id) + if let Some(method_parent_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id).opt_parent(cx) + && is_clone_like(cx, method_name, method_parent_id) && let return_type = cx.typeck_results().expr_ty(expr) && let input_type = cx.typeck_results().expr_ty(recv) && let (input_type, ref_count, _) = peel_and_count_ty_refs(input_type) - && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)) + && !(ref_count > 0 && method_parent_id.is_diag_item(cx, sym::ToOwned)) && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())) && return_type == input_type && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() @@ -40,19 +41,15 @@ pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, re } /// Returns true if the named method can be used to clone the receiver. -pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: hir::def_id::DefId) -> bool { +pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: hir::def_id::DefId) -> bool { match method_name { - sym::to_os_string => is_diag_item_method(cx, method_def_id, sym::OsStr), - sym::to_owned => is_diag_trait_item(cx, method_def_id, sym::ToOwned), - sym::to_path_buf => is_diag_item_method(cx, method_def_id, sym::Path), - sym::to_string => is_diag_trait_item(cx, method_def_id, sym::ToString), - sym::to_vec => cx - .tcx - .impl_of_assoc(method_def_id) - .filter(|&impl_did| { - cx.tcx.type_of(impl_did).instantiate_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none() - }) - .is_some(), + sym::to_os_string => method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::OsStr), + sym::to_owned => method_parent_id.is_diag_item(cx, sym::ToOwned), + sym::to_path_buf => method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::Path), + sym::to_string => method_parent_id.is_diag_item(cx, sym::ToString), + sym::to_vec => method_parent_id + .opt_impl_ty(cx) + .is_some_and(|ty| ty.instantiate_identity().is_slice()), _ => false, } } diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index ab21515f47f78..7a49a5f31f035 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,27 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::symbol::sym; use super::INEFFICIENT_TO_STRING; -/// Checks for the `INEFFICIENT_TO_STRING` lint -pub fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_name: Symbol, - receiver: &hir::Expr<'_>, - args: &[hir::Expr<'_>], - msrv: Msrv, -) { - if args.is_empty() - && method_name == sym::to_string - && let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) +pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, msrv: Msrv) { + if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && cx.tcx.is_diagnostic_item(sym::to_string_method, to_string_meth_did) && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) && let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver) @@ -61,7 +52,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if is_type_lang_item(cx, ty, hir::LangItem::String) { + if ty.is_lang_item(cx, hir::LangItem::String) { return true; } diff --git a/clippy_lints/src/methods/inspect_for_each.rs b/clippy_lints/src/methods/inspect_for_each.rs index 3706f3b670ba1..194ddf45e6f11 100644 --- a/clippy_lints/src/methods/inspect_for_each.rs +++ b/clippy_lints/src/methods/inspect_for_each.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -8,7 +8,7 @@ use super::INSPECT_FOR_EACH; /// lint use of `inspect().for_each()` for `Iterators` pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { let msg = "called `inspect(..).for_each(..)` on an `Iterator`"; let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`"; span_lint_and_help( diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index bedeb63367d06..c4b116af48713 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::has_iter_method; use rustc_errors::Applicability; use rustc_hir as hir; @@ -10,17 +10,10 @@ use rustc_span::symbol::{Symbol, sym}; use super::INTO_ITER_ON_REF; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_span: Span, - method_name: Symbol, - receiver: &hir::Expr<'_>, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, receiver: &hir::Expr<'_>) { let self_ty = cx.typeck_results().expr_ty_adjusted(receiver); if let ty::Ref(..) = self_ty.kind() - && method_name == sym::into_iter - && is_trait_method(cx, expr, sym::IntoIterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty) { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/io_other_error.rs b/clippy_lints/src/methods/io_other_error.rs index 9276261606e1e..b081e804859a5 100644 --- a/clippy_lints/src/methods/io_other_error.rs +++ b/clippy_lints/src/methods/io_other_error.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{expr_or_init, is_path_diagnostic_item, sym}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{expr_or_init, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args && !expr.span.from_expansion() && !error_kind.span.from_expansion() && let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind - && is_path_diagnostic_item(cx, path, sym::io_error_new) + && path.ty_rel_def(cx).is_diag_item(cx, sym::io_error_new) && let ExprKind::Path(QPath::Resolved(_, init_path)) = &expr_or_init(cx, error_kind).kind && let [.., error_kind_ty, error_kind_variant] = init_path.segments && cx.tcx.is_diagnostic_item(sym::io_errorkind, error_kind_ty.res.def_id()) diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index 545bef1a4c5bc..add01b6a08376 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -1,7 +1,8 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{is_assert_macro, root_macro_call}; -use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context, path_to_local}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context}; use rustc_hir::{Expr, HirId}; use rustc_lint::{LateContext, LintContext}; use rustc_span::sym; @@ -45,7 +46,8 @@ fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool { /// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization /// value depends on a `#[cfg(…)]` directive. fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { - while let Some(init) = path_to_local(expr) + while let Some(init) = expr + .res_local_id() .and_then(|id| find_binding_init(cx, id)) .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) .filter(|init| !is_under_cfg(cx, init.hir_id)) diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs index b4ab313fe98d1..b1a0b658a84ce 100644 --- a/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -1,6 +1,7 @@ use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::{get_iterator_item_ty, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::get_iterator_item_ty; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,7 +17,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, ) { let expr_ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, expr_ty, sym::Vec) + if expr_ty.is_diag_item(cx, sym::Vec) && let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)) && let ty::Adt(_, args) = expr_ty.kind() && let Some(iter_item_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty(recv)) diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index 6b64cc8b50ae7..ea2508cd7f382 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -1,7 +1,7 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -13,21 +13,21 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E let ty = cx.typeck_results().expr_ty(recv); let caller_type = if derefs_to_slice(cx, recv, ty).is_some() { "slice" - } else if is_type_diagnostic_item(cx, ty, sym::Vec) { + } else if ty.is_diag_item(cx, sym::Vec) { "Vec" - } else if is_type_diagnostic_item(cx, ty, sym::VecDeque) { + } else if ty.is_diag_item(cx, sym::VecDeque) { "VecDeque" - } else if is_type_diagnostic_item(cx, ty, sym::HashSet) { + } else if ty.is_diag_item(cx, sym::HashSet) { "HashSet" - } else if is_type_diagnostic_item(cx, ty, sym::HashMap) { + } else if ty.is_diag_item(cx, sym::HashMap) { "HashMap" - } else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) { + } else if ty.is_diag_item(cx, sym::BTreeMap) { "BTreeMap" - } else if is_type_diagnostic_item(cx, ty, sym::BTreeSet) { + } else if ty.is_diag_item(cx, sym::BTreeSet) { "BTreeSet" - } else if is_type_diagnostic_item(cx, ty, sym::LinkedList) { + } else if ty.is_diag_item(cx, sym::LinkedList) { "LinkedList" - } else if is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { + } else if ty.is_diag_item(cx, sym::BinaryHeap) { "BinaryHeap" } else { return; diff --git a/clippy_lints/src/methods/iter_filter.rs b/clippy_lints/src/methods/iter_filter.rs index adeff375c8aad..8d95b70c6a4bc 100644 --- a/clippy_lints/src/methods/iter_filter.rs +++ b/clippy_lints/src/methods/iter_filter.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::get_iterator_item_ty; use hir::ExprKind; use rustc_lint::{LateContext, LintContext}; @@ -6,7 +7,7 @@ use super::{ITER_FILTER_IS_OK, ITER_FILTER_IS_SOME}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; -use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment, sym}; +use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::QPath; @@ -107,7 +108,7 @@ fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { if let Some(expr) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(path, _, [_], _) = expr.kind && path.ident.name == sym::map - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return true; } @@ -141,7 +142,7 @@ fn expression_type( filter_arg: &hir::Expr<'_>, filter_span: Span, ) -> Option { - if !is_trait_method(cx, expr, sym::Iterator) + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) || parent_is_map(cx, expr) || span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi())) { diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index cbb1b450e60fc..2d6bc36dc5359 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -1,8 +1,8 @@ use super::ITER_KV_MAP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sym}; use rustc_hir::{Body, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( _ => return, } && let ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() - && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) + && (ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap)) { let mut applicability = rustc_errors::Applicability::MachineApplicable; let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index fd4650e1e45f6..01f35ff02d44a 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -1,7 +1,7 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, higher}; use rustc_ast::ast; use rustc_errors::Applicability; @@ -75,6 +75,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal } fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) + cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Vec) || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) } diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index 1fdbd81bf240e..6d1ee32e50269 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( iter_span: Span, nth_span: Span, ) -> bool { - let caller_type = match get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(iter_recv).peel_refs()) { + let caller_type = match cx.typeck_results().expr_ty(iter_recv).peel_refs().opt_diag_name(cx) { Some(sym::Vec) => "`Vec`", Some(sym::VecDeque) => "`VecDeque`", _ if cx.typeck_results().expr_ty_adjusted(iter_recv).peel_refs().is_slice() => "slice", diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 0f8abd0172423..e0107b75e4142 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,7 +1,8 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_item_or_ctor; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_lang_item_or_ctor, is_trait_method}; use hir::{LangItem, OwnerNode}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -13,7 +14,7 @@ use super::ITER_NTH_ZERO; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext) { diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 83e565562af08..8183c30f8c56b 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,9 +1,10 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig}; -use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -67,8 +68,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), - ExprKind::Path(ref p) if is_res_lang_ctor(cx, cx.qpath_res(p, recv.hir_id), OptionNone) => None, - ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, path_res(cx, f), OptionSome) => Some(arg), + ExprKind::Path(ref p) + if cx + .qpath_res(p, recv.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { + None + }, + ExprKind::Call(f, [arg]) if f.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => Some(arg), _ => return, }; let iter_type = match method_name { diff --git a/clippy_lints/src/methods/iter_out_of_bounds.rs b/clippy_lints/src/methods/iter_out_of_bounds.rs index fa8f9d640ee6e..8cff3bd815a1b 100644 --- a/clippy_lints/src/methods/iter_out_of_bounds.rs +++ b/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::expr_or_init; use clippy_utils::higher::VecArgs; -use clippy_utils::{expr_or_init, is_trait_method}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -58,7 +59,7 @@ fn check<'tcx>( message: &'static str, note: &'static str, ) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(len) = get_iterator_length(cx, recv) && let Some(skipped) = expr_as_u128(cx, arg) && skipped > len diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index fedb7c22eded6..661188c90ed61 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, path_to_local}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{BindingMode, Node, PatKind}; @@ -11,20 +11,20 @@ use super::ITER_SKIP_NEXT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { // lint if caller of skip is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { - let mut application = Applicability::MachineApplicable; + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { + let mut applicability = Applicability::MachineApplicable; span_lint_and_then( cx, ITER_SKIP_NEXT, expr.span.trim_start(recv.span).unwrap(), "called `skip(..).next()` on an iterator", |diag| { - if let Some(id) = path_to_local(recv) + if let Some(id) = recv.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(id) && let PatKind::Binding(ann, _, _, _) = pat.kind && ann != BindingMode::MUT { - application = Applicability::Unspecified; + applicability = Applicability::Unspecified; diag.span_help( pat.span, format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), @@ -35,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr expr.span.trim_start(recv.span).unwrap(), "use `nth` instead", format!(".nth({})", snippet(cx, arg.span, "..")), - application, + applicability, ); }, ); diff --git a/clippy_lints/src/methods/iter_skip_zero.rs b/clippy_lints/src/methods/iter_skip_zero.rs index 663e34437a307..cae31e7c9606e 100644 --- a/clippy_lints/src/methods/iter_skip_zero.rs +++ b/clippy_lints/src/methods/iter_skip_zero.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_from_proc_macro, is_trait_method}; +use clippy_utils::is_from_proc_macro; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ use super::ITER_SKIP_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { if !expr.span.from_expansion() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(arg) = ConstEvalCtxt::new(cx) .eval_local(arg_expr, expr.span.ctxt()) .and_then(|constant| { diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index 90d5d9df55eed..11dde2429adfa 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -8,7 +8,7 @@ use rustc_span::sym; use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) { span_lint( diff --git a/clippy_lints/src/methods/join_absolute_paths.rs b/clippy_lints/src/methods/join_absolute_paths.rs index 2ad070793cbb2..e84b7452c7584 100644 --- a/clippy_lints/src/methods/join_absolute_paths.rs +++ b/clippy_lints/src/methods/join_absolute_paths.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::expr_or_init; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -13,7 +13,7 @@ use super::JOIN_ABSOLUTE_PATHS; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_arg: &'tcx Expr<'tcx>, expr_span: Span) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if (is_type_diagnostic_item(cx, ty, sym::Path) || is_type_diagnostic_item(cx, ty, sym::PathBuf)) + if (ty.is_diag_item(cx, sym::Path) || ty.is_diag_item(cx, sym::PathBuf)) && let ExprKind::Lit(spanned) = expr_or_init(cx, join_arg).kind && let LitKind::Str(symbol, _) = spanned.node && let sym_str = symbol.as_str() diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index bc96815944d5f..1a5b180b0c86f 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, sym}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; @@ -18,9 +19,10 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: if let ExprKind::Closure(c) = arg.kind && matches!(c.kind, ClosureKind::Closure) && let typeck = cx.typeck_results() - && let Some(fn_id) = typeck.type_dependent_def_id(expr.hir_id) - && (is_diag_trait_item(cx, fn_id, sym::Iterator) - || ((is_diag_item_method(cx, fn_id, sym::Option) || is_diag_item_method(cx, fn_id, sym::Result)) + && let Some(fn_def) = typeck.type_dependent_def(expr.hir_id) + && (fn_def.opt_parent(cx).is_diag_item(cx, sym::Iterator) + || ((fn_def.opt_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Option) + || fn_def.opt_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Result)) && msrv.meets(cx, msrvs::OPTION_RESULT_INSPECT))) && let body = cx.tcx.hir_body(c.body) && let [param] = body.params @@ -29,7 +31,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: && let ExprKind::Block(block, _) = body.value.kind && let Some(final_expr) = block.expr && !block.stmts.is_empty() - && path_to_local_id(final_expr, arg_id) + && final_expr.res_local_id() == Some(arg_id) && typeck.expr_adjustments(final_expr).is_empty() { let mut requires_copy = false; @@ -46,7 +48,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: if let ExprKind::Closure(c) = e.kind { // Nested closures don't need to treat returns specially. let _: Option = for_each_expr(cx, cx.tcx.hir_body(c.body).value, |e| { - if path_to_local_id(e, arg_id) { + if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { (_, false) | (UseKind::Deref | UseKind::Return(..), true) => { @@ -64,7 +66,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: }); } else if matches!(e.kind, ExprKind::Ret(_)) { ret_count += 1; - } else if path_to_local_id(e, arg_id) { + } else if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { (UseKind::Return(..), false) => { diff --git a/clippy_lints/src/methods/manual_is_variant_and.rs b/clippy_lints/src/methods/manual_is_variant_and.rs index 93325ca488e4e..8f65858987b9c 100644 --- a/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/clippy_lints/src/methods/manual_is_variant_and.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -30,8 +30,8 @@ pub(super) fn check( } // 2. the caller of `map()` is neither `Option` nor `Result` - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Result); if !is_option && !is_result { return; } @@ -208,7 +208,7 @@ pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) { && cx.tcx.is_diagnostic_item(flavor.symbol(), adt.did()) && args.type_at(0).is_bool() && let ExprKind::MethodCall(_, recv, [map_expr], _) = expr2.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), flavor.symbol()) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, flavor.symbol()) && let Ok(map_func) = MapFunc::try_from(map_expr) { return emit_lint(cx, parent_expr.span, op, flavor, bool_cst, map_func, recv); diff --git a/clippy_lints/src/methods/manual_next_back.rs b/clippy_lints/src/methods/manual_next_back.rs index 9a03559b22377..b064f978588c4 100644 --- a/clippy_lints/src/methods/manual_next_back.rs +++ b/clippy_lints/src/methods/manual_next_back.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -20,8 +20,8 @@ pub(super) fn check<'tcx>( .tcx .get_diagnostic_item(sym::DoubleEndedIterator) .is_some_and(|double_ended_iterator| implements_trait(cx, rev_recv_ty, double_ended_iterator, &[])) - && is_trait_method(cx, rev_call, sym::Iterator) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(rev_call).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index 077957fa44dc8..a8e30e44488cc 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; @@ -19,9 +18,13 @@ pub(super) fn check<'tcx>( ) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Option) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Option) && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind - && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr) + && err_path.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && is_ok_wrapping(cx, map_expr) && let Some(recv_snippet) = recv.span.get_source_text(cx) && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx) @@ -42,14 +45,21 @@ pub(super) fn check<'tcx>( fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { match map_expr.kind { - ExprKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, map_expr.hir_id), ResultOk) => true, + ExprKind::Path(ref qpath) + if cx + .qpath_res(qpath, map_expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultOk) => + { + true + }, ExprKind::Closure(closure) => { let body = cx.tcx.hir_body(closure.body); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind && let ExprKind::Call(callee, [ok_arg]) = body.value.kind - && is_res_lang_ctor(cx, path_res(cx, callee), ResultOk) + && callee.res(cx).ctor_parent(cx).is_lang_item(cx, ResultOk) { - path_to_local_id(ok_arg, param_id) + ok_arg.res_local_id() == Some(param_id) } else { false } diff --git a/clippy_lints/src/methods/manual_repeat_n.rs b/clippy_lints/src/methods/manual_repeat_n.rs index 83b57cca17bf1..1a7628ed43a4f 100644 --- a/clippy_lints/src/methods/manual_repeat_n.rs +++ b/clippy_lints/src/methods/manual_repeat_n.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; -use clippy_utils::{expr_use_ctxt, fn_def_id, is_trait_method, std_or_core}; +use clippy_utils::{expr_use_ctxt, fn_def_id, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -17,7 +18,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { if !expr.span.from_expansion() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind && let Some(def_id) = fn_def_id(cx, repeat_expr) && cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id) diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index c785b23bc9c4a..2196ce92b0ab5 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_res, sym}; +use clippy_utils::sym; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -83,7 +84,7 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { // `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) + && matches!(base.basic_res(), Res::PrimTy(_)) { match seg.ident.name { sym::MAX => return Some(MinMax::Max), diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index a811dd1cee18f..4fe14f8053c94 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_path_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -35,14 +34,14 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { } } else { let ty = cx.typeck_results().expr_ty(e); - if is_type_lang_item(cx, ty, LangItem::String) - || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str)) - || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str)) + if ty.is_lang_item(cx, LangItem::String) + || (ty.is_lang_item(cx, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str)) + || (ty.is_diag_item(cx, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str)) { Some(RepeatKind::String) } else { let ty = ty.peel_refs(); - (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)).then_some(RepeatKind::String) + (ty.is_str() || ty.is_lang_item(cx, LangItem::String)).then_some(RepeatKind::String) } } } @@ -55,8 +54,11 @@ pub(super) fn check( take_arg: &Expr<'_>, ) { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind - && is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat) - && is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String) + && repeat_fn.basic_res().is_diag_item(cx, sym::iter_repeat) + && cx + .typeck_results() + .expr_ty(collect_expr) + .is_lang_item(cx, LangItem::String) && let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id) && let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) && cx.tcx.trait_of_assoc(take_id) == Some(iter_trait_id) diff --git a/clippy_lints/src/methods/manual_try_fold.rs b/clippy_lints/src/methods/manual_try_fold.rs index 23dba47f60f40..f2e127bedde56 100644 --- a/clippy_lints/src/methods/manual_try_fold.rs +++ b/clippy_lints/src/methods/manual_try_fold.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_from_proc_macro, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; @@ -20,7 +21,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { if !fold_span.in_external_macro(cx.sess().source_map()) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let init_ty = cx.typeck_results().expr_ty(init) && let Some(try_trait) = cx.tcx.lang_items().try_trait() && implements_trait(cx, init_ty, try_trait, &[]) diff --git a/clippy_lints/src/methods/map_all_any_identity.rs b/clippy_lints/src/methods/map_all_any_identity.rs index ac11baa2d54ca..ad950f75f8130 100644 --- a/clippy_lints/src/methods/map_all_any_identity.rs +++ b/clippy_lints/src/methods/map_all_any_identity.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_expr_identity_function; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_expr_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -8,7 +9,7 @@ use rustc_span::{Span, sym}; use super::MAP_ALL_ANY_IDENTITY; -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, @@ -19,8 +20,8 @@ pub(super) fn check( any_arg: &Expr<'_>, method: &str, ) { - if is_trait_method(cx, expr, sym::Iterator) - && is_trait_method(cx, recv, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) && is_expr_identity_function(cx, any_arg) && let map_any_call_span = map_call_span.with_hi(any_call_span.hi()) && let Some(map_arg) = map_arg.span.get_source_text(cx) diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 748be9bfcc626..1bc29c9c1dd1c 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::peel_blocks; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, should_call_clone_as_function}; -use clippy_utils::{is_diag_trait_item, peel_blocks}; +use clippy_utils::ty::{is_copy, should_call_clone_as_function}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; @@ -18,14 +19,13 @@ use super::MAP_CLONE; // If this `map` is called on an `Option` or a `Result` and the previous call is `as_ref`, we don't // run this lint because it would overlap with `useless_asref` which provides a better suggestion // in this case. -fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> bool { - if is_diag_trait_item(cx, method_id, sym::Iterator) { +fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_parent_id: DefId) -> bool { + if method_parent_id.is_diag_item(cx, sym::Iterator) { return true; } // We check if it's an `Option` or a `Result`. - if let Some(id) = cx.tcx.impl_of_assoc(method_id) { - let identity = cx.tcx.type_of(id).instantiate_identity(); - if !is_type_diagnostic_item(cx, identity, sym::Option) && !is_type_diagnostic_item(cx, identity, sym::Result) { + if let Some(ty) = method_parent_id.opt_impl_ty(cx) { + if !ty.is_diag_item(cx, sym::Option) && !ty.is_diag_item(cx, sym::Result) { return false; } } else { @@ -42,8 +42,8 @@ fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> } pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: Msrv) { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && should_run_lint(cx, e, method_id) + if let Some(parent_id) = cx.typeck_results().type_dependent_def_id(e.hir_id).opt_parent(cx) + && should_run_lint(cx, e, parent_id) { match arg.kind { hir::ExprKind::Closure(&hir::Closure { body, .. }) => { diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index e944eac91e7ae..1112fbc2a1c74 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,7 +12,7 @@ use super::MAP_COLLECT_RESULT_UNIT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) { // return of collect `Result<(),_>` let collect_ret_ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result) + if collect_ret_ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = collect_ret_ty.kind() && let Some(result_t) = args.types().next() && result_t.is_unit() diff --git a/clippy_lints/src/methods/map_err_ignore.rs b/clippy_lints/src/methods/map_err_ignore.rs index 41beda9c5cb4a..f7da24bed2b80 100644 --- a/clippy_lints/src/methods/map_err_ignore.rs +++ b/clippy_lints/src/methods/map_err_ignore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,7 +9,11 @@ use super::MAP_ERR_IGNORE; pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Result) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Result) && let ExprKind::Closure(&Closure { capture_clause: CaptureBy::Ref, body, diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 750f933330a25..e4ae14b6cf59c 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_trait_method, span_contains_comment}; +use clippy_utils::span_contains_comment; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -40,7 +40,7 @@ fn try_get_caller_ty_name_and_method_name( caller_expr: &Expr<'_>, map_arg: &Expr<'_>, ) -> Option<(&'static str, &'static str)> { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { if is_map_to_option(cx, map_arg) { // `(...).map(...)` has type `impl Iterator> Some(("Iterator", "filter_map")) @@ -69,7 +69,7 @@ fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool { _ => map_closure_ty.fn_sig(cx.tcx), }; let map_closure_return_ty = cx.tcx.instantiate_bound_regions_with_erased(map_closure_sig.output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option) + map_closure_return_ty.is_diag_item(cx, sym::Option) }, _ => false, } diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 6190c43578e9b..fa394526bdf1e 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; -use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; +use clippy_utils::ty::is_copy; +use clippy_utils::{is_expr_untyped_identity_function, is_mutable, path_to_local_with_projections}; use rustc_errors::Applicability; use rustc_hir::{self as hir, ExprKind, Node, PatKind}; use rustc_lint::{LateContext, LintContext}; @@ -21,9 +22,9 @@ pub(super) fn check( ) { let caller_ty = cx.typeck_results().expr_ty(caller); - if (is_trait_method(cx, expr, sym::Iterator) - || is_type_diagnostic_item(cx, caller_ty, sym::Result) - || is_type_diagnostic_item(cx, caller_ty, sym::Option)) + if (cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + || caller_ty.is_diag_item(cx, sym::Result) + || caller_ty.is_diag_item(cx, sym::Option)) && is_expr_untyped_identity_function(cx, map_arg) && let Some(call_span) = expr.span.trim_start(caller.span) { diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index df5a0de3392b0..62bdc4a3e4111 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::mutated_variables; use rustc_errors::Applicability; use rustc_hir as hir; @@ -22,8 +22,8 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) -> bool { // lint if the caller of `map()` is an `Option` or a `Result`. - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); if is_result && !msrv.meets(cx, msrvs::RESULT_MAP_OR_ELSE) { return false; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b0b4e5eedb084..c9066be51c44b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -133,6 +133,7 @@ mod unnecessary_lazy_eval; mod unnecessary_literal_unwrap; mod unnecessary_map_or; mod unnecessary_min_or_max; +mod unnecessary_option_map_or_else; mod unnecessary_result_map_or_else; mod unnecessary_sort_by; mod unnecessary_to_owned; @@ -151,7 +152,8 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, sym}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::{contains_return, iter_input_pats, peel_blocks, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; @@ -4639,6 +4641,30 @@ declare_clippy_lint! { "detects redundant calls to `Iterator::cloned`" } +declare_clippy_lint! { + /// Checks for usage of `.map_or_else()` "map closure" for `Option` type. + /// + /// ### Why is this bad? + /// This can be written more concisely by using `unwrap_or_else()`. + /// + /// ### Example + /// ```no_run + /// let k = 10; + /// let x: Option = Some(4); + /// let y = x.map_or_else(|| 2 * k, |n| n); + /// ``` + /// Use instead: + /// ```no_run + /// let k = 10; + /// let x: Option = Some(4); + /// let y = x.unwrap_or_else(|| 2 * k); + /// ``` + #[clippy::version = "1.88.0"] + pub UNNECESSARY_OPTION_MAP_OR_ELSE, + suspicious, + "making no use of the \"map closure\" when calling `.map_or_else(|| 2 * k, |n| n)`" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4820,6 +4846,7 @@ impl_lint_pass!(Methods => [ SWAP_WITH_TEMPORARY, IP_CONSTANT, REDUNDANT_ITER_CLONED, + UNNECESSARY_OPTION_MAP_OR_ELSE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4842,8 +4869,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - self.check_methods(cx, expr); - match expr.kind { ExprKind::Call(func, args) => { from_iter_instead_of_collect::check(cx, expr, args, func); @@ -4854,24 +4879,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { swap_with_temporary::check(cx, expr, func, args); ip_constant::check(cx, expr, func, args); }, - ExprKind::MethodCall(method_call, receiver, args, _) => { - let method_span = method_call.ident.span; - or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args, self.msrv); - expect_fun_call::check( - cx, - &self.format_args, - expr, - method_span, - method_call.ident.name, - receiver, - args, - ); - clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args); - clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args); - inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); - single_char_add_str::check(cx, expr, receiver, args); - into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); + ExprKind::MethodCall(..) => { + self.check_methods(cx, expr); }, ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -4886,7 +4895,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - #[allow(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; @@ -4958,7 +4966,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } impl Methods { - #[allow(clippy::too_many_lines)] + #[expect(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) { @@ -5044,7 +5052,7 @@ impl Methods { cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv); option_as_ref_cloned::check(cx, recv, span); }, - (sym::collect, []) if is_trait_method(cx, expr, sym::Iterator) => { + (sym::collect, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { Some((name @ (sym::cloned | sym::copied), recv2, [], _, _)) => { @@ -5065,17 +5073,26 @@ impl Methods { _ => {}, } }, - (sym::count, []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some((sym::cloned, recv2, [], _, _)) => { - iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned, false); - }, - Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => { - iter_count::check(cx, expr, recv2, name2); - }, - Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), - Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), - Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), - _ => {}, + (sym::count, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) => { + match method_call(recv) { + Some((sym::cloned, recv2, [], _, _)) => { + iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::RmCloned, + false, + ); + }, + Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => { + iter_count::check(cx, expr, recv2, name2); + }, + Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + _ => {}, + } }, (sym::min | sym::max, [arg]) => { unnecessary_min_or_max::check(cx, expr, name, recv, arg); @@ -5294,6 +5311,7 @@ impl Methods { }, (sym::map_or_else, [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); + unnecessary_option_map_or_else::check(cx, expr, recv, def, map); unnecessary_result_map_or_else::check(cx, expr, recv, def, map); }, (sym::next, []) => { @@ -5457,7 +5475,7 @@ impl Methods { } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); }, - (sym::try_into, []) if is_trait_method(cx, expr, sym::TryInto) => { + (sym::try_into, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); }, (sym::to_owned, []) => { @@ -5502,7 +5520,14 @@ impl Methods { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv); }, Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, name); + obfuscated_if_else::check( + cx, + expr, + t_recv, + t_arg, + then_method, + obfuscated_if_else::Unwrap::Or(u_arg), + ); }, _ => {}, } @@ -5519,9 +5544,8 @@ impl Methods { expr, t_recv, t_arg, - None, then_method, - sym::unwrap_or_default, + obfuscated_if_else::Unwrap::OrDefault, ); }, _ => {}, @@ -5538,9 +5562,8 @@ impl Methods { expr, t_recv, t_arg, - Some(u_arg), then_method, - sym::unwrap_or_else, + obfuscated_if_else::Unwrap::OrElse(u_arg), ); }, _ => { @@ -5567,8 +5590,18 @@ impl Methods { } // Handle method calls whose receiver and arguments may come from expansion if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind { + let method_span = path.ident.span; + + // Those methods do their own method name checking as they deal with multiple methods. + or_fun_call::check(cx, expr, method_span, path.ident.name, recv, args, self.msrv); + unnecessary_to_owned::check(cx, expr, path.ident.name, recv, args, self.msrv); + match (path.ident.name, args) { - (sym::expect, [_]) => { + (sym::clone, []) => { + clone_on_ref_ptr::check(cx, expr, recv); + clone_on_copy::check(cx, expr, recv); + }, + (sym::expect, [arg]) => { unwrap_expect_used::check( cx, expr, @@ -5578,6 +5611,7 @@ impl Methods { self.allow_expect_in_tests, unwrap_expect_used::Variant::Expect, ); + expect_fun_call::check(cx, &self.format_args, expr, method_span, recv, arg); }, (sym::expect_err, [_]) => { unwrap_expect_used::check( @@ -5590,6 +5624,15 @@ impl Methods { unwrap_expect_used::Variant::Expect, ); }, + (sym::insert_str | sym::push_str, _) => { + single_char_add_str::check(cx, expr, recv, args); + }, + (sym::into_iter, []) => { + into_iter_on_ref::check(cx, expr, method_span, recv); + }, + (sym::to_string, []) => { + inefficient_to_string::check(cx, expr, recv, self.msrv); + }, (sym::unwrap, []) => { unwrap_expect_used::check( cx, diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 9d2c5e6232d65..c9264e747b56d 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -13,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &' && let (_, _, Some(Mutability::Mut)) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(recv)) && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) + && cx.tcx.type_of(impl_id).is_diag_item(cx, sym::Mutex) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/needless_as_bytes.rs b/clippy_lints/src/methods/needless_as_bytes.rs index 635d06330e058..22baad40b4496 100644 --- a/clippy_lints/src/methods/needless_as_bytes.rs +++ b/clippy_lints/src/methods/needless_as_bytes.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::NEEDLESS_AS_BYTES; pub fn check(cx: &LateContext<'_>, prev_method: Symbol, method: Symbol, prev_recv: &Expr<'_>, span: Span) { let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs(); - if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() { + if ty1.is_lang_item(cx, LangItem::String) || ty1.is_str() { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app); span_lint_and_sugg( diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index 71c1576cd57d1..948ed8a25746e 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp}; use rustc_lint::LateContext; @@ -8,7 +9,7 @@ use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_path_diagnostic_item, path_to_local_id, peel_blocks, sym}; +use clippy_utils::{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 +33,7 @@ fn handle_expr( // `is_ascii`, then only `.all()` should warn. if revert != is_all && method.ident.name == sym::is_ascii - && path_to_local_id(receiver, first_param) + && receiver.res_local_id() == Some(first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char && let Some(snippet) = before_chars.get_source_text(cx) @@ -75,8 +76,8 @@ 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 - && is_path_diagnostic_item(cx, fn_path, sym::char_is_ascii) - && path_to_local_id(peels_expr_ref(arg), first_param) + && fn_path.ty_rel_def(cx).is_diag_item(cx, sym::char_is_ascii) + && peels_expr_ref(arg).res_local_id() == Some(first_param) && let Some(snippet) = before_chars.get_source_text(cx) { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 0075bf166cc18..4f005103d23f8 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -2,15 +2,11 @@ use std::ops::ControlFlow; use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{ - get_type_diagnostic_name, has_non_owning_mutable_access, make_normalized_projection, make_projection, -}; -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, sym, -}; +use clippy_utils::ty::{has_non_owning_mutable_access, make_normalized_projection, make_projection}; +use clippy_utils::{CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, sym}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; @@ -98,7 +94,7 @@ pub(super) fn check<'tcx>( if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind && let ty = cx.typeck_results().expr_ty(collect_expr) && matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::Vec | sym::VecDeque | sym::BinaryHeap | sym::LinkedList) ) && let iter_ty = cx.typeck_results().expr_ty(iter_expr) @@ -339,14 +335,18 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind { if args.is_empty() && method_name.ident.name == sym::collect - && is_trait_method(self.cx, expr, sym::Iterator) + && self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv)); self.visit_expr(recv); return; } - if path_to_local_id(recv, self.target) { + if recv.res_local_id() == Some(self.target) { if self .illegal_mutable_capture_ids .intersection(&self.current_mutably_captured_ids) @@ -384,7 +384,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { return; } - if let Some(hir_id) = path_to_local(recv) + if let Some(hir_id) = recv.res_local_id() && let Some(index) = self.hir_id_uses_map.remove(&hir_id) { if self @@ -402,7 +402,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { } } // Check if the collection is used for anything else - if path_to_local_id(expr, self.target) { + if expr.res_local_id() == Some(self.target) { self.seen_other = true; } else { walk_expr(self, expr); @@ -464,7 +464,7 @@ impl<'tcx> Visitor<'tcx> for UsedCountVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if path_to_local_id(expr, self.id) { + if expr.res_local_id() == Some(self.id) { self.count += 1; } else { walk_expr(self, expr); @@ -549,13 +549,17 @@ impl<'tcx> Visitor<'tcx> for IteratorMethodCheckVisitor<'_, 'tcx> { && (recv.hir_id == self.hir_id_of_expr || self .hir_id_of_let_binding - .is_some_and(|hid| path_to_local_id(recv, hid))) - && !is_trait_method(self.cx, expr, sym::Iterator) + .is_some_and(|hid| recv.res_local_id() == Some(hid))) + && !self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { return ControlFlow::Break(()); } else if let ExprKind::Assign(place, value, _span) = &expr.kind && value.hir_id == self.hir_id_of_expr - && let Some(id) = path_to_local(place) + && let Some(id) = place.res_local_id() { // our iterator was directly assigned to a variable self.hir_id_of_let_binding = Some(id); diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs index d77d044340dca..06e6a3c70b87d 100644 --- a/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::sym; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_hir::def::Res; @@ -15,9 +15,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name let typeck = cx.typeck_results(); let outer_ty = typeck.expr_ty(expr); - if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) { + if outer_ty.is_diag_item(cx, sym::Option) && outer_ty == typeck.expr_ty(recv) { if name == sym::as_deref_mut && recv.is_syntactic_place_expr() { - let Res::Local(binding_id) = path_res(cx, recv) else { + let Res::Local(binding_id) = *recv.basic_res() else { return; }; diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs index 1544a12e6ba83..1622fdb88bd53 100644 --- a/clippy_lints/src/methods/needless_option_take.rs +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_type = cx.typeck_results().expr_ty(expr); - is_type_diagnostic_item(cx, expr_type, sym::Option) + expr_type.is_diag_item(cx, sym::Option) } /// Returns the string of the function call that creates the temporary. diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs index 32f32f1b21673..9fa51f78c99d7 100644 --- a/clippy_lints/src/methods/no_effect_replace.rs +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -1,6 +1,6 @@ use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::LitKind; use rustc_hir::{ExprKind, LangItem}; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( arg2: &'tcx rustc_hir::Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - if !(ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) { + if !(ty.is_str() || ty.is_lang_item(cx, LangItem::String)) { return; } diff --git a/clippy_lints/src/methods/obfuscated_if_else.rs b/clippy_lints/src/methods/obfuscated_if_else.rs index 604b48656aeac..b2466bbd982d9 100644 --- a/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/clippy_lints/src/methods/obfuscated_if_else.rs @@ -5,25 +5,24 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, sym}; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::ExprKind; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::Symbol; +#[expect(clippy::needless_pass_by_value)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - then_recv: &'tcx hir::Expr<'_>, - then_arg: &'tcx hir::Expr<'_>, - unwrap_arg: Option<&'tcx hir::Expr<'_>>, + expr: &'tcx Expr<'_>, + then_recv: &'tcx Expr<'_>, + then_arg: &'tcx Expr<'_>, then_method_name: Symbol, - unwrap_method_name: Symbol, + unwrap: Unwrap<'tcx>, ) { let recv_ty = cx.typeck_results().expr_ty(then_recv); if recv_ty.is_bool() { let then_eager = switch_to_eager_eval(cx, then_arg); - let unwrap_eager = unwrap_arg.is_none_or(|arg| switch_to_eager_eval(cx, arg)); + let unwrap_eager = unwrap.arg().is_none_or(|arg| switch_to_eager_eval(cx, arg)); let mut applicability = if then_eager && unwrap_eager { Applicability::MachineApplicable @@ -40,18 +39,17 @@ pub(super) fn check<'tcx>( _ => return, }; - // FIXME: Add `unwrap_or_else` and `unwrap_or_default` symbol - let els = match unwrap_method_name { - sym::unwrap_or => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability), - sym::unwrap_or_else if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => { - let body = cx.tcx.hir_body(closure.body); - snippet_with_applicability(cx, body.value.span, "..", &mut applicability) - }, - sym::unwrap_or_else if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => { - snippet_with_applicability(cx, unwrap_arg.unwrap().span, "_", &mut applicability) + "()" + let els = match unwrap { + Unwrap::Or(arg) => snippet_with_applicability(cx, arg.span, "..", &mut applicability), + Unwrap::OrElse(arg) => match arg.kind { + ExprKind::Closure(closure) => { + let body = cx.tcx.hir_body(closure.body); + snippet_with_applicability(cx, body.value.span, "..", &mut applicability) + }, + ExprKind::Path(_) => snippet_with_applicability(cx, arg.span, "_", &mut applicability) + "()", + _ => return, }, - sym::unwrap_or_default => "Default::default()".into(), - _ => return, + Unwrap::OrDefault => "Default::default()".into(), }; let sugg = format!( @@ -83,3 +81,18 @@ pub(super) fn check<'tcx>( ); } } + +pub(super) enum Unwrap<'tcx> { + Or(&'tcx Expr<'tcx>), + OrElse(&'tcx Expr<'tcx>), + OrDefault, +} + +impl<'tcx> Unwrap<'tcx> { + fn arg(&self) -> Option<&'tcx Expr<'tcx>> { + match self { + Self::Or(a) | Self::OrElse(a) => Some(a), + Self::OrDefault => None, + } + } +} diff --git a/clippy_lints/src/methods/ok_expect.rs b/clippy_lints/src/methods/ok_expect.rs index e10bc0216e5fd..c9c1f4865b813 100644 --- a/clippy_lints/src/methods/ok_expect.rs +++ b/clippy_lints/src/methods/ok_expect.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::has_debug_impl; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -9,7 +10,7 @@ use super::OK_EXPECT; /// lint use of `ok().expect()` for `Result`s pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // lint if the caller of `ok()` is a `Result` && let result_type = cx.typeck_results().expr_ty(recv) && let Some(error_type) = get_error_type(cx, result_type) @@ -29,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().nth(1), + ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().nth(1), _ => None, } } diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs index 37a8e25bef96a..1b520a9edb566 100644 --- a/clippy_lints/src/methods/open_options.rs +++ b/clippy_lints/src/methods/open_options.rs @@ -1,7 +1,7 @@ +use clippy_utils::res::MaybeDef; use rustc_data_structures::fx::FxHashMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{paths, sym}; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -13,7 +13,7 @@ use rustc_span::source_map::Spanned; 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) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty) + ty.is_diag_item(cx, 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<'_>) { diff --git a/clippy_lints/src/methods/option_as_ref_cloned.rs b/clippy_lints/src/methods/option_as_ref_cloned.rs index 3c38deca6cd1f..591f6aacaef87 100644 --- a/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -11,7 +11,11 @@ use super::{OPTION_AS_REF_CLONED, method_call}; pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { if let Some((method @ (sym::as_ref | sym::as_mut), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(as_ref_recv).peel_refs(), sym::Option) + && cx + .typeck_results() + .expr_ty(as_ref_recv) + .peel_refs() + .is_diag_item(cx, sym::Option) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 906ead16fd0d1..3d489075ce8ac 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::peel_blocks; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{path_to_local_id, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -23,7 +23,7 @@ pub(super) fn check( let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); let option_ty = cx.typeck_results().expr_ty(as_ref_recv); - if !is_type_diagnostic_item(cx, option_ty, sym::Option) { + if !option_ty.is_diag_item(cx, sym::Option) { return; } @@ -51,7 +51,7 @@ pub(super) fn check( match &closure_expr.kind { hir::ExprKind::MethodCall(_, receiver, [], _) => { - if path_to_local_id(receiver, closure_body.params[0].pat.hir_id) + if receiver.res_local_id() == Some(closure_body.params[0].pat.hir_id) && let adj = cx .typeck_results() .expr_adjustments(receiver) @@ -72,7 +72,7 @@ pub(super) fn check( if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind && let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind { - path_to_local_id(inner2, closure_body.params[0].pat.hir_id) + inner2.res_local_id() == Some(closure_body.params[0].pat.hir_id) } else { false } diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 1a273f77fb7d0..342ffea51d656 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_res_lang_ctor, path_def_id, path_res}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -37,8 +36,8 @@ pub(super) fn check<'tcx>( def_arg: &'tcx hir::Expr<'_>, map_arg: &'tcx hir::Expr<'_>, ) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); // There are two variants of this `map_or` lint: // (1) using `map_or` as an adapter from `Result` to `Option` @@ -49,12 +48,12 @@ pub(super) fn check<'tcx>( return; } - if !is_res_lang_ctor(cx, path_res(cx, def_arg), OptionNone) { + if !def_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { // nothing to lint! return; } - let f_arg_is_some = is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome); + let f_arg_is_some = map_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome); if is_option { let self_snippet = snippet(cx, recv.span, ".."); @@ -62,7 +61,7 @@ pub(super) fn check<'tcx>( && let arg_snippet = snippet(cx, fn_decl_span, "..") && let body = cx.tcx.hir_body(body) && let Some((func, [arg_char])) = reduce_unit_expression(body.value) - && let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id)) + && let Some(id) = func.res(cx).opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) && Some(id) == cx.tcx.lang_items().option_some_variant() { let func_snippet = snippet(cx, arg_char.span, ".."); diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 4ba8e01090427..32a9b4fe7c58b 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::is_copy; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -27,7 +28,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { // lint if the caller of `map()` is an `Option` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) { + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option) { if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) { // Replacing `.map().unwrap_or()` with `.map_or(, )` can sometimes lead to // borrowck errors, see #10579 for one such instance. diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 04e4503e4097f..aed4a0075c2fd 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -4,8 +4,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::higher::VecArgs; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, sym, @@ -113,7 +114,7 @@ fn check_unwrap_or_default( let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); // Check MSRV, but only for `Result::unwrap_or_default` - if is_type_diagnostic_item(cx, receiver_ty, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { + if receiver_ty.is_diag_item(cx, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { return false; } @@ -239,7 +240,7 @@ fn check_or_fn_call<'tcx>( && let self_ty = cx.typeck_results().expr_ty(self_expr) && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES .iter() - .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) + .find(|&&i| self_ty.is_diag_item(cx, i.0) && i.2.contains(&name)) { let ctxt = span.ctxt(); let mut app = Applicability::HasPlaceholders; diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index 1a760ea733d7f..07199b84f39ec 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::lang_items::LangItem; use rustc_hir::{Expr, ExprKind}; @@ -21,14 +20,14 @@ pub(super) fn check<'tcx>( let title; let or_arg_content: Span; - if is_type_diagnostic_item(cx, ty, sym::Option) { + if ty.is_diag_item(cx, sym::Option) { title = "found `.or(Some(…)).unwrap()`"; if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::OptionSome) { or_arg_content = content; } else { return; } - } else if is_type_diagnostic_item(cx, ty, sym::Result) { + } else if ty.is_diag_item(cx, sym::Result) { title = "found `.or(Ok(…)).unwrap()`"; if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::ResultOk) { or_arg_content = content; @@ -60,7 +59,7 @@ pub(super) fn check<'tcx>( fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option { if let ExprKind::Call(some_expr, [arg]) = expr.kind - && is_res_lang_ctor(cx, path_res(cx, some_expr), item) + && some_expr.res(cx).ctor_parent(cx).is_lang_item(cx, item) { Some(arg.span.source_callsite()) } else { diff --git a/clippy_lints/src/methods/path_buf_push_overwrite.rs b/clippy_lints/src/methods/path_buf_push_overwrite.rs index 32752ef7435fb..18046ff24f9d2 100644 --- a/clippy_lints/src/methods/path_buf_push_overwrite.rs +++ b/clippy_lints/src/methods/path_buf_push_overwrite.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -12,7 +12,11 @@ use super::PATH_BUF_PUSH_OVERWRITE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::PathBuf) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::PathBuf) && let ExprKind::Lit(lit) = arg.kind && let LitKind::Str(ref path_lit, _) = lit.node && let pushed_path = Path::new(path_lit.as_str()) diff --git a/clippy_lints/src/methods/path_ends_with_ext.rs b/clippy_lints/src/methods/path_ends_with_ext.rs index d3f513e7abd27..87d1aa62b2c22 100644 --- a/clippy_lints/src/methods/path_ends_with_ext.rs +++ b/clippy_lints/src/methods/path_ends_with_ext.rs @@ -1,8 +1,8 @@ use super::PATH_ENDS_WITH_EXT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::{LitKind, StrStyle}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -23,7 +23,11 @@ pub(super) fn check( msrv: Msrv, allowed_dotfiles: &FxHashSet<&'static str>, ) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::Path) + if cx + .typeck_results() + .expr_ty(recv) + .peel_refs() + .is_diag_item(cx, sym::Path) && !path.span.from_expansion() && let ExprKind::Lit(lit) = path.kind && let LitKind::Str(path, StrStyle::Cooked) = lit.node diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index e13df18333e46..de4207c2abf45 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; -use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, is_trait_method, sym}; +use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; use rustc_lint::LateContext; @@ -8,7 +9,7 @@ use rustc_lint::LateContext; use super::RANGE_ZIP_WITH_LEN; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // range expression in `.zip()` call: `0..x.len()` && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg) && is_integer_const(cx, start, 0) @@ -82,7 +83,7 @@ fn for_loop_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Optio /// them to a closure, return the pattern of the closure. fn methods_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { if let Some(parent_expr) = get_parent_expr(cx, expr) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::MethodCall(method, recv, [arg], _) = parent_expr.kind && recv.hir_id == expr.hir_id && matches!( diff --git a/clippy_lints/src/methods/read_line_without_trim.rs b/clippy_lints/src/methods/read_line_without_trim.rs index 6738bbf0a12b9..a6dfbada53483 100644 --- a/clippy_lints/src/methods/read_line_without_trim.rs +++ b/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,8 +1,8 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; 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; @@ -32,7 +32,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { let recv_ty = cx.typeck_results().expr_ty(recv); - if is_type_diagnostic_item(cx, recv_ty, sym::Stdin) + if recv_ty.is_diag_item(cx, sym::Stdin) && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind && let Res::Local(local_id) = path.res { @@ -45,7 +45,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< if args.is_empty() && 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) + && parse_result_ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, substs) = parse_result_ty.kind() && let Some(ok_ty) = substs[0].as_type() && parse_fails_on_trailing_newline(ok_ty) diff --git a/clippy_lints/src/methods/readonly_write_lock.rs b/clippy_lints/src/methods/readonly_write_lock.rs index 40b6becd45321..a98a807d1a3ce 100644 --- a/clippy_lints/src/methods/readonly_write_lock.rs +++ b/clippy_lints/src/methods/readonly_write_lock.rs @@ -1,8 +1,8 @@ use super::READONLY_WRITE_LOCK; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::mir::{enclosing_mir, visit_local_usage}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; @@ -13,14 +13,17 @@ fn is_unwrap_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ExprKind::MethodCall(path, receiver, [], _) = expr.kind && path.ident.name == sym::unwrap { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::Result) + cx.typeck_results() + .expr_ty(receiver) + .peel_refs() + .is_diag_item(cx, sym::Result) } else { false } } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver: &Expr<'_>) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::RwLock) + if cx.typeck_results().expr_ty(receiver).peel_refs().is_diag_item(cx, sym::RwLock) && let Node::Expr(unwrap_call_expr) = cx.tcx.parent_hir_node(expr.hir_id) && is_unwrap_call(cx, unwrap_call_expr) && let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id) diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index 9111604ef53b7..57a7254605f93 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)), Applicability::MachineApplicable, ); - } else if is_type_lang_item(cx, ty, LangItem::String) { + } else if ty.is_lang_item(cx, LangItem::String) { span_lint_and_sugg( cx, REPEAT_ONCE, diff --git a/clippy_lints/src/methods/result_map_or_else_none.rs b/clippy_lints/src/methods/result_map_or_else_none.rs index af619c9e3bb17..e2946c22a46b5 100644 --- a/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/clippy_lints/src/methods/result_map_or_else_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::peel_blocks; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -19,13 +19,13 @@ pub(super) fn check<'tcx>( map_arg: &'tcx hir::Expr<'_>, ) { // lint if the caller of `map_or_else()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // We check that it is mapped as `Some`. - && is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome) + && map_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind && let body = cx.tcx.hir_body(body) // And finally we check that we return a `None` in the "else case". - && is_res_lang_ctor(cx, path_res(cx, peel_blocks(body.value)), OptionNone) + && peel_blocks(body.value).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { let msg = "called `map_or_else(|_| None, Some)` on a `Result` value"; let self_snippet = snippet(cx, recv.span, ".."); diff --git a/clippy_lints/src/methods/return_and_then.rs b/clippy_lints/src/methods/return_and_then.rs index 54f38a322b8d9..8f47306c89479 100644 --- a/clippy_lints/src/methods/return_and_then.rs +++ b/clippy_lints/src/methods/return_and_then.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir::{self as hir, Node}; use rustc_lint::LateContext; @@ -7,7 +8,6 @@ use std::ops::ControlFlow; 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::{peel_blocks, potential_return_of_enclosing_body}; @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>( } let recv_type = cx.typeck_results().expr_ty(recv); - if !matches!(get_type_diagnostic_name(cx, recv_type), Some(sym::Option | sym::Result)) { + if !matches!(recv_type.opt_diag_name(cx), Some(sym::Option | sym::Result)) { return; } diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 855babb797a21..c9c75f3f38e2d 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; -use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs, sym}; +use clippy_utils::{is_receiver_of_method_call, strip_pat_refs, sym}; use hir::ExprKind; use rustc_errors::Applicability; use rustc_hir as hir; @@ -14,7 +14,7 @@ use super::SEARCH_IS_SOME; /// lint searching an Iterator followed by `is_some()` /// or calling `find()` on a string followed by `is_some()` or `is_none()` -#[allow(clippy::too_many_arguments, clippy::too_many_lines)] +#[expect(clippy::too_many_arguments, clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'_>, expr: &'tcx hir::Expr<'_>, @@ -27,7 +27,11 @@ pub(super) fn check<'tcx>( ) { let option_check_method = if is_some { "is_some" } else { "is_none" }; // lint if caller of search is an Iterator - if is_trait_method(cx, is_some_recv, sym::Iterator) { + if cx + .ty_based_def(is_some_recv) + .opt_parent(cx) + .is_diag_item(cx, sym::Iterator) + { let msg = format!("called `{option_check_method}()` after searching an `Iterator` with `{search_method}`"); let search_snippet = snippet(cx, search_arg.span, ".."); if search_snippet.lines().count() <= 1 { @@ -109,7 +113,7 @@ pub(super) fn check<'tcx>( else if search_method == sym::find { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); - if is_type_lang_item(cx, self_ty, hir::LangItem::String) { + if self_ty.is_lang_item(cx, hir::LangItem::String) { true } else { self_ty.is_str() diff --git a/clippy_lints/src/methods/skip_while_next.rs b/clippy_lints/src/methods/skip_while_next.rs index 9f0b6c34ea2ef..2452c499b9cee 100644 --- a/clippy_lints/src/methods/skip_while_next.rs +++ b/clippy_lints/src/methods/skip_while_next.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,7 +9,7 @@ use super::SKIP_WHILE_NEXT; /// lint use of `skip_while().next()` for `Iterators` pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // lint if caller of `.skip_while().next()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint_and_help( cx, SKIP_WHILE_NEXT, diff --git a/clippy_lints/src/methods/sliced_string_as_bytes.rs b/clippy_lints/src/methods/sliced_string_as_bytes.rs index 6d4cfdb34f319..4aff194923a6c 100644 --- a/clippy_lints/src/methods/sliced_string_as_bytes.rs +++ b/clippy_lints/src/methods/sliced_string_as_bytes.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, is_range_literal}; use rustc_lint::LateContext; @@ -11,7 +11,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { if let ExprKind::Index(indexed, index, _) = recv.kind && is_range_literal(index) && let ty = cx.typeck_results().expr_ty(indexed).peel_refs() - && (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) + && (ty.is_str() || ty.is_lang_item(cx, LangItem::String)) { let mut applicability = Applicability::MaybeIncorrect; let stringish = snippet_with_applicability(cx, indexed.span, "_", &mut applicability); diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 8daa5db887acf..eee7fb0c5a813 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,10 +1,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; 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, path_to_local_id, paths, sym}; +use clippy_utils::{paths, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -214,7 +215,7 @@ fn indirect_usage<'tcx>( { let mut path_to_binding = None; let _: Option = for_each_expr(cx, init_expr, |e| { - if path_to_local_id(e, binding) { + if e.res_local_id() == Some(binding) { path_to_binding = Some(e); } ControlFlow::Continue(Descend::from(path_to_binding.is_none())) @@ -271,7 +272,6 @@ struct IterUsage { span: Span, } -#[allow(clippy::too_many_lines)] fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, @@ -351,7 +351,9 @@ fn parse_iter_usage<'tcx>( && cx .typeck_results() .type_dependent_def_id(e.hir_id) - .is_some_and(|id| is_diag_item_method(cx, id, sym::Option)) => + .opt_parent(cx) + .opt_impl_ty(cx) + .is_diag_item(cx, sym::Option) => { (Some(UnwrapKind::Unwrap), e.span) }, diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index f11a41f90f1ab..1fc0633f4f959 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{method_chain_args, sym}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -10,7 +10,7 @@ use super::STRING_EXTEND_CHARS; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) { + if !obj_ty.is_lang_item(cx, hir::LangItem::String) { return; } if let Some(arglists) = method_chain_args(arg, &[sym::chars]) { @@ -22,7 +22,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr } else { "" } - } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) { + } else if self_ty.is_lang_item(cx, hir::LangItem::String) { "&" } else { return; diff --git a/clippy_lints/src/methods/string_lit_chars_any.rs b/clippy_lints/src/methods/string_lit_chars_any.rs index f0f9d30d3000a..48e89c2998efa 100644 --- a/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/clippy_lints/src/methods/string_lit_chars_any.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local}; use itertools::Itertools; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -19,14 +20,14 @@ pub(super) fn check<'tcx>( body: &Expr<'_>, msrv: Msrv, ) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let PatKind::Binding(_, arg, _, _) = param.pat.kind && let ExprKind::Lit(lit_kind) = recv.kind && let LitKind::Str(val, _) = lit_kind.node && let ExprKind::Binary(kind, lhs, rhs) = body.kind && let BinOpKind::Eq = kind.node - && let Some(lhs_path) = path_to_local(lhs) - && let Some(rhs_path) = path_to_local(rhs) + && let Some(lhs_path) = lhs.res_local_id() + && let Some(rhs_path) = rhs.res_local_id() && let scrutinee = match (lhs_path == arg, rhs_path == arg) { (true, false) => rhs, (false, true) => lhs, diff --git a/clippy_lints/src/methods/suspicious_command_arg_space.rs b/clippy_lints/src/methods/suspicious_command_arg_space.rs index c60a49067ec09..3e9c677fe34a2 100644 --- a/clippy_lints/src/methods/suspicious_command_arg_space.rs +++ b/clippy_lints/src/methods/suspicious_command_arg_space.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_errors::{Applicability, Diag}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -10,7 +10,7 @@ use super::SUSPICIOUS_COMMAND_ARG_SPACE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if is_type_diagnostic_item(cx, ty, sym::Command) + if ty.is_diag_item(cx, sym::Command) && let hir::ExprKind::Lit(lit) = &arg.kind && let ast::LitKind::Str(s, _) = &lit.node && let Some((arg1, arg2)) = s.as_str().split_once(' ') diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index 788014d9bb632..ece97c1f758c0 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::expr_or_init; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::usage::mutated_variables; -use clippy_utils::{expr_or_init, is_trait_method}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -8,7 +9,10 @@ use rustc_span::sym; use super::SUSPICIOUS_MAP; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { - if is_trait_method(cx, count_recv, sym::Iterator) + if cx + .ty_based_def(count_recv) + .opt_parent(cx) + .is_diag_item(cx, sym::Iterator) && let hir::ExprKind::Closure(closure) = expr_or_init(cx, map_arg).kind && let closure_body = cx.tcx.hir_body(closure.body) && !cx.typeck_results().expr_ty(closure_body.value).is_unit() diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index ffc237e3c24cc..bcd1f11931fc6 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_diag_trait_item; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -11,10 +10,13 @@ use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && is_diag_trait_item(cx, method_def_id, sym::ToOwned) + if cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .opt_parent(cx) + .is_diag_item(cx, sym::ToOwned) && let input_type = cx.typeck_results().expr_ty(expr) - && is_type_diagnostic_item(cx, input_type, sym::Cow) + && input_type.is_diag_item(cx, sym::Cow) { let mut app = Applicability::MaybeIncorrect; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; diff --git a/clippy_lints/src/methods/unbuffered_bytes.rs b/clippy_lints/src/methods/unbuffered_bytes.rs index dd5566f8c8baf..fdddd5e2771db 100644 --- a/clippy_lints/src/methods/unbuffered_bytes.rs +++ b/clippy_lints/src/methods/unbuffered_bytes.rs @@ -1,6 +1,6 @@ use super::UNBUFFERED_BYTES; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::implements_trait; use rustc_hir as hir; use rustc_lint::LateContext; @@ -8,7 +8,7 @@ use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { // Lint if the `.bytes()` call is from the `Read` trait and the implementor is not buffered. - if is_trait_method(cx, expr, sym::IoRead) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IoRead) && let Some(buf_read) = cx.tcx.get_diagnostic_item(sym::IoBufRead) && let ty = cx.typeck_results().expr_ty_adjusted(recv) && !implements_trait(cx, ty, buf_read, &[]) diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index 6371fe6442826..5e247a50358e6 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_path_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::is_uninit_value_valid_for_ty; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::UNINIT_ASSUMED_INIT; /// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if let hir::ExprKind::Call(callee, []) = recv.kind - && is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit) + && callee.ty_rel_def(cx).is_diag_item(cx, sym::maybe_uninit_uninit) && !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)) { span_lint( diff --git a/clippy_lints/src/methods/unit_hash.rs b/clippy_lints/src/methods/unit_hash.rs index 3c7955bc46981..9defd5626eb47 100644 --- a/clippy_lints/src/methods/unit_hash.rs +++ b/clippy_lints/src/methods/unit_hash.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -9,7 +9,7 @@ use rustc_span::sym; use super::UNIT_HASH; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { - if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() { span_lint_and_then( cx, UNIT_HASH, diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index d260e0ef6e197..d9d642015063e 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,9 +1,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::sym; 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, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -19,7 +20,7 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'tcx>, name: Symbol, ) { - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } @@ -46,7 +47,7 @@ pub(super) fn check<'tcx>( // Check if the closure is .filter_map(|x| Some(x)) if name == sym::filter_map && let hir::ExprKind::Call(expr, args) = body.value.kind - && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome) + && expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && let hir::ExprKind::Path(_) = args[0].kind { span_lint( @@ -95,8 +96,8 @@ pub(super) fn check<'tcx>( fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match expr.kind { hir::ExprKind::Call(func, args) => { - if is_res_lang_ctor(cx, path_res(cx, func), OptionSome) { - if path_to_local_id(&args[0], arg_id) { + if func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) { + if args[0].res_local_id() == Some(arg_id) { return (false, false); } return (true, false); @@ -106,7 +107,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc hir::ExprKind::MethodCall(segment, recv, [arg], _) => { if segment.ident.name == sym::then_some && cx.typeck_results().expr_ty(recv).is_bool() - && path_to_local_id(arg, arg_id) + && arg.res_local_id() == Some(arg_id) { (false, true) } else { @@ -133,7 +134,12 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) }, - hir::ExprKind::Path(ref path) if is_res_lang_ctor(cx, cx.qpath_res(path, expr.hir_id), OptionNone) => { + hir::ExprKind::Path(ref path) + if cx + .qpath_res(path, expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { (false, true) }, _ => (true, true), diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 8e3cc9abe8328..bd471e0b18e3d 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{peel_blocks, strip_pat_refs}; use rustc_ast::ast; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -74,8 +75,8 @@ fn check_fold_with_op( && let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind && let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind - && path_to_local_id(left_expr, first_arg_id) - && (replacement.has_args || path_to_local_id(right_expr, second_arg_id)) + && left_expr.res_local_id() == Some(first_arg_id) + && (replacement.has_args || right_expr.res_local_id() == Some(second_arg_id)) { let mut applicability = Applicability::MachineApplicable; @@ -115,7 +116,7 @@ pub(super) fn check( fold_span: Span, ) { // Check that this is a call to Iterator::fold rather than just some function called fold - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } diff --git a/clippy_lints/src/methods/unnecessary_get_then_check.rs b/clippy_lints/src/methods/unnecessary_get_then_check.rs index 39fce2c40c91d..10ea0c0c3e23e 100644 --- a/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -11,11 +11,11 @@ use rustc_span::{Span, sym}; use super::UNNECESSARY_GET_THEN_CHECK; fn is_a_std_set_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::HashSet) || is_type_diagnostic_item(cx, ty, sym::BTreeSet) + ty.is_diag_item(cx, sym::HashSet) || ty.is_diag_item(cx, sym::BTreeSet) } fn is_a_std_map_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) + ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap) } pub(super) fn check( diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 20cf35363d13f..4142f9f75773e 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -1,10 +1,11 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local}; +use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr}; use core::ops::ControlFlow; use itertools::Itertools; use rustc_errors::Applicability; @@ -50,7 +51,7 @@ pub fn check_for_loop_iter( // check whether `expr` is mutable fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(hir_id) = path_to_local(expr) + if let Some(hir_id) = expr.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) { matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs index efd1a718504ce..3290bdd778243 100644 --- a/clippy_lints/src/methods/unnecessary_join.rs +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -20,8 +20,8 @@ pub(super) fn check<'tcx>( let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg); if let ty::Ref(_, ref_type, _) = collect_output_adjusted_type.kind() // the turbofish for collect is ::> - && let ty::Slice(slice) = ref_type.kind() - && is_type_lang_item(cx, *slice, LangItem::String) + && let ty::Slice(slice) = *ref_type.kind() + && slice.is_lang_item(cx, LangItem::String) // the argument for join is "" && let ExprKind::Lit(spanned) = &join_arg.kind && let LitKind::Str(symbol, _) = spanned.node diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 71e606add526e..2869547650f31 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; use hir::FnRetTy; use rustc_errors::Applicability; @@ -19,8 +19,8 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, simplify_using: &str, ) -> bool { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); if (is_option || is_result || is_bool) diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index cc4448192d3e7..410e973f855b6 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res, sym}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{last_path_segment, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; @@ -37,21 +38,23 @@ pub(super) fn check( } let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind { - let Some(qpath) = call.qpath_opt() else { return }; + let Some((qpath, hir_id)) = call.opt_qpath() else { + return; + }; let args = last_path_segment(qpath).args.map(|args| args.args); - let res = cx.qpath_res(qpath, call.hir_id()); + let res = cx.qpath_res(qpath, hir_id); - if is_res_lang_ctor(cx, res, hir::LangItem::OptionSome) { + if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionSome) { (sym::Some, call_args, get_ty_from_args(args, 0)) - } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultOk) { + } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) { (sym::Ok, call_args, get_ty_from_args(args, 0)) - } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultErr) { + } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultErr) { (sym::Err, call_args, get_ty_from_args(args, 1)) } else { return; } - } else if is_res_lang_ctor(cx, path_res(cx, init), hir::LangItem::OptionNone) { + } else if init.res(cx).ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionNone) { let call_args: &[hir::Expr<'_>] = &[]; (sym::None, call_args, None) } else { diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index 1f5e3de6e7a2d..0c01be4b18756 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -3,10 +3,11 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::{Sugg, make_binop}; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait, is_copy}; +use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id}; +use clippy_utils::{get_parent_expr, is_from_proc_macro}; use rustc_ast::LitKind::Bool; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; @@ -54,7 +55,7 @@ pub(super) fn check<'a>( return; }; - let variant = match get_type_diagnostic_name(cx, recv_ty) { + let variant = match recv_ty.opt_diag_name(cx) { Some(sym::Option) => Variant::Some, Some(sym::Result) => Variant::Ok, Some(_) | None => return, @@ -75,11 +76,11 @@ pub(super) fn check<'a>( // .map_or(true, |x| x != y) // .map_or(true, |x| y != x) - swapped comparison && ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool)) - && let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l } + && let non_binding_location = if l.res_local_id() == Some(hir_id) { r } else { l } && switch_to_eager_eval(cx, non_binding_location) - // xor, because if its both then that's a strange edge case and + // if its both then that's a strange edge case and // we can just ignore it, since by default clippy will error on this - && (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id)) + && (l.res_local_id() == Some(hir_id)) != (r.res_local_id() == Some(hir_id)) && !is_local_used(cx, non_binding_location, hir_id) && let typeck_results = cx.typeck_results() && let l_ty = typeck_results.expr_ty(l) diff --git a/clippy_lints/src/methods/unnecessary_option_map_or_else.rs b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs new file mode 100644 index 0000000000000..265619e263765 --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs @@ -0,0 +1,111 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{expr_or_init, find_binding_init, peel_blocks}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Body, BodyId, Closure, Expr, ExprKind, HirId, QPath}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::UNNECESSARY_OPTION_MAP_OR_ELSE; +use super::utils::get_last_chain_binding_hir_id; + +fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { + let msg = "unused \"map closure\" when calling `Option::map_or_else` value"; + let mut applicability = Applicability::MachineApplicable; + let self_snippet = snippet_with_applicability(cx, recv.span, "_", &mut applicability); + let err_snippet = snippet_with_applicability(cx, def_arg.span, "..", &mut applicability); + span_lint_and_sugg( + cx, + UNNECESSARY_OPTION_MAP_OR_ELSE, + expr.span, + msg, + "consider using `unwrap_or_else`", + format!("{self_snippet}.unwrap_or_else({err_snippet})"), + Applicability::MachineApplicable, + ); +} + +fn handle_qpath( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + def_arg: &Expr<'_>, + expected_hir_id: HirId, + qpath: QPath<'_>, +) { + if let QPath::Resolved(_, path) = qpath + && let Res::Local(hir_id) = path.res + && expected_hir_id == hir_id + { + emit_lint(cx, expr, recv, def_arg); + } +} + +fn handle_closure(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, body_id: BodyId) { + let body = cx.tcx.hir_body(body_id); + handle_fn_body(cx, expr, recv, def_arg, body); +} + +fn handle_fn_body(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, body: &Body<'_>) { + if let Some(first_param) = body.params.first() { + let body_expr = peel_blocks(body.value); + match body_expr.kind { + ExprKind::Path(qpath) => { + handle_qpath(cx, expr, recv, def_arg, first_param.pat.hir_id, qpath); + }, + // If this is a block (that wasn't peeled off), then it means there are statements. + ExprKind::Block(block, _) => { + if let Some(block_expr) = block.expr + // First we ensure that this is a "binding chain" (each statement is a binding + // of the previous one) and that it is a binding of the closure argument. + && let Some(last_chain_binding_id) = + get_last_chain_binding_hir_id(first_param.pat.hir_id, block.stmts) + && let ExprKind::Path(qpath) = block_expr.kind + { + handle_qpath(cx, expr, recv, def_arg, last_chain_binding_id, qpath); + } + }, + _ => {}, + } + } +} + +/// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s. +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, map_arg: &Expr<'_>) { + // lint if the caller of `map_or_else()` is an `Option` + if !cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option) { + return; + } + match map_arg.kind { + // If the second argument is a closure, we can check its body. + ExprKind::Closure(&Closure { body, .. }) => { + handle_closure(cx, expr, recv, def_arg, body); + }, + ExprKind::Path(qpath) => { + let res = cx.qpath_res(&qpath, map_arg.hir_id); + match res { + // Case 1: Local variable (could be a closure) + Res::Local(hir_id) => { + if let Some(init_expr) = find_binding_init(cx, hir_id) { + let origin = expr_or_init(cx, init_expr); + if let ExprKind::Closure(&Closure { body, .. }) = origin.kind { + handle_closure(cx, expr, recv, def_arg, body); + } + } + }, + // Case 2: Function definition + Res::Def(DefKind::Fn, def_id) => { + if let Some(local_def_id) = def_id.as_local() + && let Some(body) = cx.tcx.hir_maybe_body_owned_by(local_def_id) + { + handle_fn_body(cx, expr, recv, def_arg, body); + } + }, + _ => (), + } + }, + _ => (), + } +} diff --git a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs index f84d0d6dff0af..1f6bb60414ae1 100644 --- a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs +++ b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::peel_blocks; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; @@ -51,7 +51,7 @@ pub(super) fn check<'tcx>( map_arg: &'tcx Expr<'_>, ) { // lint if the caller of `map_or_else()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind && let body = cx.tcx.hir_body(body) && let Some(first_param) = body.params.first() diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index fa9d5332ff4f1..7cc8c79481a0f 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::std_or_core; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_trait_method, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; @@ -138,7 +139,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp ] = &closure_body.params && let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind && method_path.ident.name == sym::cmp - && is_trait_method(cx, closure_body.value, sym::Ord) + && cx + .ty_based_def(closure_body.value) + .opt_parent(cx) + .is_diag_item(cx, sym::Ord) { let (closure_body, closure_arg, reverse) = if mirrored_exprs(left_expr, left_ident, right_expr, right_ident) { ( diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 640931a828998..a6a39cb6ab30e 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -2,14 +2,11 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{ - get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_and_count_ty_refs, -}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym, -}; +use clippy_utils::{fn_def_id, get_parent_expr, is_expr_temporary_value, return_ty, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -35,12 +32,12 @@ pub fn check<'tcx>( args: &'tcx [Expr<'_>], msrv: Msrv, ) { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + if let Some(method_parent_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id).opt_parent(cx) && args.is_empty() { - if is_cloned_or_copied(cx, method_name, method_def_id) { + if is_cloned_or_copied(cx, method_name, method_parent_id) { unnecessary_iter_cloned::check(cx, expr, method_name, receiver); - } else if is_to_owned_like(cx, expr, method_name, method_def_id) { + } else if is_to_owned_like(cx, expr, method_name, method_parent_id) { if check_split_call_arg(cx, expr, method_name, receiver) { return; } @@ -48,7 +45,7 @@ pub fn check<'tcx>( // `check_addr_of_expr` and `check_into_iter_call_arg` determine whether the call is unnecessary // based on its context, that is, whether it is a referent in an `AddrOf` expression, an // argument in a `into_iter` call, or an argument in the call of some other function. - if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { + if check_addr_of_expr(cx, expr, method_name, method_parent_id, receiver) { return; } if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { @@ -66,12 +63,12 @@ pub fn check<'tcx>( /// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_addr_of_expr( cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, - method_def_id: DefId, + method_parent_id: DefId, receiver: &Expr<'_>, ) -> bool { if let Some(parent) = get_parent_expr(cx, expr) @@ -133,7 +130,7 @@ fn check_addr_of_expr( // `redundant_clone`, but copyable arrays are not. && (*referent_ty != receiver_ty || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty)) - || is_cow_into_owned(cx, method_name, method_def_id)) + || is_cow_into_owned(cx, method_name, method_parent_id)) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) { if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { @@ -159,7 +156,7 @@ fn check_addr_of_expr( // *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow) // but that's ok for Cow::into_owned specifically) && (cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() == target_ty - || is_cow_into_owned(cx, method_name, method_def_id)) + || is_cow_into_owned(cx, method_name, method_parent_id)) { if n_receiver_refs > 0 { span_lint_and_sugg( @@ -220,7 +217,7 @@ fn check_into_iter_call_arg( && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. - && !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow) + && !cx.typeck_results().expr_ty(receiver).is_diag_item(cx, sym::Cow) // Calling `iter()` on a temporary object can lead to false positives. #14242 && !is_expr_temporary_value(cx, receiver) { @@ -320,7 +317,7 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x` // implements `AsRef` but does not implement `Deref`. In this case, we have to // add `.as_ref()` to the suggestion. - let as_ref = if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) + let as_ref = if cx.typeck_results().expr_ty(expr).is_lang_item(cx, LangItem::String) && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && cx.get_associated_type(cx.typeck_results().expr_ty(receiver), deref_trait_id, sym::Target) != Some(cx.tcx.types.str_) @@ -615,21 +612,26 @@ 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 { - matches!(method_name, sym::cloned | sym::copied) && is_diag_trait_item(cx, method_def_id, sym::Iterator) +fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: DefId) -> bool { + matches!(method_name, sym::cloned | sym::copied) && method_parent_id.is_diag_item(cx, sym::Iterator) } /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. -fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { - is_cow_into_owned(cx, method_name, method_def_id) - || (method_name != sym::to_string && is_clone_like(cx, method_name, method_def_id)) - || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) +fn is_to_owned_like<'a>( + cx: &LateContext<'a>, + call_expr: &Expr<'a>, + method_name: Symbol, + method_parent_id: DefId, +) -> bool { + is_cow_into_owned(cx, method_name, method_parent_id) + || (method_name != sym::to_string && is_clone_like(cx, method_name, method_parent_id)) + || is_to_string_on_string_like(cx, call_expr, method_name, method_parent_id) } /// 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 == sym::into_owned && is_diag_item_method(cx, method_def_id, sym::Cow) +fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: DefId) -> bool { + method_name == sym::into_owned && method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::Cow) } /// Returns true if the named method is `ToString::to_string` and it's called on a type that @@ -638,9 +640,9 @@ fn is_to_string_on_string_like<'a>( cx: &LateContext<'_>, call_expr: &'a Expr<'a>, method_name: Symbol, - method_def_id: DefId, + method_parent_id: DefId, ) -> bool { - if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) { + if method_name != sym::to_string || !method_parent_id.is_diag_item(cx, sym::ToString) { return false; } @@ -673,12 +675,12 @@ fn std_map_key<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { } fn is_str_and_string(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { - original_arg_ty.is_str() && is_type_lang_item(cx, arg_ty, LangItem::String) + original_arg_ty.is_str() && arg_ty.is_lang_item(cx, LangItem::String) } fn is_slice_and_vec(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { (original_arg_ty.is_slice() || original_arg_ty.is_array() || original_arg_ty.is_array_slice()) - && is_type_diagnostic_item(cx, arg_ty, sym::Vec) + && arg_ty.is_diag_item(cx, sym::Vec) } // This function will check the following: diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index af4ade3cc0f7d..a7d9b2e0fab01 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; +use clippy_utils::{expr_or_init, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; @@ -40,9 +40,9 @@ use crate::loops::UNUSED_ENUMERATE_INDEX; pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) { let recv_ty = cx.typeck_results().expr_ty(recv); // If we call a method on a `std::iter::Enumerate` instance - if is_type_diagnostic_item(cx, recv_ty, sym::Enumerate) + if recv_ty.is_diag_item(cx, sym::Enumerate) // If we are calling a method of the `Iterator` trait - && is_trait_method(cx, call_expr, sym::Iterator) + && cx.ty_based_def(call_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // And the map argument is a closure && let ExprKind::Closure(closure) = closure_arg.kind && let closure_body = cx.tcx.hir_body(closure.body) diff --git a/clippy_lints/src/methods/unwrap_expect_used.rs b/clippy_lints/src/methods/unwrap_expect_used.rs index 027215e3b4d7b..73a407be4f210 100644 --- a/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/clippy_lints/src/methods/unwrap_expect_used.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::{is_never_like, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::is_never_like; use clippy_utils::{is_in_test, is_inside_always_const_context, is_lint_allowed}; use rustc_hir::Expr; use rustc_lint::{LateContext, Lint}; @@ -45,9 +46,9 @@ pub(super) fn check( ) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - let (kind, none_value, none_prefix) = if is_type_diagnostic_item(cx, ty, sym::Option) && !is_err { + let (kind, none_value, none_prefix) = if ty.is_diag_item(cx, sym::Option) && !is_err { ("an `Option`", "None", "") - } else if is_type_diagnostic_item(cx, ty, sym::Result) + } else if ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, substs) = ty.kind() && let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type() { diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index e56f4b80d0172..972304d79e751 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function}; -use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{get_parent_expr, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; @@ -42,11 +43,11 @@ fn get_enum_ty(enum_ty: Ty<'_>) -> Option> { pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbol, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait - let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { + let Some(def) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { return; }; - if is_diag_trait_item(cx, def_id, sym::AsRef) || is_diag_trait_item(cx, def_id, sym::AsMut) { + if def.opt_parent(cx).is_diag_item(cx, sym::AsRef) || def.opt_parent(cx).is_diag_item(cx, sym::AsMut) { // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); @@ -79,10 +80,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbo applicability, ); } - } else if let Some(impl_id) = cx.tcx.impl_of_assoc(def_id) - && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def() - && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Option | sym::Result)) - { + } else if matches!( + def.opt_parent(cx).opt_impl_ty(cx).opt_diag_name(cx), + Some(sym::Option | sym::Result) + ) { let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); @@ -137,7 +138,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) - && path_to_local_id(obj, local_id) + && obj.res_local_id() == Some(local_id) { true } else { @@ -146,7 +147,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { }, hir::ExprKind::Call(call, [recv]) => { if let hir::ExprKind::Path(qpath) = call.kind - && path_to_local_id(recv, local_id) + && recv.res_local_id() == Some(local_id) { check_qpath(cx, qpath, call.hir_id) } else { diff --git a/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs b/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs index 22df1f3f485e1..c6f54159c7a78 100644 --- a/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs +++ b/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_inside_always_const_context; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource}; use rustc_lint::LateContext; @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<' && segment.ident.name == sym::new_unchecked && let [init_arg] = args && is_inside_always_const_context(cx.tcx, expr.hir_id) - && is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero) + && cx.typeck_results().node_type(ty.hir_id).is_diag_item(cx, sym::NonZero) && msrv.meets(cx, msrvs::CONST_UNWRAP) { let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index b0cc7a785bc31..1e1b124b44866 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -1,5 +1,5 @@ -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_parent_expr, path_to_local_id, usage}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{get_parent_expr, usage}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind}; use rustc_lint::LateContext; @@ -20,7 +20,7 @@ pub(super) fn derefs_to_slice<'tcx>( match ty.kind() { ty::Slice(_) => true, ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => may_slice(cx, boxed), - ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec), + ty::Adt(..) => ty.is_diag_item(cx, sym::Vec), ty::Array(_, size) => size.try_to_target_usize(cx.tcx).is_some(), ty::Ref(_, inner, _) => may_slice(cx, *inner), _ => false, @@ -130,7 +130,7 @@ impl<'tcx> CloneOrCopyVisitor<'_, 'tcx> { fn is_binding(&self, expr: &Expr<'tcx>) -> bool { self.binding_hir_ids .iter() - .any(|hir_id| path_to_local_id(expr, *hir_id)) + .any(|&hir_id| expr.res_local_id() == Some(hir_id)) } } diff --git a/clippy_lints/src/methods/vec_resize_to_zero.rs b/clippy_lints/src/methods/vec_resize_to_zero.rs index bfb481f4fc092..5debaab2067bd 100644 --- a/clippy_lints/src/methods/vec_resize_to_zero.rs +++ b/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -19,7 +19,11 @@ pub(super) fn check<'tcx>( ) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Vec) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Vec) && let ExprKind::Lit(Spanned { node: LitKind::Int(Pu128(0), _), .. diff --git a/clippy_lints/src/methods/verbose_file_reads.rs b/clippy_lints/src/methods/verbose_file_reads.rs index 8ed61637eca21..5727843302d6a 100644 --- a/clippy_lints/src/methods/verbose_file_reads.rs +++ b/clippy_lints/src/methods/verbose_file_reads.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -19,9 +18,13 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, (msg, help): (&'static str, &'static str), ) { - if is_trait_method(cx, expr, sym::IoRead) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IoRead) && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _))) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File) + && cx + .typeck_results() + .expr_ty_adjusted(recv) + .peel_refs() + .is_diag_item(cx, sym::File) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, VERBOSE_FILE_READS, expr.span, msg, |diag| { diff --git a/clippy_lints/src/methods/waker_clone_wake.rs b/clippy_lints/src/methods/waker_clone_wake.rs index b5f34a9be2e23..3081d7f91f067 100644 --- a/clippy_lints/src/methods/waker_clone_wake.rs +++ b/clippy_lints/src/methods/waker_clone_wake.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' if let Some(did) = ty.ty_adt_def() && cx.tcx.is_diagnostic_item(sym::Waker, did.did()) && let ExprKind::MethodCall(_, waker_ref, &[], _) = recv.kind - && is_trait_method(cx, recv, sym::Clone) + && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Clone) { let mut applicability = Applicability::MachineApplicable; let snippet = snippet_with_applicability(cx, waker_ref.span.source_callsite(), "..", &mut applicability); diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 74b297c13621e..12a6f345168fe 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -81,7 +81,6 @@ impl fmt::Display for Convention { } } -#[allow(clippy::too_many_arguments)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, item_name: Symbol, diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index f9a7c562c7a59..ba62853c74571 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_trait_method, sym}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::sym; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -78,7 +79,9 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { - if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { + if cx.typeck_results().expr_ty(receiver).is_floating_point() + || cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Ord) + { match path.ident.name { sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 8822b32b1c3d6..15b773c2c64f4 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::ty::is_type_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::sym; 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}; @@ -112,11 +112,9 @@ fn should_lint<'tcx>( if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { let recv_ty = typeck_results.expr_ty(recv).peel_refs(); - if path.ident.name == sym::debug_struct && is_type_diagnostic_item(cx, recv_ty, sym::Formatter) { + if path.ident.name == sym::debug_struct && recv_ty.is_diag_item(cx, sym::Formatter) { has_debug_struct = true; - } else if path.ident.name == sym::finish_non_exhaustive - && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) - { + } else if path.ident.name == sym::finish_non_exhaustive && recv_ty.is_diag_item(cx, sym::DebugStruct) { has_finish_non_exhaustive = true; } } @@ -137,7 +135,7 @@ fn as_field_call<'tcx>( ) -> Option { if let ExprKind::MethodCall(path, recv, [debug_field, _], _) = &expr.kind && let recv_ty = typeck_results.expr_ty(recv).peel_refs() - && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) + && recv_ty.is_diag_item(cx, sym::DebugStruct) && path.ident.name == sym::field && let ExprKind::Lit(lit) = &debug_field.kind && let LitKind::Str(sym, ..) = lit.node @@ -180,7 +178,9 @@ fn check_struct<'tcx>( .fields() .iter() .filter_map(|field| { - if field_accesses.contains(&field.ident.name) || is_path_lang_item(cx, field.ty, LangItem::PhantomData) { + if field_accesses.contains(&field.ident.name) + || field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) + { None } else { Some((field.span, "this field is unused")) diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 3b44d4b60d320..ddd4271960e13 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,6 +1,7 @@ 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, sym}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{get_parent_expr, 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}; @@ -84,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. let var = if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind - && let Some(var) = path_to_local(lhs) + && let Some(var) = lhs.res_local_id() && expr.span.desugaring_kind().is_none() { var @@ -325,7 +326,7 @@ impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { return; } - if path_to_local_id(expr, self.var) + if expr.res_local_id() == Some(self.var) // Check that this is a read, not a write. && !is_in_assignment_position(self.cx, expr) { diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index fe2157ca533a6..2fef8404f824d 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,7 +1,12 @@ -use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; -use rustc_hir::Expr; -use rustc_lint::{LateContext, LateLintPass}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; +use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::ty_from_hir_ty; +use rustc_errors::{Applicability, Diag}; +use rustc_hir::{self as hir, Expr, ExprKind, Item, ItemKind, LetStmt, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::mir::Mutability; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -88,24 +93,83 @@ declare_clippy_lint! { declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]); +// NOTE: we don't use `check_expr` because that would make us lint every _use_ of such mutexes, not +// just their definitions impl<'tcx> LateLintPass<'tcx> for Mutex { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(_, subst) = ty.kind() - && is_type_diagnostic_item(cx, ty, sym::Mutex) + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !item.span.from_expansion() + && let ItemKind::Static(_, _, ty, body_id) = item.kind { - let mutex_param = subst.type_at(0); - if let Some(atomic_name) = get_atomic_name(mutex_param) { - let msg = format!( - "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`" - ); - match *mutex_param.kind() { - ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), + let body = cx.tcx.hir_body(body_id); + let mid_ty = ty_from_hir_ty(cx, ty); + check_expr(cx, body.value.peel_blocks(), &TypeAscriptionKind::Required(ty), mid_ty); + } + } + fn check_local(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx LetStmt<'_>) { + if !stmt.span.from_expansion() + && let Some(init) = stmt.init + { + let mid_ty = cx.typeck_results().expr_ty(init); + check_expr(cx, init.peel_blocks(), &TypeAscriptionKind::Optional(stmt.ty), mid_ty); + } + } +} + +/// Whether the type ascription `: Mutex` (which we'll suggest replacing with `AtomicX`) is +/// required +enum TypeAscriptionKind<'tcx> { + /// Yes; for us, this is the case for statics + Required(&'tcx hir::Ty<'tcx>), + /// No; the ascription might've been necessary in an expression like: + /// ```ignore + /// let mutex: Mutex = Mutex::new(0); + /// ``` + /// to specify the type of `0`, but since `AtomicX` already refers to a concrete type, we won't + /// need this ascription anymore. + Optional(Option<&'tcx hir::Ty<'tcx>>), +} + +fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty_ascription: &TypeAscriptionKind<'tcx>, ty: Ty<'tcx>) { + if let ty::Adt(_, subst) = ty.kind() + && ty.is_diag_item(cx, sym::Mutex) + && let mutex_param = subst.type_at(0) + && let Some(atomic_name) = get_atomic_name(mutex_param) + { + let msg = "using a `Mutex` where an atomic would do"; + let diag = |diag: &mut Diag<'_, _>| { + // if `expr = Mutex::new(arg)`, we can try emitting a suggestion + if let ExprKind::Call(qpath, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(_mutex, new)) = qpath.kind + && new.ident.name == sym::new + { + let mut applicability = Applicability::MaybeIncorrect; + let arg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability); + let mut suggs = vec![(expr.span, format!("std::sync::atomic::{atomic_name}::new({arg})"))]; + match ty_ascription { + TypeAscriptionKind::Required(ty_ascription) => { + suggs.push((ty_ascription.span, format!("std::sync::atomic::{atomic_name}"))); + }, + TypeAscriptionKind::Optional(Some(ty_ascription)) => { + // See https://github.com/rust-lang/rust-clippy/pull/15386 for why this is + // required + let colon_ascription = (cx.sess().source_map()) + .span_extend_to_prev_char_before(ty_ascription.span, ':', true) + .with_leading_whitespace(cx) + .into_span(); + suggs.push((colon_ascription, String::new())); + }, + TypeAscriptionKind::Optional(None) => {}, // nothing to remove/replace } + diag.multipart_suggestion("try", suggs, applicability); + } else { + diag.help(format!("consider using an `{atomic_name}` instead")); } + diag.help("if you just want the locking behavior and not the internal type, consider using `Mutex<()>`"); + }; + match *mutex_param.kind() { + ty::Uint(t) if t != UintTy::Usize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + ty::Int(t) if t != IntTy::Isize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + _ => span_lint_and_then(cx, MUTEX_ATOMIC, expr.span, msg, diag), } } } @@ -135,7 +199,8 @@ fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { IntTy::I128 => None, } }, - ty::RawPtr(_, _) => Some("AtomicPtr"), + // `AtomicPtr` only accepts `*mut T` + ty::RawPtr(_, Mutability::Mut) => Some("AtomicPtr"), _ => None, } } diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index b8601f77e2492..55208ae708b93 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -1,9 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::source::{indent_of, snippet, snippet_block}; -use rustc_ast::{Block, Label, ast}; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use clippy_utils::higher; +use clippy_utils::source::{indent_of, snippet_block, snippet_with_context}; +use rustc_ast::Label; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, LoopSource, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::Span; +use rustc_span::{ExpnKind, Span}; declare_clippy_lint! { /// ### What it does @@ -127,9 +130,11 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]); -impl EarlyLintPass for NeedlessContinue { - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if !expr.span.from_expansion() { +impl<'tcx> LateLintPass<'tcx> for NeedlessContinue { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + // We cannot use `from_expansion` because for loops, while loops and while let loops are desugared + // into `loop` expressions. + if !matches!(expr.span.ctxt().outer_expn_data().kind, ExpnKind::Macro(..)) { check_and_warn(cx, expr); } } @@ -188,19 +193,19 @@ impl EarlyLintPass for NeedlessContinue { /// /// - The expression is a `continue` node. /// - The expression node is a block with the first statement being a `continue`. -fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&Label>) -> bool { +fn needless_continue_in_else(else_expr: &Expr<'_>, label: Option<&Label>) -> bool { match else_expr.kind { - ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), - ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()), + ExprKind::Block(else_block, _) => is_first_block_stmt_continue(else_block, label), + ExprKind::Continue(l) => compare_labels(label, l.label.as_ref()), _ => false, } } -fn is_first_block_stmt_continue(block: &Block, label: Option<&Label>) -> bool { +fn is_first_block_stmt_continue(block: &Block<'_>, label: Option<&Label>) -> bool { block.stmts.first().is_some_and(|stmt| match stmt.kind { - ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { - if let ast::ExprKind::Continue(ref l) = e.kind { - compare_labels(label, l.as_ref()) + StmtKind::Semi(e) | StmtKind::Expr(e) => { + if let ExprKind::Continue(l) = e.kind { + compare_labels(label, l.label.as_ref()) } else { false } @@ -222,20 +227,34 @@ fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) -> } /// If `expr` is a loop expression (while/while let/for/loop), calls `func` with -/// the AST object representing the loop block of `expr`. -fn with_loop_block(expr: &ast::Expr, mut func: F) +/// the HIR object representing the loop block of `expr`. +fn with_loop_block(expr: &Expr<'_>, mut func: F) where - F: FnMut(&Block, Option<&Label>), + F: FnMut(&Block<'_>, Option<&Label>), { - if let ast::ExprKind::While(_, loop_block, label) - | ast::ExprKind::ForLoop { - body: loop_block, - label, - .. + if let Some(higher::ForLoop { body, label, .. }) = higher::ForLoop::hir(expr) + && let ExprKind::Block(block, _) = &body.kind + { + func(block, label.as_ref()); + return; } - | ast::ExprKind::Loop(loop_block, label, ..) = &expr.kind + + if let Some(higher::While { body, label, .. }) = higher::While::hir(expr) + && let ExprKind::Block(block, _) = &body.kind { - func(loop_block, label.as_ref()); + func(block, label.as_ref()); + return; + } + + if let Some(higher::WhileLet { if_then, label, .. }) = higher::WhileLet::hir(expr) + && let ExprKind::Block(block, _) = &if_then.kind + { + func(block, label.as_ref()); + return; + } + + if let ExprKind::Loop(block, label, LoopSource::Loop, ..) = expr.kind { + func(block, label.as_ref()); } } @@ -247,17 +266,18 @@ where /// - The `if` condition expression, /// - The `then` block, and /// - The `else` expression. -fn with_if_expr(stmt: &ast::Stmt, mut func: F) +fn with_if_expr(expr: &Expr<'_>, mut func: F) where - F: FnMut(&ast::Expr, &ast::Expr, &Block, &ast::Expr), + F: FnMut(&Expr<'_>, &Expr<'_>, &Block<'_>, &Expr<'_>), { - match stmt.kind { - ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { - if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.kind { - func(e, cond, if_block, else_expr); - } - }, - _ => {}, + if let Some(higher::If { + cond, + then, + r#else: Some(r#else), + }) = higher::If::hir(expr) + && let ExprKind::Block(then, _) = then.kind + { + func(expr, cond, then, r#else); } } @@ -269,20 +289,21 @@ enum LintType { } /// Data we pass around for construction of help messages. -struct LintData<'a> { +#[derive(Debug)] +struct LintData<'hir> { /// The `if` expression encountered in the above loop. - if_expr: &'a ast::Expr, + if_expr: &'hir Expr<'hir>, /// The condition expression for the above `if`. - if_cond: &'a ast::Expr, + if_cond: &'hir Expr<'hir>, /// The `then` block of the `if` statement. - if_block: &'a Block, + if_block: &'hir Block<'hir>, /// The `else` block of the `if` statement. /// Note that we only work with `if` exprs that have an `else` branch. - else_expr: &'a ast::Expr, + else_expr: &'hir Expr<'hir>, /// The 0-based index of the `if` statement in the containing loop block. - stmt_idx: usize, + stmt_idx: Option, /// The statements of the loop block. - loop_block: &'a Block, + loop_block: &'hir Block<'hir>, } const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant"; @@ -299,7 +320,7 @@ const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause"; const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression"; -fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) { +fn emit_warning(cx: &LateContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) { // snip is the whole *help* message that appears after the warning. // message is the warning message. // expr is the expression which the lint warning message refers to. @@ -325,8 +346,15 @@ fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: L ); } -fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { - let cond_code = snippet(cx, data.if_cond.span, ".."); +fn suggestion_snippet_for_continue_inside_if(cx: &LateContext<'_>, data: &LintData<'_>) -> String { + let mut applicability = Applicability::MachineApplicable; + let (cond_code, _) = snippet_with_context( + cx, + data.if_cond.span, + data.if_expr.span.ctxt(), + "..", + &mut applicability, + ); let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)); @@ -339,8 +367,15 @@ fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintD ) } -fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { - let cond_code = snippet(cx, data.if_cond.span, ".."); +fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &LintData<'_>) -> String { + let mut applicability = Applicability::MachineApplicable; + let (cond_code, _) = snippet_with_context( + cx, + data.if_cond.span, + data.if_expr.span.ctxt(), + "..", + &mut applicability, + ); // Region B let block_code = erode_from_back(&snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span))); @@ -352,18 +387,32 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin let indent = span_of_first_expr_in_block(data.if_block) .and_then(|span| indent_of(cx, span)) .unwrap_or(0); - let to_annex = data.loop_block.stmts[data.stmt_idx + 1..] - .iter() - .map(|stmt| { - let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); + let to_annex = if let Some(stmt_idx) = data.stmt_idx { + let mut lines = data.loop_block.stmts[stmt_idx + 1..] + .iter() + .map(|stmt| { + let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); + let snip = snippet_block(cx, span, "..", None); + snip.lines() + .map(|line| format!("{}{line}", " ".repeat(indent))) + .collect::>() + .join("\n") + }) + .collect::>(); + if let Some(expr) = data.loop_block.expr { + let span = expr.span; let snip = snippet_block(cx, span, "..", None); - snip.lines() + let expr_lines = snip + .lines() .map(|line| format!("{}{line}", " ".repeat(indent))) .collect::>() - .join("\n") - }) - .collect::>() - .join("\n"); + .join("\n"); + lines.push(expr_lines); + } + lines.join("\n") + } else { + String::new() + }; let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); format!( @@ -373,46 +422,53 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin ) } -fn check_last_stmt_in_expr(inner_expr: &ast::Expr, func: &F) +fn check_last_stmt_in_expr(cx: &LateContext<'_>, inner_expr: &Expr<'_>, func: &F) where F: Fn(Option<&Label>, Span), { - match &inner_expr.kind { - ast::ExprKind::Continue(continue_label) => { - func(continue_label.as_ref(), inner_expr.span); + match inner_expr.kind { + ExprKind::Continue(continue_label) => { + func(continue_label.label.as_ref(), inner_expr.span); }, - ast::ExprKind::If(_, then_block, else_block) => { - check_last_stmt_in_block(then_block, func); + ExprKind::If(_, then_block, else_block) if let ExprKind::Block(then_block, _) = then_block.kind => { + check_last_stmt_in_block(cx, then_block, func); if let Some(else_block) = else_block { - check_last_stmt_in_expr(else_block, func); + check_last_stmt_in_expr(cx, else_block, func); } }, - ast::ExprKind::Match(_, arms, _) => { + ExprKind::Match(_, arms, _) => { + let match_ty = cx.typeck_results().expr_ty(inner_expr); + if !match_ty.is_unit() && !match_ty.is_never() { + return; + } for arm in arms { - if let Some(expr) = &arm.body { - check_last_stmt_in_expr(expr, func); - } + check_last_stmt_in_expr(cx, arm.body, func); } }, - ast::ExprKind::Block(b, _) => { - check_last_stmt_in_block(b, func); + ExprKind::Block(b, _) => { + check_last_stmt_in_block(cx, b, func); }, _ => {}, } } -fn check_last_stmt_in_block(b: &Block, func: &F) +fn check_last_stmt_in_block(cx: &LateContext<'_>, b: &Block<'_>, func: &F) where F: Fn(Option<&Label>, Span), { + if let Some(expr) = b.expr { + check_last_stmt_in_expr(cx, expr, func); + return; + } + if let Some(last_stmt) = b.stmts.last() - && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind + && let StmtKind::Expr(inner_expr) | StmtKind::Semi(inner_expr) = last_stmt.kind { - check_last_stmt_in_expr(inner_expr, func); + check_last_stmt_in_expr(cx, inner_expr, func); } } -fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { +fn check_and_warn(cx: &LateContext<'_>, expr: &Expr<'_>) { with_loop_block(expr, |loop_block, label| { let p = |continue_label: Option<&Label>, span: Span| { if compare_labels(label, continue_label) { @@ -427,16 +483,51 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { } }; - let stmts = &loop_block.stmts; + let stmts = loop_block.stmts; for (i, stmt) in stmts.iter().enumerate() { let mut maybe_emitted_in_if = false; - with_if_expr(stmt, |if_expr, cond, then_block, else_expr| { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind { + with_if_expr(expr, |if_expr, cond, then_block, else_expr| { + let data = &LintData { + if_expr, + if_cond: cond, + if_block: then_block, + else_expr, + stmt_idx: Some(i), + loop_block, + }; + + maybe_emitted_in_if = true; + if needless_continue_in_else(else_expr, label) { + emit_warning( + cx, + data, + DROP_ELSE_BLOCK_AND_MERGE_MSG, + LintType::ContinueInsideElseBlock, + ); + } else if is_first_block_stmt_continue(then_block, label) { + emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock); + } else { + maybe_emitted_in_if = false; + } + }); + } + + if i == stmts.len() - 1 && loop_block.expr.is_none() && !maybe_emitted_in_if { + check_last_stmt_in_block(cx, loop_block, &p); + } + } + + if let Some(expr) = loop_block.expr { + let mut maybe_emitted_in_if = false; + + with_if_expr(expr, |if_expr, cond, then_block, else_expr| { let data = &LintData { if_expr, if_cond: cond, if_block: then_block, else_expr, - stmt_idx: i, + stmt_idx: None, loop_block, }; @@ -455,8 +546,8 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { } }); - if i == stmts.len() - 1 && !maybe_emitted_in_if { - check_last_stmt_in_block(loop_block, &p); + if !maybe_emitted_in_if { + check_last_stmt_in_block(cx, loop_block, &p); } } }); @@ -491,8 +582,12 @@ fn erode_from_back(s: &str) -> String { if ret.is_empty() { s.to_string() } else { ret } } -fn span_of_first_expr_in_block(block: &Block) -> Option { - block.stmts.first().map(|stmt| stmt.span) +fn span_of_first_expr_in_block(block: &Block<'_>) -> Option { + block + .stmts + .first() + .map(|stmt| stmt.span) + .or(block.expr.map(|expr| expr.span)) } #[cfg(test)] diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 3a6ccc2bca998..d03188f1d39b8 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind, TyKind}; @@ -7,8 +8,8 @@ use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sym; use clippy_utils::ty::has_iter_method; -use clippy_utils::{is_trait_method, sym}; declare_clippy_lint! { /// ### What it does @@ -65,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ) && method_name.ident.name == sym::for_each - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, 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() // Skip the lint if the body is not block because this is simpler than `for` loop. diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index a914267cf5002..464a91959a8eb 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_to_local; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{SourceText, SpanRangeExt, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; @@ -116,7 +116,7 @@ impl LocalAssign { } Some(Self { - lhs_id: path_to_local(lhs)?, + lhs_id: lhs.res_local_id()?, rhs_span: rhs.span.source_callsite(), span, }) diff --git a/clippy_lints/src/needless_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs index ad6313e391bd9..4bcd26c74f57f 100644 --- a/clippy_lints/src/needless_maybe_sized.rs +++ b/clippy_lints/src/needless_maybe_sized.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { } declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]); -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] struct Bound<'tcx> { /// The [`DefId`] of the type parameter the bound refers to param: DefId, diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 7052e1d0fbe5d..3d2285efbe185 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -364,7 +364,6 @@ impl MutablyUsedVariablesCtxt<'_> { } impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { - #[allow(clippy::if_same_then_else)] fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) { if let euv::Place { base: @@ -398,7 +397,6 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { fn use_cloned(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} - #[allow(clippy::if_same_then_else)] fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId, borrow: ty::BorrowKind) { self.prev_bind = None; if let euv::Place { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 455d1426aa8cb..fb5f21acf2af2 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{ - implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, -}; +use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; +use clippy_utils::{is_self, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; @@ -219,7 +218,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { diag.span_help(span, "or consider marking this type as `Copy`"); } - if is_type_diagnostic_item(cx, ty, sym::Vec) + if ty.is_diag_item(cx, sym::Vec) && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_owned()")]) && let TyKind::Path(QPath::Resolved(_, path)) = input.kind && let Some(elem_ty) = path @@ -262,7 +261,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { return; } - if is_type_lang_item(cx, ty, LangItem::String) + if ty.is_lang_item(cx, LangItem::String) && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_string()"), (sym::as_str, "")]) { @@ -362,7 +361,7 @@ fn extract_clone_suggestions<'tcx>( let mut spans = Vec::new(); for_each_expr_without_closures(body, |e| { if let ExprKind::MethodCall(seg, recv, [], _) = e.kind - && path_to_local_id(recv, id) + && recv.res_local_id() == Some(id) { if seg.ident.name == sym::capacity { return ControlFlow::Break(()); diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 2a2160c3be2d1..986a827c59c55 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::path_res; +use clippy_utils::res::MaybeQPath; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Block, Body, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -94,7 +94,7 @@ impl LateLintPass<'_> for NeedlessQuestionMark { 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 Res::Def(DefKind::Ctor(..), ctor_id) = path.res(cx) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) && let variant = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { "Some" diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 0d6666eed455c..701923cf6efc4 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{expr_type_is_certain, has_drop}; -use clippy_utils::{ - in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks, -}; +use clippy_utils::{in_automatically_derived, is_inside_always_const_context, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ @@ -109,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { } fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(def_id) = path_to_local(expr) { + if let Some(def_id) = expr.res_local_id() { self.underscore_bindings.swap_remove(&def_id); } } diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index e531f797272d2..3285023b34aa8 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::implements_trait; -use clippy_utils::{ - is_diag_trait_item, is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core, -}; +use clippy_utils::{is_from_proc_macro, last_path_segment, std_or_core}; use rustc_errors::Applicability; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, LangItem, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{EarlyBinder, TraitRef}; -use rustc_session::declare_lint_pass; +use rustc_middle::ty::{EarlyBinder, TyCtxt, TypeckResults}; +use rustc_session::impl_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -109,33 +108,96 @@ declare_clippy_lint! { suspicious, "non-canonical implementation of `PartialOrd` on an `Ord` type" } -declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); +impl_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); + +#[expect( + clippy::struct_field_names, + reason = "`_trait` suffix is meaningful on its own, \ + and creating an inner `StoredTraits` struct would just add a level of indirection" +)] +pub(crate) struct NonCanonicalImpls { + partial_ord_trait: Option, + ord_trait: Option, + clone_trait: Option, + copy_trait: Option, +} + +impl NonCanonicalImpls { + pub(crate) fn new(tcx: TyCtxt<'_>) -> Self { + let lang_items = tcx.lang_items(); + Self { + partial_ord_trait: lang_items.partial_ord_trait(), + ord_trait: tcx.get_diagnostic_item(sym::Ord), + clone_trait: lang_items.clone_trait(), + copy_trait: lang_items.copy_trait(), + } + } +} + +/// The traits that this lint looks at +enum Trait { + Clone, + PartialOrd, +} impl LateLintPass<'_> for NonCanonicalImpls { - fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { - if let ImplItemKind::Fn(_, impl_item_id) = impl_item.kind - && let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) - && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) - && let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id) - // NOTE: check this early to avoid expensive checks that come after this one - && matches!(trait_name, Some(sym::Clone | sym::PartialOrd)) + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if let ItemKind::Impl(impl_) = item.kind + // Both `PartialOrd` and `Clone` have one required method, and `PartialOrd` can have 5 methods in total + && (1..=5).contains(&impl_.items.len()) + && let Some(of_trait) = impl_.of_trait + && let Some(trait_did) = of_trait.trait_ref.trait_def_id() + // Check this early to hopefully bail out as soon as possible + && let trait_ = if Some(trait_did) == self.clone_trait { + Trait::Clone + } else if Some(trait_did) == self.partial_ord_trait { + Trait::PartialOrd + } else { + return; + } && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) - && let body = cx.tcx.hir_body(impl_item_id) - && let ExprKind::Block(block, ..) = body.value.kind - && !block.span.in_external_macro(cx.sess().source_map()) - && !is_from_proc_macro(cx, impl_item) { - if trait_name == Some(sym::Clone) - && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) - && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) - { - check_clone_on_copy(cx, impl_item, block); - } else if trait_name == Some(sym::PartialOrd) - && impl_item.ident.name == sym::partial_cmp - && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) - { - check_partial_ord_on_ord(cx, impl_item, item, &trait_impl, body, block); + let mut assoc_fns = impl_ + .items + .iter() + .map(|id| cx.tcx.hir_impl_item(*id)) + .filter_map(|assoc| { + if let ImplItemKind::Fn(_, body_id) = assoc.kind + && let body = cx.tcx.hir_body(body_id) + && let ExprKind::Block(block, ..) = body.value.kind + && !block.span.in_external_macro(cx.sess().source_map()) + { + Some((assoc, body, block)) + } else { + None + } + }); + + match trait_ { + Trait::Clone => { + if let Some(copy_trait) = self.copy_trait + && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + && implements_trait(cx, trait_impl.self_ty(), copy_trait, &[]) + { + for (assoc, _, block) in assoc_fns { + check_clone_on_copy(cx, assoc, block); + } + } + }, + Trait::PartialOrd => { + if let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + // If `Self` and `Rhs` are not the same type, then a corresponding `Ord` impl is not possible, + // since it doesn't have an `Rhs` + && let [lhs, rhs] = trait_impl.args.as_slice() + && lhs == rhs + && let Some(ord_trait) = self.ord_trait + && implements_trait(cx, trait_impl.self_ty(), ord_trait, &[]) + && let Some((assoc, body, block)) = + assoc_fns.find(|(assoc, _, _)| assoc.ident.name == sym::partial_cmp) + { + check_partial_ord_on_ord(cx, assoc, item, body, block); + } + }, } } } @@ -153,6 +215,10 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B return; } + if is_from_proc_macro(cx, impl_item) { + return; + } + span_lint_and_sugg( cx, NON_CANONICAL_CLONE_IMPL, @@ -164,7 +230,7 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B ); } - if impl_item.ident.name == sym::clone_from { + if impl_item.ident.name == sym::clone_from && !is_from_proc_macro(cx, impl_item) { span_lint_and_sugg( cx, NON_CANONICAL_CLONE_IMPL, @@ -181,7 +247,6 @@ fn check_partial_ord_on_ord<'tcx>( cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>, item: &Item<'_>, - trait_impl: &TraitRef<'_>, body: &Body<'_>, block: &Block<'tcx>, ) { @@ -206,12 +271,7 @@ fn check_partial_ord_on_ord<'tcx>( && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) { return; - } - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - else if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { + } else if is_from_proc_macro(cx, impl_item) { return; } @@ -261,7 +321,7 @@ fn expr_is_cmp<'tcx>( impl_item: &ImplItem<'_>, needs_fully_qualified: &mut bool, ) -> bool { - let impl_item_did = impl_item.owner_id.def_id; + let typeck = cx.tcx.typeck(impl_item.owner_id.def_id); match expr.kind { ExprKind::Call( Expr { @@ -271,16 +331,16 @@ fn expr_is_cmp<'tcx>( }, [cmp_expr], ) => { - is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) - // Fix #11178, allow `Self::cmp(self, ..)` too - && self_cmp_call(cx, cmp_expr, impl_item_did, needs_fully_qualified) + typeck.qpath_res(some_path, *some_hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) + // Fix #11178, allow `Self::cmp(self, ..)` + && self_cmp_call(cx, typeck, cmp_expr, needs_fully_qualified) }, ExprKind::MethodCall(_, recv, [], _) => { - cx.tcx - .typeck(impl_item_did) - .type_dependent_def_id(expr.hir_id) - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) - && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) + typeck + .type_dependent_def(expr.hir_id) + .assoc_parent(cx) + .is_diag_item(cx, sym::Into) + && self_cmp_call(cx, typeck, recv, needs_fully_qualified) }, _ => false, } @@ -289,12 +349,13 @@ fn expr_is_cmp<'tcx>( /// Returns whether this is any of `self.cmp(..)`, `Self::cmp(self, ..)` or `Ord::cmp(self, ..)`. fn self_cmp_call<'tcx>( cx: &LateContext<'tcx>, + typeck: &TypeckResults<'tcx>, cmp_expr: &'tcx Expr<'tcx>, - def_id: LocalDefId, needs_fully_qualified: &mut bool, ) -> bool { match cmp_expr.kind { - ExprKind::Call(path, [_, _]) => path_res(cx, path) + ExprKind::Call(path, [_, _]) => path + .res(typeck) .opt_def_id() .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)), ExprKind::MethodCall(_, recv, [_], ..) => { @@ -309,11 +370,7 @@ fn self_cmp_call<'tcx>( // `else` branch, it must be a method named `cmp` that isn't `Ord::cmp` *needs_fully_qualified = true; - // It's a bit annoying but `typeck_results` only gives us the CURRENT body, which we - // have none, not of any `LocalDefId` we want, so we must call the query itself to avoid - // an immediate ICE - cx.tcx - .typeck(def_id) + typeck .type_dependent_def_id(cmp_expr.hir_id) .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)) }, diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index 7ecde40aee017..61e4d419fdf06 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; 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::res::MaybeResPath; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym}; +use clippy_utils::{fn_def_id, is_no_std_crate, sym}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -188,7 +189,7 @@ impl LazyInfo { 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 Some(path_def_id) = ty.basic_res().opt_def_id() && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind && paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id) { @@ -219,7 +220,7 @@ impl LazyInfo { fn lint(&self, cx: &LateContext<'_>, sugg_map: &FxIndexMap>) { // Applicability might get adjusted to `Unspecified` later if any calls // in `calls_span_and_id` are not replaceable judging by the `sugg_map`. - let mut appl = Applicability::MachineApplicable; + let mut app = Applicability::MachineApplicable; let mut suggs = vec![(self.ty_span_no_args, "std::sync::LazyLock".to_string())]; for (span, def_id) in &self.calls_span_and_id { @@ -228,7 +229,7 @@ impl LazyInfo { suggs.push((*span, sugg)); } else { // If NO suggested replacement, not machine applicable - appl = Applicability::Unspecified; + app = Applicability::Unspecified; } } @@ -239,7 +240,7 @@ impl LazyInfo { self.ty_span_no_args, "this type has been superseded by `LazyLock` in the standard library", |diag| { - diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl); + diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, app); }, ); } diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index ec8c2299d8cbb..784ea34bac58f 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{get_expr_use_or_unification_node, path_def_id, path_to_local, path_to_local_id}; +use clippy_utils::get_expr_use_or_unification_node; +use clippy_utils::res::{MaybeQPath, MaybeResPath}; use core::cell::Cell; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -212,7 +213,7 @@ impl Usage { /// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the /// `DefId` of the function paired with the parameter's index. #[derive(Default)] -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] struct Params { params: Vec, by_id: HirIdMap, @@ -358,7 +359,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { } fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) { - if let Some(id) = path_to_local(e) + if let Some(id) = e.res_local_id() && let Some(param) = self.params.get_by_id_mut(id) { let typeck = cx.typeck_results(); @@ -370,7 +371,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { Some((Node::Expr(parent), child_id)) => match parent.kind { // Recursive call. Track which index the parameter is used in. ExprKind::Call(callee, args) - if path_def_id(cx, callee).is_some_and(|id| { + if callee.res(cx).opt_def_id().is_some_and(|id| { id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id)) }) => { @@ -395,7 +396,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { }, // Parameter update e.g. `x = x + 1` ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs) - if rhs.hir_id == child_id && path_to_local_id(lhs, id) => + if rhs.hir_id == child_id && lhs.res_local_id() == Some(id) => { return; }, diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index e062e55dad894..0a6499e095832 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -2,7 +2,7 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{LateContext, LateLintPass}; @@ -108,9 +108,7 @@ impl ArithmeticSideEffects { rhs_ty: Ty<'tcx>, ) -> bool { let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem); - let is_sat_or_wrap = |ty: Ty<'_>| { - is_type_diagnostic_item(cx, ty, sym::Saturating) || is_type_diagnostic_item(cx, ty, sym::Wrapping) - }; + let is_sat_or_wrap = |ty: Ty<'_>| ty.is_diag_item(cx, sym::Saturating) || ty.is_diag_item(cx, sym::Wrapping); // If the RHS is `NonZero`, then division or module by zero will never occur. if Self::is_non_zero_u(cx, rhs_ty) && is_div_or_rem { diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs index e87cfd103c301..d6af0234f010b 100644 --- a/clippy_lints/src/operators/bit_mask.rs +++ b/clippy_lints/src/operators/bit_mask.rs @@ -47,7 +47,6 @@ fn check_compare<'a>(cx: &LateContext<'a>, bit_op: &Expr<'a>, cmp_op: BinOpKind, } } -#[allow(clippy::too_many_lines)] fn check_bit_mask( cx: &LateContext<'_>, bit_op: BinOpKind, diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index 604f8f5da0b86..05358de5b3482 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_def_id; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_errors::Applicability; @@ -47,11 +47,14 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path).is_some_and(|did| match cx.tcx.get_diagnostic_name(did) { - Some(sym::from_str_method) => true, - Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)), - _ => false, - }) => + if path + .res(cx) + .opt_def_id() + .is_some_and(|did| match cx.tcx.get_diagnostic_name(did) { + Some(sym::from_str_method) => true, + Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)), + _ => false, + }) => { (arg, arg.span) }, diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index d897b0e8dd918..4a1da7e07a888 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -1,8 +1,8 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -18,7 +18,11 @@ pub(crate) fn check<'tcx>( ) { if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) + && cx + .typeck_results() + .expr_ty(self_arg) + .peel_refs() + .is_diag_item(cx, sym::Duration) && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval_local(right, expr.span.ctxt()) { let suggested_fn = match (method_path.ident.name, divisor) { diff --git a/clippy_lints/src/operators/integer_division.rs b/clippy_lints/src/operators/integer_division.rs index 7b98afa9b40bd..1620312474e99 100644 --- a/clippy_lints/src/operators/integer_division.rs +++ b/clippy_lints/src/operators/integer_division.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -16,7 +16,7 @@ pub(crate) fn check<'tcx>( if op == hir::BinOpKind::Div && cx.typeck_results().expr_ty(left).is_integral() && let right_ty = cx.typeck_results().expr_ty(right) - && (right_ty.is_integral() || is_type_diagnostic_item(cx, right_ty, sym::NonZero)) + && (right_ty.is_integral() || right_ty.is_diag_item(cx, 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/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 3483f3081a58c..c32c74a8fe608 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,11 +1,12 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, eager_or_lazy, expr_requires_coercion, higher, is_else_clause, - is_in_const_context, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, + is_in_const_context, peel_blocks, peel_hir_expr_while, }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -314,9 +315,9 @@ impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> { fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { let res = cx.qpath_res(qpath, pat.hir_id); - if is_res_lang_ctor(cx, res, OptionSome) { + if res.ctor_parent(cx).is_lang_item(cx, OptionSome) { return Some((inner_pat, false)); - } else if is_res_lang_ctor(cx, res, ResultOk) { + } else if res.ctor_parent(cx).is_lang_item(cx, ResultOk) { return Some((inner_pat, true)); } } @@ -379,9 +380,14 @@ fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { kind: PatExprKind::Path(qpath), hir_id, .. - }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), + }) => cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), PatKind::TupleStruct(ref qpath, [first_pat], _) => { - is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) + cx.qpath_res(qpath, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) && matches!(first_pat.kind, PatKind::Wild) }, PatKind::Wild => true, diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index ee1d59490ce9f..57127e9d22985 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{is_inside_always_const_context, return_ty}; use core::ops::ControlFlow; @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { return; } let owner = cx.tcx.local_def_id_to_hir_id(def_id).expect_owner(); - if is_type_diagnostic_item(cx, return_ty(cx, owner), sym::Result) { + if return_ty(cx, owner).is_diag_item(cx, sym::Result) { lint_impl_body(cx, span, body); } } diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 9b9024c810575..44217ac2c4fcb 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{peel_hir_expr_refs, peel_ref_operators, sugg}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; @@ -47,13 +47,21 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { } // If the expression is of type `Option` - let is_ty_option = - |expr: &Expr<'_>| is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr).peel_refs(), sym::Option); + let is_ty_option = |expr: &Expr<'_>| { + cx.typeck_results() + .expr_ty(expr) + .peel_refs() + .is_diag_item(cx, sym::Option) + }; // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { !expr.span.from_expansion() - && is_res_lang_ctor(cx, path_res(cx, peel_hir_expr_refs(expr).0), LangItem::OptionNone) + && peel_hir_expr_refs(expr) + .0 + .res(cx) + .ctor_parent(cx) + .is_lang_item(cx, LangItem::OptionNone) }; let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index 4ce6827cac9bb..a5e57d97301e3 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{path_to_local_id, sym}; +use clippy_utils::sym; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { && let PatKind::Binding(BindingMode::MUT, id, name, None) = local.pat.kind && !local.span.in_external_macro(cx.sess().source_map()) && let ty = cx.typeck_results().pat_ty(local.pat) - && is_type_diagnostic_item(cx, ty, sym::PathBuf) + && ty.is_diag_item(cx, sym::PathBuf) { self.searcher = Some(PathbufPushSearcher { local_id: id, @@ -158,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { && let Res::Local(id) = path.res && !expr.span.in_external_macro(cx.sess().source_map()) && let ty = cx.typeck_results().expr_ty(left) - && is_type_diagnostic_item(cx, ty, sym::PathBuf) + && ty.is_diag_item(cx, sym::PathBuf) { self.searcher = Some(PathbufPushSearcher { local_id: id, @@ -176,7 +176,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { if let Some(mut searcher) = self.searcher.take() && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::push { searcher.err_span = searcher.err_span.to(stmt.span); diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs index da56a785007c4..68a34d459e0df 100644 --- a/clippy_lints/src/permissions_set_readonly_false.rs +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; 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}; @@ -34,7 +34,10 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { && let ExprKind::Lit(lit) = &arg.kind && LitKind::Bool(false) == lit.node && path.ident.name == sym::set_readonly - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) + && cx + .typeck_results() + .expr_ty(receiver) + .is_diag_item(cx, sym::FsPermissions) { span_lint_and_then( cx, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 9eed46460a613..8446b6fbbea57 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, std_or_core, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, MultiSpan}; @@ -561,7 +562,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[ } // Check if this is local we care about - let Some(&args_idx) = path_to_local(e).and_then(|id| self.bindings.get(&id)) else { + let Some(&args_idx) = e.res_local_id().and_then(|id| self.bindings.get(&id)) else { return walk_expr(self, e); }; let args = &self.args[args_idx]; @@ -617,7 +618,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[ // Some methods exist on both `[T]` and `Vec`, such as `len`, where the receiver type // doesn't coerce to a slice and our adjusted type check below isn't enough, // but it would still be valid to call with a slice - if is_allowed_vec_method(self.cx, use_expr) { + if is_allowed_vec_method(use_expr) { return; } } @@ -742,8 +743,10 @@ fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option, expr: &Expr<'_>) -> bool { if let ExprKind::Call(pathexp, []) = expr.kind { - path_def_id(cx, pathexp) - .is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))) + matches!( + pathexp.basic_res().opt_diag_name(cx), + Some(sym::ptr_null | sym::ptr_null_mut) + ) } else { false } diff --git a/clippy_lints/src/pub_underscore_fields.rs b/clippy_lints/src/pub_underscore_fields.rs index 66c59cb70d361..694d44d708568 100644 --- a/clippy_lints/src/pub_underscore_fields.rs +++ b/clippy_lints/src/pub_underscore_fields.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_config::types::PubUnderscoreFieldsBehaviour; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::is_path_lang_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{FieldDef, Item, ItemKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { // We ignore fields that have `#[doc(hidden)]`. && !is_doc_hidden(cx.tcx.hir_attrs(field.hir_id)) // We ignore fields that are `PhantomData`. - && !is_path_lang_item(cx, field.ty, LangItem::PhantomData) + && !field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) { span_lint_hir_and_then( cx, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index d3a5a5dddfbeb..e67ea1f5e370d 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -4,14 +4,15 @@ use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, 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, sym, + pat_and_expr_can_be_question_mark, peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, + sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -205,7 +206,7 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ IfBlockType::IfIs(caller, caller_ty, call_sym, if_then) => { // If the block could be identified as `if x.is_none()/is_err()`, // we then only need to check the if_then return to see if it is none/err. - is_type_diagnostic_item(cx, caller_ty, smbl) + caller_ty.is_diag_item(cx, smbl) && expr_return_none_or_err(smbl, cx, if_then, caller, None) && match smbl { sym::Option => call_sym == sym::is_none, @@ -214,20 +215,20 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ } }, IfBlockType::IfLet(res, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => { - is_type_diagnostic_item(cx, let_expr_ty, smbl) + let_expr_ty.is_diag_item(cx, smbl) && match smbl { sym::Option => { // We only need to check `if let Some(x) = option` not `if let None = option`, // because the later one will be suggested as `if option.is_none()` thus causing conflict. - is_res_lang_ctor(cx, res, OptionSome) + res.ctor_parent(cx).is_lang_item(cx, OptionSome) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None) }, sym::Result => { - (is_res_lang_ctor(cx, res, ResultOk) + (res.ctor_parent(cx).is_lang_item(cx, ResultOk) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym))) - || is_res_lang_ctor(cx, res, ResultErr) + || res.ctor_parent(cx).is_lang_item(cx, ResultErr) && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym)) && if_else.is_none() }, @@ -247,8 +248,11 @@ fn expr_return_none_or_err( match peel_blocks_with_stmt(expr).kind { ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym), ExprKind::Path(ref qpath) => match smbl { - sym::Option => is_res_lang_ctor(cx, cx.qpath_res(qpath, expr.hir_id), OptionNone), - sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr), + sym::Option => cx + .qpath_res(qpath, expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), + sym::Result => expr.res_local_id().is_some() && expr.res_local_id() == cond_expr.res_local_id(), _ => false, }, ExprKind::Call(call_expr, [arg]) => { @@ -342,7 +346,10 @@ fn extract_ctor_call<'a, 'tcx>( pat: &'a Pat<'tcx>, ) -> Option<&'a Pat<'tcx>> { if let PatKind::TupleStruct(variant_path, [val_binding], _) = &pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(variant_path, pat.hir_id), expected_ctor) + && cx + .qpath_res(variant_path, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, expected_ctor) { Some(val_binding) } else { @@ -371,7 +378,7 @@ fn check_arm_is_some_or_ok<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Ar // Extract out `val` && let Some(binding) = extract_binding_pat(val_binding) // Check body is just `=> val` - && path_to_local_id(peel_blocks(arm.body), binding) + && peel_blocks(arm.body).res_local_id() == Some(binding) { true } else { @@ -393,7 +400,7 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A // check `=> return Err(...)` && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind - && is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr) + && ok_ctor.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) // check if `...` is `val` from binding or `val.into()` && is_local_or_local_into(cx, ret_expr, ok_val) { @@ -404,10 +411,10 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A }, TryMode::Option => { // Check the pat is `None` - if is_res_lang_ctor(cx, path_res(cx, arm.pat), OptionNone) + if arm.pat.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) // Check `=> return None` && let ExprKind::Ret(Some(ret_expr)) = arm_body.kind - && is_res_lang_ctor(cx, path_res(cx, ret_expr), OptionNone) + && ret_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) && !ret_expr.span.from_expansion() { true @@ -424,8 +431,10 @@ fn is_local_or_local_into(cx: &LateContext<'_>, expr: &Expr<'_>, val: HirId) -> .and_then(|(fn_def_id, _)| cx.tcx.trait_of_assoc(fn_def_id)) .is_some_and(|trait_def_id| cx.tcx.is_diagnostic_item(sym::Into, trait_def_id)); match expr.kind { - ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => is_into_call && path_to_local_id(recv, val), - _ => path_to_local_id(expr, val), + ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => { + is_into_call && recv.res_local_id() == Some(val) + }, + _ => expr.res_local_id() == Some(val), } } @@ -477,7 +486,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if_then, if_else, ) - && ((is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) + && ((is_early_return(sym::Option, cx, &if_block) && peel_blocks(if_then).res_local_id() == Some(bind_id)) || is_early_return(sym::Result, cx, &if_block)) && if_else .map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))) @@ -485,7 +494,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: .is_none() { if !is_copy(cx, caller_ty) - && let Some(hir_id) = path_to_local(let_expr) + && let Some(hir_id) = let_expr.res_local_id() && local_used_after_expr(cx, hir_id, expr) { return; @@ -521,11 +530,11 @@ impl QuestionMark { } } -fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool { +fn is_try_block(bl: &Block<'_>) -> bool { if let Some(expr) = bl.expr && let ExprKind::Call(callee, [_]) = expr.kind { - is_path_lang_item(cx, callee, LangItem::TryTraitFromOutput) + callee.opt_lang_path() == Some(LangItem::TryTraitFromOutput) } else { false } @@ -581,8 +590,8 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { } } - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if is_try_block(cx, block) { + fn check_block(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if is_try_block(block) { *self .try_block_depth_stack .last_mut() @@ -598,8 +607,8 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { self.try_block_depth_stack.pop(); } - fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if is_try_block(cx, block) { + fn check_block_post(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if is_try_block(block) { *self .try_block_depth_stack .last_mut() diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 0b2313cb7eeb9..e4c91b7efd2bd 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -2,13 +2,11 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeQPath, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{ - expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const, is_path_lang_item, - path_to_local, -}; +use clippy_utils::{expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const}; use rustc_ast::Mutability; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -321,7 +319,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option (true, Ordering::Less), _ => return None, }; - if let Some(id) = path_to_local(l) { + if let Some(id) = l.res_local_id() { if let Some(c) = ConstEvalCtxt::new(cx).eval(r) { return Some(RangeBounds { val: c, @@ -333,7 +331,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option( // Check if `expr` is the argument of a compiler-generated `IntoIter::into_iter(expr)` if let ExprKind::Call(func, [arg]) = parent_expr.kind && arg.hir_id == use_ctxt.child_id - && is_path_lang_item(cx, func, LangItem::IntoIterIntoIter) + && func.opt_lang_path() == Some(LangItem::IntoIterIntoIter) { return true; } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index de6766cbe94aa..13c1b10bf2e8c 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::{has_drop, is_copy, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, LangItem, def_id}; @@ -100,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id) || fn_name == Some(sym::to_owned_method) - || (fn_name == Some(sym::to_string_method) && is_type_lang_item(cx, arg_ty, LangItem::String)); + || (fn_name == Some(sym::to_string_method) && arg_ty.is_lang_item(cx, LangItem::String)); let from_deref = !from_borrow && matches!(fn_name, Some(sym::path_to_pathbuf | sym::os_str_to_os_string)); diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index a358eff2ce554..f2cf809d6012a 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -80,7 +81,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind && addressee.span.ctxt() == ctxt && let ExprKind::Index(indexed, range, _) = addressee.kind - && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull) + && cx + .typeck_results() + .expr_ty_adjusted(range) + .is_lang_item(cx, LangItem::RangeFull) { let (expr_ty, expr_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(expr)); let (indexed_ty, indexed_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(indexed)); diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 89d945161f629..d1fc228f4b352 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -2,9 +2,10 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::paths; use clippy_utils::paths::PathLookup; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; @@ -138,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for Regex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Call(fun, [arg]) = expr.kind - && let Some(def_id) = path_def_id(cx, fun) + && let Some(def_id) = fun.res(cx).opt_def_id() && let Some(regex_kind) = self.definitions.get(&def_id) { if let Some(&(loop_item_id, loop_span)) = self.loop_stack.last() diff --git a/clippy_lints/src/replace_box.rs b/clippy_lints/src/replace_box.rs new file mode 100644 index 0000000000000..4bbd1803a78d2 --- /dev/null +++ b/clippy_lints/src/replace_box.rs @@ -0,0 +1,111 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::{is_default_equivalent_call, local_is_initialized}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects assignments of `Default::default()` or `Box::new(value)` + /// to a place of type `Box`. + /// + /// ### Why is this bad? + /// This incurs an extra heap allocation compared to assigning the boxed + /// storage. + /// + /// ### Example + /// ```no_run + /// let mut b = Box::new(1u32); + /// b = Default::default(); + /// ``` + /// Use instead: + /// ```no_run + /// let mut b = Box::new(1u32); + /// *b = Default::default(); + /// ``` + #[clippy::version = "1.92.0"] + pub REPLACE_BOX, + perf, + "assigning a newly created box to `Box` is inefficient" +} +declare_lint_pass!(ReplaceBox => [REPLACE_BOX]); + +impl LateLintPass<'_> for ReplaceBox { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Assign(lhs, rhs, _) = &expr.kind + && !lhs.span.from_expansion() + && !rhs.span.from_expansion() + && let lhs_ty = cx.typeck_results().expr_ty(lhs) + // No diagnostic for late-initialized locals + && lhs.res_local_id().is_none_or(|local| local_is_initialized(cx, local)) + && let Some(inner_ty) = lhs_ty.boxed_ty() + { + if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, inner_ty, default_trait_id, &[]) + && is_default_call(cx, rhs) + { + span_lint_and_then( + cx, + REPLACE_BOX, + expr.span, + "creating a new box with default content", + |diag| { + let mut app = Applicability::MachineApplicable; + let suggestion = format!( + "{} = Default::default()", + Sugg::hir_with_applicability(cx, lhs, "_", &mut app).deref() + ); + + diag.note("this creates a needless allocation").span_suggestion( + expr.span, + "replace existing content with default instead", + suggestion, + app, + ); + }, + ); + } + + if inner_ty.is_sized(cx.tcx, cx.typing_env()) + && let Some(rhs_inner) = get_box_new_payload(cx, rhs) + { + span_lint_and_then(cx, REPLACE_BOX, expr.span, "creating a new box", |diag| { + let mut app = Applicability::MachineApplicable; + let suggestion = format!( + "{} = {}", + Sugg::hir_with_applicability(cx, lhs, "_", &mut app).deref(), + Sugg::hir_with_context(cx, rhs_inner, expr.span.ctxt(), "_", &mut app), + ); + + diag.note("this creates a needless allocation").span_suggestion( + expr.span, + "replace existing content with inner value instead", + suggestion, + app, + ); + }); + } + } + } +} + +fn is_default_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Call(func, _args) if is_default_equivalent_call(cx, func, Some(expr))) +} + +fn get_box_new_payload<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Call(box_new, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind + && seg.ident.name == sym::new + && ty.basic_res().is_lang_item(cx, LangItem::OwnedBox) + { + Some(arg) + } else { + None + } +} diff --git a/clippy_lints/src/reserve_after_initialization.rs b/clippy_lints/src/reserve_after_initialization.rs index 51adbbcd58bdb..ce86a9caac754 100644 --- a/clippy_lints/src/reserve_after_initialization.rs +++ b/clippy_lints/src/reserve_after_initialization.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; -use clippy_utils::{is_from_proc_macro, path_to_local_id, sym}; +use clippy_utils::{is_from_proc_macro, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind}; @@ -125,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization { if let Some(searcher) = self.searcher.take() { if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [space_hint], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::reserve && !is_from_proc_macro(cx, expr) { diff --git a/clippy_lints/src/returns/let_and_return.rs b/clippy_lints/src/returns/let_and_return.rs index e2002fb36e5a6..f54a26a77620a 100644 --- a/clippy_lints/src/returns/let_and_return.rs +++ b/clippy_lints/src/returns/let_and_return.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, path_to_local_id, span_contains_cfg}; +use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, span_contains_cfg}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, PatKind, StmtKind}; @@ -21,7 +22,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) && cx.tcx.hir_attrs(local.hir_id).is_empty() && let Some(initexpr) = &local.init && let PatKind::Binding(_, local_id, _, _) = local.pat.kind - && path_to_local_id(retexpr, local_id) + && retexpr.res_local_id() == Some(local_id) && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) && !initexpr.span.in_external_macro(cx.sess().source_map()) && !retexpr.span.in_external_macro(cx.sess().source_map()) diff --git a/clippy_lints/src/returns/needless_return_with_question_mark.rs b/clippy_lints/src/returns/needless_return_with_question_mark.rs index c05038cd1e50e..c47a3ef21e869 100644 --- a/clippy_lints/src/returns/needless_return_with_question_mark.rs +++ b/clippy_lints/src/returns/needless_return_with_question_mark.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{is_from_proc_macro, is_inside_let_else}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind}; @@ -19,7 +20,7 @@ pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind - && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) + && maybe_constr.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) // Ensure this is not the final stmt, otherwise removing it would cause a compile error && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs index ff6e6ef214b5b..688da33a17778 100644 --- a/clippy_lints/src/set_contains_or_insert.rs +++ b/clippy_lints/src/set_contains_or_insert.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::for_each_expr; use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while, sym}; use rustc_hir::{Expr, ExprKind, UnOp}; @@ -103,7 +103,7 @@ fn try_parse_op_call<'tcx>( let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); if value.span.eq_ctxt(expr.span) && path.ident.name == symbol { for sym in &[sym::HashSet, sym::BTreeSet] { - if is_type_diagnostic_item(cx, receiver_ty, *sym) { + if receiver_ty.is_diag_item(cx, *sym) { return Some((OpExpr { receiver, value, span }, *sym)); } } diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 14399867f3181..7fdea6bec5100 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_to_local_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; use rustc_data_structures::fx::FxHashMap; @@ -202,7 +202,7 @@ pub fn is_local_used_except<'tcx>( for_each_expr(cx, visitable, |e| { if except.is_some_and(|it| it == e.hir_id) { ControlFlow::Continue(Descend::No) - } else if path_to_local_id(e, id) { + } else if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { ControlFlow::Continue(Descend::Yes) diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index 9110f684bd10c..c4604fb1558df 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{indent_of, snippet}; -use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary, sym}; +use clippy_utils::{expr_or_init, get_attr, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -276,7 +277,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind && !self.ap.apas.contains_key(&hir_id) && { - if let Some(local_hir_id) = path_to_local(expr) { + if let Some(local_hir_id) = expr.res_local_id() { local_hir_id == hir_id } else { true @@ -301,7 +302,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { modify_apa_params(&mut apa); let _ = self.ap.apas.insert(hir_id, apa); } else { - let Some(hir_id) = path_to_local(expr) else { + let Some(hir_id) = expr.res_local_id() else { return; }; let Some(apa) = self.ap.apas.get_mut(&hir_id) else { @@ -319,7 +320,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { } }, hir::StmtKind::Semi(semi_expr) => { - if has_drop(semi_expr, apa.first_bind_ident, self.cx) { + if has_drop(self.cx, semi_expr, apa.first_bind_ident) { apa.has_expensive_expr_after_last_attr = false; apa.last_stmt_span = DUMMY_SP; return; @@ -416,11 +417,11 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> { } } -fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: Option, lcx: &LateContext<'_>) -> bool { +fn has_drop(cx: &LateContext<'_>, expr: &hir::Expr<'_>, first_bind_ident: Option) -> bool { if let hir::ExprKind::Call(fun, [first_arg]) = expr.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind && let Res::Def(DefKind::Fn, did) = fun_path.res - && lcx.tcx.is_diagnostic_item(sym::mem_drop, did) + && cx.tcx.is_diagnostic_item(sym::mem_drop, did) { let has_ident = |local_expr: &hir::Expr<'_>| { if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind diff --git a/clippy_lints/src/single_char_lifetime_names.rs b/clippy_lints/src/single_char_lifetime_names.rs index 8c34da0d14a4d..595c75acf031f 100644 --- a/clippy_lints/src/single_char_lifetime_names.rs +++ b/clippy_lints/src/single_char_lifetime_names.rs @@ -40,8 +40,8 @@ declare_clippy_lint! { declare_lint_pass!(SingleCharLifetimeNames => [SINGLE_CHAR_LIFETIME_NAMES]); impl EarlyLintPass for SingleCharLifetimeNames { - fn check_generic_param(&mut self, ctx: &EarlyContext<'_>, param: &GenericParam) { - if param.ident.span.in_external_macro(ctx.sess().source_map()) { + fn check_generic_param(&mut self, cx: &EarlyContext<'_>, param: &GenericParam) { + if param.ident.span.in_external_macro(cx.sess().source_map()) { return; } @@ -51,7 +51,7 @@ impl EarlyLintPass for SingleCharLifetimeNames { { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( - ctx, + cx, SINGLE_CHAR_LIFETIME_NAMES, param.ident.span, "single-character lifetime names are likely uninformative", diff --git a/clippy_lints/src/single_option_map.rs b/clippy_lints/src/single_option_map.rs index cc497c97a4725..4556d287711fe 100644 --- a/clippy_lints/src/single_option_map.rs +++ b/clippy_lints/src/single_option_map.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{path_res, peel_blocks}; +use clippy_utils::peel_blocks; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; @@ -55,10 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for SingleOptionMap { if let ExprKind::MethodCall(method_name, callee, args, _span) = func_body.kind && method_name.ident.name == sym::map && let callee_type = cx.typeck_results().expr_ty(callee) - && is_type_diagnostic_item(cx, callee_type, sym::Option) + && callee_type.is_diag_item(cx, sym::Option) && let ExprKind::Path(_path) = callee.kind - && let Res::Local(_id) = path_res(cx, callee) - && matches!(path_res(cx, callee), Res::Local(_id)) + && matches!(callee.basic_res(), Res::Local(_)) && !matches!(args[0].kind, ExprKind::Path(_)) { if let ExprKind::Closure(closure) = args[0].kind { @@ -71,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for SingleOptionMap { } else if let ExprKind::MethodCall(_segment, receiver, method_args, _span) = value.kind && matches!(receiver.kind, ExprKind::Path(_)) && method_args.iter().all(|arg| matches!(arg.kind, ExprKind::Path(_))) - && method_args.iter().all(|arg| matches!(path_res(cx, arg), Res::Local(_))) + && method_args.iter().all(|arg| matches!(arg.basic_res(), Res::Local(_))) { return; } diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index 606e852aae9e3..bf304ebcfdc05 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_def_id; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::peel_and_count_ty_refs; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -57,8 +57,7 @@ declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]); impl LateLintPass<'_> for SizeOfRef { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Call(path, [arg]) = expr.kind - && let Some(def_id) = path_def_id(cx, path) - && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) + && path.basic_res().is_diag_item(cx, sym::mem_size_of_val) && let arg_ty = cx.typeck_results().expr_ty(arg) && peel_and_count_ty_refs(arg_ty).1 > 1 { diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index f497d0700b8eb..b25fa0905feb8 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,10 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; 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, sym, -}; +use clippy_utils::{SpanlessEq, get_enclosing_block, is_integer_literal, 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}; @@ -102,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Matches initialization on reassignments. For example: `vec = Vec::with_capacity(100)` if let ExprKind::Assign(left, right, _) = expr.kind - && let Some(local_id) = path_to_local(left) + && let Some(local_id) = left.res_local_id() && let Some(size_expr) = Self::as_vec_initializer(cx, right) { let vi = VecAllocation { @@ -149,10 +147,10 @@ impl SlowVectorInit { } if let ExprKind::Call(func, [len_expr]) = expr.kind - && is_path_diagnostic_item(cx, func, sym::vec_with_capacity) + && func.ty_rel_def(cx).is_diag_item(cx, sym::vec_with_capacity) { Some(InitializedSize::Initialized(len_expr)) - } else if matches!(expr.kind, ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::vec_new)) { + } else if matches!(expr.kind, ExprKind::Call(func, []) if func.ty_rel_def(cx).is_diag_item(cx, sym::vec_new)) { Some(InitializedSize::Uninitialized) } else { None @@ -246,7 +244,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { 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) + && self_arg.res_local_id() == Some(self.vec_alloc.local_id) && path.ident.name == sym::extend && self.is_repeat_take(extend_arg) { @@ -258,7 +256,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'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) + && self_arg.res_local_id() == Some(self.vec_alloc.local_id) && path.ident.name == sym::resize // Check that is filled with 0 && is_integer_literal(fill_arg, 0) @@ -301,7 +299,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { /// Returns `true` if given expression is `repeat(0)` fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind - && is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat) + && fn_expr.basic_res().is_diag_item(self.cx, sym::iter_repeat) && is_integer_literal(repeat_arg, 0) { true diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index f63e6b3087b9e..e5347bf3e8f07 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -5,9 +5,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet, str_literal_to_char_literal}; +use clippy_utils::sym; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{path_to_local_id, sym}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; @@ -146,12 +147,12 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> { match sub_expr.kind { ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => { - if path_to_local_id(left, binding) + if left.res_local_id() == Some(binding) && let Some(span) = get_char_span(cx, right) { set_char_spans.push(span); ControlFlow::Continue(Descend::No) - } else if path_to_local_id(right, binding) + } else if right.res_local_id() == Some(binding) && let Some(span) = get_char_span(cx, left) { set_char_spans.push(span); @@ -164,7 +165,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< ExprKind::Match(match_value, [arm, _], _) => { if matching_root_macro_call(cx, sub_expr.span, sym::matches_macro).is_none() || arm.guard.is_some() - || !path_to_local_id(match_value, binding) + || match_value.res_local_id() != Some(binding) { return ControlFlow::Break(()); } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 57d5900b045ea..47306949a6990 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{ - SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, path_def_id, - peel_blocks, sym, + SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -188,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { }, ExprKind::Index(target, _idx, _) => { let e_ty = cx.typeck_results().expr_ty_adjusted(target).peel_refs(); - if e_ty.is_str() || is_type_lang_item(cx, e_ty, LangItem::String) { + if e_ty.is_str() || e_ty.is_lang_item(cx, LangItem::String) { span_lint( cx, STRING_SLICE, @@ -203,7 +202,10 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) + cx.typeck_results() + .expr_ty(e) + .peel_refs() + .is_lang_item(cx, LangItem::String) } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { @@ -253,7 +255,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if let ExprKind::Call(fun, [bytes_arg]) = e.kind // Find `std::str::converts::from_utf8` or `std::primitive::str::from_utf8` && let Some(sym::str_from_utf8 | sym::str_inherent_from_utf8) = - path_def_id(cx, fun).and_then(|id| cx.tcx.get_diagnostic_name(id)) + fun.res(cx).opt_diag_name(cx) // Find string::as_bytes && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = bytes_arg.kind diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index 33856c750d7e9..58d692db5029d 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::is_expr_unsafe; use clippy_utils::{match_libc_symbol, sym}; use rustc_errors::Applicability; @@ -61,9 +61,9 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); let mut app = Applicability::MachineApplicable; let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; - let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) { + let method_name = if ty.is_diag_item(cx, sym::cstring_type) { "as_bytes" - } else if is_type_lang_item(cx, ty, LangItem::CStr) { + } else if ty.is_lang_item(cx, LangItem::CStr) { "to_bytes" } else { return; diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 76ab3cdae22ef..c3cb2c09752fe 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_indent, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, path_to_local, std_or_core}; +use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core}; use itertools::Itertools; use rustc_data_structures::fx::FxIndexSet; @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for Swap { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn generate_swap_warning<'tcx>( block: &'tcx Block<'tcx>, cx: &LateContext<'tcx>, @@ -110,8 +110,8 @@ fn generate_swap_warning<'tcx>( if matches!(ty.kind(), ty::Slice(_)) || matches!(ty.kind(), ty::Array(_, _)) - || is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_diagnostic_item(cx, ty, sym::VecDeque) + || ty.is_diag_item(cx, sym::Vec) + || ty.is_diag_item(cx, sym::VecDeque) { let slice = Sugg::hir_with_applicability(cx, lhs1, "", &mut applicability); @@ -361,7 +361,8 @@ impl<'tcx> IndexBinding<'_, 'tcx> { // - Variable declaration is outside the suggestion span // - Variable is not used as an index or elsewhere later if !self.suggest_span.contains(init.span) - || path_to_local(expr) + || expr + .res_local_id() .is_some_and(|hir_id| !self.suggest_span.contains(self.cx.tcx.hir_span(hir_id))) || !self.is_used_other_than_swapping(first_segment.ident) { diff --git a/clippy_lints/src/swap_ptr_to_ref.rs b/clippy_lints/src/swap_ptr_to_ref.rs index ff196355a2e37..339c97d575aee 100644 --- a/clippy_lints/src/swap_ptr_to_ref.rs +++ b/clippy_lints/src/swap_ptr_to_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_def_id; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp}; @@ -41,8 +41,7 @@ declare_lint_pass!(SwapPtrToRef => [SWAP_PTR_TO_REF]); impl LateLintPass<'_> for SwapPtrToRef { fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { if let ExprKind::Call(fn_expr, [arg1, arg2]) = e.kind - && let Some(fn_id) = path_def_id(cx, fn_expr) - && cx.tcx.is_diagnostic_item(sym::mem_swap, fn_id) + && fn_expr.basic_res().is_diag_item(cx, sym::mem_swap) && let ctxt = e.span.ctxt() && let (from_ptr1, arg1_span) = is_ptr_to_ref(cx, arg1, ctxt) && let (from_ptr2, arg2_span) = is_ptr_to_ref(cx, arg2, ctxt) diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index fde8c3d9a9a76..dbd4ec77fd5f9 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_path_diagnostic_item, ty}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -96,16 +96,16 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { let lhs_ty = typeck.expr_ty(lhs); let rhs_ty = typeck.expr_ty(rhs); - if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Instant) { + if lhs_ty.is_diag_item(cx, sym::Instant) { // Instant::now() - instant if is_instant_now_call(cx, lhs) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) + && rhs_ty.is_diag_item(cx, sym::Instant) && let Some(sugg) = Sugg::hir_opt(cx, rhs) { print_manual_instant_elapsed_sugg(cx, expr, sugg); } // instant - duration - else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + else if rhs_ty.is_diag_item(cx, sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { @@ -122,8 +122,8 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); } } - } else if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Duration) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + } else if lhs_ty.is_diag_item(cx, sym::Duration) + && rhs_ty.is_diag_item(cx, sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { @@ -146,7 +146,7 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, []) = expr_block.kind - && is_path_diagnostic_item(cx, fn_expr, sym::instant_now) + && cx.ty_based_def(fn_expr).is_diag_item(cx, sym::instant_now) { true } else { @@ -170,7 +170,7 @@ fn is_chained_time_subtraction(cx: &LateContext<'_>, lhs: &Expr<'_>) -> bool { /// Returns true if the type is Duration or Instant fn is_time_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - ty::is_type_diagnostic_item(cx, ty, sym::Duration) || ty::is_type_diagnostic_item(cx, ty, sym::Instant) + ty.is_diag_item(cx, sym::Duration) || ty.is_diag_item(cx, sym::Instant) } fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { @@ -194,7 +194,7 @@ fn print_unchecked_duration_subtraction_sugg( let typeck = cx.typeck_results(); let left_ty = typeck.expr_ty(left_expr); - let lint_msg = if ty::is_type_diagnostic_item(cx, left_ty, sym::Instant) { + let lint_msg = if left_ty.is_diag_item(cx, sym::Instant) { "unchecked subtraction of a 'Duration' from an 'Instant'" } else { "unchecked subtraction between 'Duration' values" diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 3e847543e1c16..71c4172c98528 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_in_const_context, is_path_diagnostic_item, sym}; +use clippy_utils::{is_in_const_context, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -62,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { } }, hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => { - if is_path_diagnostic_item(cx, to_digits_call, sym::char_to_digit) { + if to_digits_call.res(cx).is_diag_item(cx, sym::char_to_digit) { Some((false, char_arg, radix_arg)) } else { None diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 9182a55081f40..352b8526b0217 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -238,7 +238,7 @@ impl TraitBounds { } } - #[allow(clippy::mutable_key_type)] + #[expect(clippy::mutable_key_type)] fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) { struct SpanlessTy<'cx, 'tcx> { ty: &'tcx Ty<'tcx>, diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index 7acf3be51fb7e..e109b1c50e227 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use clippy_utils::is_integer_literal; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -40,7 +41,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t }, // Catching: // `std::mem::transmute(std::ptr::null::())` - ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => { + ExprKind::Call(func1, []) if func1.basic_res().is_diag_item(cx, sym::ptr_null) => { lint_expr(cx, expr); true }, diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index 544014bd32b30..1a6262f2ff767 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use clippy_utils::is_integer_literal; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -35,7 +36,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching: // `std::mem::transmute(std::ptr::null::())` if let ExprKind::Call(func1, []) = arg.kind - && is_path_diagnostic_item(cx, func1, sym::ptr_null) + && func1.basic_res().is_diag_item(cx, sym::ptr_null) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index 3c21d194b81d5..5d0945bece553 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{is_from_proc_macro, path_to_local}; use itertools::Itertools; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind, Node, PatKind}; @@ -152,7 +153,7 @@ fn all_bindings_are_for_conv<'tcx>( locals: &[&Expr<'_>], kind: ToType, ) -> bool { - let Some(locals) = locals.iter().map(|e| path_to_local(e)).collect::>>() else { + let Some(locals) = locals.iter().map(|e| e.res_local_id()).collect::>>() else { return false; }; let local_parents = locals.iter().map(|l| cx.tcx.parent_hir_node(*l)).collect::>(); diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index 24fe4e08a5b44..985057cd6734b 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -33,7 +34,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let param = qpath_generic_tys(qpath).next()?; - let id = path_def_id(cx, param)?; + let id = param.basic_res().opt_def_id()?; cx.tcx .get_diagnostic_name(id) .filter(|&name| { diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 515be5adeed0b..ccb027f77bf5f 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -693,7 +693,7 @@ impl Types { } } -#[allow(clippy::struct_excessive_bools, clippy::struct_field_names)] +#[expect(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Default)] struct CheckTyContext { is_in_trait_impl: bool, diff --git a/clippy_lints/src/types/option_option.rs b/clippy_lints/src/types/option_option.rs index d12d14f2b1413..10df007f2a13d 100644 --- a/clippy_lints/src/types/option_option.rs +++ b/clippy_lints/src/types/option_option.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ use super::OPTION_OPTION; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { if cx.tcx.is_diagnostic_item(sym::Option, def_id) && let Some(arg) = qpath_generic_tys(qpath).next() - && path_def_id(cx, arg) == Some(def_id) + && arg.basic_res().opt_def_id() == Some(def_id) { span_lint( cx, diff --git a/clippy_lints/src/types/owned_cow.rs b/clippy_lints/src/types/owned_cow.rs index 8933994d18558..0eef373be04ea 100644 --- a/clippy_lints/src/types/owned_cow.rs +++ b/clippy_lints/src/types/owned_cow.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -30,10 +31,10 @@ pub(super) fn check(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, def_id: DefId) } fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String)> { - if clippy_utils::is_path_lang_item(cx, cty, hir::LangItem::String) { + if cty.basic_res().is_lang_item(cx, hir::LangItem::String) { return Some((cty.span, "str".into())); } - if clippy_utils::is_path_diagnostic_item(cx, cty, sym::Vec) { + if cty.basic_res().is_diag_item(cx, sym::Vec) { return if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = cty.kind && let [.., last_seg] = path.segments && let Some(args) = last_seg.args @@ -45,7 +46,7 @@ fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String) None }; } - if clippy_utils::is_path_diagnostic_item(cx, cty, sym::cstring_type) { + if cty.basic_res().is_diag_item(cx, sym::cstring_type) { return Some(( cty.span, (if clippy_utils::is_no_std_crate(cx) { @@ -58,7 +59,7 @@ fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String) } // Neither OsString nor PathBuf are available outside std for (diag, repl) in [(sym::OsString, "std::ffi::OsStr"), (sym::PathBuf, "std::path::Path")] { - if clippy_utils::is_path_diagnostic_item(cx, cty, diag) { + if cty.basic_res().is_diag_item(cx, diag) { return Some((cty.span, repl.into())); } } diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index c4fd0fbf87a90..46d9febb187fc 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath, TyKind}; @@ -28,8 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ let Some(ty) = qpath_generic_tys(qpath).next() else { return false; }; - let Some(id) = path_def_id(cx, ty) else { return false }; - if !cx.tcx.is_diagnostic_item(sym::Vec, id) { + if !ty.basic_res().is_diag_item(cx, sym::Vec) { return false; } let TyKind::Path(qpath) = &ty.kind else { return false }; @@ -70,8 +70,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { - let Some(id) = path_def_id(cx, ty) else { return false }; - if !cx.tcx.is_diagnostic_item(sym::Vec, id) { + if !ty.basic_res().is_diag_item(cx, sym::Vec) { return false; } let TyKind::Path(qpath) = &ty.kind else { return false }; @@ -106,7 +105,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { let ty = qpath_generic_tys(qpath).next()?; - let id = path_def_id(cx, ty)?; + let id = ty.basic_res().opt_def_id()?; let path = match cx.tcx.get_diagnostic_name(id) { Some(sym::OsString) => "std::ffi::OsStr", Some(sym::PathBuf) => "std::path::Path", diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index 7b13debc01ef8..e3d536822d5f8 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -10,8 +11,7 @@ use super::RC_MUTEX; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { if cx.tcx.is_diagnostic_item(sym::Rc, def_id) && let Some(arg) = qpath_generic_tys(qpath).next() - && let Some(id) = path_def_id(cx, arg) - && cx.tcx.is_diagnostic_item(sym::Mutex, id) + && arg.basic_res().is_diag_item(cx, sym::Mutex) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, RC_MUTEX, hir_ty.span, "usage of `Rc>`", |diag| { diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 0ba51daf027d1..dbae2cc335165 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath, TyKind}; @@ -40,7 +41,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: let Some(ty) = qpath_generic_tys(qpath).next() else { return false; }; - let Some(id) = path_def_id(cx, ty) else { return false }; + let Some(id) = ty.basic_res().opt_def_id() else { + return false; + }; let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) { Some(sym::Arc) => ("Arc", ty), Some(sym::Rc) => ("Rc", ty), diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index e843e169113e7..e6663182fec53 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{expr_or_init, fn_def_id_with_node_args, path_def_id}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{expr_or_init, fn_def_id_with_node_args}; use rustc_ast::BinOpKind; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; @@ -317,7 +318,7 @@ where if let ExprKind::Call(f, _) = expr.kind && let ExprKind::Path(qpath) = f.kind && is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id) - && let Some(method_def_id) = path_def_id(self.cx, f) + && let Some(method_def_id) = f.res(self.cx).opt_def_id() && let Some(trait_def_id) = self.cx.tcx.trait_of_assoc(method_def_id) && self.cx.tcx.is_diagnostic_item(sym::Default, trait_def_id) { diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 751e9b0034277..9afa9d65c2613 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -7,8 +7,8 @@ use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; use clippy_utils::visitors::{Descend, for_each_expr}; use hir::HirId; -use rustc_hir as hir; -use rustc_hir::{Block, BlockCheckMode, Impl, ItemKind, Node, UnsafeSource}; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, Block, BlockCheckMode, FnSig, Impl, ItemKind, Node, UnsafeSource}; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { && !block.span.in_external_macro(cx.tcx.sess.source_map()) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) && !is_unsafe_from_proc_macro(cx, block.span) - && !block_has_safety_comment(cx, block.span) + && !block_has_safety_comment(cx, block.span, self.accept_comment_above_attributes) && !block_parents_have_safety_comment( self.accept_comment_above_statement, self.accept_comment_above_attributes, @@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { if let Some(tail) = block.expr && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id) && !tail.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = + && let HasSafetyComment::Yes(pos, _) = stmt_has_safety_comment(cx, tail.span, tail.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) { @@ -168,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { }; if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = + && let HasSafetyComment::Yes(pos, _) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { @@ -191,8 +191,12 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { let mk_spans = |pos: BytePos| { let source_map = cx.tcx.sess.source_map(); - let span = Span::new(pos, pos, SyntaxContext::root(), None); - let help_span = source_map.span_extend_to_next_char(span, '\n', true); + let help_span = Span::new( + pos, + pos + BytePos(u32::try_from("SAFETY:".len()).unwrap()), + SyntaxContext::root(), + None, + ); let span = if source_map.is_multiline(item.span) { source_map.span_until_char(item.span, '\n') } else { @@ -201,16 +205,16 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { (span, help_span) }; - let item_has_safety_comment = item_has_safety_comment(cx, item); + let item_has_safety_comment = item_has_safety_comment(cx, item, self.accept_comment_above_attributes); match item_has_safety_comment { - HasSafetyComment::Yes(pos) => check_has_safety_comment(cx, item, mk_spans(pos)), + HasSafetyComment::Yes(pos, is_doc) => check_has_safety_comment(cx, item, mk_spans(pos), is_doc), HasSafetyComment::No => check_has_no_safety_comment(cx, item), HasSafetyComment::Maybe => {}, } } } -fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, help_span): (Span, Span)) { +fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, help_span): (Span, Span), is_doc: bool) { match &item.kind { ItemKind::Impl(Impl { of_trait: Some(of_trait), @@ -252,6 +256,40 @@ fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, h } } }, + // Unsafe functions with a SAFETY comment are suggested to change it to a `# Safety` comment + ItemKind::Fn { + sig: FnSig { header, .. }, + .. + } if header.is_unsafe() => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { + span_lint_and_then( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + format!( + "{} has unnecessary safety comment", + cx.tcx.def_descr(item.owner_id.to_def_id()), + ), + |diag| { + if is_doc { + // If it's already within a doc comment, we try to suggest the change + + diag.span_suggestion( + help_span, + "consider changing it to a `# Safety` section", + "# Safety", + Applicability::MachineApplicable, + ); + } else { + diag.span_help( + help_span, + "consider changing the `safety` comment for a `# Safety` doc comment", + ); + } + }, + ); + } + }, // Aside from unsafe impls and consts/statics with an unsafe block, items in general // do not have safety invariants that need to be documented, so lint those. _ => { @@ -272,6 +310,7 @@ fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, h }, } } + fn check_has_no_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) { if let ItemKind::Impl(Impl { of_trait: Some(of_trait), @@ -407,21 +446,21 @@ fn block_parents_have_safety_comment( cx: &LateContext<'_>, id: HirId, ) -> bool { - let (span, hir_id) = match cx.tcx.parent_hir_node(id) { - Node::Expr(expr) if let Some(inner) = find_unsafe_block_parent_in_expr(cx, expr) => inner, + let span = match cx.tcx.parent_hir_node(id) { + Node::Expr(expr) if let Some((span, _)) = find_unsafe_block_parent_in_expr(cx, expr) => span, Node::Stmt(hir::Stmt { kind: - hir::StmtKind::Let(hir::LetStmt { span, hir_id, .. }) - | hir::StmtKind::Expr(hir::Expr { span, hir_id, .. }) - | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }), + hir::StmtKind::Let(hir::LetStmt { span, .. }) + | hir::StmtKind::Expr(hir::Expr { span, .. }) + | hir::StmtKind::Semi(hir::Expr { span, .. }), .. }) - | Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), + | Node::LetStmt(hir::LetStmt { span, .. }) => *span, - node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node) + node if let Some((span, _)) = span_and_hid_of_item_alike_node(&node) && is_const_or_static(&node) => { - (span, hir_id) + span }, _ => return false, @@ -429,24 +468,7 @@ fn block_parents_have_safety_comment( // if unsafe block is part of a let/const/static statement, // and accept_comment_above_statement is set to true // we accept the safety comment in the line the precedes this statement. - accept_comment_above_statement - && span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes) -} - -/// Extends `span` to also include its attributes, then checks if that span has a safety comment. -fn span_with_attrs_has_safety_comment( - cx: &LateContext<'_>, - span: Span, - hir_id: HirId, - accept_comment_above_attributes: bool, -) -> bool { - let span = if accept_comment_above_attributes { - include_attrs_in_span(cx, hir_id, span) - } else { - span - }; - - span_has_safety_comment(cx, span) + accept_comment_above_statement && span_has_safety_comment(cx, span, accept_comment_above_attributes) } /// Checks if an expression is "branchy", e.g. loop, match/if/etc. @@ -458,7 +480,7 @@ fn is_branchy(expr: &hir::Expr<'_>) -> bool { } /// Checks if the lines immediately preceding the block contain a safety comment. -fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn block_has_safety_comment(cx: &LateContext<'_>, span: Span, accept_comment_above_attributes: bool) -> bool { // This intentionally ignores text before the start of a function so something like: // ``` // // SAFETY: reason @@ -468,30 +490,25 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // attributes and doc comments. matches!( - span_from_macro_expansion_has_safety_comment(cx, span), - HasSafetyComment::Yes(_) - ) || span_has_safety_comment(cx, span) -} - -fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span { - span.to(cx.tcx.hir_attrs(hir_id).iter().fold(span, |acc, attr| { - if attr.is_doc_comment().is_some() { - return acc; - } - acc.to(attr.span()) - })) + span_from_macro_expansion_has_safety_comment(cx, span, accept_comment_above_attributes), + HasSafetyComment::Yes(_, _) + ) || span_has_safety_comment(cx, span, accept_comment_above_attributes) } +#[derive(Debug)] enum HasSafetyComment { - Yes(BytePos), + Yes(BytePos, bool), No, Maybe, } /// Checks if the lines immediately preceding the item contain a safety comment. -#[allow(clippy::collapsible_match)] -fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment { - match span_from_macro_expansion_has_safety_comment(cx, item.span) { +fn item_has_safety_comment( + cx: &LateContext<'_>, + item: &hir::Item<'_>, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, item.span, accept_comment_above_attributes) { HasSafetyComment::Maybe => (), has_safety_comment => return has_safety_comment, } @@ -536,29 +553,26 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf return if comment_start_line.line >= unsafe_line.line { HasSafetyComment::No } else { - match text_has_safety_comment( + text_has_safety_comment( src, &unsafe_line.sf.lines() [(comment_start_line.line + usize::from(!include_first_line_of_file))..=unsafe_line.line], unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + accept_comment_above_attributes, + ) }; } HasSafetyComment::Maybe } /// Checks if the lines immediately preceding the item contain a safety comment. -#[allow(clippy::collapsible_match)] fn stmt_has_safety_comment( cx: &LateContext<'_>, span: Span, hir_id: HirId, accept_comment_above_attributes: bool, ) -> HasSafetyComment { - match span_from_macro_expansion_has_safety_comment(cx, span) { + match span_from_macro_expansion_has_safety_comment(cx, span, accept_comment_above_attributes) { HasSafetyComment::Maybe => (), has_safety_comment => return has_safety_comment, } @@ -572,13 +586,6 @@ fn stmt_has_safety_comment( _ => return HasSafetyComment::Maybe, }; - // if span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attrib - // } - let mut span = span; - if accept_comment_above_attributes { - span = include_attrs_in_span(cx, hir_id, span); - } - let source_map = cx.sess().source_map(); if let Some(comment_start) = comment_start && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) @@ -589,14 +596,12 @@ fn stmt_has_safety_comment( return if comment_start_line.line >= unsafe_line.line { HasSafetyComment::No } else { - match text_has_safety_comment( + text_has_safety_comment( src, &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + accept_comment_above_attributes, + ) }; } HasSafetyComment::Maybe @@ -649,7 +654,11 @@ fn comment_start_before_item_in_mod( }) } -fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment { +fn span_from_macro_expansion_has_safety_comment( + cx: &LateContext<'_>, + span: Span, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); if ctxt == SyntaxContext::root() { @@ -665,14 +674,12 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span && let Some(src) = unsafe_line.sf.src.as_deref() { if macro_line.line < unsafe_line.line { - match text_has_safety_comment( + text_has_safety_comment( src, &unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + accept_comment_above_attributes, + ) } else { HasSafetyComment::No } @@ -715,7 +722,7 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option { None } -fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn span_has_safety_comment(cx: &LateContext<'_>, span: Span, accept_comment_above_attributes: bool) -> bool { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); if ctxt.is_root() @@ -731,12 +738,15 @@ fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } // ^-------------^ body_line.line < unsafe_line.line - && text_has_safety_comment( - src, - &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, + && matches!( + text_has_safety_comment( + src, + &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + accept_comment_above_attributes, + ), + HasSafetyComment::Yes(..) ) - .is_some() } else { // Problem getting source text. Pretend a comment was found. true @@ -747,7 +757,15 @@ fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { } /// Checks if the given text has a safety comment for the immediately proceeding line. -fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos: BytePos) -> Option { +/// +/// If `accept_comment_above_attributes` is true, it will ignore attributes inbetween blocks of +/// comments +fn text_has_safety_comment( + src: &str, + line_starts: &[RelativeBytePos], + start_pos: BytePos, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { let mut lines = line_starts .array_windows::<2>() .rev() @@ -758,9 +776,12 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos let trimmed = text.trim_start(); Some((start + (text.len() - trimmed.len()), trimmed)) }) - .filter(|(_, text)| !text.is_empty()); + .filter(|(_, text)| !(text.is_empty() || (accept_comment_above_attributes && is_attribute(text)))); + + let Some((line_start, line)) = lines.next() else { + return HasSafetyComment::No; + }; - let (line_start, line) = lines.next()?; let mut in_codeblock = false; // Check for a sequence of line comments. if line.starts_with("//") { @@ -773,12 +794,17 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos in_codeblock = !in_codeblock; } - if line.to_ascii_uppercase().contains("SAFETY:") && !in_codeblock { - return Some(start_pos + BytePos(u32::try_from(line_start).unwrap())); + if !in_codeblock && let Some(safety_pos) = line.to_ascii_uppercase().find("SAFETY:") { + return HasSafetyComment::Yes( + start_pos + + BytePos(u32::try_from(line_start).unwrap()) + + BytePos(u32::try_from(safety_pos).unwrap()), + line.starts_with("///"), + ); } match lines.next() { Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s), - _ => return None, + _ => return HasSafetyComment::No, } } } @@ -789,19 +815,30 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos if line.starts_with("/*") { let src = &src[line_start..line_starts.last().unwrap().to_usize()]; let mut tokens = tokenize(src, FrontmatterAllowed::No); - return (src[..tokens.next().unwrap().len as usize] - .to_ascii_uppercase() - .contains("SAFETY:") - && tokens.all(|t| t.kind == TokenKind::Whitespace)) - .then_some(start_pos + BytePos(u32::try_from(line_start).unwrap())); + let a = tokens.next(); + if let Some(safety_pos) = src[..a.unwrap().len as usize].to_ascii_uppercase().find("SAFETY:") + && tokens.all(|t| t.kind == TokenKind::Whitespace) + { + return HasSafetyComment::Yes( + start_pos + + BytePos(u32::try_from(line_start).unwrap()) + + BytePos(u32::try_from(safety_pos).unwrap()), + line.starts_with("/**"), + ); + } + return HasSafetyComment::No; } match lines.next() { Some(x) => (line_start, line) = x, - None => return None, + None => return HasSafetyComment::No, } } } +fn is_attribute(text: &str) -> bool { + (text.starts_with("#[") || text.starts_with("#![")) && text.trim_end().ends_with(']') +} + fn span_and_hid_of_item_alike_node(node: &Node<'_>) -> Option<(Span, HirId)> { match node { Node::Item(item) => Some((item.span, item.owner_id.into())), diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 51116b5eba9e1..df06982904b37 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; 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, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::ty::is_uninit_value_valid_for_ty; +use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, 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; @@ -139,7 +140,7 @@ enum VecLocation<'tcx> { impl<'tcx> VecLocation<'tcx> { pub fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { match self { - VecLocation::Local(hir_id) => path_to_local_id(expr, hir_id), + VecLocation::Local(hir_id) => expr.res_local_id() == Some(hir_id), VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr), } } @@ -183,7 +184,10 @@ 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) + cx.typeck_results() + .expr_ty(self_expr) + .peel_refs() + .is_diag_item(cx, sym::Vec) && path.ident.name == sym::reserve } @@ -205,10 +209,7 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt match expr.kind { 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 == sym::set_len - && !is_integer_literal(arg, 0) - { + if self_type.is_diag_item(cx, sym::Vec) && path.ident.name == sym::set_len && !is_integer_literal(arg, 0) { Some((self_expr, expr.span)) } else { None diff --git a/clippy_lints/src/unnecessary_literal_bound.rs b/clippy_lints/src/unnecessary_literal_bound.rs index 9f107fbeec03a..2603884440d16 100644 --- a/clippy_lints/src/unnecessary_literal_bound.rs +++ b/clippy_lints/src/unnecessary_literal_bound.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLiteralBound { return; }; - if path_res(cx, inner_hir_ty) != Res::PrimTy(PrimTy::Str) { + if !matches!(inner_hir_ty.basic_res(), Res::PrimTy(PrimTy::Str)) { return; } diff --git a/clippy_lints/src/unnecessary_map_on_constructor.rs b/clippy_lints/src/unnecessary_map_on_constructor.rs index d3700d05b014b..94b1a34455ff0 100644 --- a/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { return; } if let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind - && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)) + && let Some(sym::Option | sym::Result) = cx.typeck_results().expr_ty(recv).opt_diag_name(cx) { let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, [arg, ..]) = recv.kind && let hir::ExprKind::Path(constructor_path) = constructor.kind diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 28f4884fa3110..0388450c9f7e2 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { && let LitKind::Str(symbol, _) = spanned.node && symbol.is_empty() && let inner_expr_type = cx.typeck_results().expr_ty(inner_expr) - && is_type_lang_item(cx, inner_expr_type, LangItem::String) + && inner_expr_type.is_lang_item(cx, LangItem::String) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/unnecessary_struct_initialization.rs b/clippy_lints/src/unnecessary_struct_initialization.rs index 5792b6b3178d7..51596859e4c7b 100644 --- a/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/clippy_lints/src/unnecessary_struct_initialization.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; -use clippy_utils::{get_parent_expr, is_mutable, path_to_local}; +use clippy_utils::{get_parent_expr, is_mutable}; use rustc_hir::{Expr, ExprField, ExprKind, Path, QPath, StructTailExpr, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -162,7 +163,7 @@ fn check_references(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) && parent_ty.is_any_ptr() { - if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && path_to_local(expr_b).is_some() { + if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && expr_b.res_local_id().is_some() { // When the type implements `Copy`, a reference to the new struct works on the // copy. Using the original would borrow it. return false; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 849c0b438a5a0..29747cf0e447a 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -2,9 +2,10 @@ use std::borrow::Cow; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty}; +use clippy_utils::{contains_return, return_ty}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::intravisit::FnKind; @@ -122,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if !ret_expr.span.from_expansion() // Check if a function call. && let ExprKind::Call(func, [arg]) = ret_expr.kind - && is_res_lang_ctor(cx, path_res(cx, func), lang_item) + && func.res(cx).ctor_parent(cx).is_lang_item(cx, lang_item) // Make sure the function argument does not contain a return expression. && !contains_return(arg) { diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index af3ad4566c46a..cff798f7a89a0 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,6 +1,7 @@ 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, paths, peel_blocks, sym}; +use clippy_utils::res::MaybeDef; +use clippy_utils::{paths, peel_blocks, sym}; use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -136,7 +137,10 @@ fn non_consuming_err_arm<'a>(cx: &LateContext<'a>, arm: &hir::Arm<'a>) -> bool { } if let PatKind::TupleStruct(ref path, [inner_pat], _) = arm.pat.kind { - return is_res_lang_ctor(cx, cx.qpath_res(path, inner_pat.hir_id), hir::LangItem::ResultErr); + return cx + .qpath_res(path, inner_pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, hir::LangItem::ResultErr); } false @@ -203,7 +207,7 @@ fn is_ok_wild_or_dotdot_pattern<'a>(cx: &LateContext<'a>, pat: &hir::Pat<'a>) -> if let PatKind::TupleStruct(ref path, inner_pat, _) = pat.kind // we check against Result::Ok to avoid linting on Err(_) or something else. - && is_res_lang_ctor(cx, cx.qpath_res(path, pat.hir_id), hir::LangItem::ResultOk) + && cx.qpath_res(path, pat.hir_id).ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) { if matches!(inner_pat, []) { return true; diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 5224b62e9fc71..668663e4cf794 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; -use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::ty::peel_and_count_ty_refs; +use clippy_utils::{fn_def_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; @@ -49,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { // Don't lint `Peekable`s returned from a block if let Some(expr) = block.expr && let Some(ty) = cx.typeck_results().expr_ty_opt(peel_ref_operators(cx, expr)) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { return; } @@ -62,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { && !init.span.from_expansion() && let Some(ty) = cx.typeck_results().expr_ty_opt(init) && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { let mut vis = PeekableVisitor::new(cx, binding); @@ -116,7 +117,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> ControlFlow<()> { - if path_to_local_id(ex, self.expected_hir_id) { + if ex.res_local_id() == Some(self.expected_hir_id) { for (_, node) in self.cx.tcx.hir_parent_iter(ex.hir_id) { match node { Node::Expr(expr) => { @@ -160,7 +161,11 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { // foo.some_method() excluding Iterator methods if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) - && !is_trait_method(self.cx, expr, sym::Iterator) + && !self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { return ControlFlow::Break(()); } @@ -212,7 +217,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { if let Some(ty) = cx.typeck_results().expr_ty_opt(arg) && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { true } else { diff --git a/clippy_lints/src/unused_result_ok.rs b/clippy_lints/src/unused_result_ok.rs index f5ed10fb76094..fe323a0b7b0c9 100644 --- a/clippy_lints/src/unused_result_ok.rs +++ b/clippy_lints/src/unused_result_ok.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -37,7 +37,7 @@ impl LateLintPass<'_> for UnusedResultOk { if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(ok_path, recv, [], ..) = expr.kind //check is expr.ok() has type Result.ok(, _) && ok_path.ident.name == sym::ok - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) && !stmt.span.in_external_macro(cx.sess().source_map()) { let ctxt = expr.span.ctxt(); diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index aee8028a75de7..99201a1ca215f 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,9 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::ty::get_type_diagnostic_name; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::usage::is_potentially_local_place; -use clippy_utils::{can_use_if_let_chains, higher, path_to_local, sym}; +use clippy_utils::{can_use_if_let_chains, higher, 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, UnOp}; @@ -147,7 +147,7 @@ fn collect_unwrap_info<'tcx>( is_entire_condition: bool, ) -> Vec> { fn option_or_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<(UnwrappableKind, bool)> { - match (get_type_diagnostic_name(cx, ty)?, method_name) { + match (ty.opt_diag_name(cx)?, method_name) { (sym::Option, sym::is_some) => Some((UnwrappableKind::Option, true)), (sym::Option, sym::is_none) => Some((UnwrappableKind::Option, false)), (sym::Result, sym::is_ok) => Some((UnwrappableKind::Result, true)), @@ -169,7 +169,7 @@ fn collect_unwrap_info<'tcx>( }, ExprKind::Unary(UnOp::Not, expr) => collect_unwrap_info(cx, if_expr, expr, branch, !invert, false), ExprKind::MethodCall(method_name, receiver, [], _) - if let Some(local_id) = path_to_local(receiver) + if let Some(local_id) = receiver.res_local_id() && let ty = cx.typeck_results().expr_ty(receiver) && let name = method_name.ident.name && let Some((kind, unwrappable)) = option_or_result_call(cx, ty, name) => @@ -318,7 +318,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { // find `unwrap[_err]()` or `expect("...")` calls: if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind && let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg) - && let Some(id) = path_to_local(self_arg) + && let Some(id) = self_arg.res_local_id() && matches!(method_name.ident.name, sym::unwrap | sym::expect | sym::unwrap_err) && let call_to_unwrap = matches!(method_name.ident.name, sym::unwrap | sym::expect) && let Some(unwrappable) = self.unwrappables.iter() diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 9d5be922f43f8..f46dedf1261e9 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -58,6 +58,7 @@ declare_clippy_lint! { pub struct UseSelf { msrv: Msrv, stack: Vec, + recursive_self_in_type_definitions: bool, } impl UseSelf { @@ -65,6 +66,7 @@ impl UseSelf { Self { msrv: conf.msrv, stack: Vec::new(), + recursive_self_in_type_definitions: conf.recursive_self_in_type_definitions, } } } @@ -84,10 +86,10 @@ const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { - // We push the self types of `impl`s on a stack here. Only the top type on the stack is - // relevant for linting, since this is the self type of the `impl` we're currently in. To - // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that - // we're in an `impl` or nested item, that we don't want to lint + // We push the self types of items on a stack here. Only the top type on the stack is + // relevant for linting, since this is the self type of the item we're currently in. To + // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal that + // we're in an item or nested item that we don't want to lint let stack_item = if let ItemKind::Impl(Impl { self_ty, generics, .. }) = item.kind && let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind && let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args @@ -112,6 +114,15 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { impl_id: item.owner_id.def_id, types_to_skip, } + } else if let ItemKind::Struct(..) | ItemKind::Enum(..) = item.kind + && self.recursive_self_in_type_definitions + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + { + StackItem::Check { + impl_id: item.owner_id.def_id, + types_to_skip: FxHashSet::default(), + } } else { StackItem::NoCheck }; diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1b137017ecb46..0cf5b9431a342 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_modulo_regions}; -use clippy_utils::{ - get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, -}; +use clippy_utils::ty::{is_copy, same_type_modulo_regions}; +use clippy_utils::{get_parent_expr, is_ty_alias, sym}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; @@ -133,7 +132,7 @@ fn into_iter_bound<'tcx>( /// Extracts the receiver of a `.into_iter()` method call. fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> { if let ExprKind::MethodCall(name, recv, [], _) = expr.kind - && is_trait_method(cx, expr, sym::IntoIterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && name.ident.name == sym::into_iter { Some(recv) @@ -181,7 +180,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { path.ident.name, sym::map | sym::map_err | sym::map_break | sym::map_continue ) && has_eligible_receiver(cx, recv, e) - && (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From)) + && matches!( + arg.res(cx).assoc_parent(cx).opt_diag_name(cx), + Some(sym::Into | sym::From) + ) && let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind() && let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice() && same_type_modulo_regions(from_ty, to_ty) @@ -204,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(name, recv, [], _) => { - if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into { + if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) && name.ident.name == sym::into { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_modulo_regions(a, b) { @@ -308,7 +310,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } - if let Some(id) = path_to_local(recv) + if let Some(id) = recv.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(id) && let PatKind::Binding(ann, ..) = pat.kind && ann != BindingMode::MUT @@ -364,11 +366,11 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if is_trait_method(cx, e, sym::TryInto) + if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) && name.ident.name == sym::try_into && let a = cx.typeck_results().expr_ty(e) && let b = cx.typeck_results().expr_ty(recv) - && is_type_diagnostic_item(cx, a, sym::Result) + && a.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() && same_type_modulo_regions(a_type, b) @@ -393,7 +395,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(arg); if name == sym::try_from_fn - && is_type_diagnostic_item(cx, a, sym::Result) + && a.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() && same_type_modulo_regions(a_type, b) @@ -439,13 +441,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool { - if is_inherent_method_call(cx, expr) { + if cx.ty_based_def(expr).opt_parent(cx).is_impl(cx) { matches!( - get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)), + cx.typeck_results().expr_ty(recv).opt_diag_name(cx), Some(sym::Option | sym::Result | sym::ControlFlow) ) } else { - is_trait_method(cx, expr, sym::Iterator) + cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) } } diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index ece29362a39f9..68e51dace2db2 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -1,4 +1,5 @@ -use clippy_utils::{MaybePath, get_attr, higher, path_def_id, sym}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{get_attr, higher, sym}; use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; @@ -205,7 +206,6 @@ struct PrintVisitor<'a, 'tcx> { first: Cell, } -#[allow(clippy::unused_self)] impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { @@ -269,16 +269,16 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } - fn qpath<'p>(&self, qpath: &Binding<&QPath<'_>>, has_hir_id: &Binding<&impl MaybePath<'p>>) { + fn qpath(&self, qpath: &Binding<&QPath<'_>>, hir_id_binding: &str, hir_id: HirId) { if let QPath::LangItem(lang_item, ..) = *qpath.value { chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); - } else if let Some(def_id) = self.cx.qpath_res(qpath.value, has_hir_id.value.hir_id()).opt_def_id() + } else if let Some(def_id) = self.cx.qpath_res(qpath.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()" + "let Some({def_id}) = cx.qpath_res({qpath}, {hir_id_binding}.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})"); @@ -292,14 +292,14 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } } - fn maybe_path<'p>(&self, path: &Binding<&impl MaybePath<'p>>) { - if let Some(id) = path_def_id(self.cx, path.value) + fn maybe_path<'p>(&self, path: &Binding>) { + if let Some(id) = path.value.res(self.cx).opt_def_id() && !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()); + chain!(self, "{path}.res(cx).is_lang_item(cx, 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})"); + chain!(self, "{path}.res(cx).is_diag_item(cx, sym::{name})"); } else { chain!( self, @@ -410,7 +410,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(field!(arm.body)); } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { if let Some(higher::While { condition, body, .. }) = higher::While::hir(expr.value) { bind!(self, condition, body); @@ -672,7 +672,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { StructTailExpr::None | StructTailExpr::DefaultFields(_) => None, }); kind!("Struct({qpath}, {fields}, {base})"); - self.qpath(qpath, expr); + self.qpath(qpath, &expr.name, expr.value.hir_id); self.slice(fields, |field| { self.ident(field!(field.ident)); self.expr(field!(field.expr)); @@ -758,7 +758,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { let ignore = etc.is_some(); bind!(self, qpath, fields); kind!("Struct(ref {qpath}, {fields}, {ignore})"); - self.qpath(qpath, pat); + self.qpath(qpath, &pat.name, pat.value.hir_id); self.slice(fields, |field| { self.ident(field!(field.ident)); self.pat(field!(field.pat)); @@ -772,7 +772,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, pat); + self.qpath(qpath, &pat.name, pat.value.hir_id); self.slice(fields, |pat| self.pat(pat)); }, PatKind::Tuple(fields, skip_pos) => { diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 52b30ddce12b8..b87db836869d6 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -10,7 +10,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment, sym}; +use clippy_utils::{get_parent_expr, higher, is_in_test, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -124,7 +124,7 @@ impl UselessVec { if let Some(parent) = get_parent_expr(cx, expr) && (adjusts_to_slice(cx, expr) || matches!(parent.kind, ExprKind::Index(..)) - || is_allowed_vec_method(cx, parent)) + || is_allowed_vec_method(parent)) { ControlFlow::Continue(()) } else { @@ -304,11 +304,11 @@ fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// Checks if the given expression is a method call to a `Vec` method /// that also exists on slices. If this returns true, it means that /// this expression does not actually require a `Vec` and could just work with an array. -pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { +pub fn is_allowed_vec_method(e: &Expr<'_>) -> bool { if let ExprKind::MethodCall(path, _, [], _) = e.kind { matches!(path.ident.name, sym::as_ptr | sym::is_empty | sym::len) } else { - is_trait_method(cx, e, sym::IntoIterator) + false } } diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 8d873536295dc..5d074208c029c 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, path_to_local_id, sym}; +use clippy_utils::{get_parent_expr, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -201,7 +202,7 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { if let Some(searcher) = self.searcher.take() { if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [_], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::push { self.searcher = Some(VecPushSearcher { diff --git a/clippy_lints/src/volatile_composites.rs b/clippy_lints/src/volatile_composites.rs new file mode 100644 index 0000000000000..6402c3ef72c41 --- /dev/null +++ b/clippy_lints/src/volatile_composites.rs @@ -0,0 +1,180 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::MaybeDef; +use clippy_utils::sym; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// + /// This lint warns when volatile load/store operations + /// (`write_volatile`/`read_volatile`) are applied to composite types. + /// + /// ### Why is this bad? + /// + /// Volatile operations are typically used with memory mapped IO devices, + /// where the precise number and ordering of load and store instructions is + /// important because they can have side effects. This is well defined for + /// primitive types like `u32`, but less well defined for structures and + /// other composite types. In practice it's implementation defined, and the + /// behavior can be rustc-version dependent. + /// + /// As a result, code should only apply `write_volatile`/`read_volatile` to + /// primitive types to be fully well-defined. + /// + /// ### Example + /// ```no_run + /// struct MyDevice { + /// addr: usize, + /// count: usize + /// } + /// + /// fn start_device(device: *mut MyDevice, addr: usize, count: usize) { + /// unsafe { + /// device.write_volatile(MyDevice { addr, count }); + /// } + /// } + /// ``` + /// Instead, operate on each primtive field individually: + /// ```no_run + /// struct MyDevice { + /// addr: usize, + /// count: usize + /// } + /// + /// fn start_device(device: *mut MyDevice, addr: usize, count: usize) { + /// unsafe { + /// (&raw mut (*device).addr).write_volatile(addr); + /// (&raw mut (*device).count).write_volatile(count); + /// } + /// } + /// ``` + #[clippy::version = "1.92.0"] + pub VOLATILE_COMPOSITES, + nursery, + "warn about volatile read/write applied to composite types" +} +declare_lint_pass!(VolatileComposites => [VOLATILE_COMPOSITES]); + +/// Zero-sized types are intrinsically safe to use volatile on since they won't +/// actually generate *any* loads or stores. But this is also used to skip zero-sized +/// fields of `#[repr(transparent)]` structures. +fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + cx.layout_of(ty).is_ok_and(|layout| layout.is_zst()) +} + +/// A thin raw pointer or reference. +fn is_narrow_ptr<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::RawPtr(inner, _) | ty::Ref(_, inner, _) => inner.has_trivial_sizedness(cx.tcx, ty::SizedTraitKind::Sized), + _ => false, + } +} + +/// Enum with some fixed representation and no data-carrying variants. +fn is_enum_repr_c<'tcx>(_cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.ty_adt_def().is_some_and(|adt_def| { + adt_def.is_enum() && adt_def.repr().inhibit_struct_field_reordering() && adt_def.is_payloadfree() + }) +} + +/// `#[repr(transparent)]` structures are also OK if the only non-zero +/// sized field contains a volatile-safe type. +fn is_struct_repr_transparent<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let ty::Adt(adt_def, args) = ty.kind() + && adt_def.is_struct() + && adt_def.repr().transparent() + && let [fieldty] = adt_def + .all_fields() + .filter_map(|field| { + let fty = field.ty(cx.tcx, args); + if is_zero_sized_ty(cx, fty) { None } else { Some(fty) } + }) + .collect::>() + .as_slice() + { + is_volatile_safe_ty(cx, *fieldty) + } else { + false + } +} + +/// SIMD can be useful to get larger single loads/stores, though this is still +/// pretty machine-dependent. +fn is_simd_repr<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let ty::Adt(adt_def, _args) = ty.kind() + && adt_def.is_struct() + && adt_def.repr().simd() + { + let (_size, simdty) = ty.simd_size_and_type(cx.tcx); + is_volatile_safe_ty(cx, simdty) + } else { + false + } +} + +/// Top-level predicate for whether a type is volatile-safe or not. +fn is_volatile_safe_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_primitive() + || is_narrow_ptr(cx, ty) + || is_zero_sized_ty(cx, ty) + || is_enum_repr_c(cx, ty) + || is_simd_repr(cx, ty) + || is_struct_repr_transparent(cx, ty) + // We can't know about a generic type, so just let it pass to avoid noise + || ty.has_non_region_param() +} + +/// Print diagnostic for volatile read/write on non-volatile-safe types. +fn report_volatile_safe<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { + if !is_volatile_safe_ty(cx, ty) { + span_lint( + cx, + VOLATILE_COMPOSITES, + expr.span, + format!("type `{ty}` is not volatile-compatible"), + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for VolatileComposites { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + // Check our expr is calling a method with pattern matching + match expr.kind { + // Look for method calls to `write_volatile`/`read_volatile`, which + // apply to both raw pointers and std::ptr::NonNull. + ExprKind::MethodCall(name, self_arg, _, _) + if matches!(name.ident.name, sym::read_volatile | sym::write_volatile) => + { + let self_ty = cx.typeck_results().expr_ty(self_arg); + match self_ty.kind() { + // Raw pointers + ty::RawPtr(innerty, _) => report_volatile_safe(cx, expr, *innerty), + // std::ptr::NonNull + ty::Adt(_, args) if self_ty.is_diag_item(cx, sym::NonNull) => { + report_volatile_safe(cx, expr, args.type_at(0)); + }, + _ => (), + } + }, + + // Also plain function calls to std::ptr::{read,write}_volatile + ExprKind::Call(func, [arg_ptr, ..]) => { + if let ExprKind::Path(ref qpath) = func.kind + && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() + && matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::ptr_read_volatile | sym::ptr_write_volatile) + ) + && let ty::RawPtr(ptrty, _) = cx.typeck_results().expr_ty_adjusted(arg_ptr).kind() + { + report_volatile_safe(cx, expr, *ptrty); + } + }, + _ => {}, + } + } +} diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index cd6c11b512742..a8351690068df 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_indent}; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{ConstArgKind, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::IsSuggestable; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -72,40 +73,60 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: // check if expr is a call or has a call inside it if inner_expr.can_have_side_effects() { let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); + let inner_expr_ty = cx.typeck_results().expr_ty(inner_expr); let return_type = cx.typeck_results().expr_ty(expr); let inner_expr = snippet(cx, inner_expr.span.source_callsite(), ".."); + let indent = snippet_indent(cx, expr.span).unwrap_or_default(); let vec = if is_vec { "vec!" } else { "" }; let (span, sugg) = match parent_hir_node { Node::LetStmt(l) => ( l.span, format!( - "{inner_expr}; let {var_name}: {return_type} = {vec}[];", + "{inner_expr};\n{indent}let {var_name}: {return_type} = {vec}[];", var_name = snippet(cx, l.pat.span.source_callsite(), "..") ), ), Node::Expr(x) if let ExprKind::Assign(l, _, _) = x.kind => ( x.span, format!( - "{inner_expr}; {var_name} = {vec}[] as {return_type}", + "{inner_expr};\n{indent}{var_name} = {vec}[] as {return_type}", var_name = snippet(cx, l.span.source_callsite(), "..") ), ), - _ => (expr.span, format!("{{ {inner_expr}; {vec}[] as {return_type} }}")), + // NOTE: don't use the stmt span to avoid touching the trailing semicolon + Node::Stmt(_) => (expr.span, format!("{inner_expr};\n{indent}{vec}[] as {return_type}")), + _ => ( + expr.span, + format!( + "\ +{{ +{indent} {inner_expr}; +{indent} {vec}[] as {return_type} +{indent}}}" + ), + ), }; + let span = span.source_callsite(); span_lint_and_then( cx, ZERO_REPEAT_SIDE_EFFECTS, - span.source_callsite(), + span, "expression with side effects as the initial value in a zero-sized array initializer", |diag| { - diag.span_suggestion_verbose( - span.source_callsite(), - "consider performing the side effect separately", - sugg, - Applicability::Unspecified, - ); + if (!inner_expr_ty.is_never() || cx.tcx.features().never_type()) + && return_type.is_suggestable(cx.tcx, true) + { + diag.span_suggestion_verbose( + span, + "consider performing the side effect separately", + sugg, + Applicability::Unspecified, + ); + } else { + diag.help("consider performing the side effect separately"); + } }, ); } diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index f1572fd65bbff..bf133d26ed9d0 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_type_diagnostic_item, ty_from_hir_ty}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::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 _; @@ -48,7 +49,7 @@ impl LateLintPass<'_> for ZeroSizedMapValues { && !in_trait_impl(cx, hir_ty.hir_id) // We don't care about infer vars && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()) - && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) + && (ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap)) && let ty::Adt(_, args) = ty.kind() && let ty = args.type_at(1) // Ensure that no type information is missing, to avoid a delayed bug in the compiler if this is not the case. diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index a934d2094e004..0319f3e656e1b 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -1,7 +1,7 @@ use ControlFlow::{Break, Continue}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{fn_def_id, get_enclosing_block}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; use rustc_errors::Applicability; @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind && let child_ty = cx.typeck_results().expr_ty(expr) - && is_type_diagnostic_item(cx, child_ty, sym::Child) + && child_ty.is_diag_item(cx, sym::Child) { match cx.tcx.parent_hir_node(expr.hir_id) { Node::LetStmt(local) @@ -168,7 +168,7 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { return walk_expr(self, ex); } - if path_to_local_id(ex, self.local_id) { + if ex.res_local_id() == Some(self.local_id) { match self.cx.tcx.parent_hir_node(ex.hir_id) { Node::Stmt(Stmt { kind: StmtKind::Semi(_), diff --git a/clippy_lints_internal/Cargo.toml b/clippy_lints_internal/Cargo.toml index a8293a1ad395d..16b45322e30f1 100644 --- a/clippy_lints_internal/Cargo.toml +++ b/clippy_lints_internal/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] clippy_config = { path = "../clippy_config" } clippy_utils = { path = "../clippy_utils" } +itertools = "0.12" regex = { version = "1.5" } rustc-semver = "1.1" diff --git a/clippy_lints_internal/src/internal_paths.rs b/clippy_lints_internal/src/internal_paths.rs index dc1e30ab2bdd3..95bdf27b019c6 100644 --- a/clippy_lints_internal/src/internal_paths.rs +++ b/clippy_lints_internal/src/internal_paths.rs @@ -2,13 +2,18 @@ use clippy_utils::paths::{PathLookup, PathNS}; use clippy_utils::{sym, type_path, value_path}; // Paths inside rustc +pub static APPLICABILITY: PathLookup = type_path!(rustc_errors::Applicability); +pub static EARLY_CONTEXT: PathLookup = type_path!(rustc_lint::EarlyContext); 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 LATE_CONTEXT: PathLookup = type_path!(rustc_lint::LateContext); 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); +#[expect(clippy::unnecessary_def_path, reason = "for uniform checking in internal lint")] +pub static TY_CTXT: PathLookup = type_path!(rustc_middle::ty::TyCtxt); // Paths in clippy itself pub static CLIPPY_SYM_MODULE: PathLookup = type_path!(clippy_utils::sym); diff --git a/clippy_lints_internal/src/lib.rs b/clippy_lints_internal/src/lib.rs index 43cde86504f5c..d686ba73387cb 100644 --- a/clippy_lints_internal/src/lib.rs +++ b/clippy_lints_internal/src/lib.rs @@ -41,6 +41,7 @@ mod produce_ice; mod symbols; mod unnecessary_def_path; mod unsorted_clippy_utils_paths; +mod unusual_names; use rustc_lint::{Lint, LintStore}; @@ -59,6 +60,7 @@ static LINTS: &[&Lint] = &[ symbols::SYMBOL_AS_STR, unnecessary_def_path::UNNECESSARY_DEF_PATH, unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS, + unusual_names::UNUSUAL_NAMES, ]; pub fn register_lints(store: &mut LintStore) { @@ -74,4 +76,5 @@ pub fn register_lints(store: &mut LintStore) { 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())); + store.register_late_pass(|_| Box::new(unusual_names::UnusualNames)); } diff --git a/clippy_lints_internal/src/produce_ice.rs b/clippy_lints_internal/src/produce_ice.rs index 3a813b4b9a223..630843049da25 100644 --- a/clippy_lints_internal/src/produce_ice.rs +++ b/clippy_lints_internal/src/produce_ice.rs @@ -25,9 +25,9 @@ declare_tool_lint! { declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, ctx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { if is_trigger_fn(fn_kind) { - ctx.sess() + cx.sess() .dcx() .span_delayed_bug(span, "Would you like some help with that?"); } diff --git a/clippy_lints_internal/src/unnecessary_def_path.rs b/clippy_lints_internal/src/unnecessary_def_path.rs index 8877f1faf0ee8..f2e8d3579d851 100644 --- a/clippy_lints_internal/src/unnecessary_def_path.rs +++ b/clippy_lints_internal/src/unnecessary_def_path.rs @@ -1,7 +1,8 @@ use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::{PathNS, lookup_path}; -use clippy_utils::{path_def_id, peel_ref_operators}; +use clippy_utils::peel_ref_operators; +use clippy_utils::res::MaybeQPath; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -26,7 +27,7 @@ declare_tool_lint! { /// /// Use instead: /// ```rust,ignore - /// is_type_diagnostic_item(cx, ty, sym::Vec) + /// ty.is_diag_item(cx, sym::Vec) /// ``` pub clippy::UNNECESSARY_DEF_PATH, Warn, @@ -53,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { let path: Vec = segments .iter() .map(|segment| { - if let Some(const_def_id) = path_def_id(cx, segment) + if let Some(const_def_id) = segment.res(cx).opt_def_id() && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(const_def_id) && let Some(value) = value.to_u32().discard_err() { diff --git a/clippy_lints_internal/src/unusual_names.rs b/clippy_lints_internal/src/unusual_names.rs new file mode 100644 index 0000000000000..e11a2868fb69c --- /dev/null +++ b/clippy_lints_internal/src/unusual_names.rs @@ -0,0 +1,99 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::paths::PathLookup; +use clippy_utils::sym; +use itertools::Itertools; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, Pat, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; +use rustc_span::{Span, Symbol}; + +use crate::internal_paths::{APPLICABILITY, EARLY_CONTEXT, LATE_CONTEXT, TY_CTXT}; + +declare_tool_lint! { + /// ### What it does + /// Checks if variables of some types use the usual name. + /// + /// ### Why is this bad? + /// Restricting the identifiers used for common things in + /// Clippy sources increases consistency. + /// + /// ### Example + /// Check that an `rustc_errors::Applicability` variable is + /// named either `app` or `applicability`, and not + /// `a` or `appl`. + pub clippy::UNUSUAL_NAMES, + Warn, + "commonly used concepts should use usual same variable name.", + report_in_external_macro: true +} + +declare_lint_pass!(UnusualNames => [UNUSUAL_NAMES]); + +const USUAL_NAMES: [(&PathLookup, &str, &[Symbol]); 4] = [ + ( + &APPLICABILITY, + "rustc_errors::Applicability", + &[sym::app, sym::applicability], + ), + (&EARLY_CONTEXT, "rustc_lint::EarlyContext", &[sym::cx]), + (&LATE_CONTEXT, "rustc_lint::LateContext", &[sym::cx]), + (&TY_CTXT, "rustc_middle::ty::TyCtxt", &[sym::tcx]), +]; + +impl<'tcx> LateLintPass<'tcx> for UnusualNames { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Let(let_stmt) = stmt.kind + && let Some(init_expr) = let_stmt.init + { + check_pat_name_for_ty(cx, let_stmt.pat, cx.typeck_results().expr_ty(init_expr), "variable"); + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _span: Span, + def_id: LocalDefId, + ) { + if matches!(kind, FnKind::Closure) { + return; + } + for (param, ty) in body + .params + .iter() + .zip(cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder().inputs()) + { + check_pat_name_for_ty(cx, param.pat, *ty, "parameter"); + } + } +} + +fn check_pat_name_for_ty(cx: &LateContext<'_>, pat: &Pat<'_>, ty: Ty<'_>, kind: &str) { + if let PatKind::Binding(_, _, ident, _) = pat.kind { + let ty = ty.peel_refs(); + for (usual_ty, ty_str, usual_names) in USUAL_NAMES { + if usual_ty.matches_ty(cx, ty) + && !usual_names.contains(&ident.name) + && ident.name != kw::SelfLower + && !ident.name.as_str().starts_with('_') + { + let usual_names = usual_names.iter().map(|name| format!("`{name}`")).join(" or "); + span_lint_and_help( + cx, + UNUSUAL_NAMES, + ident.span, + format!("unusual name for a {kind} of type `{ty_str}`"), + None, + format!("prefer using {usual_names}"), + ); + } + } + } +} diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 1f678a6a29f08..9d12b46b9546f 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-10-06 +nightly-2025-10-16 ``` diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index ad69e6eb184e1..b01e160e22972 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -143,7 +143,7 @@ pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { } } -#[allow(clippy::too_many_lines)] // Just a big match statement +#[expect(clippy::too_many_lines, reason = "big match statement")] pub fn eq_expr(l: &Expr, r: &Expr) -> bool { use ExprKind::*; if !over(&l.attrs, &r.attrs, eq_attr) { @@ -328,7 +328,7 @@ pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> b over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) } -#[expect(clippy::too_many_lines)] // Just a big match statement +#[expect(clippy::too_many_lines, reason = "big match statement")] pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { use ItemKind::*; match (l, r) { diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 948a7203402d5..ff3e7b94f03bf 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -490,17 +490,14 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { None => ident_search_pat(last.ident).1, } } else { - // this shouldn't be possible, but sure - #[allow( - clippy::collapsible_else_if, - reason = "we want to keep these cases together, since they are both impossible" - )] - if qself_path.is_some() { - // last `>` in `` - Pat::Str(">") - } else { - Pat::Str("") - } + // this shouldn't be possible + Pat::Str( + if qself_path.is_some() { + ">" // last `>` in `` + } else { + "" + } + ) }; (start, end) }, diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 9ba796137cc3a..7b8c7f3f0d6b4 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -2,10 +2,11 @@ //! //! This cannot use rustc's const eval, aka miri, as arbitrary HIR expressions cannot be lowered to //! executable MIR bodies, so we have to do this instead. -#![allow(clippy::float_cmp)] +#![expect(clippy::float_cmp)] +use crate::res::MaybeDef; use crate::source::{SpanRangeExt, walk_span_to_context}; -use crate::{clip, is_direct_expn_of, paths, sext, sym, unsext}; +use crate::{clip, is_direct_expn_of, sext, sym, unsext}; use rustc_abi::Size; use rustc_apfloat::Float; @@ -50,15 +51,15 @@ pub enum Constant { /// `true` or `false`. Bool(bool), /// An array of constants. - Vec(Vec), + Vec(Vec), /// Also an array, but with only one constant, repeated N times. - Repeat(Box, u64), + Repeat(Box, u64), /// A tuple of constants. - Tuple(Vec), + Tuple(Vec), /// A raw pointer. RawPtr(u128), /// A reference - Ref(Box), + Ref(Box), /// A literal with syntax error. Err, } @@ -805,10 +806,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> { | sym::i128_legacy_const_max ) ) || self.tcx.opt_parent(did).is_some_and(|parent| { - paths::F16_CONSTS.matches(&self.tcx, parent) - || paths::F32_CONSTS.matches(&self.tcx, parent) - || paths::F64_CONSTS.matches(&self.tcx, parent) - || paths::F128_CONSTS.matches(&self.tcx, parent) + parent.is_diag_item(&self.tcx, sym::f16_consts_mod) + || parent.is_diag_item(&self.tcx, sym::f32_consts_mod) + || parent.is_diag_item(&self.tcx, sym::f64_consts_mod) + || parent.is_diag_item(&self.tcx, sym::f128_consts_mod) })) => { did diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 6f1bc28fbab81..3383e66fe3688 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -3,7 +3,7 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::consts::{ConstEvalCtxt, Constant}; -use crate::ty::is_type_diagnostic_item; +use crate::res::MaybeDef; use crate::{is_expn_of, sym}; use rustc_ast::ast; @@ -14,6 +14,7 @@ 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)`. +#[derive(Debug)] pub struct ForLoop<'tcx> { /// `for` loop item pub pat: &'tcx Pat<'tcx>, @@ -212,7 +213,7 @@ pub struct Range<'a> { impl<'a> Range<'a> { /// Higher a `hir` range to something similar to `ast::ExprKind::Range`. - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] pub fn hir(expr: &'a Expr<'_>) -> Option> { match expr.kind { ExprKind::Call(path, [arg1, arg2]) @@ -318,6 +319,7 @@ pub struct While<'hir> { pub body: &'hir Expr<'hir>, /// Span of the loop header pub span: Span, + pub label: Option, } impl<'hir> While<'hir> { @@ -333,13 +335,18 @@ impl<'hir> While<'hir> { }), .. }, - _, + label, LoopSource::While, span, ) = expr.kind && !has_let_expr(condition) { - return Some(Self { condition, body, span }); + return Some(Self { + condition, + body, + span, + label, + }); } None } @@ -446,7 +453,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - if let ExprKind::Call(func, args) = expr.kind { match func.kind { ExprKind::Path(QPath::TypeRelative(ty, name)) - if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) => + if cx.typeck_results().node_type(ty.hir_id).is_diag_item(cx, sym::Vec) => { if name.ident.name == sym::new { return Some(VecInitKind::New); @@ -462,7 +469,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - }, ExprKind::Path(QPath::Resolved(_, path)) if cx.tcx.is_diagnostic_item(sym::default_fn, path.res.opt_def_id()?) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) => + && cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Vec) => { return Some(VecInitKind::Default); }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2218c5e4e44f0..c8943523e1798 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -30,8 +30,10 @@ extern crate rustc_ast; extern crate rustc_attr_parsing; extern crate rustc_const_eval; extern crate rustc_data_structures; -// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate. -#[allow(unused_extern_crates)] +#[expect( + unused_extern_crates, + reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate." +)] extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; @@ -62,6 +64,7 @@ pub mod msrvs; pub mod numeric_literal; pub mod paths; pub mod qualify_min_const_fn; +pub mod res; pub mod source; pub mod str_utils; pub mod sugg; @@ -128,6 +131,7 @@ use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::msrvs::Msrv; +use crate::res::{MaybeDef, MaybeResPath}; 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; @@ -169,7 +173,8 @@ macro_rules! extract_msrv_attr { /// // ^^^ input /// ``` pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { - while let Some(init) = path_to_local(expr) + while let Some(init) = expr + .res_local_id() .and_then(|id| find_binding_init(cx, id)) .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) { @@ -247,19 +252,6 @@ pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { } } -/// Checks if a `Res` refers to a constructor of a `LangItem` -/// For example, use this to check whether a function call or a pattern is `Some(..)`. -pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool { - if let Res::Def(DefKind::Ctor(..), id) = res - && let Some(lang_id) = cx.tcx.lang_items().get(lang_item) - && let Some(id) = cx.tcx.opt_parent(id) - { - id == lang_id - } else { - false - } -} - /// Checks if `{ctor_call_id}(...)` is `{enum_item}::{variant_name}(...)`. pub fn is_enum_variant_ctor( cx: &LateContext<'_>, @@ -332,13 +324,17 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone)) + if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)) } /// Checks if `arm` has the form `None => None`. pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { is_none_pattern(cx, arm.pat) - && matches!(peel_blocks(arm.body).kind, ExprKind::Path(qpath) if is_res_lang_ctor(cx, cx.qpath_res(&qpath, arm.body.hir_id), OptionNone)) + && matches!( + peel_blocks(arm.body).kind, + ExprKind::Path(qpath) + if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone) + ) } /// Checks if the given `QPath` belongs to a type alias. @@ -350,40 +346,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { } } -/// 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) { - cx.tcx.trait_of_assoc(method_id).is_none() - } else { - false - } -} - -/// Checks if a method is defined in an impl of a diagnostic item -pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(impl_did) = cx.tcx.impl_of_assoc(def_id) - && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() - { - return cx.tcx.is_diagnostic_item(diag_item, adt.did()); - } - false -} - -/// Checks if a method is in a diagnostic item trait -pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(trait_did) = cx.tcx.trait_of_assoc(def_id) { - return cx.tcx.is_diagnostic_item(diag_item, trait_did); - } - false -} - -/// Checks if the method call given in `expr` belongs to the given trait. -pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { - cx.typeck_results() - .type_dependent_def_id(expr.hir_id) - .is_some_and(|did| is_diag_trait_item(cx, did, diag_item)) -} - /// Checks if the `def_id` belongs to a function that is part of a trait impl. pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id)) @@ -395,25 +357,6 @@ pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool } } -/// Checks if the given expression is a path referring an item on the trait -/// that is marked with the given diagnostic item. -/// -/// For checking method call expressions instead of path expressions, use -/// [`is_trait_method`]. -/// -/// For example, this can be used to find if an expression like `u64::default` -/// refers to an item of the trait `Default`, which is associated with the -/// `diag_item` of `sym::Default`. -pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { - if let ExprKind::Path(ref qpath) = expr.kind { - cx.qpath_res(qpath, expr.hir_id) - .opt_def_id() - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item)) - } else { - false - } -} - pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { match *path { QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"), @@ -433,38 +376,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool { - path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id)) -} - -/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if -/// it matches the given diagnostic item. -pub fn is_path_diagnostic_item<'tcx>( - cx: &LateContext<'_>, - maybe_path: &impl MaybePath<'tcx>, - diag_item: Symbol, -) -> bool { - path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id)) -} - -/// 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 - && let Res::Local(id) = path.res - { - return Some(id); - } - None -} - -/// Returns true if the expression is a path to a local with the specified `HirId`. -/// Use this function to see if an expression matches a function argument or a match binding. -pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool { - path_to_local(expr) == Some(id) -} - /// If the expression is a path to a local (with optional projections), /// returns the canonical `HirId` of the local. /// @@ -482,56 +393,6 @@ pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option { } } -pub trait MaybePath<'hir> { - fn hir_id(&self) -> HirId; - fn qpath_opt(&self) -> Option<&QPath<'hir>>; -} - -macro_rules! maybe_path { - ($ty:ident, $kind:ident) => { - impl<'hir> MaybePath<'hir> for hir::$ty<'hir> { - fn hir_id(&self) -> HirId { - self.hir_id - } - fn qpath_opt(&self) -> Option<&QPath<'hir>> { - match &self.kind { - hir::$kind::Path(qpath) => Some(qpath), - _ => None, - } - } - } - }; -} -maybe_path!(Expr, ExprKind); -impl<'hir> MaybePath<'hir> for Pat<'hir> { - fn hir_id(&self) -> HirId { - self.hir_id - } - fn qpath_opt(&self) -> Option<&QPath<'hir>> { - match &self.kind { - PatKind::Expr(PatExpr { - kind: PatExprKind::Path(qpath), - .. - }) => Some(qpath), - _ => None, - } - } -} -maybe_path!(Ty, TyKind); - -/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err` -pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res { - match maybe_path.qpath_opt() { - None => Res::Err, - Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()), - } -} - -/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID -pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option { - path_res(cx, maybe_path).opt_def_id() -} - /// 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: @@ -658,9 +519,9 @@ pub fn is_default_equivalent_call( whole_call_expr: Option<&Expr<'_>>, ) -> bool { if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind - && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() - && (is_diag_trait_item(cx, repl_def_id, sym::Default) - || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath)) + && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx) + && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default) + || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath)) { return true; } @@ -769,7 +630,10 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { }, ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)), ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg), - ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), + ExprKind::Path(qpath) => cx + .qpath_res(qpath, e.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)), _ => false, @@ -784,14 +648,14 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: & ExprKind::Lit(hir::Lit { node: LitKind::Str(sym, _), .. - }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String), - ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), + }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String), + ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec), ExprKind::Repeat(_, len) => { if let ConstArgKind::Anon(anon_const) = len.kind && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind && let LitKind::Int(v, _) = const_lit.node { - return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec); + return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec); } }, _ => (), @@ -1670,9 +1534,12 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind && ddpos.as_opt_usize().is_none() - && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk) + && cx + .qpath_res(path, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultOk) && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind - && path_to_local_id(arm.body, hir_id) + && arm.body.res_local_id() == Some(hir_id) { return true; } @@ -1681,7 +1548,9 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind { - is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr) + cx.qpath_res(path, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) } else { false } @@ -1975,7 +1844,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< match (pat.kind, expr.kind) { (PatKind::Binding(_, id, _, _), _) if by_hir => { - path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty() + expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty() }, (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => { matches!(path.segments, [ segment] if segment.ident.name == ident.name) @@ -2048,7 +1917,7 @@ pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)), - _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)), + _ => expr.basic_res().is_diag_item(cx, sym::convert_identity), } } @@ -2812,7 +2681,6 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtx moved_before_use, same_ctxt, }, - #[allow(unreachable_patterns)] Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow"), None => ExprUseCtxt { node: Node::Crate(cx.tcx.hir_root_module()), @@ -2916,12 +2784,18 @@ pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( else_body: &Expr<'_>, ) -> Option<&'a Pat<'hir>> { if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome) + && cx + .qpath_res(&pat_path, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionSome) && !is_refutable(cx, inner_pat) && let else_body = peel_blocks(else_body) && let ExprKind::Ret(Some(ret_val)) = else_body.kind && let ExprKind::Path(ret_path) = ret_val.kind - && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone) + && cx + .qpath_res(&ret_path, ret_val.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) { Some(inner_pat) } else { @@ -3495,7 +3369,7 @@ pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) - /// Returns `true` if `expr` designates a mutable static, a mutable local binding, or an expression /// that can be owned. pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(hir_id) = path_to_local(expr) + if let Some(hir_id) = expr.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) { matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 9ba644fdd20ec..a066427d6bc11 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -134,7 +134,7 @@ pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option { } /// Returns the `mir::Body` containing the node associated with `hir_id`. -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&Body<'_>> { let body_owner_local_def_id = tcx.hir_enclosing_body_owner(hir_id); if tcx.hir_body_owner_kind(body_owner_local_def_id).is_fn_or_closure() { diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 152b4272c26ca..f2bffc8156afc 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -15,7 +15,6 @@ use std::ops::ControlFlow; /// Collects the possible borrowers of each local. /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// possible borrowers of `a`. -#[allow(clippy::module_name_repetitions)] struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { possible_borrower: TransitiveRelation, body: &'b mir::Body<'tcx>, @@ -167,7 +166,6 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { } /// Result of `PossibleBorrowerVisitor`. -#[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { /// Mapping `Local -> its possible borrowers` pub map: FxHashMap>, diff --git a/clippy_utils/src/mir/possible_origin.rs b/clippy_utils/src/mir/possible_origin.rs index 3d253fd2bb148..fee22c436b0fb 100644 --- a/clippy_utils/src/mir/possible_origin.rs +++ b/clippy_utils/src/mir/possible_origin.rs @@ -8,7 +8,6 @@ use rustc_middle::mir; /// Collect possible borrowed for every `&mut` local. /// For example, `_1 = &mut _2` generate _1: {_2,...} /// Known Problems: not sure all borrowed are tracked -#[allow(clippy::module_name_repetitions)] pub(super) struct PossibleOriginVisitor<'a, 'tcx> { possible_origin: TransitiveRelation, body: &'a mir::Body<'tcx>, diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 5ab8e16d88ed3..8aa663163caf1 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -4,7 +4,8 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -use crate::{MaybePath, path_def_id, sym}; +use crate::res::MaybeQPath; +use crate::sym; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; @@ -96,8 +97,11 @@ impl PathLookup { } /// 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)) + pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> bool { + maybe_path + .res(cx) + .opt_def_id() + .is_some_and(|def_id| self.matches(cx, def_id)) } /// Checks if the path resolves to `ty`'s definition, must be an `Adt` @@ -127,11 +131,6 @@ path_macros! { macro_path: PathNS::Macro, } -pub static F16_CONSTS: PathLookup = type_path!(core::f16::consts); -pub static F32_CONSTS: PathLookup = type_path!(core::f32::consts); -pub static F64_CONSTS: PathLookup = type_path!(core::f64::consts); -pub static F128_CONSTS: PathLookup = type_path!(core::f128::consts); - // Paths in external crates pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt); pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt); diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index b9027fea468eb..7722bebdbbe0d 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -232,9 +232,7 @@ fn check_statement<'tcx>( StatementKind::FakeRead(box (_, place)) => check_place(cx, *place, span, body, msrv), // just an assignment - StatementKind::SetDiscriminant { place, .. } => { - check_place(cx, **place, span, body, msrv) - }, + StatementKind::SetDiscriminant { place, .. } => check_place(cx, **place, span, body, msrv), StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(cx, op, span, body, msrv), diff --git a/clippy_utils/src/res.rs b/clippy_utils/src/res.rs new file mode 100644 index 0000000000000..90b9529278964 --- /dev/null +++ b/clippy_utils/src/res.rs @@ -0,0 +1,643 @@ +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{ + self as hir, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, TyKind, +}; +use rustc_lint::LateContext; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::{AdtDef, AdtKind, Binder, EarlyBinder, Ty, TypeckResults}; +use rustc_span::{Ident, Symbol}; + +/// Either a `HirId` or a type which can be identified by one. +pub trait HasHirId: Copy { + fn hir_id(self) -> HirId; +} +impl HasHirId for HirId { + #[inline] + fn hir_id(self) -> HirId { + self + } +} +impl HasHirId for &Expr<'_> { + #[inline] + fn hir_id(self) -> HirId { + self.hir_id + } +} + +type DefRes = (DefKind, DefId); + +pub trait MaybeTypeckRes<'tcx> { + /// Gets the contained `TypeckResults`. + /// + /// With debug assertions enabled this will always return `Some`. `None` is + /// only returned so logic errors can be handled by not emitting a lint on + /// release builds. + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>>; + + /// Gets the type-dependent resolution of the specified node. + /// + /// With debug assertions enabled this will always return `Some`. `None` is + /// only returned so logic errors can be handled by not emitting a lint on + /// release builds. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_based_def(&self, node: impl HasHirId) -> Option { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn f(typeck: &TypeckResults<'_>, id: HirId) -> Option { + if typeck.hir_owner == id.owner { + let def = typeck.type_dependent_def(id); + debug_assert!( + def.is_some(), + "attempted type-dependent lookup for a node with no definition\ + \n node `{id:?}`", + ); + def + } else { + debug_assert!( + false, + "attempted type-dependent lookup for a node in the wrong body\ + \n in body `{:?}`\ + \n expected body `{:?}`", + typeck.hir_owner, id.owner, + ); + None + } + } + self.typeck_res().and_then(|typeck| f(typeck, node.hir_id())) + } +} +impl<'tcx> MaybeTypeckRes<'tcx> for LateContext<'tcx> { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>> { + if let Some(typeck) = self.maybe_typeck_results() { + Some(typeck) + } else { + // It's possible to get the `TypeckResults` for any other body, but + // attempting to lookup the type of something across bodies like this + // is a good indication of a bug. + debug_assert!(false, "attempted type-dependent lookup in a non-body context"); + None + } + } +} +impl<'tcx> MaybeTypeckRes<'tcx> for TypeckResults<'tcx> { + #[inline] + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>> { + Some(self) + } +} + +/// A `QPath` with the `HirId` of the node containing it. +type QPathId<'tcx> = (&'tcx QPath<'tcx>, HirId); + +/// A HIR node which might be a `QPath`. +pub trait MaybeQPath<'a>: Copy { + /// If this node is a path gets both the contained path and the `HirId` to + /// use for type dependant lookup. + fn opt_qpath(self) -> Option>; + + /// If this node is a `QPath::LangItem` gets the item it resolves to. + #[inline] + fn opt_lang_path(self) -> Option { + match self.opt_qpath() { + Some((&QPath::LangItem(item, _), _)) => Some(item), + _ => None, + } + } + + /// If this is a path gets its resolution. Returns `Res::Err` otherwise. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res { + match *qpath { + QPath::Resolved(_, p) => p.res, + QPath::TypeRelative(..) | QPath::LangItem(..) if let Some((kind, id)) = typeck.ty_based_def(id) => { + Res::Def(kind, id) + }, + QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => Res::Err, + } + } + + /// If this is a path with the specified name as its final segment gets its + /// resolution. Returns `Res::Err` otherwise. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>, name: Symbol) -> Res { + match *qpath { + QPath::Resolved(_, p) + if let [.., seg] = p.segments + && seg.ident.name == name => + { + p.res + }, + QPath::TypeRelative(_, seg) + if seg.ident.name == name + && let Some((kind, id)) = typeck.ty_based_def(id) => + { + Res::Def(kind, id) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck, name), + _ => Res::Err, + } + } + + /// If this is a path gets both its resolution and final segment. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res_with_seg<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> (Res, Option<&'a PathSegment<'a>>) { + #[cfg_attr(debug_assertions, track_caller)] + fn f<'a>(qpath: &QPath<'a>, id: HirId, typeck: &TypeckResults<'_>) -> (Res, Option<&'a PathSegment<'a>>) { + match *qpath { + QPath::Resolved(_, p) if let [.., seg] = p.segments => (p.res, Some(seg)), + QPath::TypeRelative(_, seg) if let Some((kind, id)) = typeck.ty_based_def(id) => { + (Res::Def(kind, id), Some(seg)) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => (Res::Err, None), + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => (Res::Err, None), + } + } + + /// If this is a path without an explicit `Self` type gets its resolution. + /// Returns `Res::Err` otherwise. + /// + /// Only paths to trait items can optionally contain a `Self` type. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeless_res<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res { + match *qpath { + QPath::Resolved( + None + | Some(&hir::Ty { + kind: TyKind::Infer(()), + .. + }), + p, + ) => p.res, + QPath::TypeRelative( + &hir::Ty { + kind: TyKind::Infer(()), + .. + }, + _, + ) if let Some((kind, id)) = typeck.ty_based_def(id) => Res::Def(kind, id), + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => Res::Err, + } + } + + /// If this is a path without an explicit `Self` type to an item with the + /// specified name gets its resolution. Returns `Res::Err` otherwise. + /// + /// Only paths to trait items can optionally contain a `Self` type. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeless_res_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>, name: Symbol) -> Res { + match *qpath { + QPath::Resolved( + None + | Some(&hir::Ty { + kind: TyKind::Infer(()), + .. + }), + p, + ) if let [.., seg] = p.segments + && seg.ident.name == name => + { + p.res + }, + QPath::TypeRelative( + &hir::Ty { + kind: TyKind::Infer(()), + .. + }, + seg, + ) if seg.ident.name == name + && let Some((kind, id)) = typeck.ty_based_def(id) => + { + Res::Def(kind, id) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck, name), + _ => Res::Err, + } + } + + /// If this is a type-relative path gets the definition it resolves to. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Option { + match self.opt_qpath() { + Some((QPath::TypeRelative(..), id)) => typeck.ty_based_def(id), + _ => None, + } + } + + /// If this is a type-relative path to an item with the specified name gets + /// the definition it resolves to. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Option { + match self.opt_qpath() { + Some((&QPath::TypeRelative(_, seg), id)) if seg.ident.name == name => typeck.ty_based_def(id), + _ => None, + } + } + + /// If this is a type-relative path gets the definition it resolves to and + /// its final segment. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def_with_seg<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Option<(DefRes, &'a PathSegment<'a>)> { + match self.opt_qpath() { + Some((QPath::TypeRelative(_, seg), id)) if let Some(def) = typeck.ty_based_def(id) => Some((def, seg)), + _ => None, + } + } +} + +impl<'tcx> MaybeQPath<'tcx> for QPathId<'tcx> { + #[inline] + fn opt_qpath(self) -> Option> { + Some((self.0, self.1)) + } +} +impl<'tcx> MaybeQPath<'tcx> for &'tcx Expr<'_> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + ExprKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx> MaybeQPath<'tcx> for &'tcx PatExpr<'_> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + PatExprKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx, AmbigArg> MaybeQPath<'tcx> for &'tcx hir::Ty<'_, AmbigArg> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + TyKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx> MaybeQPath<'tcx> for &'_ Pat<'tcx> { + #[inline] + fn opt_qpath(self) -> Option> { + match self.kind { + PatKind::Expr(e) => e.opt_qpath(), + _ => None, + } + } +} +impl<'tcx, T: MaybeQPath<'tcx>> MaybeQPath<'tcx> for Option { + #[inline] + fn opt_qpath(self) -> Option> { + self.and_then(T::opt_qpath) + } +} +impl<'tcx, T: Copy + MaybeQPath<'tcx>> MaybeQPath<'tcx> for &Option { + #[inline] + fn opt_qpath(self) -> Option> { + self.and_then(T::opt_qpath) + } +} + +/// A resolved path and the explicit `Self` type if there is one. +type OptResPath<'tcx> = (Option<&'tcx hir::Ty<'tcx>>, Option<&'tcx Path<'tcx>>); + +/// A HIR node which might be a `QPath::Resolved`. +/// +/// The following are resolved paths: +/// * A path to a module or crate item. +/// * A path to a trait item via the trait's name. +/// * A path to a struct or variant constructor via the original type's path. +/// * A local. +/// +/// All other paths are `TypeRelative` and require using `PathRes` to lookup the +/// resolution. +pub trait MaybeResPath<'a>: Copy { + /// If this node is a resolved path gets both the contained path and the + /// type associated with it. + fn opt_res_path(self) -> OptResPath<'a>; + + /// If this node is a resolved path gets it's resolution. Returns `Res::Err` + /// otherwise. + #[inline] + fn basic_res(self) -> &'a Res { + self.opt_res_path().1.map_or(&Res::Err, |p| &p.res) + } + + /// If this node is a path to a local gets the local's `HirId`. + #[inline] + fn res_local_id(self) -> Option { + if let (_, Some(p)) = self.opt_res_path() + && let Res::Local(id) = p.res + { + Some(id) + } else { + None + } + } + + /// If this node is a path to a local gets the local's `HirId` and identifier. + fn res_local_id_and_ident(self) -> Option<(HirId, &'a Ident)> { + if let (_, Some(p)) = self.opt_res_path() + && let Res::Local(id) = p.res + && let [seg] = p.segments + { + Some((id, &seg.ident)) + } else { + None + } + } +} +impl<'a> MaybeResPath<'a> for &'a Path<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + (None, Some(self)) + } + + #[inline] + fn basic_res(self) -> &'a Res { + &self.res + } +} +impl<'a> MaybeResPath<'a> for &QPath<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match *self { + QPath::Resolved(ty, path) => (ty, Some(path)), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &Expr<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + ExprKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &PatExpr<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + PatExprKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a, AmbigArg> MaybeResPath<'a> for &hir::Ty<'a, AmbigArg> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + TyKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &Pat<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match self.kind { + PatKind::Expr(e) => e.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a, T: MaybeResPath<'a>> MaybeResPath<'a> for Option { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match self { + Some(x) => T::opt_res_path(x), + None => (None, None), + } + } + + #[inline] + fn basic_res(self) -> &'a Res { + self.map_or(&Res::Err, T::basic_res) + } +} + +/// A type which may either contain a `DefId` or be referred to by a `DefId`. +pub trait MaybeDef: Copy { + fn opt_def_id(self) -> Option; + + /// Gets this definition's id and kind. This will lookup the kind in the def + /// tree if needed. + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)>; + + /// Gets the diagnostic name of this definition if it has one. + #[inline] + fn opt_diag_name<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + self.opt_def_id().and_then(|id| tcx.tcx().get_diagnostic_name(id)) + } + + /// Checks if this definition has the specified diagnostic name. + #[inline] + fn is_diag_item<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>, name: Symbol) -> bool { + self.opt_def_id() + .is_some_and(|id| tcx.tcx().is_diagnostic_item(name, id)) + } + + /// Checks if this definition is the specified `LangItem`. + #[inline] + fn is_lang_item<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>, item: LangItem) -> bool { + self.opt_def_id() + .is_some_and(|id| tcx.tcx().lang_items().get(item) == Some(id)) + } + + /// If this definition is an impl block gets its type. + #[inline] + fn opt_impl_ty<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option>> { + match self.opt_def(tcx) { + Some((DefKind::Impl { .. }, id)) => Some(tcx.tcx().type_of(id)), + _ => None, + } + } + + /// Gets the parent of this definition if it has one. + #[inline] + fn opt_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + self.opt_def_id().and_then(|id| tcx.tcx().opt_parent(id)) + } + + /// Checks if this definition is an impl block. + #[inline] + fn is_impl<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> bool { + matches!(self.opt_def(tcx), Some((DefKind::Impl { .. }, _))) + } + + /// If this definition is a constructor gets the `DefId` of it's type or variant. + #[inline] + fn ctor_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::Ctor(..), id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } + + /// If this definition is an associated item of an impl or trait gets the + /// `DefId` of its parent. + #[inline] + fn assoc_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } + + /// If this definition is an associated function of an impl or trait gets the + /// `DefId` of its parent. + #[inline] + fn assoc_fn_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::AssocFn, id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } +} +impl MaybeDef for DefId { + #[inline] + fn opt_def_id(self) -> Option { + Some(self) + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.opt_def_id().map(|id| (tcx.tcx().def_kind(id), id)) + } +} +impl MaybeDef for (DefKind, DefId) { + #[inline] + fn opt_def_id(self) -> Option { + Some(self.1) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + Some(self) + } +} +impl MaybeDef for AdtDef<'_> { + #[inline] + fn opt_def_id(self) -> Option { + Some(self.did()) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + let did = self.did(); + match self.adt_kind() { + AdtKind::Enum => Some((DefKind::Enum, did)), + AdtKind::Struct => Some((DefKind::Struct, did)), + AdtKind::Union => Some((DefKind::Union, did)), + } + } +} +impl MaybeDef for Ty<'_> { + #[inline] + fn opt_def_id(self) -> Option { + self.ty_adt_def().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.ty_adt_def().opt_def(tcx) + } +} +impl MaybeDef for Res { + #[inline] + fn opt_def_id(self) -> Option { + Res::opt_def_id(&self) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + match self { + Res::Def(kind, id) => Some((kind, id)), + _ => None, + } + } +} +impl MaybeDef for Option { + #[inline] + fn opt_def_id(self) -> Option { + self.and_then(T::opt_def_id) + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.and_then(|x| T::opt_def(x, tcx)) + } +} +impl MaybeDef for EarlyBinder<'_, T> { + #[inline] + fn opt_def_id(self) -> Option { + self.skip_binder().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.skip_binder().opt_def(tcx) + } +} +impl MaybeDef for Binder<'_, T> { + #[inline] + fn opt_def_id(self) -> Option { + self.skip_binder().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.skip_binder().opt_def(tcx) + } +} diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 638d329031231..83d7f6bc565d7 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -143,7 +143,6 @@ pub trait SpanRangeExt: SpanRange { map_range(cx.sess().source_map(), self.into_range(), f) } - #[allow(rustdoc::invalid_rust_codeblocks, reason = "The codeblock is intentionally broken")] /// Extends the range to include all preceding whitespace characters. /// /// The range will not be expanded if it would cross a line boundary, the line the range would diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index a63333c9b48f9..581c2b02839dc 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -33,7 +33,7 @@ pub enum Sugg<'a> { /// or `-`, but only if the type with and without the operator is kept identical. /// It means that doubling the operator can be used to remove it instead, in /// order to provide better suggestions. - UnOp(UnOp, Box>), + UnOp(UnOp, Box), } /// Literal constant `0`, for convenience. diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 2d0d4a5319f3c..8e8a80a6a9c95 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -34,6 +34,7 @@ macro_rules! generate { // // `cargo dev fmt` ensures that the content of the `generate!()` macro call stays sorted. generate! { + Applicability, AsyncReadExt, AsyncWriteExt, BACKSLASH_SINGLE_QUOTE: r"\'", @@ -45,10 +46,12 @@ generate! { Current, DOUBLE_QUOTE: "\"", Deserialize, + EarlyContext, EarlyLintPass, IntoIter, Itertools, LF: "\n", + LateContext, Lazy, Lint, LowerExp, @@ -75,7 +78,9 @@ generate! { Weak, abs, ambiguous_glob_reexports, + app, append, + applicability, arg, as_bytes, as_deref, @@ -115,7 +120,6 @@ generate! { collapsible_if, collect, const_ptr, - consts, contains, copied, copy_from, @@ -125,6 +129,7 @@ generate! { count_ones, create, create_new, + cx, cycle, cyclomatic_complexity, de, @@ -176,6 +181,7 @@ generate! { hidden_glob_reexports, hygiene, insert, + insert_str, inspect, int_roundings, into, @@ -259,12 +265,14 @@ generate! { powi, product, push, + push_str, read, read_exact, read_line, read_to_end, read_to_string, read_unaligned, + read_volatile, redundant_imports, redundant_pub_crate, regex, @@ -286,8 +294,10 @@ generate! { rsplit_terminator, rsplitn, rsplitn_mut, + rustc_errors, rustc_lint, rustc_lint_defs, + rustc_middle, rustc_span, rustfmt_skip, rwlock, @@ -330,6 +340,7 @@ generate! { symbol, take, take_while, + tcx, then, then_some, to_ascii_lowercase, @@ -374,6 +385,7 @@ generate! { wrapping_offset, write, write_unaligned, + write_volatile, writeln, zip, } diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index ebf4f2cd3263c..a90d64e972c1f 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -3,7 +3,6 @@ #![allow(clippy::module_name_repetitions)] use core::ops::ControlFlow; -use itertools::Itertools; use rustc_abi::VariantIdx; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -22,8 +21,8 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, - GenericArgKind, GenericArgsRef, GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, - TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, + GenericArgKind, GenericArgsRef, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; @@ -34,8 +33,8 @@ use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::{iter, mem}; -use crate::path_res; use crate::paths::{PathNS, lookup_path_str}; +use crate::res::{MaybeDef, MaybeQPath}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -157,20 +156,6 @@ pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Optio .and_then(|iter_did| cx.get_associated_type(ty, iter_did, sym::Item)) } -/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type -/// implements a trait marked with a diagnostic item use [`implements_trait`]. -/// -/// For a further exploitation what diagnostic items are see [diagnostic items] in -/// rustc-dev-guide. -/// -/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html -pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()), - _ => None, - } -} - /// Returns true if `ty` is a type on which calling `Clone` through a function instead of /// as a method, such as `Arc::clone()` is considered idiomatic. /// @@ -178,7 +163,7 @@ pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option, ty: Ty<'_>) -> bool { matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::Arc | sym::ArcWeak | sym::Rc | sym::RcWeak) ) } @@ -379,42 +364,6 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { } } -/// Checks if the type is a reference equals to a diagnostic item -pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match ty.kind() { - ty::Ref(_, ref_ty, _) => is_type_diagnostic_item(cx, *ref_ty, diag_item), - _ => false, - } -} - -/// Checks if the type is equal to a diagnostic item. To check if a type implements a -/// trait marked with a diagnostic item use [`implements_trait`]. -/// -/// For a further exploitation what diagnostic items are see [diagnostic items] in -/// rustc-dev-guide. -/// -/// --- -/// -/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` -/// -/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html -pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()), - _ => false, - } -} - -/// Checks if the type is equal to a lang item. -/// -/// Returns `false` if the `LangItem` is not defined. -pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: LangItem) -> bool { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.lang_items().get(lang_item) == Some(adt.did()), - _ => false, - } -} - /// Return `true` if the passed `typ` is `isize` or `usize`. pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) @@ -433,9 +382,9 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { false } // Check for std types which implement drop, but only for memory allocation. - else if is_type_lang_item(cx, ty, LangItem::OwnedBox) + else if ty.is_lang_item(cx, LangItem::OwnedBox) || matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type | sym::RcWeak | sym::ArcWeak) ) { @@ -628,7 +577,7 @@ impl<'tcx> ExprFnSig<'tcx> { /// If the expression is function like, get the signature for it. pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option> { - if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) { + if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr.res(cx) { Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate_identity(), Some(id))) } else { ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs()) @@ -953,9 +902,10 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { } } +#[cfg(debug_assertions)] /// Asserts that the given arguments match the generic parameters of the given item. -#[allow(dead_code)] fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[GenericArg<'tcx>]) { + use itertools::Itertools; let g = tcx.generics_of(did); let parent = g.parent.map(|did| tcx.generics_of(did)); let count = g.parent_count + g.own_params.len(); @@ -971,7 +921,7 @@ fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[Generi note: the expected arguments are: `[{}]`\n\ the given arguments are: `{args:#?}`", args.len(), - params.clone().map(GenericParamDefKind::descr).format(", "), + params.clone().map(ty::GenericParamDefKind::descr).format(", "), ); if let Some((idx, (param, arg))) = @@ -980,13 +930,13 @@ fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[Generi .zip(args.iter().map(|&x| x.kind())) .enumerate() .find(|(_, (param, arg))| match (param, arg) { - (GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) - | (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) - | (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) => false, + (ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) + | (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) + | (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) => false, ( - GenericParamDefKind::Lifetime - | GenericParamDefKind::Type { .. } - | GenericParamDefKind::Const { .. }, + ty::GenericParamDefKind::Lifetime + | ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. }, _, ) => true, }) @@ -996,7 +946,7 @@ fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[Generi note: the expected arguments are `[{}]`\n\ the given arguments are `{args:#?}`", param.descr(), - params.clone().map(GenericParamDefKind::descr).format(", "), + params.clone().map(ty::GenericParamDefKind::descr).format(", "), ); } } @@ -1299,11 +1249,14 @@ pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> /// Check if `ty` is an `Option` and return its argument type if it is. pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - match ty.kind() { - ty::Adt(adt, args) => cx - .tcx - .is_diagnostic_item(sym::Option, adt.did()) - .then(|| args.type_at(0)), + match *ty.kind() { + ty::Adt(adt, args) + if let [arg] = &**args + && let Some(arg) = arg.as_type() + && adt.is_diag_item(cx, sym::Option) => + { + Some(arg) + }, _ => None, } } @@ -1357,7 +1310,7 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: 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() || is_type_diagnostic_item(cx, ty, sym::Vec) + ty.is_slice() || ty.is_array() || ty.is_diag_item(cx, sym::Vec) } pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index d9c7e6eac9f67..d46c7bdcd4c1d 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -329,7 +329,7 @@ fn update_res( None } -#[allow(clippy::cast_possible_truncation)] +#[expect(clippy::cast_possible_truncation)] fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let Some(callee_def_id) = (match expr.kind { ExprKind::Call(callee, _) => { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 6eccbcdb12281..e27f1dabeefab 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,4 +1,5 @@ use crate::macros::root_macro_call_first_node; +use crate::res::MaybeResPath; use crate::visitors::{Descend, Visitable, for_each_expr, for_each_expr_without_closures}; use crate::{self as utils, get_enclosing_loop_or_multi_call_closure}; use core::ops::ControlFlow; @@ -196,7 +197,7 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool { for_each_expr(cx, v, |e| { - if utils::path_to_local_id(e, local_id) { + if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) @@ -222,7 +223,7 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr let mut past_expr = false; for_each_expr(cx, block, |e| { if past_expr { - if utils::path_to_local_id(e, local_id) { + if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { ControlFlow::Continue(Descend::Yes) diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index c9f5401ebe776..84e4f043e34f1 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,7 +1,8 @@ +use crate::get_enclosing_block; use crate::msrvs::Msrv; use crate::qualify_min_const_fn::is_stable_const_fn; +use crate::res::MaybeResPath; use crate::ty::needs_ordered_drop; -use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -312,7 +313,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { for_each_expr(cx, visitable, |e| { - if path_to_local_id(e, id) { + if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) @@ -564,7 +565,7 @@ pub fn for_each_local_use_after_expr<'tcx, B>( if self.res.is_break() { return; } - if path_to_local_id(e, self.local_id) { + if e.res_local_id() == Some(self.local_id) { self.res = (self.f)(e); } else { walk_expr(self, e); @@ -591,7 +592,7 @@ pub fn for_each_local_use_after_expr<'tcx, B>( // Calls the given function for every unconsumed temporary created by the expression. Note the // function is only guaranteed to be called for types which need to be dropped, but it may be called // for other types. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub fn for_each_unconsumed_temporary<'tcx, B>( cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>, @@ -740,7 +741,7 @@ pub fn for_each_local_assignment<'tcx, B>( fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { if let ExprKind::Assign(lhs, rhs, _) = e.kind && self.res.is_continue() - && path_to_local_id(lhs, self.local_id) + && lhs.res_local_id() == Some(self.local_id) { self.res = (self.f)(rhs); self.visit_expr(rhs); @@ -785,7 +786,7 @@ pub fn local_used_once<'tcx>( let mut expr = None; let cf = for_each_expr(cx, visitable, |e| { - if path_to_local_id(e, id) && expr.replace(e).is_some() { + if e.res_local_id() == Some(id) && expr.replace(e).is_some() { ControlFlow::Break(()) } else { ControlFlow::Continue(()) diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index 3b2ebf0c28ac2..5d5214805b968 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -2,7 +2,7 @@ use clap::{Parser, Subcommand, ValueEnum}; use std::num::NonZero; use std::path::PathBuf; -#[allow(clippy::struct_excessive_bools)] +#[expect(clippy::struct_excessive_bools)] #[derive(Parser, Clone, Debug)] #[command(args_conflicts_with_subcommands = true)] pub(crate) struct LintcheckConfig { diff --git a/lintcheck/src/input.rs b/lintcheck/src/input.rs index 1ed059d2fb114..7dda2b7b25f81 100644 --- a/lintcheck/src/input.rs +++ b/lintcheck/src/input.rs @@ -180,7 +180,7 @@ impl CrateWithSource { /// copies a local folder #[expect(clippy::too_many_lines)] fn download_and_extract(&self) -> Crate { - #[allow(clippy::result_large_err)] + #[expect(clippy::result_large_err)] fn get(path: &str) -> Result { const MAX_RETRIES: u8 = 4; let mut retries = 0; diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 3a60cfa79f410..b30df79023784 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -66,7 +66,7 @@ struct Crate { impl Crate { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy /// issued - #[allow(clippy::too_many_arguments, clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn run_clippy_lints( &self, clippy_driver_path: &Path, @@ -314,7 +314,7 @@ fn main() { } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn lintcheck(config: LintcheckConfig) { let clippy_ver = build_clippy(config.perf); let clippy_driver_path = fs::canonicalize(format!( diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e936f5dc3b7a5..d5d96448a97d3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-10-06" +channel = "nightly-2025-10-16" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/driver.rs b/src/driver.rs index 6bddcbfd94ce4..102ca3fa69f7e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -133,8 +133,7 @@ struct ClippyCallbacks { } impl rustc_driver::Callbacks for ClippyCallbacks { - // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level` - #[allow(rustc::bad_opt_access)] + #[expect(rustc::bad_opt_access, reason = "necessary in clippy driver to set `mir_opt_level`")] fn config(&mut self, config: &mut interface::Config) { let conf_path = clippy_config::lookup_conf_file(); let previous = config.register_lints.take(); @@ -182,15 +181,13 @@ impl rustc_driver::Callbacks for ClippyCallbacks { } } -#[allow(clippy::ignored_unit_patterns)] fn display_help() { println!("{}", help_message()); } const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml"; -#[allow(clippy::too_many_lines)] -#[allow(clippy::ignored_unit_patterns)] +#[expect(clippy::too_many_lines)] pub fn main() { // See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs // about jemalloc. diff --git a/src/main.rs b/src/main.rs index 3c2eec1f05b90..688161c7bfcb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,12 +10,10 @@ use std::process::{self, Command}; use anstream::println; -#[allow(clippy::ignored_unit_patterns)] fn show_help() { println!("{}", help_message()); } -#[allow(clippy::ignored_unit_patterns)] fn show_version() { let version_info = rustc_tools_util::get_version_info!(); println!("{version_info}"); diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 71cd8a6c03cc9..1ac6889352786 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -291,7 +291,6 @@ fn run_ui_toml(cx: &TestContext) { } // Allow `Default::default` as `OptWithSpan` is not nameable -#[allow(clippy::default_trait_access)] fn run_ui_cargo(cx: &TestContext) { if IS_RUSTC_TEST_SUITE { return; @@ -473,7 +472,7 @@ struct DiagnosticCollector { } impl DiagnosticCollector { - #[allow(clippy::assertions_on_constants)] + #[expect(clippy::assertions_on_constants)] fn spawn() -> (Self, thread::JoinHandle<()>) { assert!(!IS_RUSTC_TEST_SUITE && !RUN_INTERNAL_TESTS); diff --git a/tests/symbols-used.rs b/tests/symbols-used.rs index a1049ba64d549..f78f15103cc4e 100644 --- a/tests/symbols-used.rs +++ b/tests/symbols-used.rs @@ -18,7 +18,6 @@ type Result = std::result::Result; type AnyError = Box; #[test] -#[allow(clippy::case_sensitive_file_extension_comparisons)] fn all_symbols_are_used() -> Result<()> { if option_env!("RUSTC_TEST_SUITE").is_some() { return Ok(()); diff --git a/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.stderr b/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.stderr new file mode 100644 index 0000000000000..51c46e4f97b2c --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.stderr @@ -0,0 +1,7 @@ +error: error reading Clippy's configuration file: unknown variant `FooBar`, expected one of `crate`, `file`, `module` + --> $DIR/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml:1:28 + | +1 | inherent-impl-lint-scope = "FooBar" + | ^^^^^^^^ + +error: could not compile `config_fail` (bin "config_fail") due to 1 previous error diff --git a/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.toml b/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.toml new file mode 100644 index 0000000000000..0a4cb6e368f67 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "config_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml b/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml new file mode 100644 index 0000000000000..7ad426743fb8b --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "FooBar" diff --git a/tests/ui-cargo/multiple_inherent_impl/config_fail/src/main.rs b/tests/ui-cargo/multiple_inherent_impl/config_fail/src/main.rs new file mode 100644 index 0000000000000..7d5e783022480 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/config_fail/src/main.rs @@ -0,0 +1,3 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] +fn main() {} diff --git a/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.stderr b/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.stderr new file mode 100644 index 0000000000000..15e0086cf99cc --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.stderr @@ -0,0 +1,57 @@ +error: multiple implementations of this structure + --> src/main.rs:11:1 + | +11 | / impl S { +12 | | //^ Must trigger +13 | | fn second() {} +14 | | } + | |_^ + | +note: first implementation here + --> src/main.rs:7:1 + | + 7 | / impl S { + 8 | | fn first() {} + 9 | | } + | |_^ +note: the lint level is defined here + --> src/main.rs:2:9 + | + 2 | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> src/main.rs:22:5 + | +22 | / impl T { +23 | | //^ Must trigger +24 | | fn second() {} +25 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:16:1 + | +16 | / impl T { +17 | | fn first() {} +18 | | } + | |_^ + +error: multiple implementations of this structure + --> src/main.rs:36:1 + | +36 | / impl b::T { +37 | | //^ Must trigger +38 | | fn second() {} +39 | | } + | |_^ + | +note: first implementation here + --> src/b.rs:4:1 + | + 4 | / impl T { + 5 | | fn first() {} + 6 | | } + | |_^ + +error: could not compile `crate_fail` (bin "crate_fail") due to 3 previous errors diff --git a/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.toml b/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.toml new file mode 100644 index 0000000000000..a280da1bd8964 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "crate_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/tests/ui-cargo/multiple_inherent_impl/crate_fail/clippy.toml b/tests/ui-cargo/multiple_inherent_impl/crate_fail/clippy.toml new file mode 100644 index 0000000000000..262e42e6fccad --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/crate_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "crate" diff --git a/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/b.rs b/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/b.rs new file mode 100644 index 0000000000000..9d01e61925f75 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/b.rs @@ -0,0 +1,6 @@ +pub struct S; +pub struct T; + +impl T { + fn first() {} +} diff --git a/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/main.rs b/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/main.rs new file mode 100644 index 0000000000000..ad95a42955093 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/main.rs @@ -0,0 +1,41 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] + +struct S; +struct T; + +impl S { + fn first() {} +} + +impl S { + //^ Must trigger + fn second() {} +} + +impl T { + fn first() {} +} + +mod a { + use super::T; + impl T { + //^ Must trigger + fn second() {} + } +} + +mod b; + +impl b::S { + //^ Must NOT trigger + fn first() {} + fn second() {} +} + +impl b::T { + //^ Must trigger + fn second() {} +} + +fn main() {} diff --git a/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.stderr b/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.stderr new file mode 100644 index 0000000000000..eb7cf4e0e6ffb --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.stderr @@ -0,0 +1,58 @@ +error: multiple implementations of this structure + --> src/main.rs:13:5 + | +13 | / impl S { +14 | | //^ Must trigger +15 | | fn second() {} +16 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:6:1 + | + 6 | / impl S { + 7 | | fn first() {} + 8 | | } + | |_^ +note: the lint level is defined here + --> src/main.rs:2:9 + | + 2 | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> src/main.rs:26:5 + | +26 | / impl S { +27 | | //^ Must trigger +28 | | +29 | | fn second() {} +30 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:22:5 + | +22 | / impl S { +23 | | fn first() {} +24 | | } + | |_____^ + +error: multiple implementations of this structure + --> src/c.rs:17:5 + | +17 | / impl T { +18 | | //^ Must trigger +19 | | fn second() {} +20 | | } + | |_____^ + | +note: first implementation here + --> src/c.rs:10:5 + | +10 | / impl T { +11 | | fn first() {} +12 | | } + | |_____^ + +error: could not compile `file_fail` (bin "file_fail") due to 3 previous errors diff --git a/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.toml b/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.toml new file mode 100644 index 0000000000000..7f767c65b98cb --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "file_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/tests/ui-cargo/multiple_inherent_impl/file_fail/clippy.toml b/tests/ui-cargo/multiple_inherent_impl/file_fail/clippy.toml new file mode 100644 index 0000000000000..4229797a91f36 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/file_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "file" diff --git a/tests/ui-cargo/multiple_inherent_impl/file_fail/src/c.rs b/tests/ui-cargo/multiple_inherent_impl/file_fail/src/c.rs new file mode 100644 index 0000000000000..7757061e87b04 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/file_fail/src/c.rs @@ -0,0 +1,21 @@ +pub struct S; +struct T; + +impl S { + fn first() {} +} + +mod d { + use super::T; + impl T { + fn first() {} + } +} + +mod e { + use super::T; + impl T { + //^ Must trigger + fn second() {} + } +} diff --git a/tests/ui-cargo/multiple_inherent_impl/file_fail/src/main.rs b/tests/ui-cargo/multiple_inherent_impl/file_fail/src/main.rs new file mode 100644 index 0000000000000..97514fd30e047 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/file_fail/src/main.rs @@ -0,0 +1,40 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] + +struct S; + +impl S { + fn first() {} +} + +mod a { + use super::S; + + impl S { + //^ Must trigger + fn second() {} + } +} + +mod b { + struct S; + + impl S { + fn first() {} + } + + impl S { + //^ Must trigger + + fn second() {} + } +} + +mod c; + +impl c::S { + //^ Must NOT trigger + fn second() {} +} + +fn main() {} diff --git a/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.stderr b/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.stderr new file mode 100644 index 0000000000000..dd02afa1d39b3 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.stderr @@ -0,0 +1,41 @@ +error: multiple implementations of this structure + --> src/main.rs:26:5 + | +26 | / impl S { +27 | | //^ Must trigger +28 | | +29 | | fn second() {} +30 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:22:5 + | +22 | / impl S { +23 | | fn first() {} +24 | | } + | |_____^ +note: the lint level is defined here + --> src/main.rs:2:9 + | + 2 | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> src/c.rs:12:1 + | +12 | / impl T { +13 | | //^ Must trigger +14 | | fn second() {} +15 | | } + | |_^ + | +note: first implementation here + --> src/c.rs:8:1 + | + 8 | / impl T { + 9 | | fn first() {} +10 | | } + | |_^ + +error: could not compile `module_fail` (bin "module_fail") due to 2 previous errors diff --git a/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.toml b/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.toml new file mode 100644 index 0000000000000..4b57af0d8fefb --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "module_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/tests/ui-cargo/multiple_inherent_impl/module_fail/clippy.toml b/tests/ui-cargo/multiple_inherent_impl/module_fail/clippy.toml new file mode 100644 index 0000000000000..293dfa183fcea --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/module_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "module" diff --git a/tests/ui-cargo/multiple_inherent_impl/module_fail/src/c.rs b/tests/ui-cargo/multiple_inherent_impl/module_fail/src/c.rs new file mode 100644 index 0000000000000..1d51ebe5d22a0 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/module_fail/src/c.rs @@ -0,0 +1,15 @@ +pub struct S; +struct T; + +impl S { + fn first() {} +} + +impl T { + fn first() {} +} + +impl T { + //^ Must trigger + fn second() {} +} diff --git a/tests/ui-cargo/multiple_inherent_impl/module_fail/src/main.rs b/tests/ui-cargo/multiple_inherent_impl/module_fail/src/main.rs new file mode 100644 index 0000000000000..17b1b777f7be0 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/module_fail/src/main.rs @@ -0,0 +1,40 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] + +struct S; + +impl S { + fn first() {} +} + +mod a { + use super::S; + + impl S { + //^ Must NOT trigger + fn second() {} + } +} + +mod b { + struct S; + + impl S { + fn first() {} + } + + impl S { + //^ Must trigger + + fn second() {} + } +} + +mod c; + +impl c::S { + //^ Must NOT trigger + fn second() {} +} + +fn main() {} diff --git a/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr b/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr index 59a7146ac90fd..bfe2486c85029 100644 --- a/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr +++ b/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr @@ -5,10 +5,10 @@ error: module has unnecessary safety comment | ^^^^^^^^ | help: consider removing the safety comment - --> src/main.rs:1:1 + --> src/main.rs:1:4 | 1 | // SAFETY: ... - | ^^^^^^^^^^^^^^ + | ^^^^^^^ = note: requested on the command line with `-D clippy::unnecessary-safety-comment` error: module has unnecessary safety comment @@ -18,9 +18,9 @@ error: module has unnecessary safety comment | ^^^^^^^^ | help: consider removing the safety comment - --> src/main.rs:4:1 + --> src/main.rs:4:4 | 4 | // SAFETY: ... - | ^^^^^^^^^^^^^^ + | ^^^^^^^ error: could not compile `undocumented_unsafe_blocks` (bin "undocumented_unsafe_blocks") 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 20aeb4bb8498e..2d9503c5ac53a 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -49,6 +49,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect excessive-nesting-threshold future-size-threshold ignore-interior-mutability + inherent-impl-lint-scope large-error-threshold lint-commented-code literal-representation-threshold @@ -66,6 +67,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold @@ -144,6 +146,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect excessive-nesting-threshold future-size-threshold ignore-interior-mutability + inherent-impl-lint-scope large-error-threshold lint-commented-code literal-representation-threshold @@ -161,6 +164,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold @@ -239,6 +243,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni excessive-nesting-threshold future-size-threshold ignore-interior-mutability + inherent-impl-lint-scope large-error-threshold lint-commented-code literal-representation-threshold @@ -256,6 +261,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr index bfc14be5421fc..61e5af81d8276 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr @@ -247,10 +247,10 @@ LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` @@ -289,10 +289,10 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:8 | LL | // SAFETY: this is more than one level away, so it should warn - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:545:12 @@ -342,17 +342,137 @@ LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; | = help: consider adding a safety comment on the preceding line +error: constant has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:701:5 + | +LL | const UNIX_EPOCH_JULIAN_DAY: i32 = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:699:8 + | +LL | // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + | ^^^^^^^ + error: statement has unnecessary safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:721:5 | LL | _ = bar(); | ^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:720:8 | LL | // SAFETY: unnecessary_safety_comment triggers here - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:741:5 + | +LL | mod x {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:740:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:746:5 + | +LL | mod y {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:744:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:751:5 + | +LL | mod z {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:750:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:759:5 + | +LL | mod y {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:757:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: statement has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:774:9 + | +LL | let x = 34; + | ^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:772:12 + | +LL | // SAFETY: ... + | ^^^^^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:781:5 + | +LL | unsafe fn unsafe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:780:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:787:5 + | +LL | unsafe fn unsafe_block_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:785:8 + | +LL | SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:791:5 + | +LL | fn safe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:790:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:795:5 + | +LL | fn safe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:794:9 + | +LL | /// SAFETY: Bla + | ^^^^^^^ -error: aborting due to 40 previous errors +error: aborting due to 50 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index cebfc48a884f6..e252cffea9160 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -247,10 +247,10 @@ LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` @@ -297,10 +297,10 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:8 | LL | // SAFETY: this is more than one level away, so it should warn - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:545:12 @@ -439,24 +439,104 @@ LL | unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian = help: consider adding a safety comment on the preceding line error: statement has unnecessary safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:721:5 | LL | _ = bar(); | ^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:720:8 | LL | // SAFETY: unnecessary_safety_comment triggers here - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:733:12 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:735:12 | LL | return unsafe { h() }; | ^^^^^^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: aborting due to 53 previous errors +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:741:5 + | +LL | mod x {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:740:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:751:5 + | +LL | mod z {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:750:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:766:9 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:781:5 + | +LL | unsafe fn unsafe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:780:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:787:5 + | +LL | unsafe fn unsafe_block_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:785:8 + | +LL | SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:791:5 + | +LL | fn safe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:790:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:795:5 + | +LL | fn safe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:794:9 + | +LL | /// SAFETY: Bla + | ^^^^^^^ + +error: aborting due to 60 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index a2d7c1b6c7967..db9e81cf10a1a 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -701,6 +701,8 @@ mod issue_11709_regression { const UNIX_EPOCH_JULIAN_DAY: i32 = unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer(); //~[disabled]^ undocumented_unsafe_blocks + // This shouldn't be linted, Issue #15755 + //~[default]^^^^ unnecessary_safety_comment } fn issue_13039() { @@ -734,4 +736,64 @@ fn rfl_issue15034() -> i32 { //~[disabled]^ ERROR: unsafe block missing a safety comment } +mod issue_14555 { + // SAFETY: ... + mod x {} + //~^ unnecessary_safety_comment + + // SAFETY: ... + #[doc(hidden)] + mod y {} + //~[default]^ unnecessary_safety_comment + + #[doc(hidden)] + // SAFETY: ... + mod z {} + //~^ unnecessary_safety_comment +} + +mod issue_15754 { + #[must_use] + // SAFETY: ... + #[doc(hidden)] + mod y {} + //~[default]^ unnecessary_safety_comment + + fn foo() { + #[doc(hidden)] + // SAFETY: unnecessary_safety_comment should not trigger here + #[allow(unsafe_code)] + unsafe {} + //~[disabled]^ undocumented_unsafe_blocks + } + + fn bar() { + #[doc(hidden)] + // SAFETY: ... + #[allow(clippy::unnecessary_cast)] + let x = 34; + //~[default]^ unnecessary_safety_comment + } +} + +mod unsafe_fns { + // SAFETY: Bla + unsafe fn unsafe_comment() {} + //~^ unnecessary_safety_comment + + /* + SAFETY: Bla + */ + unsafe fn unsafe_block_comment() {} + //~^ unnecessary_safety_comment + + // SAFETY: Bla + fn safe_comment() {} + //~^ unnecessary_safety_comment + + /// SAFETY: Bla + fn safe_doc_comment() {} + //~^ unnecessary_safety_comment +} + fn main() {} diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.fixed b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.fixed new file mode 100644 index 0000000000000..cc8d5028b7276 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.fixed @@ -0,0 +1,20 @@ +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled + +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] + +mod unsafe_fns { + /// # Safety Bla + unsafe fn unsafe_doc_comment() {} + //~^ unnecessary_safety_comment + + /** + * # Safety Bla + */ + unsafe fn unsafe_block_doc_comment() {} + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.stderr new file mode 100644 index 0000000000000..95e47dc7ed639 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.stderr @@ -0,0 +1,22 @@ +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:10:5 + | +LL | /// SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | unsafe fn unsafe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:16:5 + | +LL | * SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | */ +LL | unsafe fn unsafe_block_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.fixed b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.fixed new file mode 100644 index 0000000000000..cc8d5028b7276 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.fixed @@ -0,0 +1,20 @@ +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled + +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] + +mod unsafe_fns { + /// # Safety Bla + unsafe fn unsafe_doc_comment() {} + //~^ unnecessary_safety_comment + + /** + * # Safety Bla + */ + unsafe fn unsafe_block_doc_comment() {} + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.stderr new file mode 100644 index 0000000000000..95e47dc7ed639 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.stderr @@ -0,0 +1,22 @@ +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:10:5 + | +LL | /// SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | unsafe fn unsafe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:16:5 + | +LL | * SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | */ +LL | unsafe fn unsafe_block_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs new file mode 100644 index 0000000000000..14b91126caa66 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs @@ -0,0 +1,20 @@ +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled + +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] + +mod unsafe_fns { + /// SAFETY: Bla + unsafe fn unsafe_doc_comment() {} + //~^ unnecessary_safety_comment + + /** + * SAFETY: Bla + */ + unsafe fn unsafe_block_doc_comment() {} + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/tests/ui-toml/use_self/default/clippy.toml b/tests/ui-toml/use_self/default/clippy.toml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/ui-toml/use_self/disabled/clippy.toml b/tests/ui-toml/use_self/disabled/clippy.toml new file mode 100644 index 0000000000000..866cc5c624b5f --- /dev/null +++ b/tests/ui-toml/use_self/disabled/clippy.toml @@ -0,0 +1 @@ +recursive-self-in-type-definitions = false diff --git a/tests/ui-toml/use_self/use_self.default.fixed b/tests/ui-toml/use_self/use_self.default.fixed new file mode 100644 index 0000000000000..288e304c60a8e --- /dev/null +++ b/tests/ui-toml/use_self/use_self.default.fixed @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Self) {} + //~[default,disabled]^ use_self +} diff --git a/tests/ui-toml/use_self/use_self.default.stderr b/tests/ui-toml/use_self/use_self.default.stderr new file mode 100644 index 0000000000000..34cfdfd938aa1 --- /dev/null +++ b/tests/ui-toml/use_self/use_self.default.stderr @@ -0,0 +1,17 @@ +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:10:22 + | +LL | flag: Option>, + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:15:13 + | +LL | fn x(_: Basic) {} + | ^^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/use_self/use_self.disabled.fixed b/tests/ui-toml/use_self/use_self.disabled.fixed new file mode 100644 index 0000000000000..227606e69005e --- /dev/null +++ b/tests/ui-toml/use_self/use_self.disabled.fixed @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Self) {} + //~[default,disabled]^ use_self +} diff --git a/tests/ui-toml/use_self/use_self.disabled.stderr b/tests/ui-toml/use_self/use_self.disabled.stderr new file mode 100644 index 0000000000000..1801744f0d417 --- /dev/null +++ b/tests/ui-toml/use_self/use_self.disabled.stderr @@ -0,0 +1,11 @@ +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:15:13 + | +LL | fn x(_: Basic) {} + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/use_self/use_self.rs b/tests/ui-toml/use_self/use_self.rs new file mode 100644 index 0000000000000..d20006d4df1ae --- /dev/null +++ b/tests/ui-toml/use_self/use_self.rs @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Basic) {} + //~[default,disabled]^ use_self +} diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index e453299edbcfc..ff9fe2425ff9f 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -23,13 +23,13 @@ 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 - && is_path_diagnostic_item(cx, func, sym::string_new) + && func.res(cx).is_diag_item(cx, 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 - && is_path_diagnostic_item(cx, func1, sym::mem_drop) + && func1.res(cx).is_diag_item(cx, sym::mem_drop) && args1.len() == 1 { // report your lint here diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout index 2b179d45112ee..024121b14f9bc 100644 --- a/tests/ui/author/call.stdout +++ b/tests/ui/author/call.stdout @@ -1,7 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && is_path_diagnostic_item(cx, func, sym::cmp_min) + && func.res(cx).is_diag_item(cx, 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/issue_3849.stdout b/tests/ui/author/issue_3849.stdout index f02ea5bf075f7..b88058f974dba 100644 --- a/tests/ui/author/issue_3849.stdout +++ b/tests/ui/author/issue_3849.stdout @@ -1,7 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && is_path_diagnostic_item(cx, func, sym::transmute) + && func.res(cx).is_diag_item(cx, sym::transmute) && args.len() == 1 && let PatKind::Wild = local.pat.kind { diff --git a/tests/ui/char_lit_as_u8_unfixable.rs b/tests/ui/char_lit_as_u8_unfixable.rs index e5c094f158ec7..c8774c7f30916 100644 --- a/tests/ui/char_lit_as_u8_unfixable.rs +++ b/tests/ui/char_lit_as_u8_unfixable.rs @@ -1,4 +1,3 @@ -//@no-rustfix #![warn(clippy::char_lit_as_u8)] fn main() { diff --git a/tests/ui/char_lit_as_u8_unfixable.stderr b/tests/ui/char_lit_as_u8_unfixable.stderr index 49e555ae638a2..4c2770cd2a4e0 100644 --- a/tests/ui/char_lit_as_u8_unfixable.stderr +++ b/tests/ui/char_lit_as_u8_unfixable.stderr @@ -1,5 +1,5 @@ error: casting a character literal to `u8` truncates - --> tests/ui/char_lit_as_u8_unfixable.rs:6:13 + --> tests/ui/char_lit_as_u8_unfixable.rs:5:13 | LL | let _ = '❤' as u8; | ^^^^^^^^^ diff --git a/tests/ui/clone_on_ref_ptr.fixed b/tests/ui/clone_on_ref_ptr.fixed index 8ef4b36566363..ede9d171517e7 100644 --- a/tests/ui/clone_on_ref_ptr.fixed +++ b/tests/ui/clone_on_ref_ptr.fixed @@ -49,3 +49,33 @@ mod issue2076 { //~^ clone_on_ref_ptr } } + +#[allow( + clippy::needless_borrow, + reason = "the suggestion creates `Weak::clone(&rec)`, but `rec` is already a reference" +)] +mod issue15009 { + use std::rc::{Rc, Weak}; + use std::sync::atomic::{AtomicU32, Ordering}; + + fn main() { + let counter = AtomicU32::new(0); + let counter_ref = &counter; + let factorial = Rc::new_cyclic(move |rec| { + let rec = std::rc::Weak::::clone(&rec) as Weak u32>; + //~^ clone_on_ref_ptr + move |x| { + // can capture env + counter_ref.fetch_add(1, Ordering::Relaxed); + match x { + 0 => 1, + x => x * rec.upgrade().unwrap()(x - 1), + } + } + }); + println!("{}", factorial(5)); // 120 + println!("{}", counter.load(Ordering::Relaxed)); // 6 + println!("{}", factorial(7)); // 5040 + println!("{}", counter.load(Ordering::Relaxed)); // 14 + } +} diff --git a/tests/ui/clone_on_ref_ptr.rs b/tests/ui/clone_on_ref_ptr.rs index fbd787099aee3..5999b4069d0f2 100644 --- a/tests/ui/clone_on_ref_ptr.rs +++ b/tests/ui/clone_on_ref_ptr.rs @@ -49,3 +49,33 @@ mod issue2076 { //~^ clone_on_ref_ptr } } + +#[allow( + clippy::needless_borrow, + reason = "the suggestion creates `Weak::clone(&rec)`, but `rec` is already a reference" +)] +mod issue15009 { + use std::rc::{Rc, Weak}; + use std::sync::atomic::{AtomicU32, Ordering}; + + fn main() { + let counter = AtomicU32::new(0); + let counter_ref = &counter; + let factorial = Rc::new_cyclic(move |rec| { + let rec = rec.clone() as Weak u32>; + //~^ clone_on_ref_ptr + move |x| { + // can capture env + counter_ref.fetch_add(1, Ordering::Relaxed); + match x { + 0 => 1, + x => x * rec.upgrade().unwrap()(x - 1), + } + } + }); + println!("{}", factorial(5)); // 120 + println!("{}", counter.load(Ordering::Relaxed)); // 6 + println!("{}", factorial(7)); // 5040 + println!("{}", counter.load(Ordering::Relaxed)); // 14 + } +} diff --git a/tests/ui/clone_on_ref_ptr.stderr b/tests/ui/clone_on_ref_ptr.stderr index b15f0e803a352..b8ddc3058c012 100644 --- a/tests/ui/clone_on_ref_ptr.stderr +++ b/tests/ui/clone_on_ref_ptr.stderr @@ -37,5 +37,11 @@ error: using `.clone()` on a ref-counted pointer LL | Some(try_opt!(Some(rc)).clone()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` -error: aborting due to 6 previous errors +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:65:23 + | +LL | let rec = rec.clone() as Weak u32>; + | ^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rec)` + +error: aborting due to 7 previous errors diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 8931a3aa09c61..84f958ee84584 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -304,16 +304,25 @@ pub fn test_2(x: Issue9647) { } } -// https://github.com/rust-lang/rust-clippy/issues/14281 -fn lint_emitted_at_right_node(opt: Option>) { - let n = match opt { - #[expect(clippy::collapsible_match)] - Some(n) => match n { - Ok(n) => n, - _ => return, - }, - None => return, - }; +mod issue_13287 { + enum Token { + Name, + Other, + } + + struct Error { + location: u32, + token: Option, + } + + fn struct_field_pat_with_binding_mode(err: Option) { + if let Some(Error { ref token, .. }) = err { + if let Some(Token::Name) = token { + //~^ collapsible_match + println!("token used as a ref"); + } + } + } } pub fn issue_14155() { @@ -357,6 +366,18 @@ pub fn issue_14155() { } } +// https://github.com/rust-lang/rust-clippy/issues/14281 +fn lint_emitted_at_right_node(opt: Option>) { + let n = match opt { + #[expect(clippy::collapsible_match)] + Some(n) => match n { + Ok(n) => n, + _ => return, + }, + None => return, + }; +} + fn make() -> T { unimplemented!() } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 14b1c1b187e4f..d217948d4ca6c 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -230,7 +230,7 @@ help: the outer pattern can be modified to include the inner pattern LL | if let Issue9647::A { a, .. } = x { | ^ replace this binding LL | if let Some(u) = a { - | ^^^^^^^ with this pattern, prefixed by `a`: + | ^^^^^^^ with this pattern, prefixed by `a: ` error: this `if let` can be collapsed into the outer `if let` --> tests/ui/collapsible_match.rs:299:9 @@ -250,8 +250,25 @@ LL | if let Issue9647::A { a: Some(a), .. } = x { LL | if let Some(u) = a { | ^^^^^^^ with this pattern +error: this `if let` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:320:13 + | +LL | / if let Some(Token::Name) = token { +LL | | +LL | | println!("token used as a ref"); +LL | | } + | |_____________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:319:29 + | +LL | if let Some(Error { ref token, .. }) = err { + | ^^^^^^^^^ replace this binding +LL | if let Some(Token::Name) = token { + | ^^^^^^^^^^^^^^^^^ with this pattern, prefixed by `token: ` + error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:322:9 + --> tests/ui/collapsible_match.rs:331:9 | LL | / match *last { LL | | @@ -263,7 +280,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:321:17 + --> tests/ui/collapsible_match.rs:330:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().copied()` @@ -274,7 +291,7 @@ LL | "a" | "b" => { | ^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:332:9 + --> tests/ui/collapsible_match.rs:341:9 | LL | / match &last { LL | | @@ -286,7 +303,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:331:17 + --> tests/ui/collapsible_match.rs:340:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().as_ref()` @@ -297,7 +314,7 @@ LL | &&"a" | &&"b" => { | ^^^^^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:342:9 + --> tests/ui/collapsible_match.rs:351:9 | LL | / match &mut last { LL | | @@ -309,7 +326,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:341:17 + --> tests/ui/collapsible_match.rs:350:17 | LL | if let Some(mut last) = arr.last_mut() { | ^^^^^^^^ -------------- use: `arr.last_mut().as_mut()` @@ -319,5 +336,5 @@ LL | if let Some(mut last) = arr.last_mut() { LL | &mut &mut "a" | &mut &mut "b" => { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/crashes/ice-15684.rs b/tests/ui/crashes/ice-15684.rs new file mode 100644 index 0000000000000..12f36042a0fea --- /dev/null +++ b/tests/ui/crashes/ice-15684.rs @@ -0,0 +1,10 @@ +#![warn(clippy::unnecessary_safety_comment)] + +fn foo() -> i32 { + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + #[must_use] + return 33; + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/tests/ui/crashes/ice-15684.stderr b/tests/ui/crashes/ice-15684.stderr new file mode 100644 index 0000000000000..0d4eb624a2b12 --- /dev/null +++ b/tests/ui/crashes/ice-15684.stderr @@ -0,0 +1,16 @@ +error: statement has unnecessary safety comment + --> tests/ui/crashes/ice-15684.rs:6:5 + | +LL | return 33; + | ^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui/crashes/ice-15684.rs:4:8 + | +LL | // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index b8adb7601db64..6b60421c35490 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -133,7 +133,7 @@ fn issue14558() { fn main() {} mod issue15708 { - // Check that the lint posts on the type definition node + // Check that `allow`/`expect` attributes are recognized on the type definition node #[expect(clippy::expl_impl_clone_on_copy)] #[derive(Copy)] struct S; @@ -144,3 +144,16 @@ mod issue15708 { } } } + +mod issue15842 { + #[derive(Copy)] + struct S; + + // Check that `allow`/`expect` attributes are recognized on the `impl Clone` node + #[expect(clippy::expl_impl_clone_on_copy)] + impl Clone for S { + fn clone(&self) -> Self { + S + } + } +} diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index 7dbc38a2f621f..2b97a58e9d6f6 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -9,16 +9,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:16:1 - | -LL | / impl Clone for Qux { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` @@ -33,16 +24,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:42:1 - | -LL | / impl<'a> Clone for Lt<'a> { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:55:1 @@ -55,16 +37,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:55:1 - | -LL | / impl Clone for BigArray { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:68:1 @@ -77,16 +50,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:68:1 - | -LL | / impl Clone for FnPtr { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:90:1 @@ -99,16 +63,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:90:1 - | -LL | / impl Clone for Generic2 { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: aborting due to 5 previous errors diff --git a/tests/ui/explicit_write_in_test.rs b/tests/ui/explicit_write_in_test.rs new file mode 100644 index 0000000000000..df020b7f1382a --- /dev/null +++ b/tests/ui/explicit_write_in_test.rs @@ -0,0 +1,9 @@ +//@ check-pass +#![warn(clippy::explicit_write)] + +#[test] +fn test() { + use std::io::Write; + writeln!(std::io::stderr(), "I am an explicit write.").unwrap(); + eprintln!("I am not an explicit write."); +} diff --git a/tests/ui/explicit_write_in_test.stderr b/tests/ui/explicit_write_in_test.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/ui/impl.rs b/tests/ui/impl.rs index 1b9e4a5cdee13..15cb61c6eebd9 100644 --- a/tests/ui/impl.rs +++ b/tests/ui/impl.rs @@ -68,7 +68,20 @@ struct OneAllowedImpl; impl OneAllowedImpl {} #[allow(clippy::multiple_inherent_impl)] impl OneAllowedImpl {} -impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. +impl OneAllowedImpl {} +//~^ multiple_inherent_impl + +#[expect(clippy::multiple_inherent_impl)] +struct ExpectedFulfilled; + +impl ExpectedFulfilled {} +impl ExpectedFulfilled {} + +struct OneExpected; +impl OneExpected {} +#[expect(clippy::multiple_inherent_impl)] +impl OneExpected {} +impl OneExpected {} //~^ multiple_inherent_impl fn main() {} diff --git a/tests/ui/impl.stderr b/tests/ui/impl.stderr index 355927b782537..93d4b3998f908 100644 --- a/tests/ui/impl.stderr +++ b/tests/ui/impl.stderr @@ -57,7 +57,7 @@ LL | | } error: multiple implementations of this structure --> tests/ui/impl.rs:71:1 | -LL | impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. +LL | impl OneAllowedImpl {} | ^^^^^^^^^^^^^^^^^^^^^^ | note: first implementation here @@ -66,5 +66,17 @@ note: first implementation here LL | impl OneAllowedImpl {} | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: multiple implementations of this structure + --> tests/ui/impl.rs:84:1 + | +LL | impl OneExpected {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: first implementation here + --> tests/ui/impl.rs:81:1 + | +LL | impl OneExpected {} + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/infinite_loops.rs b/tests/ui/infinite_loops.rs index 7d01a7fb61fc1..0bde31aca0304 100644 --- a/tests/ui/infinite_loops.rs +++ b/tests/ui/infinite_loops.rs @@ -1,7 +1,7 @@ //@no-rustfix: multiple suggestions add `-> !` to the same fn //@aux-build:proc_macros.rs -#![allow(clippy::never_loop)] +#![allow(clippy::never_loop, clippy::while_let_loop)] #![warn(clippy::infinite_loop)] extern crate proc_macros; diff --git a/tests/ui/legacy_numeric_constants_unfixable.rs b/tests/ui/legacy_numeric_constants_unfixable.rs index 9bf0f7f355aee..084d97fdc0f74 100644 --- a/tests/ui/legacy_numeric_constants_unfixable.rs +++ b/tests/ui/legacy_numeric_constants_unfixable.rs @@ -77,3 +77,14 @@ fn msrv_juust_right() { use std::u32::MAX; //~^ ERROR: importing a legacy numeric constant } + +macro_rules! foo { + ($a: ty) => { + let _ = <$a>::max_value(); + let _ = (<$a>::max_value)(); + }; +} + +fn issue15805() { + foo!(u8); +} diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed new file mode 100644 index 0000000000000..d1f798bd5c54b --- /dev/null +++ b/tests/ui/manual_assert.edition2018.fixed @@ -0,0 +1,98 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 + +#![warn(clippy::manual_assert)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] + +macro_rules! one { + () => { + 1 + }; +} + +fn main() { + let a = vec![1, 2, 3]; + let c = Some(2); + if !a.is_empty() + && a.len() == 3 + && c.is_some() + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + { + panic!("qaqaq{:?}", a); + } + //~^ manual_assert + assert!(a.is_empty(), "qaqaq{:?}", a); + //~^ manual_assert + assert!(a.is_empty(), "qwqwq"); + if a.len() == 3 { + println!("qwq"); + println!("qwq"); + println!("qwq"); + } + if let Some(b) = c { + panic!("orz {}", b); + } + if a.len() == 3 { + panic!("qaqaq"); + } else { + println!("qwq"); + } + let b = vec![1, 2, 3]; + //~^ manual_assert + assert!(!b.is_empty(), "panic1"); + //~^ manual_assert + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + //~^ manual_assert + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + //~^ manual_assert + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + //~^ manual_assert + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + //~^ manual_assert + assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } +} + +fn issue7730(a: u8) { + // Suggestion should preserve comment + //~^ manual_assert + // comment + /* this is a + multiline + comment */ + /// Doc comment + // comment after `panic!` + assert!(a <= 2, "panic with comment"); +} + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = //~^ manual_assert + assert!(N != 0, ); + } +} + +fn issue15227(left: u64, right: u64) -> u64 { + macro_rules! is_x86_feature_detected { + ($feature:literal) => { + $feature.len() > 0 && $feature.starts_with("ss") + }; + } + + //~^ manual_assert + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); + unsafe { todo!() } +} diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 2e9c9045caae1..c81a855275270 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:32:5 + --> tests/ui/manual_assert.rs:30:5 | LL | / if !a.is_empty() { LL | | @@ -9,17 +9,14 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qaqaq{:?}", a); -LL - } +LL ~ LL + assert!(a.is_empty(), "qaqaq{:?}", a); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:36:5 + --> tests/ui/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | @@ -27,17 +24,14 @@ LL | | panic!("qwqwq"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qwqwq"); -LL - } +LL ~ LL + assert!(a.is_empty(), "qwqwq"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:54:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | @@ -45,17 +39,14 @@ LL | | panic!("panic1"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() { -LL - -LL - panic!("panic1"); -LL - } +LL ~ LL + assert!(!b.is_empty(), "panic1"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:58:5 + --> tests/ui/manual_assert.rs:56:5 | LL | / if b.is_empty() && a.is_empty() { LL | | @@ -63,17 +54,14 @@ LL | | panic!("panic2"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() && a.is_empty() { -LL - -LL - panic!("panic2"); -LL - } +LL ~ LL + assert!(!(b.is_empty() && a.is_empty()), "panic2"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:60:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | @@ -81,17 +69,14 @@ LL | | panic!("panic3"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() && !b.is_empty() { -LL - -LL - panic!("panic3"); -LL - } +LL ~ LL + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:66:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if b.is_empty() || a.is_empty() { LL | | @@ -99,17 +84,14 @@ LL | | panic!("panic4"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() || a.is_empty() { -LL - -LL - panic!("panic4"); -LL - } +LL ~ LL + assert!(!(b.is_empty() || a.is_empty()), "panic4"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:70:5 + --> tests/ui/manual_assert.rs:68:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | @@ -117,17 +99,14 @@ LL | | panic!("panic5"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() || !b.is_empty() { -LL - -LL - panic!("panic5"); -LL - } +LL ~ LL + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:74:5 + --> tests/ui/manual_assert.rs:72:5 | LL | / if a.is_empty() { LL | | @@ -135,17 +114,14 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() { -LL - -LL - panic!("with expansion {}", one!()) -LL - } +LL ~ LL + assert!(!a.is_empty(), "with expansion {}", one!()); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:87:5 + --> tests/ui/manual_assert.rs:85:5 | LL | / if a > 2 { LL | | @@ -156,22 +132,20 @@ LL | | panic!("panic with comment") // comment after `panic!` LL | | } | |_____^ | -help: try instead - | -LL - if a > 2 { -LL - -LL - // comment -LL - /* this is a -LL - multiline -LL - comment */ -LL - /// Doc comment -LL - panic!("panic with comment") // comment after `panic!` -LL - } +help: replace `if`-then-`panic!` with `assert!` + | +LL ~ +LL + // comment +LL + /* this is a +LL + multiline +LL + comment */ +LL + /// Doc comment +LL + // comment after `panic!` LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:102:25 + --> tests/ui/manual_assert.rs:100:25 | LL | const BAR: () = if N == 0 { | _________________________^ @@ -180,17 +154,14 @@ LL | | panic!() LL | | }; | |_________^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - const BAR: () = if N == 0 { -LL - -LL - panic!() -LL - }; -LL + const BAR: () = assert!(N != 0, ); +LL ~ const BAR: () = +LL ~ assert!(N != 0, ); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:116:5 + --> tests/ui/manual_assert.rs:114:5 | LL | / if !is_x86_feature_detected!("ssse3") { LL | | @@ -198,12 +169,9 @@ LL | | panic!("SSSE3 is not supported"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !is_x86_feature_detected!("ssse3") { -LL - -LL - panic!("SSSE3 is not supported"); -LL - } +LL ~ LL + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); | diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed new file mode 100644 index 0000000000000..d1f798bd5c54b --- /dev/null +++ b/tests/ui/manual_assert.edition2021.fixed @@ -0,0 +1,98 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 + +#![warn(clippy::manual_assert)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] + +macro_rules! one { + () => { + 1 + }; +} + +fn main() { + let a = vec![1, 2, 3]; + let c = Some(2); + if !a.is_empty() + && a.len() == 3 + && c.is_some() + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + { + panic!("qaqaq{:?}", a); + } + //~^ manual_assert + assert!(a.is_empty(), "qaqaq{:?}", a); + //~^ manual_assert + assert!(a.is_empty(), "qwqwq"); + if a.len() == 3 { + println!("qwq"); + println!("qwq"); + println!("qwq"); + } + if let Some(b) = c { + panic!("orz {}", b); + } + if a.len() == 3 { + panic!("qaqaq"); + } else { + println!("qwq"); + } + let b = vec![1, 2, 3]; + //~^ manual_assert + assert!(!b.is_empty(), "panic1"); + //~^ manual_assert + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + //~^ manual_assert + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + //~^ manual_assert + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + //~^ manual_assert + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + //~^ manual_assert + assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } +} + +fn issue7730(a: u8) { + // Suggestion should preserve comment + //~^ manual_assert + // comment + /* this is a + multiline + comment */ + /// Doc comment + // comment after `panic!` + assert!(a <= 2, "panic with comment"); +} + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = //~^ manual_assert + assert!(N != 0, ); + } +} + +fn issue15227(left: u64, right: u64) -> u64 { + macro_rules! is_x86_feature_detected { + ($feature:literal) => { + $feature.len() > 0 && $feature.starts_with("ss") + }; + } + + //~^ manual_assert + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); + unsafe { todo!() } +} diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 2e9c9045caae1..c81a855275270 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:32:5 + --> tests/ui/manual_assert.rs:30:5 | LL | / if !a.is_empty() { LL | | @@ -9,17 +9,14 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qaqaq{:?}", a); -LL - } +LL ~ LL + assert!(a.is_empty(), "qaqaq{:?}", a); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:36:5 + --> tests/ui/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | @@ -27,17 +24,14 @@ LL | | panic!("qwqwq"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qwqwq"); -LL - } +LL ~ LL + assert!(a.is_empty(), "qwqwq"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:54:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | @@ -45,17 +39,14 @@ LL | | panic!("panic1"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() { -LL - -LL - panic!("panic1"); -LL - } +LL ~ LL + assert!(!b.is_empty(), "panic1"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:58:5 + --> tests/ui/manual_assert.rs:56:5 | LL | / if b.is_empty() && a.is_empty() { LL | | @@ -63,17 +54,14 @@ LL | | panic!("panic2"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() && a.is_empty() { -LL - -LL - panic!("panic2"); -LL - } +LL ~ LL + assert!(!(b.is_empty() && a.is_empty()), "panic2"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:60:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | @@ -81,17 +69,14 @@ LL | | panic!("panic3"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() && !b.is_empty() { -LL - -LL - panic!("panic3"); -LL - } +LL ~ LL + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:66:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if b.is_empty() || a.is_empty() { LL | | @@ -99,17 +84,14 @@ LL | | panic!("panic4"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() || a.is_empty() { -LL - -LL - panic!("panic4"); -LL - } +LL ~ LL + assert!(!(b.is_empty() || a.is_empty()), "panic4"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:70:5 + --> tests/ui/manual_assert.rs:68:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | @@ -117,17 +99,14 @@ LL | | panic!("panic5"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() || !b.is_empty() { -LL - -LL - panic!("panic5"); -LL - } +LL ~ LL + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:74:5 + --> tests/ui/manual_assert.rs:72:5 | LL | / if a.is_empty() { LL | | @@ -135,17 +114,14 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() { -LL - -LL - panic!("with expansion {}", one!()) -LL - } +LL ~ LL + assert!(!a.is_empty(), "with expansion {}", one!()); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:87:5 + --> tests/ui/manual_assert.rs:85:5 | LL | / if a > 2 { LL | | @@ -156,22 +132,20 @@ LL | | panic!("panic with comment") // comment after `panic!` LL | | } | |_____^ | -help: try instead - | -LL - if a > 2 { -LL - -LL - // comment -LL - /* this is a -LL - multiline -LL - comment */ -LL - /// Doc comment -LL - panic!("panic with comment") // comment after `panic!` -LL - } +help: replace `if`-then-`panic!` with `assert!` + | +LL ~ +LL + // comment +LL + /* this is a +LL + multiline +LL + comment */ +LL + /// Doc comment +LL + // comment after `panic!` LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:102:25 + --> tests/ui/manual_assert.rs:100:25 | LL | const BAR: () = if N == 0 { | _________________________^ @@ -180,17 +154,14 @@ LL | | panic!() LL | | }; | |_________^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - const BAR: () = if N == 0 { -LL - -LL - panic!() -LL - }; -LL + const BAR: () = assert!(N != 0, ); +LL ~ const BAR: () = +LL ~ assert!(N != 0, ); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:116:5 + --> tests/ui/manual_assert.rs:114:5 | LL | / if !is_x86_feature_detected!("ssse3") { LL | | @@ -198,12 +169,9 @@ LL | | panic!("SSSE3 is not supported"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !is_x86_feature_detected!("ssse3") { -LL - -LL - panic!("SSSE3 is not supported"); -LL - } +LL ~ LL + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); | diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index ab02bd5f5e533..7611795542230 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -2,8 +2,6 @@ //@[edition2018] edition:2018 //@[edition2021] edition:2021 -//@no-rustfix: need to change the suggestion to a multipart suggestion - #![warn(clippy::manual_assert)] #![allow(dead_code, unused_doc_comments)] #![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] diff --git a/tests/ui/manual_div_ceil.fixed b/tests/ui/manual_div_ceil.fixed index 58ee6978fc125..cd91be87ec175 100644 --- a/tests/ui/manual_div_ceil.fixed +++ b/tests/ui/manual_div_ceil.fixed @@ -100,3 +100,8 @@ fn issue_13950() { let _ = (-8 + y) / -7; let _ = (y - 8) / -7; } + +fn issue_15705(size: u64, c: &u64) { + let _ = size.div_ceil(*c); + //~^ manual_div_ceil +} diff --git a/tests/ui/manual_div_ceil.rs b/tests/ui/manual_div_ceil.rs index aa0d81b22a0e2..9899c7d775c23 100644 --- a/tests/ui/manual_div_ceil.rs +++ b/tests/ui/manual_div_ceil.rs @@ -100,3 +100,8 @@ fn issue_13950() { let _ = (-8 + y) / -7; let _ = (y - 8) / -7; } + +fn issue_15705(size: u64, c: &u64) { + let _ = (size + c - 1) / c; + //~^ manual_div_ceil +} diff --git a/tests/ui/manual_div_ceil.stderr b/tests/ui/manual_div_ceil.stderr index 9be5a19bf391f..44de3ba99be78 100644 --- a/tests/ui/manual_div_ceil.stderr +++ b/tests/ui/manual_div_ceil.stderr @@ -125,5 +125,11 @@ error: manually reimplementing `div_ceil` LL | let _ = (7 + x) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` -error: aborting due to 19 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:105:13 + | +LL | let _ = (size + c - 1) / c; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `size.div_ceil(*c)` + +error: aborting due to 20 previous errors diff --git a/tests/ui/manual_rotate.fixed b/tests/ui/manual_rotate.fixed index 49db8661369e9..1012ffc1aa2d8 100644 --- a/tests/ui/manual_rotate.fixed +++ b/tests/ui/manual_rotate.fixed @@ -4,6 +4,7 @@ fn main() { let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64); let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64); let a_u32 = 1u32; + const N: u32 = 5; // True positives let y_u8 = x_u8.rotate_right(3); //~^ manual_rotate @@ -29,6 +30,9 @@ fn main() { //~^ manual_rotate let y_u64_as = (x_u32 as u64).rotate_right(8); //~^ manual_rotate + // shift by a const + let _ = x_i64.rotate_right(N); + //~^ manual_rotate // False positives - can't be replaced with a rotation let y_u8_false = (x_u8 >> 6) | (x_u8 << 3); @@ -40,3 +44,20 @@ fn main() { let mut l = vec![12_u8, 34]; let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5); } + +fn issue13028() { + let s = 5; + let u = 5; + let x: u32 = 123456; + + let _ = x.rotate_left(s); + //~^ manual_rotate + let _ = x.rotate_left(s); + //~^ manual_rotate + // still works with consts + let _ = x.rotate_right(9); + //~^ manual_rotate + + // don't lint, because `s` and `u` are different variables, albeit with the same value + let _ = (x << s) | (x >> (32 - u)); +} diff --git a/tests/ui/manual_rotate.rs b/tests/ui/manual_rotate.rs index 6445e60aa25da..3cdc79673c816 100644 --- a/tests/ui/manual_rotate.rs +++ b/tests/ui/manual_rotate.rs @@ -4,6 +4,7 @@ fn main() { let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64); let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64); let a_u32 = 1u32; + const N: u32 = 5; // True positives let y_u8 = (x_u8 >> 3) | (x_u8 << 5); //~^ manual_rotate @@ -29,6 +30,9 @@ fn main() { //~^ manual_rotate let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56); //~^ manual_rotate + // shift by a const + let _ = (x_i64 >> N) | (x_i64 << (64 - N)); + //~^ manual_rotate // False positives - can't be replaced with a rotation let y_u8_false = (x_u8 >> 6) | (x_u8 << 3); @@ -40,3 +44,20 @@ fn main() { let mut l = vec![12_u8, 34]; let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5); } + +fn issue13028() { + let s = 5; + let u = 5; + let x: u32 = 123456; + + let _ = (x << s) | (x >> (32 - s)); + //~^ manual_rotate + let _ = (x << s) | (x >> (31 ^ s)); + //~^ manual_rotate + // still works with consts + let _ = (x >> 9) | (x << (32 - 9)); + //~^ manual_rotate + + // don't lint, because `s` and `u` are different variables, albeit with the same value + let _ = (x << s) | (x >> (32 - u)); +} diff --git a/tests/ui/manual_rotate.stderr b/tests/ui/manual_rotate.stderr index a28721fbb94c8..ea04ee028db67 100644 --- a/tests/ui/manual_rotate.stderr +++ b/tests/ui/manual_rotate.stderr @@ -1,5 +1,5 @@ error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:8:16 + --> tests/ui/manual_rotate.rs:9:16 | LL | let y_u8 = (x_u8 >> 3) | (x_u8 << 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u8.rotate_right(3)` @@ -8,64 +8,88 @@ LL | let y_u8 = (x_u8 >> 3) | (x_u8 << 5); = help: to override `-D warnings` add `#[allow(clippy::manual_rotate)]` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:10:17 + --> tests/ui/manual_rotate.rs:11:17 | LL | let y_u16 = (x_u16 >> 7) | (x_u16 << 9); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u16.rotate_right(7)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:12:17 + --> tests/ui/manual_rotate.rs:13:17 | LL | let y_u32 = (x_u32 >> 8) | (x_u32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:14:17 + --> tests/ui/manual_rotate.rs:15:17 | LL | let y_u64 = (x_u64 >> 9) | (x_u64 << 55); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u64.rotate_right(9)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:16:16 + --> tests/ui/manual_rotate.rs:17:16 | LL | let y_i8 = (x_i8 >> 3) | (x_i8 << 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i8.rotate_right(3)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:18:17 + --> tests/ui/manual_rotate.rs:19:17 | LL | let y_i16 = (x_i16 >> 7) | (x_i16 << 9); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i16.rotate_right(7)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:20:17 + --> tests/ui/manual_rotate.rs:21:17 | LL | let y_i32 = (x_i32 >> 8) | (x_i32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:22:17 + --> tests/ui/manual_rotate.rs:23:17 | LL | let y_i64 = (x_i64 >> 9) | (x_i64 << 55); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(9)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:25:22 + --> tests/ui/manual_rotate.rs:26:22 | LL | let y_u32_plus = (x_u32 >> 8) + (x_u32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:28:25 + --> tests/ui/manual_rotate.rs:29:25 | LL | let y_u32_complex = ((x_u32 | 3256) >> 8) | ((x_u32 | 3256) << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 | 3256).rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:30:20 + --> tests/ui/manual_rotate.rs:31:20 | LL | let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 as u64).rotate_right(8)` -error: aborting due to 11 previous errors +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:34:13 + | +LL | let _ = (x_i64 >> N) | (x_i64 << (64 - N)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(N)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:53:13 + | +LL | let _ = (x << s) | (x >> (32 - s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_left(s)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:55:13 + | +LL | let _ = (x << s) | (x >> (31 ^ s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_left(s)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:58:13 + | +LL | let _ = (x >> 9) | (x << (32 - 9)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_right(9)` + +error: aborting due to 15 previous errors diff --git a/tests/ui/manual_unwrap_or_default.fixed b/tests/ui/manual_unwrap_or_default.fixed index 189fe876aa5d4..11023ac1142a7 100644 --- a/tests/ui/manual_unwrap_or_default.fixed +++ b/tests/ui/manual_unwrap_or_default.fixed @@ -32,6 +32,15 @@ fn main() { let x: Result = Ok(String::new()); x.unwrap_or_default(); + + // edge case + // because the `Some(bizarro)` pattern is not actually reachable, + // changing this match to `unwrap_or_default` would have side effects + let bizarro = Some(String::new()); + match bizarro { + _ => String::new(), + Some(bizarro) => bizarro, + }; } // Issue #12531 diff --git a/tests/ui/manual_unwrap_or_default.rs b/tests/ui/manual_unwrap_or_default.rs index ca87926763c9c..bf06d9af7d219 100644 --- a/tests/ui/manual_unwrap_or_default.rs +++ b/tests/ui/manual_unwrap_or_default.rs @@ -64,6 +64,15 @@ fn main() { } else { String::new() }; + + // edge case + // because the `Some(bizarro)` pattern is not actually reachable, + // changing this match to `unwrap_or_default` would have side effects + let bizarro = Some(String::new()); + match bizarro { + _ => String::new(), + Some(bizarro) => bizarro, + }; } // Issue #12531 diff --git a/tests/ui/manual_unwrap_or_default.stderr b/tests/ui/manual_unwrap_or_default.stderr index e8f38a2e3899e..031100832b16f 100644 --- a/tests/ui/manual_unwrap_or_default.stderr +++ b/tests/ui/manual_unwrap_or_default.stderr @@ -76,7 +76,7 @@ LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:74:24 + --> tests/ui/manual_unwrap_or_default.rs:83:24 | LL | Some(_) => match *b { | ________________________^ @@ -87,7 +87,7 @@ LL | | }, | |_____________^ help: replace it with: `(*b).unwrap_or_default()` error: if let can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:143:5 + --> tests/ui/manual_unwrap_or_default.rs:152:5 | LL | / if let Some(x) = Some(42) { LL | | diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_like_matches_macro.fixed similarity index 97% rename from tests/ui/match_expr_like_matches_macro.fixed rename to tests/ui/match_like_matches_macro.fixed index 8530ab16bfd7d..a1c95e8a94f14 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_like_matches_macro.fixed @@ -1,7 +1,6 @@ #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, - dead_code, clippy::equatable_if_let, clippy::needless_borrowed_reference, clippy::redundant_guards @@ -14,11 +13,11 @@ fn main() { let _y = matches!(x, Some(0)); //~^^^^ match_like_matches_macro - // Lint + // No lint: covered by `redundant_pattern_matching` let _w = x.is_some(); //~^^^^ redundant_pattern_matching - // Turn into is_none + // No lint: covered by `redundant_pattern_matching` let _z = x.is_none(); //~^^^^ redundant_pattern_matching diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_like_matches_macro.rs similarity index 97% rename from tests/ui/match_expr_like_matches_macro.rs rename to tests/ui/match_like_matches_macro.rs index 81017936889e4..eb419ba5bf8de 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_like_matches_macro.rs @@ -1,7 +1,6 @@ #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, - dead_code, clippy::equatable_if_let, clippy::needless_borrowed_reference, clippy::redundant_guards @@ -17,14 +16,14 @@ fn main() { }; //~^^^^ match_like_matches_macro - // Lint + // No lint: covered by `redundant_pattern_matching` let _w = match x { Some(_) => true, _ => false, }; //~^^^^ redundant_pattern_matching - // Turn into is_none + // No lint: covered by `redundant_pattern_matching` let _z = match x { Some(_) => false, None => true, diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_like_matches_macro.stderr similarity index 84% rename from tests/ui/match_expr_like_matches_macro.stderr rename to tests/ui/match_like_matches_macro.stderr index 8fceb05bc6e87..ae277ce4dca68 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:14:14 + --> tests/ui/match_like_matches_macro.rs:13:14 | LL | let _y = match x { | ______________^ @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/match_expr_like_matches_macro.rs:21:14 + --> tests/ui/match_like_matches_macro.rs:20:14 | LL | let _w = match x { | ______________^ @@ -25,7 +25,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/match_expr_like_matches_macro.rs:28:14 + --> tests/ui/match_like_matches_macro.rs:27:14 | LL | let _z = match x { | ______________^ @@ -35,7 +35,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:35:15 + --> tests/ui/match_like_matches_macro.rs:34:15 | LL | let _zz = match x { | _______________^ @@ -45,13 +45,13 @@ LL | | }; | |_____^ help: try: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:42:16 + --> tests/ui/match_like_matches_macro.rs:41:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:67:20 + --> tests/ui/match_like_matches_macro.rs:66:20 | LL | let _ans = match x { | ____________________^ @@ -62,7 +62,7 @@ LL | | }; | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:78:20 + --> tests/ui/match_like_matches_macro.rs:77:20 | LL | let _ans = match x { | ____________________^ @@ -74,7 +74,7 @@ LL | | }; | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:89:20 + --> tests/ui/match_like_matches_macro.rs:88:20 | LL | let _ans = match x { | ____________________^ @@ -85,7 +85,7 @@ LL | | }; | |_________^ help: try: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:150:18 + --> tests/ui/match_like_matches_macro.rs:149:18 | LL | let _z = match &z { | __________________^ @@ -95,7 +95,7 @@ LL | | }; | |_________^ help: try: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:160:18 + --> tests/ui/match_like_matches_macro.rs:159:18 | LL | let _z = match &z { | __________________^ @@ -105,7 +105,7 @@ LL | | }; | |_________^ help: try: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:178:21 + --> tests/ui/match_like_matches_macro.rs:177:21 | LL | let _ = match &z { | _____________________^ @@ -115,7 +115,7 @@ LL | | }; | |_____________^ help: try: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:193:20 + --> tests/ui/match_like_matches_macro.rs:192:20 | LL | let _res = match &val { | ____________________^ @@ -125,7 +125,7 @@ LL | | }; | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:206:20 + --> tests/ui/match_like_matches_macro.rs:205:20 | LL | let _res = match &val { | ____________________^ @@ -135,7 +135,7 @@ LL | | }; | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:265:14 + --> tests/ui/match_like_matches_macro.rs:264:14 | LL | let _y = match Some(5) { | ______________^ diff --git a/tests/ui/must_use_unit_unfixable.rs b/tests/ui/must_use_unit_unfixable.rs index 0dba7996bac33..8eeaf36dca290 100644 --- a/tests/ui/must_use_unit_unfixable.rs +++ b/tests/ui/must_use_unit_unfixable.rs @@ -1,5 +1,3 @@ -//@no-rustfix - #[cfg_attr(all(), must_use, deprecated)] fn issue_12320() {} //~^ must_use_unit diff --git a/tests/ui/must_use_unit_unfixable.stderr b/tests/ui/must_use_unit_unfixable.stderr index 087682199afb9..8b5e556b1b2ed 100644 --- a/tests/ui/must_use_unit_unfixable.stderr +++ b/tests/ui/must_use_unit_unfixable.stderr @@ -1,11 +1,11 @@ error: this unit-returning function has a `#[must_use]` attribute - --> tests/ui/must_use_unit_unfixable.rs:4:1 + --> tests/ui/must_use_unit_unfixable.rs:2:1 | LL | fn issue_12320() {} | ^^^^^^^^^^^^^^^^ | help: remove `must_use` - --> tests/ui/must_use_unit_unfixable.rs:3:19 + --> tests/ui/must_use_unit_unfixable.rs:1:19 | LL | #[cfg_attr(all(), must_use, deprecated)] | ^^^^^^^^ @@ -13,13 +13,13 @@ LL | #[cfg_attr(all(), must_use, deprecated)] = help: to override `-D warnings` add `#[allow(clippy::must_use_unit)]` error: this unit-returning function has a `#[must_use]` attribute - --> tests/ui/must_use_unit_unfixable.rs:8:1 + --> tests/ui/must_use_unit_unfixable.rs:6:1 | LL | fn issue_12320_2() {} | ^^^^^^^^^^^^^^^^^^ | help: remove `must_use` - --> tests/ui/must_use_unit_unfixable.rs:7:44 + --> tests/ui/must_use_unit_unfixable.rs:5:44 | LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)] | ^^^^^^^^ diff --git a/tests/ui/mutex_atomic.fixed b/tests/ui/mutex_atomic.fixed new file mode 100644 index 0000000000000..e4218726019f6 --- /dev/null +++ b/tests/ui/mutex_atomic.fixed @@ -0,0 +1,67 @@ +#![warn(clippy::mutex_integer)] +#![warn(clippy::mutex_atomic)] +#![allow(clippy::borrow_as_ptr)] + +use std::sync::Mutex; + +fn main() { + let _ = std::sync::atomic::AtomicBool::new(true); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicUsize::new(5usize); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicIsize::new(9isize); + //~^ mutex_atomic + + let mut x = 4u32; + // `AtomicPtr` only accepts `*mut T`, so this should not lint + let _ = Mutex::new(&x as *const u32); + + let _ = std::sync::atomic::AtomicPtr::new(&mut x as *mut u32); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicU32::new(0u32); + //~^ mutex_integer + + let _ = std::sync::atomic::AtomicI32::new(0i32); + //~^ mutex_integer + + let _ = Mutex::new(0f32); // there are no float atomics, so this should not lint + let _ = std::sync::atomic::AtomicU8::new(0u8); + //~^ mutex_integer + + let _ = std::sync::atomic::AtomicI16::new(0i16); + //~^ mutex_integer + + let _x = std::sync::atomic::AtomicI8::new(0); + //~^ mutex_integer + + const X: i64 = 0; + let _ = std::sync::atomic::AtomicI64::new(X); + //~^ mutex_integer + + // there are no 128 atomics, so these two should not lint + { + let _ = Mutex::new(0u128); + let _x: Mutex = Mutex::new(0); + } +} + +// don't lint on _use_, only declaration +fn issue13378() { + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + //~^ mutex_integer + + let mtx = std::sync::atomic::AtomicI32::new(0); + //~^ mutex_integer + // This will still lint, since we're reassigning the mutex to a variable -- oh well. + // But realistically something like this won't really come up. + let reassigned = mtx; + //~^ mutex_integer + + // don't eat the `)` when removing the type ascription -- see + // https://github.com/rust-lang/rust-clippy/issues/15377 + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); + //~^ mutex_integer +} diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index 7db5c9f274f6e..95f2b135903f7 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -2,47 +2,66 @@ #![warn(clippy::mutex_atomic)] #![allow(clippy::borrow_as_ptr)] +use std::sync::Mutex; + fn main() { - use std::sync::Mutex; - Mutex::new(true); + let _ = Mutex::new(true); //~^ mutex_atomic - Mutex::new(5usize); + let _ = Mutex::new(5usize); //~^ mutex_atomic - Mutex::new(9isize); + let _ = Mutex::new(9isize); //~^ mutex_atomic let mut x = 4u32; - Mutex::new(&x as *const u32); - //~^ mutex_atomic + // `AtomicPtr` only accepts `*mut T`, so this should not lint + let _ = Mutex::new(&x as *const u32); - Mutex::new(&mut x as *mut u32); + let _ = Mutex::new(&mut x as *mut u32); //~^ mutex_atomic - Mutex::new(0u32); + let _ = Mutex::new(0u32); //~^ mutex_integer - Mutex::new(0i32); + let _ = Mutex::new(0i32); //~^ mutex_integer - Mutex::new(0f32); // there are no float atomics, so this should not lint - Mutex::new(0u8); + let _ = Mutex::new(0f32); // there are no float atomics, so this should not lint + let _ = Mutex::new(0u8); //~^ mutex_integer - Mutex::new(0i16); + let _ = Mutex::new(0i16); //~^ mutex_integer let _x: Mutex = Mutex::new(0); //~^ mutex_integer const X: i64 = 0; - Mutex::new(X); + let _ = Mutex::new(X); //~^ mutex_integer // there are no 128 atomics, so these two should not lint { - Mutex::new(0u128); + let _ = Mutex::new(0u128); let _x: Mutex = Mutex::new(0); } } + +// don't lint on _use_, only declaration +fn issue13378() { + static MTX: Mutex = Mutex::new(0); + //~^ mutex_integer + + let mtx = Mutex::new(0); + //~^ mutex_integer + // This will still lint, since we're reassigning the mutex to a variable -- oh well. + // But realistically something like this won't really come up. + let reassigned = mtx; + //~^ mutex_integer + + // don't eat the `)` when removing the type ascription -- see + // https://github.com/rust-lang/rust-clippy/issues/15377 + let (funky_mtx): Mutex = Mutex::new(0); + //~^ mutex_integer +} diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index a6d5d60fbf05b..0afc6d541deab 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,74 +1,134 @@ -error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:7:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:8:13 | -LL | Mutex::new(true); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(true); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicBool::new(true)` | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-atomic` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` -error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:10:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:11:13 | -LL | Mutex::new(5usize); - | ^^^^^^^^^^^^^^^^^^ - -error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:13:5 +LL | let _ = Mutex::new(5usize); + | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicUsize::new(5usize)` | -LL | Mutex::new(9isize); - | ^^^^^^^^^^^^^^^^^^ + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:17:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:14:13 + | +LL | let _ = Mutex::new(9isize); + | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicIsize::new(9isize)` | -LL | Mutex::new(&x as *const u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:20:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:21:13 | -LL | Mutex::new(&mut x as *mut u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(&mut x as *mut u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicPtr::new(&mut x as *mut u32)` + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicU32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:23:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:24:13 | -LL | Mutex::new(0u32); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0u32); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU32::new(0u32)` | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-integer` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` -error: consider using an `AtomicI32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:26:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:27:13 + | +LL | let _ = Mutex::new(0i32); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0i32)` | -LL | Mutex::new(0i32); - | ^^^^^^^^^^^^^^^^ + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicU8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:30:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:31:13 | -LL | Mutex::new(0u8); - | ^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0u8); + | ^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU8::new(0u8)` + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI16` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:33:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:34:13 + | +LL | let _ = Mutex::new(0i16); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI16::new(0i16)` | -LL | Mutex::new(0i16); - | ^^^^^^^^^^^^^^^^ + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:36:25 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:37:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - let _x: Mutex = Mutex::new(0); +LL + let _x = std::sync::atomic::AtomicI8::new(0); + | + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:41:13 + | +LL | let _ = Mutex::new(X); + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI64::new(X)` + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:53:30 + | +LL | static MTX: Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - static MTX: Mutex = Mutex::new(0); +LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + | + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:56:15 + | +LL | let mtx = Mutex::new(0); + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0)` + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI64` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:40:5 +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:60:22 + | +LL | let reassigned = mtx; + | ^^^ + | + = help: consider using an `AtomicI32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:65:35 + | +LL | let (funky_mtx): Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - let (funky_mtx): Mutex = Mutex::new(0); +LL + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); | -LL | Mutex::new(X); - | ^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/mutex_atomic_unfixable.rs b/tests/ui/mutex_atomic_unfixable.rs new file mode 100644 index 0000000000000..0c04f48cf8a9e --- /dev/null +++ b/tests/ui/mutex_atomic_unfixable.rs @@ -0,0 +1,13 @@ +//@no-rustfix +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] + +use std::sync::Mutex; + +fn issue13378() { + static MTX: Mutex = Mutex::new(0); + //~^ mutex_integer + + // unfixable because we don't fix this `lock` + let mut guard = MTX.lock().unwrap(); + *guard += 1; +} diff --git a/tests/ui/mutex_atomic_unfixable.stderr b/tests/ui/mutex_atomic_unfixable.stderr new file mode 100644 index 0000000000000..27ffb1304c695 --- /dev/null +++ b/tests/ui/mutex_atomic_unfixable.stderr @@ -0,0 +1,17 @@ +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic_unfixable.rs:7:30 + | +LL | static MTX: Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + = note: `-D clippy::mutex-integer` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` +help: try + | +LL - static MTX: Mutex = Mutex::new(0); +LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/needless_borrow_pat.fixed b/tests/ui/needless_borrow_pat.fixed index fe966a716df71..507186676c160 100644 --- a/tests/ui/needless_borrow_pat.fixed +++ b/tests/ui/needless_borrow_pat.fixed @@ -1,5 +1,3 @@ -// FIXME: run-rustfix waiting on multi-span suggestions - #![warn(clippy::needless_borrow)] #![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)] diff --git a/tests/ui/needless_borrow_pat.rs b/tests/ui/needless_borrow_pat.rs index a6b43855cad10..ef0f97301bcf2 100644 --- a/tests/ui/needless_borrow_pat.rs +++ b/tests/ui/needless_borrow_pat.rs @@ -1,5 +1,3 @@ -// FIXME: run-rustfix waiting on multi-span suggestions - #![warn(clippy::needless_borrow)] #![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)] diff --git a/tests/ui/needless_borrow_pat.stderr b/tests/ui/needless_borrow_pat.stderr index 25c570eb7ff73..34f167cca2235 100644 --- a/tests/ui/needless_borrow_pat.stderr +++ b/tests/ui/needless_borrow_pat.stderr @@ -1,5 +1,5 @@ error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:59:14 + --> tests/ui/needless_borrow_pat.rs:57:14 | LL | Some(ref x) => x, | ^^^^^ help: try: `x` @@ -8,7 +8,7 @@ LL | Some(ref x) => x, = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:66:14 + --> tests/ui/needless_borrow_pat.rs:64:14 | LL | Some(ref x) => *x, | ^^^^^ @@ -20,7 +20,7 @@ LL + Some(x) => x, | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:73:14 + --> tests/ui/needless_borrow_pat.rs:71:14 | LL | Some(ref x) => { | ^^^^^ @@ -35,19 +35,19 @@ LL ~ f1(x); | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:85:14 + --> tests/ui/needless_borrow_pat.rs:83:14 | LL | Some(ref x) => m1!(x), | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:91:15 + --> tests/ui/needless_borrow_pat.rs:89:15 | LL | let _ = |&ref x: &&String| { | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:98:10 + --> tests/ui/needless_borrow_pat.rs:96:10 | LL | let (ref y,) = (&x,); | ^^^^^ @@ -61,13 +61,13 @@ LL ~ let _: &String = y; | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:110:14 + --> tests/ui/needless_borrow_pat.rs:108:14 | LL | Some(ref x) => x.0, | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:121:14 + --> tests/ui/needless_borrow_pat.rs:119:14 | LL | E::A(ref x) | E::B(ref x) => *x, | ^^^^^ ^^^^^ @@ -79,13 +79,13 @@ LL + E::A(x) | E::B(x) => x, | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:128:21 + --> tests/ui/needless_borrow_pat.rs:126:21 | LL | if let Some(ref x) = Some(&String::new()); | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:138:12 + --> tests/ui/needless_borrow_pat.rs:136:12 | LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { | ^^^^^ @@ -100,13 +100,13 @@ LL ~ x | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:147:11 + --> tests/ui/needless_borrow_pat.rs:145:11 | LL | fn f(&ref x: &&String) { | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:157:11 + --> tests/ui/needless_borrow_pat.rs:155:11 | LL | fn f(&ref x: &&String) { | ^^^^^ diff --git a/tests/ui/needless_continue.rs b/tests/ui/needless_continue.rs index e873db6dee14e..88b7905e7fa80 100644 --- a/tests/ui/needless_continue.rs +++ b/tests/ui/needless_continue.rs @@ -244,3 +244,101 @@ mod issue_4077 { true } } + +#[allow(clippy::let_unit_value)] +mod issue14550 { + fn match_with_value(mut producer: impl Iterator>) -> Result { + let mut counter = 2; + loop { + match producer.next().unwrap() { + Ok(ok) => break Ok((ok + 1) as u32), + Err(12) => { + counter -= 1; + continue; + }, + err => err?, + }; + } + } + + fn inside_macro() { + macro_rules! mac { + ($e:expr => $($rest:tt);*) => { + loop { + match $e { + 1 => continue, + 2 => break, + n => println!("{n}"), + } + $($rest;)* + } + }; + } + + mac!(2 => ); + mac!(1 => {println!("foobar")}); + } + + mod partially_inside_macro { + macro_rules! select { + ( + $expr:expr, + $( $pat:pat => $then:expr ),* + ) => { + fn foo() { + loop { + match $expr { + $( + $pat => $then, + )* + } + } + } + }; + } + + select!(Some(1), + Some(1) => { + println!("one"); + continue; + }, + Some(2) => {}, + None => break, + _ => () + ); + + macro_rules! choose { + ( + $expr:expr, + $case:expr + ) => { + fn bar() { + loop { + match $expr { + $case => { + println!("matched"); + continue; + }, + _ => { + println!("not matched"); + break; + }, + } + } + } + }; + } + + choose!(todo!(), 5); + } +} + +fn issue15548() { + loop { + if todo!() { + } else { + //~^ needless_continue + continue; + } + } +} diff --git a/tests/ui/needless_continue.stderr b/tests/ui/needless_continue.stderr index 878c1e731e32a..7a65872c85cd2 100644 --- a/tests/ui/needless_continue.stderr +++ b/tests/ui/needless_continue.stderr @@ -220,5 +220,21 @@ LL | | } do_something(); } -error: aborting due to 15 previous errors +error: this `else` block is redundant + --> tests/ui/needless_continue.rs:339:16 + | +LL | } else { + | ________________^ +LL | | +LL | | continue; +LL | | } + | |_________^ + | + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if todo!() { + // merged code follows: + + } + +error: aborting due to 16 previous errors diff --git a/tests/ui/non_canonical_clone_impl.fixed b/tests/ui/non_canonical_clone_impl.fixed index 11616f28825b0..d9be98de69b17 100644 --- a/tests/ui/non_canonical_clone_impl.fixed +++ b/tests/ui/non_canonical_clone_impl.fixed @@ -4,7 +4,7 @@ #![no_main] extern crate proc_macros; -use proc_macros::with_span; +use proc_macros::inline_macros; // lint @@ -100,18 +100,45 @@ impl Clone for Uwu { impl Copy for Uwu {} -// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 -#[derive(proc_macro_derive::NonCanonicalClone)] -pub struct G; +#[inline_macros] +mod issue12788 { + use proc_macros::{external, with_span}; -with_span!( - span + // lint -- not an external macro + inline!( + #[derive(Copy)] + pub struct A; - #[derive(Copy)] - struct H; - impl Clone for H { - fn clone(&self) -> Self { - todo!() + impl Clone for A { + fn clone(&self) -> Self { *self } } - } -); + ); + + // do not lint -- should skip external macros + external!( + #[derive(Copy)] + pub struct B; + + impl Clone for B { + fn clone(&self) -> Self { + todo!() + } + } + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(Copy)] + struct D; + impl Clone for D { + fn clone(&self) -> Self { + todo!() + } + } + ); +} diff --git a/tests/ui/non_canonical_clone_impl.rs b/tests/ui/non_canonical_clone_impl.rs index 7d101915517fe..3db22bdbcf318 100644 --- a/tests/ui/non_canonical_clone_impl.rs +++ b/tests/ui/non_canonical_clone_impl.rs @@ -4,7 +4,7 @@ #![no_main] extern crate proc_macros; -use proc_macros::with_span; +use proc_macros::inline_macros; // lint @@ -114,18 +114,48 @@ impl Clone for Uwu { impl Copy for Uwu {} -// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 -#[derive(proc_macro_derive::NonCanonicalClone)] -pub struct G; +#[inline_macros] +mod issue12788 { + use proc_macros::{external, with_span}; -with_span!( - span + // lint -- not an external macro + inline!( + #[derive(Copy)] + pub struct A; - #[derive(Copy)] - struct H; - impl Clone for H { - fn clone(&self) -> Self { - todo!() + impl Clone for A { + fn clone(&self) -> Self { + //~^ non_canonical_clone_impl + todo!() + } } - } -); + ); + + // do not lint -- should skip external macros + external!( + #[derive(Copy)] + pub struct B; + + impl Clone for B { + fn clone(&self) -> Self { + todo!() + } + } + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(Copy)] + struct D; + impl Clone for D { + fn clone(&self) -> Self { + todo!() + } + } + ); +} diff --git a/tests/ui/non_canonical_clone_impl.stderr b/tests/ui/non_canonical_clone_impl.stderr index 5500984527170..cf36a8f49f812 100644 --- a/tests/ui/non_canonical_clone_impl.stderr +++ b/tests/ui/non_canonical_clone_impl.stderr @@ -41,5 +41,17 @@ LL | | *self = source.clone(); LL | | } | |_____^ help: remove it -error: aborting due to 4 previous errors +error: non-canonical implementation of `clone` on a `Copy` type + --> tests/ui/non_canonical_clone_impl.rs:127:37 + | +LL | fn clone(&self) -> Self { + | _____________________________________^ +LL | | +LL | | todo!() +LL | | } + | |_____________^ help: change this to: `{ *self }` + | + = note: this error originates in the macro `__inline_mac_mod_issue12788` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors diff --git a/tests/ui/non_canonical_partial_ord_impl.fixed b/tests/ui/non_canonical_partial_ord_impl.fixed index 6915e1984fb1e..aa23fd99ad77b 100644 --- a/tests/ui/non_canonical_partial_ord_impl.fixed +++ b/tests/ui/non_canonical_partial_ord_impl.fixed @@ -1,5 +1,9 @@ +//@aux-build:proc_macro_derive.rs #![no_main] +extern crate proc_macros; +use proc_macros::inline_macros; + use std::cmp::Ordering; // lint @@ -163,6 +167,73 @@ impl PartialOrd for I { } } +#[inline_macros] +mod issue12788 { + use std::cmp::Ordering; + + use proc_macros::{external, with_span}; + + // lint -- not an external macro + inline!( + #[derive(PartialEq, Eq)] + pub struct A; + + impl Ord for A { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for A { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } + } + ); + + // do not lint -- should skip external macros + external!( + #[derive(PartialEq, Eq)] + pub struct B; + + impl Ord for B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for B { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(PartialEq, Eq)] + pub struct D; + + impl Ord for D { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for D { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); +} + // #13640, do not lint #[derive(Eq, PartialEq)] diff --git a/tests/ui/non_canonical_partial_ord_impl.rs b/tests/ui/non_canonical_partial_ord_impl.rs index 7ce4cdc9aec83..da7f73f7c4bef 100644 --- a/tests/ui/non_canonical_partial_ord_impl.rs +++ b/tests/ui/non_canonical_partial_ord_impl.rs @@ -1,5 +1,9 @@ +//@aux-build:proc_macro_derive.rs #![no_main] +extern crate proc_macros; +use proc_macros::inline_macros; + use std::cmp::Ordering; // lint @@ -167,6 +171,75 @@ impl PartialOrd for I { } } +#[inline_macros] +mod issue12788 { + use std::cmp::Ordering; + + use proc_macros::{external, with_span}; + + // lint -- not an external macro + inline!( + #[derive(PartialEq, Eq)] + pub struct A; + + impl Ord for A { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for A { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + ); + + // do not lint -- should skip external macros + external!( + #[derive(PartialEq, Eq)] + pub struct B; + + impl Ord for B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for B { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(PartialEq, Eq)] + pub struct D; + + impl Ord for D { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for D { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); +} + // #13640, do not lint #[derive(Eq, PartialEq)] diff --git a/tests/ui/non_canonical_partial_ord_impl.stderr b/tests/ui/non_canonical_partial_ord_impl.stderr index 9bd6b1f726df2..8e55603dd9dac 100644 --- a/tests/ui/non_canonical_partial_ord_impl.stderr +++ b/tests/ui/non_canonical_partial_ord_impl.stderr @@ -1,5 +1,5 @@ error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:16:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:20:1 | LL | / impl PartialOrd for A { LL | | @@ -15,7 +15,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]` error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:51:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:55:1 | LL | / impl PartialOrd for C { LL | | @@ -32,7 +32,22 @@ LL + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp | error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:198:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:191:9 + | +LL | / impl PartialOrd for A { +LL | | +LL | | fn partial_cmp(&self, other: &Self) -> Option { + | | _____________________________________________________________________- +LL | || todo!(); +LL | || } + | ||_____________- help: change this to: `{ Some(self.cmp(other)) }` +LL | | } + | |__________^ + | + = note: this error originates in the macro `__inline_mac_mod_issue12788` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> tests/ui/non_canonical_partial_ord_impl.rs:271:1 | LL | / impl PartialOrd for K { LL | | @@ -45,7 +60,7 @@ LL | | } | |__^ error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:216:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:289:1 | LL | / impl PartialOrd for L { LL | | @@ -57,5 +72,5 @@ LL | || } LL | | } | |__^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 6ce067f5c2462..a2ecea773efe5 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -6,7 +6,8 @@ clippy::redundant_locals, clippy::manual_midpoint, clippy::manual_unwrap_or_default, - clippy::manual_unwrap_or + clippy::manual_unwrap_or, + clippy::unnecessary_option_map_or_else )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 096d3aabf28db..3adbc785237f6 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -6,7 +6,8 @@ clippy::redundant_locals, clippy::manual_midpoint, clippy::manual_unwrap_or_default, - clippy::manual_unwrap_or + clippy::manual_unwrap_or, + clippy::unnecessary_option_map_or_else )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 21a80ae038d8c..f5578f63c946b 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:13:5 + --> tests/ui/option_if_let_else.rs:14:5 | LL | / if let Some(x) = string { LL | | @@ -13,19 +13,19 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::option_if_let_else)]` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:32:13 + --> tests/ui/option_if_let_else.rs:33:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:34:13 + --> tests/ui/option_if_let_else.rs:35:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:36:13 + --> tests/ui/option_if_let_else.rs:37:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -47,13 +47,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:43:13 + --> tests/ui/option_if_let_else.rs:44:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:45:13 + --> tests/ui/option_if_let_else.rs:46:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -75,7 +75,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:52:13 + --> tests/ui/option_if_let_else.rs:53:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -97,7 +97,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:62:5 + --> tests/ui/option_if_let_else.rs:63:5 | LL | / if let Some(x) = arg { LL | | @@ -118,7 +118,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:76:13 + --> tests/ui/option_if_let_else.rs:77:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -131,7 +131,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(side_effect, |x| x)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:86:13 + --> tests/ui/option_if_let_else.rs:87:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -154,7 +154,7 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:120:13 + --> tests/ui/option_if_let_else.rs:121:13 | LL | / if let Some(idx) = s.find('.') { LL | | @@ -165,7 +165,7 @@ LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:132:5 + --> tests/ui/option_if_let_else.rs:133:5 | LL | / if let Ok(binding) = variable { LL | | @@ -189,7 +189,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:157:5 + --> tests/ui/option_if_let_else.rs:158:5 | LL | / match r { LL | | @@ -199,7 +199,7 @@ LL | | } | |_____^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:166:5 + --> tests/ui/option_if_let_else.rs:167:5 | LL | / if let Ok(s) = r { s.to_owned() } LL | | @@ -207,13 +207,13 @@ LL | | else { Vec::new() } | |_______________________^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:173:13 + --> tests/ui/option_if_let_else.rs:174:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:184:13 + --> tests/ui/option_if_let_else.rs:185:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -235,13 +235,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:213:13 + --> tests/ui/option_if_let_else.rs:214:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:218:13 + --> tests/ui/option_if_let_else.rs:219:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -263,7 +263,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:258:13 + --> tests/ui/option_if_let_else.rs:259:13 | LL | let _ = match s { | _____________^ @@ -274,7 +274,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:263:13 + --> tests/ui/option_if_let_else.rs:264:13 | LL | let _ = match Some(10) { | _____________^ @@ -285,7 +285,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:270:13 + --> tests/ui/option_if_let_else.rs:271:13 | LL | let _ = match res { | _____________^ @@ -296,7 +296,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:275:13 + --> tests/ui/option_if_let_else.rs:276:13 | LL | let _ = match res { | _____________^ @@ -307,13 +307,13 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:280:13 + --> tests/ui/option_if_let_else.rs:281:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:298:17 + --> tests/ui/option_if_let_else.rs:299:17 | LL | let _ = match initial { | _________________^ @@ -324,7 +324,7 @@ LL | | }; | |_________^ help: try: `initial.as_ref().map_or(42, |value| do_something(value))` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:306:17 + --> tests/ui/option_if_let_else.rs:307:17 | LL | let _ = match initial { | _________________^ @@ -335,7 +335,7 @@ LL | | }; | |_________^ help: try: `initial.as_mut().map_or(42, |value| do_something2(value))` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:330:24 + --> tests/ui/option_if_let_else.rs:331:24 | LL | let mut _hashmap = if let Some(hm) = &opt { | ________________________^ @@ -347,19 +347,19 @@ LL | | }; | |_____^ help: try: `opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:337:19 + --> tests/ui/option_if_let_else.rs:338:19 | LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:388:22 + --> tests/ui/option_if_let_else.rs:389:22 | LL | let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*opt_raw_ptr).map_or(1, |o| o)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:394:13 + --> tests/ui/option_if_let_else.rs:395:13 | LL | let _ = match res { | _____________^ diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 386351aa39f51..314da0804a5fa 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -5,6 +5,7 @@ clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap, clippy::unnecessary_result_map_or_else, + clippy::unnecessary_option_map_or_else, clippy::useless_vec )] diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index e27f9aa65c37a..2a19614026ec5 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -5,6 +5,7 @@ clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap, clippy::unnecessary_result_map_or_else, + clippy::unnecessary_option_map_or_else, clippy::useless_vec )] diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 6bce06ab20eb6..3d55f2cd1f9fc 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:52:22 + --> tests/ui/or_fun_call.rs:53:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)` @@ -8,7 +8,7 @@ LL | with_constructor.unwrap_or(make()); = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:56:14 + --> tests/ui/or_fun_call.rs:57:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` @@ -17,205 +17,205 @@ LL | with_new.unwrap_or(Vec::new()); = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:60:21 + --> tests/ui/or_fun_call.rs:61:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:64:14 + --> tests/ui/or_fun_call.rs:65:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:68:19 + --> tests/ui/or_fun_call.rs:69:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:72:24 + --> tests/ui/or_fun_call.rs:73:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:76:23 + --> tests/ui/or_fun_call.rs:77:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:96:18 + --> tests/ui/or_fun_call.rs:97:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:100:18 + --> tests/ui/or_fun_call.rs:101:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:104:14 + --> tests/ui/or_fun_call.rs:105:14 | LL | with_vec.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:108:21 + --> tests/ui/or_fun_call.rs:109:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:112:19 + --> tests/ui/or_fun_call.rs:113:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:116:23 + --> tests/ui/or_fun_call.rs:117:23 | LL | map_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:120:21 + --> tests/ui/or_fun_call.rs:121:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:124:25 + --> tests/ui/or_fun_call.rs:125:25 | LL | btree_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:128:21 + --> tests/ui/or_fun_call.rs:129:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:133:17 + --> tests/ui/or_fun_call.rs:134:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:138:21 + --> tests/ui/or_fun_call.rs:139:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:141:21 + --> tests/ui/or_fun_call.rs:142:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:166:35 + --> tests/ui/or_fun_call.rs:167:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:209:18 + --> tests/ui/or_fun_call.rs:210:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:217:14 + --> tests/ui/or_fun_call.rs:218:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:220:14 + --> tests/ui/or_fun_call.rs:221:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:296:25 + --> tests/ui/or_fun_call.rs:297:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:298:25 + --> tests/ui/or_fun_call.rs:299:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:301:25 + --> tests/ui/or_fun_call.rs:302:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:332:18 + --> tests/ui/or_fun_call.rs:333:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:336:28 + --> tests/ui/or_fun_call.rs:337:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:340:27 + --> tests/ui/or_fun_call.rs:341:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:344:22 + --> tests/ui/or_fun_call.rs:345:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:348:23 + --> tests/ui/or_fun_call.rs:349:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:352:25 + --> tests/ui/or_fun_call.rs:353:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:356:25 + --> tests/ui/or_fun_call.rs:357:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:398:17 + --> tests/ui/or_fun_call.rs:399:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:403:17 + --> tests/ui/or_fun_call.rs:404:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:408:17 + --> tests/ui/or_fun_call.rs:409:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,79 +235,79 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:414:17 + --> tests/ui/or_fun_call.rs:415:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:419:17 + --> tests/ui/or_fun_call.rs:420:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:426:21 + --> tests/ui/or_fun_call.rs:427:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:441:19 + --> tests/ui/or_fun_call.rs:442:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:443:19 + --> tests/ui/or_fun_call.rs:444:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:446:19 + --> tests/ui/or_fun_call.rs:447:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:457:15 + --> tests/ui/or_fun_call.rs:458:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:467:15 + --> tests/ui/or_fun_call.rs:468:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:477:15 + --> tests/ui/or_fun_call.rs:478:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:483:17 + --> tests/ui/or_fun_call.rs:484:17 | LL | let _ = opt.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:485:17 + --> tests/ui/or_fun_call.rs:486:17 | LL | let _ = res.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Default::default())` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:491:17 + --> tests/ui/or_fun_call.rs:492:17 | LL | let _ = opt.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:493:17 + --> tests/ui/or_fun_call.rs:494:17 | LL | let _ = res.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` diff --git a/tests/ui/replace_box.fixed b/tests/ui/replace_box.fixed new file mode 100644 index 0000000000000..58c8ed1691d77 --- /dev/null +++ b/tests/ui/replace_box.fixed @@ -0,0 +1,72 @@ +#![warn(clippy::replace_box)] + +fn with_default(b: &mut Box) { + **b = T::default(); + //~^ replace_box +} + +fn with_sized(b: &mut Box, t: T) { + **b = t; + //~^ replace_box +} + +fn with_unsized(b: &mut Box<[u32]>) { + // No lint for assigning to Box where T: !Default + *b = Box::new([42; N]); +} + +macro_rules! create_default { + () => { + Default::default() + }; +} + +macro_rules! create_zero_box { + () => { + Box::new(0) + }; +} + +macro_rules! same { + ($v:ident) => { + $v + }; +} + +macro_rules! mac { + (three) => { + 3u32 + }; +} + +fn main() { + let mut b = Box::new(1u32); + *b = Default::default(); + //~^ replace_box + *b = Default::default(); + //~^ replace_box + + // No lint for assigning to the storage + *b = Default::default(); + *b = u32::default(); + + // No lint if either LHS or RHS originates in macro + b = create_default!(); + b = create_zero_box!(); + same!(b) = Default::default(); + + *b = 5; + //~^ replace_box + + *b = mac!(three); + //~^ replace_box + + // No lint for assigning to Box where T: !Default + let mut b = Box::::from("hi".to_string()); + b = Default::default(); + + // No lint for late initializations + #[allow(clippy::needless_late_init)] + let bb: Box; + bb = Default::default(); +} diff --git a/tests/ui/replace_box.rs b/tests/ui/replace_box.rs new file mode 100644 index 0000000000000..e1fb223e4f211 --- /dev/null +++ b/tests/ui/replace_box.rs @@ -0,0 +1,72 @@ +#![warn(clippy::replace_box)] + +fn with_default(b: &mut Box) { + *b = Box::new(T::default()); + //~^ replace_box +} + +fn with_sized(b: &mut Box, t: T) { + *b = Box::new(t); + //~^ replace_box +} + +fn with_unsized(b: &mut Box<[u32]>) { + // No lint for assigning to Box where T: !Default + *b = Box::new([42; N]); +} + +macro_rules! create_default { + () => { + Default::default() + }; +} + +macro_rules! create_zero_box { + () => { + Box::new(0) + }; +} + +macro_rules! same { + ($v:ident) => { + $v + }; +} + +macro_rules! mac { + (three) => { + 3u32 + }; +} + +fn main() { + let mut b = Box::new(1u32); + b = Default::default(); + //~^ replace_box + b = Box::default(); + //~^ replace_box + + // No lint for assigning to the storage + *b = Default::default(); + *b = u32::default(); + + // No lint if either LHS or RHS originates in macro + b = create_default!(); + b = create_zero_box!(); + same!(b) = Default::default(); + + b = Box::new(5); + //~^ replace_box + + b = Box::new(mac!(three)); + //~^ replace_box + + // No lint for assigning to Box where T: !Default + let mut b = Box::::from("hi".to_string()); + b = Default::default(); + + // No lint for late initializations + #[allow(clippy::needless_late_init)] + let bb: Box; + bb = Default::default(); +} diff --git a/tests/ui/replace_box.stderr b/tests/ui/replace_box.stderr new file mode 100644 index 0000000000000..7d9c85da17314 --- /dev/null +++ b/tests/ui/replace_box.stderr @@ -0,0 +1,52 @@ +error: creating a new box + --> tests/ui/replace_box.rs:4:5 + | +LL | *b = Box::new(T::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `**b = T::default()` + | + = note: this creates a needless allocation + = note: `-D clippy::replace-box` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::replace_box)]` + +error: creating a new box + --> tests/ui/replace_box.rs:9:5 + | +LL | *b = Box::new(t); + | ^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `**b = t` + | + = note: this creates a needless allocation + +error: creating a new box with default content + --> tests/ui/replace_box.rs:44:5 + | +LL | b = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with default instead: `*b = Default::default()` + | + = note: this creates a needless allocation + +error: creating a new box with default content + --> tests/ui/replace_box.rs:46:5 + | +LL | b = Box::default(); + | ^^^^^^^^^^^^^^^^^^ help: replace existing content with default instead: `*b = Default::default()` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:58:5 + | +LL | b = Box::new(5); + | ^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*b = 5` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:61:5 + | +LL | b = Box::new(mac!(three)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*b = mac!(three)` + | + = note: this creates a needless allocation + +error: aborting due to 6 previous errors + diff --git a/tests/ui/unnecessary_option_map_or_else.fixed b/tests/ui/unnecessary_option_map_or_else.fixed new file mode 100644 index 0000000000000..9974ee2d08eb5 --- /dev/null +++ b/tests/ui/unnecessary_option_map_or_else.fixed @@ -0,0 +1,75 @@ +#![warn(clippy::unnecessary_option_map_or_else)] +#![allow( + clippy::let_and_return, + clippy::let_unit_value, + clippy::unnecessary_lazy_evaluations, + clippy::unnecessary_literal_unwrap +)] + +const fn identity(x: T) -> T { + x +} + +const fn double_it(x: i32) -> i32 { + x * 2 +} + +fn main() { + // Expected errors + // Basic scenario + let option = Some(()); + option.unwrap_or_else(|| ()); //~ ERROR: unused "map closure" when calling + + // Type ascription + let option = Some(()); + option.unwrap_or_else(|| ()); //~ ERROR: unused "map closure" when calling + + // Auto-deref + let string = String::new(); + let option = Some(&string); + let _: &str = option.unwrap_or_else(|| &string); //~ ERROR: unused "map closure" when calling + + // Temporary variable + let option = Some(()); + option.unwrap_or_else(|| ()); + + // Identity + let string = String::new(); + let option = Some(&string); + let _: &str = option.unwrap_or_else(|| &string); //~ ERROR: unused "map closure" when calling + + // Closure bound to a variable + let do_nothing = |x: String| x; + let string = String::new(); + let option = Some(string.clone()); + let _: String = option.unwrap_or_else(|| string); //~ ERROR: unused "map closure" when calling + + // Correct usages + let option = Some(()); + option.map_or_else(|| (), |_| ()); + + let option = Some(()); + option.map_or_else(|| (), |_: ()| ()); + + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |_| &string); + + let option = Some(()); + option.map_or_else( + || (), + |_| { + let tmp = (); + tmp + }, + ); + + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, double_it); + + let increase = |x: i32| x + 1; + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, increase); +} diff --git a/tests/ui/unnecessary_option_map_or_else.rs b/tests/ui/unnecessary_option_map_or_else.rs new file mode 100644 index 0000000000000..9b53f3fcd521c --- /dev/null +++ b/tests/ui/unnecessary_option_map_or_else.rs @@ -0,0 +1,82 @@ +#![warn(clippy::unnecessary_option_map_or_else)] +#![allow( + clippy::let_and_return, + clippy::let_unit_value, + clippy::unnecessary_lazy_evaluations, + clippy::unnecessary_literal_unwrap +)] + +const fn identity(x: T) -> T { + x +} + +const fn double_it(x: i32) -> i32 { + x * 2 +} + +fn main() { + // Expected errors + // Basic scenario + let option = Some(()); + option.map_or_else(|| (), |x| x); //~ ERROR: unused "map closure" when calling + + // Type ascription + let option = Some(()); + option.map_or_else(|| (), |x: ()| x); //~ ERROR: unused "map closure" when calling + + // Auto-deref + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |x| x); //~ ERROR: unused "map closure" when calling + + // Temporary variable + let option = Some(()); + option.map_or_else( + //~^ ERROR: unused "map closure" when calling + || (), + |x| { + let tmp = x; + tmp + }, + ); + + // Identity + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, identity); //~ ERROR: unused "map closure" when calling + + // Closure bound to a variable + let do_nothing = |x: String| x; + let string = String::new(); + let option = Some(string.clone()); + let _: String = option.map_or_else(|| string, do_nothing); //~ ERROR: unused "map closure" when calling + + // Correct usages + let option = Some(()); + option.map_or_else(|| (), |_| ()); + + let option = Some(()); + option.map_or_else(|| (), |_: ()| ()); + + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |_| &string); + + let option = Some(()); + option.map_or_else( + || (), + |_| { + let tmp = (); + tmp + }, + ); + + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, double_it); + + let increase = |x: i32| x + 1; + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, increase); +} diff --git a/tests/ui/unnecessary_option_map_or_else.stderr b/tests/ui/unnecessary_option_map_or_else.stderr new file mode 100644 index 0000000000000..d90875e4efc7c --- /dev/null +++ b/tests/ui/unnecessary_option_map_or_else.stderr @@ -0,0 +1,47 @@ +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:21:5 + | +LL | option.map_or_else(|| (), |x| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + | + = note: `-D clippy::unnecessary-option-map-or-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_option_map_or_else)]` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:25:5 + | +LL | option.map_or_else(|| (), |x: ()| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:30:19 + | +LL | let _: &str = option.map_or_else(|| &string, |x| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:34:5 + | +LL | / option.map_or_else( +LL | | +LL | | || (), +LL | | |x| { +... | +LL | | }, +LL | | ); + | |_____^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:46:19 + | +LL | let _: &str = option.map_or_else(|| &string, identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:52:21 + | +LL | let _: String = option.map_or_else(|| string, do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| string)` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/unnecessary_safety_comment.stderr b/tests/ui/unnecessary_safety_comment.stderr index 732e6767c1780..6ad94f986434d 100644 --- a/tests/ui/unnecessary_safety_comment.stderr +++ b/tests/ui/unnecessary_safety_comment.stderr @@ -5,10 +5,10 @@ LL | const CONST: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:5:5 + --> tests/ui/unnecessary_safety_comment.rs:5:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` @@ -19,10 +19,10 @@ LL | static STATIC: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:9:5 + --> tests/ui/unnecessary_safety_comment.rs:9:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: struct has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:14:5 @@ -31,10 +31,10 @@ LL | struct Struct; | ^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:13:5 + --> tests/ui/unnecessary_safety_comment.rs:13:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: enum has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:18:5 @@ -43,10 +43,10 @@ LL | enum Enum {} | ^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:17:5 + --> tests/ui/unnecessary_safety_comment.rs:17:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: module has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:22:5 @@ -55,10 +55,10 @@ LL | mod module {} | ^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:21:5 + --> tests/ui/unnecessary_safety_comment.rs:21:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: impl has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:42:13 @@ -70,10 +70,10 @@ LL | with_safety_comment!(i32); | ------------------------- in this macro invocation | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:41:13 + --> tests/ui/unnecessary_safety_comment.rs:41:16 | LL | // Safety: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ = note: this error originates in the macro `with_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: expression has unnecessary safety comment @@ -83,10 +83,10 @@ LL | 24 | ^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:59:5 + --> tests/ui/unnecessary_safety_comment.rs:59:8 | LL | // SAFETY: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ error: statement has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:52:5 @@ -95,10 +95,10 @@ LL | let num = 42; | ^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:51:5 + --> tests/ui/unnecessary_safety_comment.rs:51:8 | LL | // SAFETY: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ error: statement has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:56:5 @@ -107,10 +107,10 @@ LL | if num > 24 {} | ^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:55:5 + --> tests/ui/unnecessary_safety_comment.rs:55:8 | LL | // SAFETY: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 9 previous errors diff --git a/tests/ui/use_self_structs.fixed b/tests/ui/use_self_structs.fixed new file mode 100644 index 0000000000000..bd7bc3e0c1f69 --- /dev/null +++ b/tests/ui/use_self_structs.fixed @@ -0,0 +1,134 @@ +#![warn(clippy::use_self)] +#![allow(clippy::type_complexity)] + +fn main() {} + +struct Basic { + flag: Option>, + //~^ use_self +} + +struct BasicSelf { + okay: Option>, +} + +struct Generic<'q, T: From> { + t: &'q T, + flag: Option>, + //~^ use_self +} + +struct GenericSelf<'q, T: From> { + t: &'q T, + okay: Option>, +} + +struct MixedLifetimes<'q, T: From + 'static> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteType<'q, T: From> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteAndGeneric<'q, T: From> { + t: &'q T, + flag: Option>, + //~^ use_self + okay: Option>>, +} + +struct ConcreteAndGenericSelf<'q, T: From> { + t: &'q T, + okay_1: Option>, + okay_2: Option>>, +} + +macro_rules! recursive_struct { + ($name:ident) => { + struct $name { + okay: Option>, + } + }; +} + +recursive_struct!(X); +recursive_struct!(Y); +recursive_struct!(Z); + +struct Tree { + left: Option>, + //~^ use_self + right: Option>, + //~^ use_self +} + +struct TreeSelf { + left: Option>, + right: Option>, +} + +struct TreeMixed { + left: Option>, + right: Option>, + //~^ use_self +} + +struct Nested { + flag: Option>>>, + //~^ use_self +} + +struct NestedSelf { + okay: Option>>>, +} + +struct Tuple(Option>); +//~^ use_self + +struct TupleSelf(Option>); + +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +struct Containers { + flag: Vec>>>>>>, + //~^ use_self +} + +struct ContainersSelf { + okay: Vec>>>>>>, +} + +type Wrappers = Vec>>>>>>; + +struct Alias { + flag: Wrappers, + //~^ use_self +} + +struct AliasSelf { + okay: Wrappers, +} + +struct Array { + flag: [Option>; N], + //~^ use_self +} + +struct ArraySelf { + okay: [Option>; N], +} + +enum Enum { + Nil, + Cons(Box), + //~^ use_self +} + +enum EnumSelf { + Nil, + Cons(Box), +} diff --git a/tests/ui/use_self_structs.rs b/tests/ui/use_self_structs.rs new file mode 100644 index 0000000000000..624f158164ab0 --- /dev/null +++ b/tests/ui/use_self_structs.rs @@ -0,0 +1,134 @@ +#![warn(clippy::use_self)] +#![allow(clippy::type_complexity)] + +fn main() {} + +struct Basic { + flag: Option>, + //~^ use_self +} + +struct BasicSelf { + okay: Option>, +} + +struct Generic<'q, T: From> { + t: &'q T, + flag: Option>>, + //~^ use_self +} + +struct GenericSelf<'q, T: From> { + t: &'q T, + okay: Option>, +} + +struct MixedLifetimes<'q, T: From + 'static> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteType<'q, T: From> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteAndGeneric<'q, T: From> { + t: &'q T, + flag: Option>>, + //~^ use_self + okay: Option>>, +} + +struct ConcreteAndGenericSelf<'q, T: From> { + t: &'q T, + okay_1: Option>, + okay_2: Option>>, +} + +macro_rules! recursive_struct { + ($name:ident) => { + struct $name { + okay: Option>, + } + }; +} + +recursive_struct!(X); +recursive_struct!(Y); +recursive_struct!(Z); + +struct Tree { + left: Option>, + //~^ use_self + right: Option>, + //~^ use_self +} + +struct TreeSelf { + left: Option>, + right: Option>, +} + +struct TreeMixed { + left: Option>, + right: Option>, + //~^ use_self +} + +struct Nested { + flag: Option>>>, + //~^ use_self +} + +struct NestedSelf { + okay: Option>>>, +} + +struct Tuple(Option>); +//~^ use_self + +struct TupleSelf(Option>); + +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +struct Containers { + flag: Vec>>>>>>, + //~^ use_self +} + +struct ContainersSelf { + okay: Vec>>>>>>, +} + +type Wrappers = Vec>>>>>>; + +struct Alias { + flag: Wrappers, + //~^ use_self +} + +struct AliasSelf { + okay: Wrappers, +} + +struct Array { + flag: [Option>>; N], + //~^ use_self +} + +struct ArraySelf { + okay: [Option>; N], +} + +enum Enum { + Nil, + Cons(Box), + //~^ use_self +} + +enum EnumSelf { + Nil, + Cons(Box), +} diff --git a/tests/ui/use_self_structs.stderr b/tests/ui/use_self_structs.stderr new file mode 100644 index 0000000000000..766d1d4fd2c05 --- /dev/null +++ b/tests/ui/use_self_structs.stderr @@ -0,0 +1,77 @@ +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:7:22 + | +LL | flag: Option>, + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:17:22 + | +LL | flag: Option>>, + | ^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:38:22 + | +LL | flag: Option>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:62:22 + | +LL | left: Option>, + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:64:23 + | +LL | right: Option>, + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:75:23 + | +LL | right: Option>, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:80:33 + | +LL | flag: Option>>>, + | ^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:88:25 + | +LL | struct Tuple(Option>); + | ^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:97:46 + | +LL | flag: Vec>>>>>>, + | ^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:108:20 + | +LL | flag: Wrappers, + | ^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:117:23 + | +LL | flag: [Option>>; N], + | ^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:127:14 + | +LL | Cons(Box), + | ^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 12 previous errors + diff --git a/tests/ui/volatile_composites.rs b/tests/ui/volatile_composites.rs new file mode 100644 index 0000000000000..e7e7dafe18afa --- /dev/null +++ b/tests/ui/volatile_composites.rs @@ -0,0 +1,221 @@ +#![feature(ptr_metadata)] +#![feature(portable_simd)] +#![warn(clippy::volatile_composites)] + +use std::ptr::null_mut; + +#[repr(C)] +#[derive(Copy, Clone, Default)] +struct MyDevRegisters { + baseaddr: usize, + count: usize, +} + +#[repr(transparent)] +struct Wrapper((), T, ()); + +// Not to be confused with std::ptr::NonNull +struct NonNull(T); + +impl NonNull { + fn write_volatile(&self, _arg: &T) { + unimplemented!("Something entirely unrelated to std::ptr::NonNull"); + } +} + +fn main() { + let regs = MyDevRegisters { + baseaddr: 0xabc123, + count: 42, + }; + + const DEVICE_ADDR: *mut MyDevRegisters = 0xdead as *mut _; + + // Raw pointer methods + unsafe { + (&raw mut (*DEVICE_ADDR).baseaddr).write_volatile(regs.baseaddr); // OK + (&raw mut (*DEVICE_ADDR).count).write_volatile(regs.count); // OK + + DEVICE_ADDR.write_volatile(regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: (&raw const (*DEVICE_ADDR).baseaddr).read_volatile(), // OK + count: (&raw const (*DEVICE_ADDR).count).read_volatile(), // OK + }; + + let _regs = DEVICE_ADDR.read_volatile(); + //~^ volatile_composites + } + + // std::ptr functions + unsafe { + std::ptr::write_volatile(&raw mut (*DEVICE_ADDR).baseaddr, regs.baseaddr); // OK + std::ptr::write_volatile(&raw mut (*DEVICE_ADDR).count, regs.count); // OK + + std::ptr::write_volatile(DEVICE_ADDR, regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: std::ptr::read_volatile(&raw const (*DEVICE_ADDR).baseaddr), // OK + count: std::ptr::read_volatile(&raw const (*DEVICE_ADDR).count), // OK + }; + + let _regs = std::ptr::read_volatile(DEVICE_ADDR); + //~^ volatile_composites + } + + // core::ptr functions + unsafe { + core::ptr::write_volatile(&raw mut (*DEVICE_ADDR).baseaddr, regs.baseaddr); // OK + core::ptr::write_volatile(&raw mut (*DEVICE_ADDR).count, regs.count); // OK + + core::ptr::write_volatile(DEVICE_ADDR, regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: core::ptr::read_volatile(&raw const (*DEVICE_ADDR).baseaddr), // OK + count: core::ptr::read_volatile(&raw const (*DEVICE_ADDR).count), // OK + }; + + let _regs = core::ptr::read_volatile(DEVICE_ADDR); + //~^ volatile_composites + } + + // std::ptr::NonNull + unsafe { + let ptr = std::ptr::NonNull::new(DEVICE_ADDR).unwrap(); + + ptr.write_volatile(regs); + //~^ volatile_composites + + let _regs = ptr.read_volatile(); + //~^ volatile_composites + } + + // Red herring + { + let thing = NonNull("hello".to_string()); + + thing.write_volatile(&"goodbye".into()); // OK + } + + // Zero size types OK + unsafe { + struct Empty; + + (0xdead as *mut Empty).write_volatile(Empty); // OK + // Note that this is OK because Wrapper is itself ZST, not because of the repr transparent + // handling tested below. + (0xdead as *mut Wrapper).write_volatile(Wrapper((), Empty, ())); // OK + } + + // Via repr transparent newtype + unsafe { + (0xdead as *mut Wrapper).write_volatile(Wrapper((), 123, ())); // OK + (0xdead as *mut Wrapper>).write_volatile(Wrapper((), Wrapper((), 123, ()), ())); // OK + + (0xdead as *mut Wrapper).write_volatile(Wrapper((), MyDevRegisters::default(), ())); + //~^ volatile_composites + } + + // Plain type alias OK + unsafe { + type MyU64 = u64; + + (0xdead as *mut MyU64).write_volatile(123); // OK + } + + // Wide pointers are not OK as data + unsafe { + let things: &[u32] = &[1, 2, 3]; + + (0xdead as *mut *const u32).write_volatile(things.as_ptr()); // OK + + let wideptr: *const [u32] = std::ptr::from_raw_parts(things.as_ptr(), things.len()); + (0xdead as *mut *const [u32]).write_volatile(wideptr); + //~^ volatile_composites + } + + // Plain pointers and pointers with lifetimes are OK + unsafe { + let v: u32 = 123; + let rv: &u32 = &v; + + (0xdead as *mut &u32).write_volatile(rv); // OK + } + + // C-style enums are OK + unsafe { + // Bad: need some specific repr + enum PlainEnum { + A = 1, + B = 2, + C = 3, + } + + (0xdead as *mut PlainEnum).write_volatile(PlainEnum::A); + //~^ volatile_composites + + // OK + #[repr(u32)] + enum U32Enum { + A = 1, + B = 2, + C = 3, + } + + (0xdead as *mut U32Enum).write_volatile(U32Enum::A); // OK + + // OK + #[repr(C)] + enum CEnum { + A = 1, + B = 2, + C = 3, + } + (0xdead as *mut CEnum).write_volatile(CEnum::A); // OK + + // Nope + enum SumType { + A(String), + B(u32), + C, + } + (0xdead as *mut SumType).write_volatile(SumType::C); + //~^ volatile_composites + + // A repr on a complex sum type is not good enough + #[repr(C)] + enum ReprSumType { + A(String), + B(u32), + C, + } + (0xdead as *mut ReprSumType).write_volatile(ReprSumType::C); + //~^ volatile_composites + } + + // SIMD is OK + unsafe { + (0xdead as *mut std::simd::u32x4).write_volatile(std::simd::u32x4::splat(1)); // OK + } + + // Can't see through generic wrapper + unsafe { + do_device_write::(0xdead as *mut _, Default::default()); // OK + } + + let mut s = String::from("foo"); + unsafe { + std::ptr::write_volatile(&mut s, String::from("bar")); + //~^ volatile_composites + } +} + +// Generic OK +unsafe fn do_device_write(ptr: *mut T, v: T) { + unsafe { + ptr.write_volatile(v); // OK + } +} diff --git a/tests/ui/volatile_composites.stderr b/tests/ui/volatile_composites.stderr new file mode 100644 index 0000000000000..1545fc913ed00 --- /dev/null +++ b/tests/ui/volatile_composites.stderr @@ -0,0 +1,89 @@ +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:39:9 + | +LL | DEVICE_ADDR.write_volatile(regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::volatile-composites` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::volatile_composites)]` + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:47:21 + | +LL | let _regs = DEVICE_ADDR.read_volatile(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:56:9 + | +LL | std::ptr::write_volatile(DEVICE_ADDR, regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:64:21 + | +LL | let _regs = std::ptr::read_volatile(DEVICE_ADDR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:73:9 + | +LL | core::ptr::write_volatile(DEVICE_ADDR, regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:81:21 + | +LL | let _regs = core::ptr::read_volatile(DEVICE_ADDR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:89:9 + | +LL | ptr.write_volatile(regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:92:21 + | +LL | let _regs = ptr.read_volatile(); + | ^^^^^^^^^^^^^^^^^^^ + +error: type `Wrapper` is not volatile-compatible + --> tests/ui/volatile_composites.rs:118:9 + | +LL | (0xdead as *mut Wrapper).write_volatile(Wrapper((), MyDevRegisters::default(), ())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `*const [u32]` is not volatile-compatible + --> tests/ui/volatile_composites.rs:136:9 + | +LL | (0xdead as *mut *const [u32]).write_volatile(wideptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::PlainEnum` is not volatile-compatible + --> tests/ui/volatile_composites.rs:157:9 + | +LL | (0xdead as *mut PlainEnum).write_volatile(PlainEnum::A); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::SumType` is not volatile-compatible + --> tests/ui/volatile_composites.rs:185:9 + | +LL | (0xdead as *mut SumType).write_volatile(SumType::C); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::ReprSumType` is not volatile-compatible + --> tests/ui/volatile_composites.rs:195:9 + | +LL | (0xdead as *mut ReprSumType).write_volatile(ReprSumType::C); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `std::string::String` is not volatile-compatible + --> tests/ui/volatile_composites.rs:211:9 + | +LL | std::ptr::write_volatile(&mut s, String::from("bar")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors + diff --git a/tests/ui/while_let_loop.rs b/tests/ui/while_let_loop.rs index 95062c9f46c7d..f28c504742fd7 100644 --- a/tests/ui/while_let_loop.rs +++ b/tests/ui/while_let_loop.rs @@ -22,6 +22,19 @@ fn main() { break; } + loop { + //~^ while_let_loop + let Some(_x) = y else { break }; + } + + loop { + // no error, else branch does something other than break + let Some(_x) = y else { + let _z = 1; + break; + }; + } + loop { //~^ while_let_loop diff --git a/tests/ui/while_let_loop.stderr b/tests/ui/while_let_loop.stderr index ed42628a53e7f..b9aee6eb42ecf 100644 --- a/tests/ui/while_let_loop.stderr +++ b/tests/ui/while_let_loop.stderr @@ -17,6 +17,15 @@ error: this loop could be written as a `while let` loop | LL | / loop { LL | | +LL | | let Some(_x) = y else { break }; +LL | | } + | |_____^ help: try: `while let Some(_x) = y { .. }` + +error: this loop could be written as a `while let` loop + --> tests/ui/while_let_loop.rs:38:5 + | +LL | / loop { +LL | | LL | | LL | | match y { ... | @@ -25,7 +34,7 @@ LL | | } | |_____^ help: try: `while let Some(_x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:34:5 + --> tests/ui/while_let_loop.rs:47:5 | LL | / loop { LL | | @@ -37,7 +46,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:45:5 + --> tests/ui/while_let_loop.rs:58:5 | LL | / loop { LL | | @@ -48,7 +57,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:77:5 + --> tests/ui/while_let_loop.rs:90:5 | LL | / loop { LL | | @@ -68,7 +77,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:167:9 + --> tests/ui/while_let_loop.rs:180:9 | LL | / loop { LL | | @@ -88,7 +97,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:182:5 + --> tests/ui/while_let_loop.rs:195:5 | LL | / loop { LL | | @@ -107,7 +116,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:194:5 + --> tests/ui/while_let_loop.rs:207:5 | LL | / loop { LL | | @@ -126,7 +135,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:206:5 + --> tests/ui/while_let_loop.rs:219:5 | LL | / loop { LL | | @@ -137,7 +146,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = Some(3) { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:218:5 + --> tests/ui/while_let_loop.rs:231:5 | LL | / loop { LL | | @@ -156,7 +165,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:230:5 + --> tests/ui/while_let_loop.rs:243:5 | LL | / loop { LL | | @@ -177,5 +186,5 @@ LL + .. LL + } | -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index e6c451ce7399e..b5fca36f3f089 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -1,5 +1,6 @@ #![warn(clippy::zero_repeat_side_effects)] #![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] +#![allow(clippy::no_effect)] // only fires _after_ the fix fn f() -> i32 { println!("side effect"); @@ -13,36 +14,47 @@ fn main() { // should trigger // on arrays - f(); let a: [i32; 0] = []; + f(); + let a: [i32; 0] = []; //~^ zero_repeat_side_effects let mut b; - f(); b = [] as [i32; 0]; + f(); + b = [] as [i32; 0]; //~^ zero_repeat_side_effects // on vecs // vecs dont support inferring value of consts - f(); let c: std::vec::Vec = vec![]; + f(); + let c: std::vec::Vec = vec![]; //~^ zero_repeat_side_effects let d; - f(); d = vec![] as std::vec::Vec; + f(); + d = vec![] as std::vec::Vec; //~^ zero_repeat_side_effects // for macros - println!("side effect"); let e: [(); 0] = []; + println!("side effect"); + let e: [(); 0] = []; //~^ zero_repeat_side_effects // for nested calls - { f() }; let g: [i32; 0] = []; + { f() }; + let g: [i32; 0] = []; //~^ zero_repeat_side_effects // as function param - drop({ f(); vec![] as std::vec::Vec }); + drop({ + f(); + vec![] as std::vec::Vec + }); //~^ zero_repeat_side_effects // when singled out/not part of assignment/local - { f(); vec![] as std::vec::Vec }; + f(); + vec![] as std::vec::Vec; //~^ zero_repeat_side_effects - { f(); [] as [i32; 0] }; + f(); + [] as [i32; 0]; //~^ zero_repeat_side_effects // should not trigger @@ -96,8 +108,14 @@ fn issue_14681() { foo(&[Some(0i64); 0]); foo(&[Some(Some(0i64)); 0]); - foo(&{ Some(f()); [] as [std::option::Option; 0] }); + foo(&{ + Some(f()); + [] as [std::option::Option; 0] + }); //~^ zero_repeat_side_effects - foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + foo(&{ + Some(Some(S::new())); + [] as [std::option::Option>; 0] + }); //~^ zero_repeat_side_effects } diff --git a/tests/ui/zero_repeat_side_effects.rs b/tests/ui/zero_repeat_side_effects.rs index f8a497976aa43..ea043d21638cc 100644 --- a/tests/ui/zero_repeat_side_effects.rs +++ b/tests/ui/zero_repeat_side_effects.rs @@ -1,5 +1,6 @@ #![warn(clippy::zero_repeat_side_effects)] #![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] +#![allow(clippy::no_effect)] // only fires _after_ the fix fn f() -> i32 { println!("side effect"); diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index 771b71c686ae4..49e850d035343 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -1,5 +1,5 @@ error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:16:5 + --> tests/ui/zero_repeat_side_effects.rs:17:5 | LL | let a = [f(); 0]; | ^^^^^^^^^^^^^^^^^ @@ -8,128 +8,134 @@ LL | let a = [f(); 0]; = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` help: consider performing the side effect separately | -LL - let a = [f(); 0]; -LL + f(); let a: [i32; 0] = []; +LL ~ f(); +LL + let a: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:19:5 + --> tests/ui/zero_repeat_side_effects.rs:20:5 | LL | b = [f(); 0]; | ^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - b = [f(); 0]; -LL + f(); b = [] as [i32; 0]; +LL ~ f(); +LL ~ b = [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:24:5 + --> tests/ui/zero_repeat_side_effects.rs:25:5 | LL | let c = vec![f(); 0]; | ^^^^^^^^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - let c = vec![f(); 0]; -LL + f(); let c: std::vec::Vec = vec![]; +LL ~ f(); +LL + let c: std::vec::Vec = vec![]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:27:5 + --> tests/ui/zero_repeat_side_effects.rs:28:5 | LL | d = vec![f(); 0]; | ^^^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - d = vec![f(); 0]; -LL + f(); d = vec![] as std::vec::Vec; +LL ~ f(); +LL ~ d = vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:31:5 + --> tests/ui/zero_repeat_side_effects.rs:32:5 | LL | let e = [println!("side effect"); 0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - let e = [println!("side effect"); 0]; -LL + println!("side effect"); let e: [(); 0] = []; +LL ~ println!("side effect"); +LL + let e: [(); 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:35:5 + --> tests/ui/zero_repeat_side_effects.rs:36:5 | LL | let g = [{ f() }; 0]; | ^^^^^^^^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - let g = [{ f() }; 0]; -LL + { f() }; let g: [i32; 0] = []; +LL ~ { f() }; +LL + let g: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:39:10 + --> tests/ui/zero_repeat_side_effects.rs:40:10 | LL | drop(vec![f(); 0]); | ^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - drop(vec![f(); 0]); -LL + drop({ f(); vec![] as std::vec::Vec }); +LL ~ drop({ +LL + f(); +LL + vec![] as std::vec::Vec +LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:43:5 + --> tests/ui/zero_repeat_side_effects.rs:44:5 | LL | vec![f(); 0]; | ^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - vec![f(); 0]; -LL + { f(); vec![] as std::vec::Vec }; +LL ~ f(); +LL ~ vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:45:5 + --> tests/ui/zero_repeat_side_effects.rs:46:5 | LL | [f(); 0]; | ^^^^^^^^ | help: consider performing the side effect separately | -LL - [f(); 0]; -LL + { f(); [] as [i32; 0] }; +LL ~ f(); +LL ~ [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:99:10 + --> tests/ui/zero_repeat_side_effects.rs:100:10 | LL | foo(&[Some(f()); 0]); | ^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - foo(&[Some(f()); 0]); -LL + foo(&{ Some(f()); [] as [std::option::Option; 0] }); +LL ~ foo(&{ +LL + Some(f()); +LL + [] as [std::option::Option; 0] +LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:101:10 + --> tests/ui/zero_repeat_side_effects.rs:102:10 | LL | foo(&[Some(Some(S::new())); 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL - foo(&[Some(Some(S::new())); 0]); -LL + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); +LL ~ foo(&{ +LL + Some(Some(S::new())); +LL + [] as [std::option::Option>; 0] +LL ~ }); | error: aborting due to 11 previous errors diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.fixed b/tests/ui/zero_repeat_side_effects_never_pattern.fixed new file mode 100644 index 0000000000000..3d037516f75c8 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_never_pattern.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::diverging_sub_expression)] +#![feature(never_type)] + +fn issue_14998() { + // nameable type thanks to `never_type` being enabled, suggest + panic!(); + let _data: [!; 0] = []; + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.rs b/tests/ui/zero_repeat_side_effects_never_pattern.rs new file mode 100644 index 0000000000000..3dc1929bcdc76 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_never_pattern.rs @@ -0,0 +1,9 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::diverging_sub_expression)] +#![feature(never_type)] + +fn issue_14998() { + // nameable type thanks to `never_type` being enabled, suggest + let _data = [panic!(); 0]; + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.stderr b/tests/ui/zero_repeat_side_effects_never_pattern.stderr new file mode 100644 index 0000000000000..280955740cc4c --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_never_pattern.stderr @@ -0,0 +1,16 @@ +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_never_pattern.rs:7:5 + | +LL | let _data = [panic!(); 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` +help: consider performing the side effect separately + | +LL ~ panic!(); +LL + let _data: [!; 0] = []; + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/zero_repeat_side_effects_unfixable.rs b/tests/ui/zero_repeat_side_effects_unfixable.rs new file mode 100644 index 0000000000000..82f0884056ab0 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_unfixable.rs @@ -0,0 +1,13 @@ +//@no-rustfix +#![warn(clippy::zero_repeat_side_effects)] +#![expect(clippy::diverging_sub_expression)] + +fn issue_14998() { + // unnameable types, don't suggest + let _data = [|| 3i32; 0]; + //~^ zero_repeat_side_effects + + // unnameable type because `never_type` is not enabled, don't suggest + let _data = [panic!(); 0]; + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects_unfixable.stderr b/tests/ui/zero_repeat_side_effects_unfixable.stderr new file mode 100644 index 0000000000000..450617f3782c4 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_unfixable.stderr @@ -0,0 +1,20 @@ +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_unfixable.rs:7:5 + | +LL | let _data = [|| 3i32; 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider performing the side effect separately + = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` + +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_unfixable.rs:11:5 + | +LL | let _data = [panic!(); 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider performing the side effect separately + +error: aborting due to 2 previous errors + diff --git a/triagebot.toml b/triagebot.toml index b2fb50918f583..db951b95ef50a 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -45,6 +45,9 @@ reviewed_label = "S-waiting-on-author" [autolabel."S-waiting-on-review"] new_pr = true +[autolabel."needs-fcp"] +trigger_files = ["clippy_lints/src/declared_lints.rs"] + [concern] # These labels are set when there are unresolved concerns, removed otherwise labels = ["S-waiting-on-concerns"] @@ -60,6 +63,7 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB users_on_vacation = [ "matthiaskrgr", "Manishearth", + "blyxyas", ] [assign.owners] From 6a0aa123a05fc7197fd50b2ce9508bfc6923439b Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 7 Oct 2025 15:49:06 -0500 Subject: [PATCH 022/108] Fix clippy for impl_trait_header changes --- .../src/derive/derive_ord_xor_partial_ord.rs | 2 +- .../src/derive/derived_hash_with_manual_eq.rs | 2 +- clippy_lints/src/fallible_impl_from.rs | 8 +++----- clippy_lints/src/from_over_into.rs | 3 +-- clippy_lints/src/implicit_saturating_sub.rs | 6 ++---- clippy_lints/src/methods/suspicious_splitn.rs | 3 +-- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/non_canonical_impls.rs | 12 ++++++------ clippy_lints/src/non_send_fields_in_send_ty.rs | 2 +- clippy_lints/src/only_used_in_recursion.rs | 15 +++++++-------- clippy_lints/src/return_self_not_must_use.rs | 3 +-- clippy_lints/src/unused_io_amount.rs | 5 ++--- clippy_lints/src/use_self.rs | 5 +++-- clippy_lints_internal/src/msrv_attr_impl.rs | 5 +---- 14 files changed, 31 insertions(+), 42 deletions(-) diff --git a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs index 274c699ff9d24..2bd5e2cbfb1ad 100644 --- a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs +++ b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs @@ -28,7 +28,7 @@ pub(super) fn check<'tcx>( return; } - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + let trait_ref = cx.tcx.impl_trait_ref(impl_id); // Only care about `impl PartialOrd for Foo` // For `impl PartialOrd for A, input_types is [A, B] diff --git a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs index afc02ce32d48e..dc3fbe5d7012c 100644 --- a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs +++ b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs @@ -27,7 +27,7 @@ pub(super) fn check<'tcx>( return; } - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + let trait_ref = cx.tcx.impl_trait_ref(impl_id); // Only care about `impl PartialEq for Foo` // For `impl PartialEq for A, input_types is [A, B] diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 4446b912bf7ee..c42998ffc3f59 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -52,11 +52,9 @@ declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]); impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { // check for `impl From for ..` - if let hir::ItemKind::Impl(_) = &item.kind - && let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) - && cx - .tcx - .is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id) + if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = &item.kind + && let impl_trait_id = cx.tcx.impl_trait_id(item.owner_id) + && cx.tcx.is_diagnostic_item(sym::From, impl_trait_id) { lint_impl_body(cx, item.owner_id, item.span); } diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index fd807290dc090..ed55f90848f89 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -76,8 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { // `impl Into for self_ty` && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args && span_is_local(item.span) - && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) - .map(ty::EarlyBinder::instantiate_identity) + && let middle_trait_ref = cx.tcx.impl_trait_ref(item.owner_id).instantiate_identity() && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) && !matches!(middle_trait_ref.args.type_at(1).kind(), ty::Alias(ty::Opaque, _)) && self.msrv.meets(cx, msrvs::RE_REBALANCING_COHERENCE) diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 678a29924e528..7b6f8729cb759 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -339,8 +339,7 @@ fn check_with_condition<'tcx>( ExprKind::Path(QPath::TypeRelative(_, name)) => { if name.ident.name == sym::MIN && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_assoc(const_id) - && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && let Some(impl_id) = cx.tcx.inherent_impl_of_assoc(const_id) && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() { print_lint_and_sugg(cx, var_name, expr); @@ -350,8 +349,7 @@ fn check_with_condition<'tcx>( if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind && name.ident.name == sym::min_value && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_assoc(func_id) - && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && let Some(impl_id) = cx.tcx.inherent_impl_of_assoc(func_id) && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() { print_lint_and_sugg(cx, var_name, expr); diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index 9876681ddbb3a..be1481ebb996e 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -10,8 +10,7 @@ use super::SUSPICIOUS_SPLITN; pub(super) fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { if count <= 1 && let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_assoc(call_id) - && cx.tcx.impl_trait_ref(impl_id).is_none() + && let Some(impl_id) = cx.tcx.inherent_impl_of_assoc(call_id) && let self_ty = cx.tcx.type_of(impl_id).instantiate_identity() && (self_ty.is_slice() || self_ty.is_str()) { diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 6323e728666ce..bccc72c2a516c 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -167,7 +167,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let container_id = assoc_item.container_id(cx.tcx); let trait_def_id = match assoc_item.container { AssocContainer::Trait => Some(container_id), - AssocContainer::TraitImpl(_) => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + AssocContainer::TraitImpl(_) => Some(cx.tcx.impl_trait_id(container_id)), AssocContainer::InherentImpl => None, }; diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 3285023b34aa8..e11f775018ed8 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, LangItem, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{EarlyBinder, TyCtxt, TypeckResults}; +use rustc_middle::ty::{TyCtxt, TypeckResults}; use rustc_session::impl_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -173,10 +173,11 @@ impl LateLintPass<'_> for NonCanonicalImpls { } }); + let trait_impl = cx.tcx.impl_trait_ref(item.owner_id).skip_binder(); + match trait_ { Trait::Clone => { if let Some(copy_trait) = self.copy_trait - && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) && implements_trait(cx, trait_impl.self_ty(), copy_trait, &[]) { for (assoc, _, block) in assoc_fns { @@ -185,10 +186,9 @@ impl LateLintPass<'_> for NonCanonicalImpls { } }, Trait::PartialOrd => { - if let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) - // If `Self` and `Rhs` are not the same type, then a corresponding `Ord` impl is not possible, - // since it doesn't have an `Rhs` - && let [lhs, rhs] = trait_impl.args.as_slice() + // If `Self` and `Rhs` are not the same type, then a corresponding `Ord` impl is not possible, + // since it doesn't have an `Rhs` + if let [lhs, rhs] = trait_impl.args.as_slice() && lhs == rhs && let Some(ord_trait) = self.ord_trait && implements_trait(cx, trait_impl.self_ty(), ord_trait, &[]) diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs index b810bc01fbdcb..fd5562f310e48 100644 --- a/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -87,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { && let Some(trait_id) = of_trait.trait_ref.trait_def_id() && send_trait == trait_id && of_trait.polarity == ImplPolarity::Positive - && let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) + && let ty_trait_ref = cx.tcx.impl_trait_ref(item.owner_id) && let self_ty = ty_trait_ref.instantiate_identity().self_ty() && let ty::Adt(adt_def, impl_trait_args) = self_ty.kind() { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 784ea34bac58f..51644f7dce6cf 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -6,9 +6,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdMap; -use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind}; +use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemImplKind, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, ConstKind, EarlyBinder, GenericArgKind, GenericArgsRef}; +use rustc_middle::ty::{self, ConstKind, GenericArgKind, GenericArgsRef}; use rustc_session::impl_lint_pass; use rustc_span::Span; use rustc_span::symbol::{Ident, kw}; @@ -320,15 +320,14 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { Node::ImplItem(&ImplItem { kind: ImplItemKind::Fn(ref sig, _), owner_id, + impl_kind, .. }) => { - if let Node::Item(item) = cx.tcx.parent_hir_node(owner_id.into()) - && let Some(trait_ref) = cx - .tcx - .impl_trait_ref(item.owner_id) - .map(EarlyBinder::instantiate_identity) - && let Some(trait_item_id) = cx.tcx.trait_item_of(owner_id) + if let ImplItemImplKind::Trait { trait_item_def_id, .. } = impl_kind + && let Ok(trait_item_id) = trait_item_def_id { + let impl_id = cx.tcx.parent(owner_id.into()); + let trait_ref = cx.tcx.impl_trait_ref(impl_id).instantiate_identity(); ( trait_item_id, FnKind::ImplTraitFn( diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index b057396034c07..83a226b29e75f 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -113,10 +113,9 @@ impl<'tcx> LateLintPass<'tcx> for ReturnSelfNotMustUse { ) { if matches!(kind, FnKind::Method(_, _)) // We are only interested in methods, not in functions or associated functions. - && let Some(impl_def) = cx.tcx.impl_of_assoc(fn_def.to_def_id()) // We don't want this method to be te implementation of a trait because the // `#[must_use]` should be put on the trait definition directly. - && cx.tcx.trait_id_of_impl(impl_def).is_none() + && cx.tcx.inherent_impl_of_assoc(fn_def.to_def_id()).is_some() { let hir_id = cx.tcx.local_def_id_to_hir_id(fn_def); check_method(cx, decl, fn_def, span, hir_id.expect_owner()); diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index cff798f7a89a0..2884bbd9280d2 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -85,9 +85,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { /// get desugared to match. fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) { let fn_def_id = block.hir_id.owner.to_def_id(); - if let Some(impl_id) = cx.tcx.impl_of_assoc(fn_def_id) - && let Some(trait_id) = cx.tcx.trait_id_of_impl(impl_id) - { + if let Some(impl_id) = cx.tcx.trait_impl_of_assoc(fn_def_id) { + let trait_id = cx.tcx.impl_trait_id(impl_id); // We don't want to lint inside io::Read or io::Write implementations, as the author has more // information about their trait implementation than our lint, see https://github.com/rust-lang/rust-clippy/issues/4836 if let Some(trait_name) = cx.tcx.get_diagnostic_name(trait_id) diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index f46dedf1261e9..eba60501ae214 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -10,7 +10,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{ self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParamKind, HirId, Impl, - ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, + ImplItemImplKind, ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty as MiddleTy; @@ -142,13 +142,14 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait // declaration. The collection of those types is all this method implementation does. if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind + && let ImplItemImplKind::Trait { .. } = impl_item.impl_kind && let Some(&mut StackItem::Check { impl_id, ref mut types_to_skip, .. }) = self.stack.last_mut() - && let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id) { + let impl_trait_ref = cx.tcx.impl_trait_ref(impl_id); // `self_ty` is the semantic self type of `impl for `. This cannot be // `Self`. let self_ty = impl_trait_ref.instantiate_identity().self_ty(); diff --git a/clippy_lints_internal/src/msrv_attr_impl.rs b/clippy_lints_internal/src/msrv_attr_impl.rs index 66aeb910891a2..e529bc2d2fa5b 100644 --- a/clippy_lints_internal/src/msrv_attr_impl.rs +++ b/clippy_lints_internal/src/msrv_attr_impl.rs @@ -26,10 +26,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { items, .. }) = &item.kind - && let Some(trait_ref) = cx - .tcx - .impl_trait_ref(item.owner_id) - .map(EarlyBinder::instantiate_identity) + && let trait_ref = cx.tcx.impl_trait_ref(item.owner_id).instantiate_identity() && 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() From 269679e4351a0204be47760f7821bbd6fc7d7194 Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Fri, 17 Oct 2025 20:03:29 +0500 Subject: [PATCH 023/108] Update link to the Code of Conduct --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index e3708bc485399..133072e0586b4 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,3 +1,3 @@ # The Rust Code of Conduct -The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). +The Code of Conduct for this repository [can be found online](https://rust-lang.org/policies/code-of-conduct/). From 4463ba7aded4f50f31ce0dc1a0e991e8ee2a39c4 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 12 Oct 2025 13:58:09 +0200 Subject: [PATCH 024/108] clean-up tests --- tests/ui/option_map_unit_fn_fixable.fixed | 5 +- tests/ui/option_map_unit_fn_fixable.rs | 5 +- tests/ui/option_map_unit_fn_fixable.stderr | 48 ++++++++++---------- tests/ui/option_map_unit_fn_unfixable.rs | 1 - tests/ui/option_map_unit_fn_unfixable.stderr | 8 ++-- tests/ui/result_map_unit_fn_fixable.fixed | 4 +- tests/ui/result_map_unit_fn_fixable.rs | 4 +- tests/ui/result_map_unit_fn_fixable.stderr | 42 ++++++++--------- tests/ui/result_map_unit_fn_unfixable.rs | 5 +- tests/ui/result_map_unit_fn_unfixable.stderr | 12 ++--- 10 files changed, 64 insertions(+), 70 deletions(-) diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 340be7c7e932a..ddba1cd1291fa 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -1,6 +1,5 @@ #![warn(clippy::option_map_unit_fn)] -#![allow(unused)] -#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] +#![expect(clippy::unnecessary_wraps)] fn do_nothing(_: T) {} @@ -98,7 +97,7 @@ fn option_map_unit_fn() { if let Some(a) = option() { do_nothing(a) } //~^ option_map_unit_fn - if let Some(value) = option() { println!("{:?}", value) } + if let Some(value) = option() { println!("{value:?}") } //~^ option_map_unit_fn } diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index d902c87379b77..7bd1fe92bdb6e 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -1,6 +1,5 @@ #![warn(clippy::option_map_unit_fn)] -#![allow(unused)] -#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] +#![expect(clippy::unnecessary_wraps)] fn do_nothing(_: T) {} @@ -98,7 +97,7 @@ fn option_map_unit_fn() { option().map(do_nothing); //~^ option_map_unit_fn - option().map(|value| println!("{:?}", value)); + option().map(|value| println!("{value:?}")); //~^ option_map_unit_fn } diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index 2405aa9a7ccfa..0fd6e0f5c66cd 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,5 +1,5 @@ error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:37:5 + --> tests/ui/option_map_unit_fn_fixable.rs:36:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -10,7 +10,7 @@ LL | x.field.map(do_nothing); = help: to override `-D warnings` add `#[allow(clippy::option_map_unit_fn)]` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:40:5 + --> tests/ui/option_map_unit_fn_fixable.rs:39:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -18,7 +18,7 @@ LL | x.field.map(do_nothing); | help: try: `if let Some(x_field) = x.field { do_nothing(x_field) }` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:43:5 + --> tests/ui/option_map_unit_fn_fixable.rs:42:5 | LL | x.field.map(diverge); | ^^^^^^^^^^^^^^^^^^^^- @@ -26,7 +26,7 @@ LL | x.field.map(diverge); | help: try: `if let Some(x_field) = x.field { diverge(x_field) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:50:5 + --> tests/ui/option_map_unit_fn_fixable.rs:49:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -34,7 +34,7 @@ LL | x.field.map(|value| x.do_option_nothing(value + captured)); | help: try: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:53:5 + --> tests/ui/option_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -42,7 +42,7 @@ LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | help: try: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:57:5 + --> tests/ui/option_map_unit_fn_fixable.rs:56:5 | LL | x.field.map(|value| do_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -50,7 +50,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | help: try: `if let Some(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:60:5 + --> tests/ui/option_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -58,7 +58,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | help: try: `if let Some(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:63:5 + --> tests/ui/option_map_unit_fn_fixable.rs:62:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -66,7 +66,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | help: try: `if let Some(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:66:5 + --> tests/ui/option_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -74,7 +74,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | help: try: `if let Some(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:70:5 + --> tests/ui/option_map_unit_fn_fixable.rs:69:5 | LL | x.field.map(|value| diverge(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -82,7 +82,7 @@ LL | x.field.map(|value| diverge(value + captured)); | help: try: `if let Some(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:73:5 + --> tests/ui/option_map_unit_fn_fixable.rs:72:5 | LL | x.field.map(|value| { diverge(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -90,7 +90,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | help: try: `if let Some(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:76:5 + --> tests/ui/option_map_unit_fn_fixable.rs:75:5 | LL | x.field.map(|value| { diverge(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -98,7 +98,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | help: try: `if let Some(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:79:5 + --> tests/ui/option_map_unit_fn_fixable.rs:78:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -106,7 +106,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | help: try: `if let Some(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:85:5 + --> tests/ui/option_map_unit_fn_fixable.rs:84:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -114,7 +114,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | help: try: `if let Some(value) = x.field { let y = plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:88:5 + --> tests/ui/option_map_unit_fn_fixable.rs:87:5 | LL | x.field.map(|value| { plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -122,7 +122,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | help: try: `if let Some(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:91:5 + --> tests/ui/option_map_unit_fn_fixable.rs:90:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -130,7 +130,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | help: try: `if let Some(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:95:5 + --> tests/ui/option_map_unit_fn_fixable.rs:94:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -138,7 +138,7 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | help: try: `if let Some(ref value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:98:5 + --> tests/ui/option_map_unit_fn_fixable.rs:97:5 | LL | option().map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^^- @@ -146,15 +146,15 @@ LL | option().map(do_nothing); | help: try: `if let Some(a) = option() { do_nothing(a) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:101:5 + --> tests/ui/option_map_unit_fn_fixable.rs:100:5 | -LL | option().map(|value| println!("{:?}", value)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- +LL | option().map(|value| println!("{value:?}")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try: `if let Some(value) = option() { println!("{:?}", value) }` + | help: try: `if let Some(value) = option() { println!("{value:?}") }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:108:5 + --> tests/ui/option_map_unit_fn_fixable.rs:107:5 | LL | x.map(|x| unsafe { f(x) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -162,7 +162,7 @@ LL | x.map(|x| unsafe { f(x) }); | help: try: `if let Some(x) = x { unsafe { f(x) } }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> tests/ui/option_map_unit_fn_fixable.rs:110:5 + --> tests/ui/option_map_unit_fn_fixable.rs:109:5 | LL | x.map(|x| unsafe { { f(x) } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- diff --git a/tests/ui/option_map_unit_fn_unfixable.rs b/tests/ui/option_map_unit_fn_unfixable.rs index dd2f80fefbeec..8e40cec1ae9a4 100644 --- a/tests/ui/option_map_unit_fn_unfixable.rs +++ b/tests/ui/option_map_unit_fn_unfixable.rs @@ -1,5 +1,4 @@ #![warn(clippy::option_map_unit_fn)] -#![allow(unused)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_unfixable.stderr b/tests/ui/option_map_unit_fn_unfixable.stderr index 8527850143295..4746a4ed1aa09 100644 --- a/tests/ui/option_map_unit_fn_unfixable.stderr +++ b/tests/ui/option_map_unit_fn_unfixable.stderr @@ -1,23 +1,23 @@ error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:17:5 + --> tests/ui/option_map_unit_fn_unfixable.rs:16:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); | ^ not found in this scope error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:20:5 + --> tests/ui/option_map_unit_fn_unfixable.rs:19:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); | ^ not found in this scope error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:25:5 + --> tests/ui/option_map_unit_fn_unfixable.rs:24:5 | LL | x.field.map(|value| { | ^ not found in this scope error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:30:5 + --> tests/ui/option_map_unit_fn_unfixable.rs:29:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); | ^ not found in this scope diff --git a/tests/ui/result_map_unit_fn_fixable.fixed b/tests/ui/result_map_unit_fn_fixable.fixed index 6bee013c3f470..b810e85a45fac 100644 --- a/tests/ui/result_map_unit_fn_fixable.fixed +++ b/tests/ui/result_map_unit_fn_fixable.fixed @@ -1,6 +1,4 @@ #![warn(clippy::result_map_unit_fn)] -#![allow(unused)] -#![allow(clippy::uninlined_format_args)] fn do_nothing(_: T) {} @@ -92,7 +90,7 @@ fn result_map_unit_fn() { if let Ok(ref value) = x.field { do_nothing(value + captured) } //~^ result_map_unit_fn - if let Ok(value) = x.field { println!("{:?}", value) } + if let Ok(value) = x.field { println!("{value:?}") } //~^ result_map_unit_fn } diff --git a/tests/ui/result_map_unit_fn_fixable.rs b/tests/ui/result_map_unit_fn_fixable.rs index a206cfe6842ff..18cd557f04544 100644 --- a/tests/ui/result_map_unit_fn_fixable.rs +++ b/tests/ui/result_map_unit_fn_fixable.rs @@ -1,6 +1,4 @@ #![warn(clippy::result_map_unit_fn)] -#![allow(unused)] -#![allow(clippy::uninlined_format_args)] fn do_nothing(_: T) {} @@ -92,7 +90,7 @@ fn result_map_unit_fn() { x.field.map(|ref value| { do_nothing(value + captured) }); //~^ result_map_unit_fn - x.field.map(|value| println!("{:?}", value)); + x.field.map(|value| println!("{value:?}")); //~^ result_map_unit_fn } diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr index eca844e06cc06..66e98392f6dc5 100644 --- a/tests/ui/result_map_unit_fn_fixable.stderr +++ b/tests/ui/result_map_unit_fn_fixable.stderr @@ -1,5 +1,5 @@ error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:34:5 + --> tests/ui/result_map_unit_fn_fixable.rs:32:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -10,7 +10,7 @@ LL | x.field.map(do_nothing); = help: to override `-D warnings` add `#[allow(clippy::result_map_unit_fn)]` error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:37:5 + --> tests/ui/result_map_unit_fn_fixable.rs:35:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -18,7 +18,7 @@ LL | x.field.map(do_nothing); | help: try: `if let Ok(x_field) = x.field { do_nothing(x_field) }` error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:40:5 + --> tests/ui/result_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(diverge); | ^^^^^^^^^^^^^^^^^^^^- @@ -26,7 +26,7 @@ LL | x.field.map(diverge); | help: try: `if let Ok(x_field) = x.field { diverge(x_field) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:47:5 + --> tests/ui/result_map_unit_fn_fixable.rs:45:5 | LL | x.field.map(|value| x.do_result_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -34,7 +34,7 @@ LL | x.field.map(|value| x.do_result_nothing(value + captured)); | help: try: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:50:5 + --> tests/ui/result_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -42,7 +42,7 @@ LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); | help: try: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:54:5 + --> tests/ui/result_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| do_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -50,7 +50,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | help: try: `if let Ok(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:57:5 + --> tests/ui/result_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -58,7 +58,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | help: try: `if let Ok(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:60:5 + --> tests/ui/result_map_unit_fn_fixable.rs:58:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -66,7 +66,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | help: try: `if let Ok(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:63:5 + --> tests/ui/result_map_unit_fn_fixable.rs:61:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -74,7 +74,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | help: try: `if let Ok(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:67:5 + --> tests/ui/result_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| diverge(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -82,7 +82,7 @@ LL | x.field.map(|value| diverge(value + captured)); | help: try: `if let Ok(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:70:5 + --> tests/ui/result_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { diverge(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -90,7 +90,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | help: try: `if let Ok(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:73:5 + --> tests/ui/result_map_unit_fn_fixable.rs:71:5 | LL | x.field.map(|value| { diverge(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -98,7 +98,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | help: try: `if let Ok(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:76:5 + --> tests/ui/result_map_unit_fn_fixable.rs:74:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -106,7 +106,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | help: try: `if let Ok(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:82:5 + --> tests/ui/result_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -114,7 +114,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | help: try: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:85:5 + --> tests/ui/result_map_unit_fn_fixable.rs:83:5 | LL | x.field.map(|value| { plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -122,7 +122,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | help: try: `if let Ok(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:88:5 + --> tests/ui/result_map_unit_fn_fixable.rs:86:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -130,7 +130,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | help: try: `if let Ok(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:92:5 + --> tests/ui/result_map_unit_fn_fixable.rs:90:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -138,12 +138,12 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | help: try: `if let Ok(ref value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_fixable.rs:95:5 + --> tests/ui/result_map_unit_fn_fixable.rs:93:5 | -LL | x.field.map(|value| println!("{:?}", value)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- +LL | x.field.map(|value| println!("{value:?}")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | | - | help: try: `if let Ok(value) = x.field { println!("{:?}", value) }` + | help: try: `if let Ok(value) = x.field { println!("{value:?}") }` error: aborting due to 18 previous errors diff --git a/tests/ui/result_map_unit_fn_unfixable.rs b/tests/ui/result_map_unit_fn_unfixable.rs index fe3d8ece39f4c..e3f5c7f996dad 100644 --- a/tests/ui/result_map_unit_fn_unfixable.rs +++ b/tests/ui/result_map_unit_fn_unfixable.rs @@ -1,7 +1,8 @@ +//@no-rustfix #![warn(clippy::result_map_unit_fn)] #![feature(never_type)] -#![allow(unused, clippy::unnecessary_map_on_constructor)] -//@no-rustfix +#![allow(clippy::unnecessary_map_on_constructor)] + struct HasResult { field: Result, } diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index a6e38d808afa7..dc9c398884efc 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -1,5 +1,5 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:23:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:24:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -10,7 +10,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); = help: to override `-D warnings` add `#[allow(clippy::result_map_unit_fn)]` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:28:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:29:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -18,7 +18,7 @@ LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) | help: try: `if let Ok(value) = x.field { ... }` error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:34:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:35:5 | LL | // x.field.map(|value| { LL | || @@ -31,7 +31,7 @@ LL | || }); | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:40:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:41:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -39,7 +39,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); | help: try: `if let Ok(value) = x.field { ... }` error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:46:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:47:5 | LL | "12".parse::().map(diverge); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -47,7 +47,7 @@ LL | "12".parse::().map(diverge); | help: try: `if let Ok(a) = "12".parse::() { diverge(a) }` error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` - --> tests/ui/result_map_unit_fn_unfixable.rs:54:5 + --> tests/ui/result_map_unit_fn_unfixable.rs:55:5 | LL | y.map(do_nothing); | ^^^^^^^^^^^^^^^^^- From b0ecbdf5a258aeff7572e5f253c1e5d30982c276 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 12 Oct 2025 14:06:00 +0200 Subject: [PATCH 025/108] fix `option_map_unit_fn_unfixable.rs` The errors were unrelated to the lint... --- tests/ui/option_map_unit_fn_unfixable.rs | 19 +++-- tests/ui/option_map_unit_fn_unfixable.stderr | 75 ++++++++++++++++---- 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/tests/ui/option_map_unit_fn_unfixable.rs b/tests/ui/option_map_unit_fn_unfixable.rs index 8e40cec1ae9a4..a6d7ea4a4c8d9 100644 --- a/tests/ui/option_map_unit_fn_unfixable.rs +++ b/tests/ui/option_map_unit_fn_unfixable.rs @@ -1,4 +1,6 @@ +//@no-rustfix #![warn(clippy::option_map_unit_fn)] +#![allow(clippy::unnecessary_wraps, clippy::unnecessary_map_on_constructor)] // only fires before the fix fn do_nothing(_: T) {} @@ -10,14 +12,19 @@ fn plus_one(value: usize) -> usize { value + 1 } +struct HasOption { + field: Option, +} + #[rustfmt::skip] fn option_map_unit_fn() { + let x = HasOption { field: Some(10) }; x.field.map(|value| { do_nothing(value); do_nothing(value) }); - //~^ ERROR: cannot find value + //~^ option_map_unit_fn x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); - //~^ ERROR: cannot find value + //~^ option_map_unit_fn // Suggestion for the let block should be `{ ... }` as it's too difficult to build a // proper suggestion for these cases @@ -25,18 +32,22 @@ fn option_map_unit_fn() { do_nothing(value); do_nothing(value) }); - //~^^^^ ERROR: cannot find value + //~^^^^ option_map_unit_fn x.field.map(|value| { do_nothing(value); do_nothing(value); }); - //~^ ERROR: cannot find value + //~^ option_map_unit_fn // The following should suggest `if let Some(_X) ...` as it's difficult to generate a proper let variable name for them Some(42).map(diverge); + //~^ option_map_unit_fn "12".parse::().ok().map(diverge); + //~^ option_map_unit_fn Some(plus_one(1)).map(do_nothing); + //~^ option_map_unit_fn // Should suggest `if let Some(_y) ...` to not override the existing foo variable let y = Some(42); y.map(do_nothing); + //~^ option_map_unit_fn } fn main() {} diff --git a/tests/ui/option_map_unit_fn_unfixable.stderr b/tests/ui/option_map_unit_fn_unfixable.stderr index 4746a4ed1aa09..de0bc4d55055c 100644 --- a/tests/ui/option_map_unit_fn_unfixable.stderr +++ b/tests/ui/option_map_unit_fn_unfixable.stderr @@ -1,27 +1,72 @@ -error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:16:5 +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:23:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); - | ^ not found in this scope + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(value) = x.field { ... }` + | + = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::option_map_unit_fn)]` -error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:19:5 +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:26:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); - | ^ not found in this scope + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(value) = x.field { ... }` -error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:24:5 +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:31:5 + | +LL | // x.field.map(|value| { +LL | || do_nothing(value); +LL | || do_nothing(value) +LL | || }); + | ||______^- help: try: `if let Some(value) = x.field { ... }` + | |______| | -LL | x.field.map(|value| { - | ^ not found in this scope -error[E0425]: cannot find value `x` in this scope - --> tests/ui/option_map_unit_fn_unfixable.rs:29:5 +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:36:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); - | ^ not found in this scope + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(value) = x.field { ... }` + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:40:5 + | +LL | Some(42).map(diverge); + | ^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(a) = Some(42) { diverge(a) }` + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:42:5 + | +LL | "12".parse::().ok().map(diverge); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(a) = "12".parse::().ok() { diverge(a) }` + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:44:5 + | +LL | Some(plus_one(1)).map(do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(a) = Some(plus_one(1)) { do_nothing(a) }` + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` + --> tests/ui/option_map_unit_fn_unfixable.rs:49:5 + | +LL | y.map(do_nothing); + | ^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(_y) = y { do_nothing(_y) }` -error: aborting due to 4 previous errors +error: aborting due to 8 previous errors -For more information about this error, try `rustc --explain E0425`. From 98db0984084d08e639821778608e494b6b2fc16b Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 12 Oct 2025 14:08:52 +0200 Subject: [PATCH 026/108] improve the suggestion - make them `verbose` -- the ones spanning multiple lines were especially egregious - give a more descriptive help message --- clippy_lints/src/map_unit_fn.rs | 9 +- tests/ui/option_map_unit_fn_fixable.stderr | 209 +++++++++++++------ tests/ui/option_map_unit_fn_unfixable.stderr | 88 +++++--- tests/ui/result_map_unit_fn_fixable.stderr | 179 +++++++++++----- tests/ui/result_map_unit_fn_unfixable.stderr | 74 +++++-- 5 files changed, 389 insertions(+), 170 deletions(-) diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 681dc2ab5bc0c..b07d4fe81f8a9 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -214,6 +214,9 @@ fn lint_map_unit_fn( }; let fn_arg = &map_args.1[0]; + #[expect(clippy::items_after_statements, reason = "the const is only used below")] + const SUGG_MSG: &str = "use `if let` instead"; + if is_unit_function(cx, fn_arg) { let mut applicability = Applicability::MachineApplicable; let msg = suggestion_msg("function", map_type); @@ -226,7 +229,7 @@ fn lint_map_unit_fn( ); span_lint_and_then(cx, lint, expr.span, msg, |diag| { - diag.span_suggestion(stmt.span, "try", suggestion, applicability); + diag.span_suggestion_verbose(stmt.span, SUGG_MSG, suggestion, applicability); }); } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) { let msg = suggestion_msg("closure", map_type); @@ -242,7 +245,7 @@ fn lint_map_unit_fn( snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), snippet_with_context(cx, reduced_expr_span, var_arg.span.ctxt(), "_", &mut applicability).0, ); - diag.span_suggestion(stmt.span, "try", suggestion, applicability); + diag.span_suggestion_verbose(stmt.span, SUGG_MSG, suggestion, applicability); } else { let suggestion = format!( "if let {0}({1}) = {2} {{ ... }}", @@ -250,7 +253,7 @@ fn lint_map_unit_fn( snippet(cx, binding.pat.span, "_"), snippet(cx, var_arg.span, "_"), ); - diag.span_suggestion(stmt.span, "try", suggestion, Applicability::HasPlaceholders); + diag.span_suggestion_verbose(stmt.span, SUGG_MSG, suggestion, Applicability::HasPlaceholders); } }); } diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index 0fd6e0f5c66cd..70b6985406f44 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -2,172 +2,255 @@ error: called `map(f)` on an `Option` value where `f` is a function that returns --> tests/ui/option_map_unit_fn_fixable.rs:36:5 | LL | x.field.map(do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(x_field) = x.field { do_nothing(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_map_unit_fn)]` +help: use `if let` instead + | +LL - x.field.map(do_nothing); +LL + if let Some(x_field) = x.field { do_nothing(x_field) } + | error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:39:5 | LL | x.field.map(do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(x_field) = x.field { do_nothing(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(do_nothing); +LL + if let Some(x_field) = x.field { do_nothing(x_field) } + | error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:42:5 | LL | x.field.map(diverge); - | ^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(x_field) = x.field { diverge(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(diverge); +LL + if let Some(x_field) = x.field { diverge(x_field) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:49:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| x.do_option_nothing(value + captured)); +LL + if let Some(value) = x.field { x.do_option_nothing(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { x.do_option_plus_one(value + captured); }); +LL + if let Some(value) = x.field { x.do_option_plus_one(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:56:5 | LL | x.field.map(|value| do_nothing(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| do_nothing(value + captured)); +LL + if let Some(value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value + captured) }); +LL + if let Some(value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:62:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { do_nothing(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value + captured); }); +LL + if let Some(value) = x.field { do_nothing(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { do_nothing(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { do_nothing(value + captured); } }); +LL + if let Some(value) = x.field { do_nothing(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:69:5 | LL | x.field.map(|value| diverge(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { diverge(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| diverge(value + captured)); +LL + if let Some(value) = x.field { diverge(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:72:5 | LL | x.field.map(|value| { diverge(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { diverge(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { diverge(value + captured) }); +LL + if let Some(value) = x.field { diverge(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:75:5 | LL | x.field.map(|value| { diverge(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { diverge(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { diverge(value + captured); }); +LL + if let Some(value) = x.field { diverge(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:78:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { diverge(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { diverge(value + captured); } }); +LL + if let Some(value) = x.field { diverge(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:84:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { let y = plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { let y = plus_one(value + captured); }); +LL + if let Some(value) = x.field { let y = plus_one(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:87:5 | LL | x.field.map(|value| { plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { plus_one(value + captured); }); +LL + if let Some(value) = x.field { plus_one(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:90:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { plus_one(value + captured); } }); +LL + if let Some(value) = x.field { plus_one(value + captured); } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:94:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(ref value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|ref value| { do_nothing(value + captured) }); +LL + if let Some(ref value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:97:5 | LL | option().map(do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(a) = option() { do_nothing(a) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - option().map(do_nothing); +LL + if let Some(a) = option() { do_nothing(a) } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:100:5 | LL | option().map(|value| println!("{value:?}")); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = option() { println!("{value:?}") }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - option().map(|value| println!("{value:?}")); +LL + if let Some(value) = option() { println!("{value:?}") } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:107:5 | LL | x.map(|x| unsafe { f(x) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(x) = x { unsafe { f(x) } }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.map(|x| unsafe { f(x) }); +LL + if let Some(x) = x { unsafe { f(x) } } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_fixable.rs:109:5 | LL | x.map(|x| unsafe { { f(x) } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(x) = x { unsafe { f(x) } }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.map(|x| unsafe { { f(x) } }); +LL + if let Some(x) = x { unsafe { f(x) } } + | error: aborting due to 21 previous errors diff --git a/tests/ui/option_map_unit_fn_unfixable.stderr b/tests/ui/option_map_unit_fn_unfixable.stderr index de0bc4d55055c..8cc246a38d37b 100644 --- a/tests/ui/option_map_unit_fn_unfixable.stderr +++ b/tests/ui/option_map_unit_fn_unfixable.stderr @@ -2,71 +2,105 @@ error: called `map(f)` on an `Option` value where `f` is a closure that returns --> tests/ui/option_map_unit_fn_unfixable.rs:23:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { ... }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_map_unit_fn)]` +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value); do_nothing(value) }); +LL + if let Some(value) = x.field { ... } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_unfixable.rs:26:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { ... }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); +LL + if let Some(value) = x.field { ... } + | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_unfixable.rs:31:5 | -LL | // x.field.map(|value| { -LL | || do_nothing(value); -LL | || do_nothing(value) -LL | || }); - | ||______^- help: try: `if let Some(value) = x.field { ... }` - | |______| +LL | / x.field.map(|value| { +LL | | do_nothing(value); +LL | | do_nothing(value) +LL | | }); + | |______^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { +LL - do_nothing(value); +LL - do_nothing(value) +LL - }); +LL + if let Some(value) = x.field { ... } | error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> tests/ui/option_map_unit_fn_unfixable.rs:36:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(value) = x.field { ... }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value); do_nothing(value); }); +LL + if let Some(value) = x.field { ... } + | error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> tests/ui/option_map_unit_fn_unfixable.rs:40:5 | LL | Some(42).map(diverge); - | ^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(a) = Some(42) { diverge(a) }` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - Some(42).map(diverge); +LL + if let Some(a) = Some(42) { diverge(a) } + | error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> tests/ui/option_map_unit_fn_unfixable.rs:42:5 | LL | "12".parse::().ok().map(diverge); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(a) = "12".parse::().ok() { diverge(a) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - "12".parse::().ok().map(diverge); +LL + if let Some(a) = "12".parse::().ok() { diverge(a) } + | error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> tests/ui/option_map_unit_fn_unfixable.rs:44:5 | LL | Some(plus_one(1)).map(do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(a) = Some(plus_one(1)) { do_nothing(a) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - Some(plus_one(1)).map(do_nothing); +LL + if let Some(a) = Some(plus_one(1)) { do_nothing(a) } + | error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> tests/ui/option_map_unit_fn_unfixable.rs:49:5 | LL | y.map(do_nothing); - | ^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Some(_y) = y { do_nothing(_y) }` + | ^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - y.map(do_nothing); +LL + if let Some(_y) = y { do_nothing(_y) } + | error: aborting due to 8 previous errors diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr index 66e98392f6dc5..d8f6239764d13 100644 --- a/tests/ui/result_map_unit_fn_fixable.stderr +++ b/tests/ui/result_map_unit_fn_fixable.stderr @@ -2,148 +2,219 @@ error: called `map(f)` on an `Result` value where `f` is a function that returns --> tests/ui/result_map_unit_fn_fixable.rs:32:5 | LL | x.field.map(do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(x_field) = x.field { do_nothing(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::result_map_unit_fn)]` +help: use `if let` instead + | +LL - x.field.map(do_nothing); +LL + if let Ok(x_field) = x.field { do_nothing(x_field) } + | error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:35:5 | LL | x.field.map(do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(x_field) = x.field { do_nothing(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(do_nothing); +LL + if let Ok(x_field) = x.field { do_nothing(x_field) } + | error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(diverge); - | ^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(x_field) = x.field { diverge(x_field) }` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(diverge); +LL + if let Ok(x_field) = x.field { diverge(x_field) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:45:5 | LL | x.field.map(|value| x.do_result_nothing(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| x.do_result_nothing(value + captured)); +LL + if let Ok(value) = x.field { x.do_result_nothing(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { x.do_result_plus_one(value + captured); }); +LL + if let Ok(value) = x.field { x.do_result_plus_one(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| do_nothing(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| do_nothing(value + captured)); +LL + if let Ok(value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value + captured) }); +LL + if let Ok(value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:58:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { do_nothing(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value + captured); }); +LL + if let Ok(value) = x.field { do_nothing(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:61:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { do_nothing(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { do_nothing(value + captured); } }); +LL + if let Ok(value) = x.field { do_nothing(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| diverge(value + captured)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { diverge(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| diverge(value + captured)); +LL + if let Ok(value) = x.field { diverge(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { diverge(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { diverge(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { diverge(value + captured) }); +LL + if let Ok(value) = x.field { diverge(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:71:5 | LL | x.field.map(|value| { diverge(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { diverge(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { diverge(value + captured); }); +LL + if let Ok(value) = x.field { diverge(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:74:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { diverge(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { diverge(value + captured); } }); +LL + if let Ok(value) = x.field { diverge(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { let y = plus_one(value + captured); }); +LL + if let Ok(value) = x.field { let y = plus_one(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:83:5 | LL | x.field.map(|value| { plus_one(value + captured); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { plus_one(value + captured); }); +LL + if let Ok(value) = x.field { plus_one(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:86:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { plus_one(value + captured); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { { plus_one(value + captured); } }); +LL + if let Ok(value) = x.field { plus_one(value + captured); } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:90:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(ref value) = x.field { do_nothing(value + captured) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|ref value| { do_nothing(value + captured) }); +LL + if let Ok(ref value) = x.field { do_nothing(value + captured) } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:93:5 | LL | x.field.map(|value| println!("{value:?}")); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { println!("{value:?}") }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| println!("{value:?}")); +LL + if let Ok(value) = x.field { println!("{value:?}") } + | error: aborting due to 18 previous errors diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index dc9c398884efc..9f80ec1bbbd07 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -2,57 +2,85 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns --> tests/ui/result_map_unit_fn_unfixable.rs:24:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { ... }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::result_map_unit_fn)]` +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value); do_nothing(value) }); +LL + if let Ok(value) = x.field { ... } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_unfixable.rs:29:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { ... }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); +LL + if let Ok(value) = x.field { ... } + | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_unfixable.rs:35:5 | -LL | // x.field.map(|value| { -LL | || -LL | || -LL | || do_nothing(value); -LL | || do_nothing(value) -LL | || }); - | ||______^- help: try: `if let Ok(value) = x.field { ... }` - | |______| +LL | / x.field.map(|value| { +LL | | +LL | | +LL | | do_nothing(value); +LL | | do_nothing(value) +LL | | }); + | |______^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { +LL - +LL - +LL - do_nothing(value); +LL - do_nothing(value) +LL - }); +LL + if let Ok(value) = x.field { ... } | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_unfixable.rs:41:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(value) = x.field { ... }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - x.field.map(|value| { do_nothing(value); do_nothing(value); }); +LL + if let Ok(value) = x.field { ... } + | error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> tests/ui/result_map_unit_fn_unfixable.rs:47:5 | LL | "12".parse::().map(diverge); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(a) = "12".parse::() { diverge(a) }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - "12".parse::().map(diverge); +LL + if let Ok(a) = "12".parse::() { diverge(a) } + | error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> tests/ui/result_map_unit_fn_unfixable.rs:55:5 | LL | y.map(do_nothing); - | ^^^^^^^^^^^^^^^^^- - | | - | help: try: `if let Ok(_y) = y { do_nothing(_y) }` + | ^^^^^^^^^^^^^^^^^ + | +help: use `if let` instead + | +LL - y.map(do_nothing); +LL + if let Ok(_y) = y { do_nothing(_y) } + | error: aborting due to 6 previous errors From c6426f5dd7c3cde75e6faf83d477cd016c5b44e5 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 19 Oct 2025 17:52:30 +0200 Subject: [PATCH 027/108] Revert "fix(elidable_lifetime_names): avoid overlapping spans in suggestions" This reverts commit 25881bfc3400005b047c8eb553fb7bb2fab25950. --- clippy_lints/src/lifetimes.rs | 97 ++++++++--------------------------- 1 file changed, 22 insertions(+), 75 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 519ec228c8840..2779052e17a25 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -856,89 +856,36 @@ fn elision_suggestions( .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) .collect::>(); - if !elidable_lts - .iter() - .all(|lt| explicit_params.iter().any(|param| param.def_id == *lt)) - { - return None; - } - - let mut suggestions = if elidable_lts.is_empty() { - vec![] - } else if elidable_lts.len() == explicit_params.len() { + let mut suggestions = if elidable_lts.len() == explicit_params.len() { // if all the params are elided remove the whole generic block // // fn x<'a>() {} // ^^^^ vec![(generics.span, String::new())] } else { - match &explicit_params[..] { - // no params, nothing to elide - [] => unreachable!("handled by `elidable_lts.is_empty()`"), - [param] => { - if elidable_lts.contains(¶m.def_id) { - unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") + elidable_lts + .iter() + .map(|&id| { + let pos = explicit_params.iter().position(|param| param.def_id == id)?; + let param = explicit_params.get(pos)?; + + let span = if let Some(next) = explicit_params.get(pos + 1) { + // fn x<'prev, 'a, 'next>() {} + // ^^^^ + param.span.until(next.span) } else { - unreachable!("handled by `elidable_lts.is_empty()`") - } - }, - [_, _, ..] => { - // Given a list like `<'a, 'b, 'c, 'd, ..>`, - // - // If there is a cluster of elidable lifetimes at the beginning, say `'a` and `'b`, we should - // suggest removing them _and_ the trailing comma. The span for that is `a.span.until(c.span)`: - // <'a, 'b, 'c, 'd, ..> => <'a, 'b, 'c, 'd, ..> - // ^^ ^^ ^^^^^^^^ - // - // And since we know that `'c` isn't elidable--otherwise it would've been in the cluster--we can go - // over all the lifetimes after it, and for each elidable one, add a suggestion spanning the - // lifetime itself and the comma before, because each individual suggestion is guaranteed to leave - // the list valid: - // <.., 'c, 'd, 'e, 'f, 'g, ..> => <.., 'c, 'd, 'e, 'f, 'g, ..> - // ^^ ^^ ^^ ^^^^ ^^^^^^^^ - // - // In case there is no such starting cluster, we only need to do the second part of the algorithm: - // <'a, 'b, 'c, 'd, 'e, 'f, 'g, ..> => <'a, 'b , 'c, 'd, 'e, 'f, 'g, ..> - // ^^ ^^ ^^ ^^ ^^^^^^^^^ ^^^^^^^^ - - // Split off the starting cluster - // TODO: use `slice::split_once` once stabilized (github.com/rust-lang/rust/issues/112811): - // ``` - // let Some(split) = explicit_params.split_once(|param| !elidable_lts.contains(¶m.def_id)) else { - // // there were no lifetime param that couldn't be elided - // unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") - // }; - // match split { /* .. */ } - // ``` - let Some(split_pos) = explicit_params - .iter() - .position(|param| !elidable_lts.contains(¶m.def_id)) - else { - // there were no lifetime param that couldn't be elided - unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") + // `pos` should be at least 1 here, because the param in position 0 would either have a `next` + // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. + let prev = explicit_params.get(pos - 1)?; + + // fn x<'prev, 'a>() {} + // ^^^^ + param.span.with_lo(prev.span.hi()) }; - let split = explicit_params - .split_at_checked(split_pos) - .expect("got `split_pos` from `position` on the same Vec"); - - match split { - ([..], []) => unreachable!("handled by `elidable_lts.len() == explicit_params.len()`"), - ([], [_]) => unreachable!("handled by `explicit_params.len() == 1`"), - (cluster, rest @ [rest_first, ..]) => { - // the span for the cluster - (cluster.first().map(|fw| fw.span.until(rest_first.span)).into_iter()) - // the span for the remaining lifetimes (calculations independent of the cluster) - .chain( - rest.array_windows() - .filter(|[_, curr]| elidable_lts.contains(&curr.def_id)) - .map(|[prev, curr]| curr.span.with_lo(prev.span.hi())), - ) - .map(|sp| (sp, String::new())) - .collect() - }, - } - }, - } + + Some((span, String::new())) + }) + .collect::>>()? }; suggestions.extend(usages.iter().map(|&usage| { From 9ee9fd00d0ee8d3476d8f857a2042c62796a6bb2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 19 Oct 2025 23:57:41 +0200 Subject: [PATCH 028/108] refactor(manual_div_ceil): move to under `operators/` --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 - .../src/{ => operators}/manual_div_ceil.rs | 147 +++++++----------- clippy_lints/src/operators/mod.rs | 30 ++++ 4 files changed, 83 insertions(+), 98 deletions(-) rename clippy_lints/src/{ => operators}/manual_div_ceil.rs (50%) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 375d179681da3..9d2532f8e47f8 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -298,7 +298,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, crate::manual_clamp::MANUAL_CLAMP_INFO, - crate::manual_div_ceil::MANUAL_DIV_CEIL_INFO, crate::manual_float_methods::MANUAL_IS_FINITE_INFO, crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, crate::manual_hash_one::MANUAL_HASH_ONE_INFO, @@ -592,6 +591,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::operators::IMPOSSIBLE_COMPARISONS_INFO, crate::operators::INEFFECTIVE_BIT_MASK_INFO, crate::operators::INTEGER_DIVISION_INFO, + crate::operators::MANUAL_DIV_CEIL_INFO, crate::operators::MANUAL_IS_MULTIPLE_OF_INFO, crate::operators::MANUAL_MIDPOINT_INFO, crate::operators::MISREFACTORED_ASSIGN_OP_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a4ad9424b3eb1..96077d0bd00f6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -203,7 +203,6 @@ mod manual_assert; mod manual_async_fn; mod manual_bits; mod manual_clamp; -mod manual_div_ceil; mod manual_float_methods; mod manual_hash_one; mod manual_ignore_case_cmp; @@ -807,7 +806,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); - store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); store.register_late_pass(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf))); store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); store.register_late_pass(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)); diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/operators/manual_div_ceil.rs similarity index 50% rename from clippy_lints/src/manual_div_ceil.rs rename to clippy_lints/src/operators/manual_div_ceil.rs index ee531741a5153..98aa474215370 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/operators/manual_div_ceil.rs @@ -1,4 +1,3 @@ -use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; @@ -7,111 +6,69 @@ use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::LateContext; use rustc_middle::ty::{self}; -use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -declare_clippy_lint! { - /// ### What it does - /// Checks for an expression like `(x + (y - 1)) / y` which is a common manual reimplementation - /// of `x.div_ceil(y)`. - /// - /// ### Why is this bad? - /// It's simpler, clearer and more readable. - /// - /// ### Example - /// ```no_run - /// let x: i32 = 7; - /// let y: i32 = 4; - /// let div = (x + (y - 1)) / y; - /// ``` - /// Use instead: - /// ```no_run - /// #![feature(int_roundings)] - /// let x: i32 = 7; - /// let y: i32 = 4; - /// let div = x.div_ceil(y); - /// ``` - #[clippy::version = "1.83.0"] - pub MANUAL_DIV_CEIL, - complexity, - "manually reimplementing `div_ceil`" -} - -pub struct ManualDivCeil { - msrv: Msrv, -} - -impl ManualDivCeil { - #[must_use] - pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } - } -} +use super::MANUAL_DIV_CEIL; -impl_lint_pass!(ManualDivCeil => [MANUAL_DIV_CEIL]); +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>, msrv: Msrv) { + let mut applicability = Applicability::MachineApplicable; -impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { - let mut applicability = Applicability::MachineApplicable; - - if let ExprKind::Binary(div_op, div_lhs, div_rhs) = expr.kind - && div_op.node == BinOpKind::Div - && check_int_ty_and_feature(cx, div_lhs) - && check_int_ty_and_feature(cx, div_rhs) - && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind - && self.msrv.meets(cx, msrvs::DIV_CEIL) + if op == BinOpKind::Div + && check_int_ty_and_feature(cx, lhs) + && check_int_ty_and_feature(cx, rhs) + && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = lhs.kind + && msrv.meets(cx, msrvs::DIV_CEIL) + { + // (x + (y - 1)) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, rhs) { - // (x + (y - 1)) / y - if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind - && inner_op.node == BinOpKind::Add - && sub_op.node == BinOpKind::Sub - && check_literal(sub_rhs) - && check_eq_expr(cx, sub_lhs, div_rhs) - { - build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); - return; - } + build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); + return; + } - // ((y - 1) + x) / y - if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind - && inner_op.node == BinOpKind::Add - && sub_op.node == BinOpKind::Sub - && check_literal(sub_rhs) - && check_eq_expr(cx, sub_lhs, div_rhs) - { - build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability); - return; - } + // ((y - 1) + x) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, rhs) + { + build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability); + return; + } - // (x + y - 1) / y - if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind - && inner_op.node == BinOpKind::Sub - && add_op.node == BinOpKind::Add - && check_literal(inner_rhs) - && check_eq_expr(cx, add_rhs, div_rhs) - { - build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability); - } + // (x + y - 1) / y + if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Sub + && add_op.node == BinOpKind::Add + && check_literal(inner_rhs) + && check_eq_expr(cx, add_rhs, rhs) + { + build_suggestion(cx, expr, add_lhs, rhs, &mut applicability); + } - // (x + (Y - 1)) / Y - if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, div_rhs) { - build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); - } + // (x + (Y - 1)) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, rhs) { + build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); + } - // ((Y - 1) + x) / Y - if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, div_rhs) { - build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability); - } + // ((Y - 1) + x) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, rhs) { + build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability); + } - // (x - (-Y - 1)) / Y - if inner_op.node == BinOpKind::Sub - && let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = div_rhs.kind - && differ_by_one(abs_div_rhs, inner_rhs) - { - build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); - } + // (x - (-Y - 1)) / Y + if inner_op.node == BinOpKind::Sub + && let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = rhs.kind + && differ_by_one(abs_div_rhs, inner_rhs) + { + build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); } } } diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index aaea4ff11fc37..f2aae8ca458d3 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -11,6 +11,7 @@ mod float_cmp; mod float_equality_without_abs; mod identity_op; mod integer_division; +mod manual_div_ceil; mod manual_is_multiple_of; mod manual_midpoint; mod misrefactored_assign_op; @@ -860,6 +861,33 @@ declare_clippy_lint! { "manual implementation of `.is_multiple_of()`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for an expression like `(x + (y - 1)) / y` which is a common manual reimplementation + /// of `x.div_ceil(y)`. + /// + /// ### Why is this bad? + /// It's simpler, clearer and more readable. + /// + /// ### Example + /// ```no_run + /// let x: i32 = 7; + /// let y: i32 = 4; + /// let div = (x + (y - 1)) / y; + /// ``` + /// Use instead: + /// ```no_run + /// #![feature(int_roundings)] + /// let x: i32 = 7; + /// let y: i32 = 4; + /// let div = x.div_ceil(y); + /// ``` + #[clippy::version = "1.83.0"] + pub MANUAL_DIV_CEIL, + complexity, + "manually reimplementing `div_ceil`" +} + pub struct Operators { arithmetic_context: numeric_arithmetic::Context, verbose_bit_mask_threshold: u64, @@ -906,6 +934,7 @@ impl_lint_pass!(Operators => [ SELF_ASSIGNMENT, MANUAL_MIDPOINT, MANUAL_IS_MULTIPLE_OF, + MANUAL_DIV_CEIL, ]); impl<'tcx> LateLintPass<'tcx> for Operators { @@ -944,6 +973,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { rhs, self.modulo_arithmetic_allow_comparison_to_zero, ); + manual_div_ceil::check(cx, e, op.node, lhs, rhs, self.msrv); }, ExprKind::AssignOp(op, lhs, rhs) => { let bin_op = op.node.into(); From 70de06f70fee073850b5b2129a9782b86ac505e0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 20 Oct 2025 00:06:51 +0200 Subject: [PATCH 029/108] clean-up a bit --- clippy_lints/src/empty_enum.rs | 5 ++--- tests/ui/empty_enum.rs | 2 +- tests/ui/empty_enum_without_never_type.rs | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index b0389fd9a2f62..2a0dde6566826 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -62,11 +62,10 @@ declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); impl LateLintPass<'_> for EmptyEnum { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if let ItemKind::Enum(..) = item.kind + if let ItemKind::Enum(.., def) = item.kind + && def.variants.is_empty() // Only suggest the `never_type` if the feature is enabled && cx.tcx.features().never_type() - && let Some(adt) = cx.tcx.type_of(item.owner_id).instantiate_identity().ty_adt_def() - && adt.variants().is_empty() { span_lint_and_help( cx, diff --git a/tests/ui/empty_enum.rs b/tests/ui/empty_enum.rs index 439fd0974f5f8..ea5a00bb78436 100644 --- a/tests/ui/empty_enum.rs +++ b/tests/ui/empty_enum.rs @@ -1,7 +1,7 @@ -#![allow(dead_code)] #![warn(clippy::empty_enum)] // Enable never type to test empty enum lint #![feature(never_type)] + enum Empty {} //~^ empty_enum diff --git a/tests/ui/empty_enum_without_never_type.rs b/tests/ui/empty_enum_without_never_type.rs index 3661a15372086..cec53c5552466 100644 --- a/tests/ui/empty_enum_without_never_type.rs +++ b/tests/ui/empty_enum_without_never_type.rs @@ -1,6 +1,5 @@ //@ check-pass -#![allow(dead_code)] #![warn(clippy::empty_enum)] // `never_type` is not enabled; this test has no stderr file From c8c23bcf5b1a4da39029577224a9be01860143fa Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 20 Oct 2025 00:14:14 +0200 Subject: [PATCH 030/108] fix(empty_enum): don't lint if all variants happen to be `cfg`-d out --- clippy_lints/src/empty_enum.rs | 6 ++---- tests/ui/empty_enum.rs | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index 2a0dde6566826..19871ac5c50fc 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::span_contains_cfg; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -25,10 +26,6 @@ declare_clippy_lint! { /// matched to mark code unreachable. If the field is not visible, then the struct /// acts like any other struct with private fields. /// - /// * If the enum has no variants only because all variants happen to be - /// [disabled by conditional compilation][cfg], then it would be appropriate - /// to allow the lint, with `#[allow(empty_enum)]`. - /// /// For further information, visit /// [the never type’s documentation][`!`]. /// @@ -66,6 +63,7 @@ impl LateLintPass<'_> for EmptyEnum { && def.variants.is_empty() // Only suggest the `never_type` if the feature is enabled && cx.tcx.features().never_type() + && !span_contains_cfg(cx, item.span) { span_lint_and_help( cx, diff --git a/tests/ui/empty_enum.rs b/tests/ui/empty_enum.rs index ea5a00bb78436..009d8c430490f 100644 --- a/tests/ui/empty_enum.rs +++ b/tests/ui/empty_enum.rs @@ -5,4 +5,21 @@ enum Empty {} //~^ empty_enum +mod issue15910 { + enum NotReallyEmpty { + #[cfg(false)] + Hidden, + } + + enum OneVisibleVariant { + #[cfg(false)] + Hidden, + Visible, + } + + enum CfgInsideVariant { + Variant(#[cfg(false)] String), + } +} + fn main() {} From 5578908f912803b8c5faf9886306552742958233 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 20 Oct 2025 00:31:02 +0200 Subject: [PATCH 031/108] chore(empty_enum): rename to `empty_enums` According to the lint naming guidelines[^1], lint names should use the plural form. [^1]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/deprecated_lints.rs | 2 + .../src/{empty_enum.rs => empty_enums.rs} | 8 +- clippy_lints/src/lib.rs | 4 +- tests/ui/{empty_enum.rs => empty_enums.rs} | 4 +- .../{empty_enum.stderr => empty_enums.stderr} | 6 +- ...e.rs => empty_enums_without_never_type.rs} | 2 +- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 156 +++++++++--------- 11 files changed, 101 insertions(+), 88 deletions(-) rename clippy_lints/src/{empty_enum.rs => empty_enums.rs} (95%) rename tests/ui/{empty_enum.rs => empty_enums.rs} (88%) rename tests/ui/{empty_enum.stderr => empty_enums.stderr} (75%) rename tests/ui/{empty_enum_without_never_type.rs => empty_enums_without_never_type.rs} (77%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37d46d3496677..9279267ebf0a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6253,6 +6253,7 @@ Released 2018-09-13 [`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum [`empty_enum_variants_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum_variants_with_brackets +[`empty_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enums [`empty_line_after_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 9d2532f8e47f8..bd2971fa150de 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -135,7 +135,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::duplicate_mod::DUPLICATE_MOD_INFO, crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO, crate::empty_drop::EMPTY_DROP_INFO, - crate::empty_enum::EMPTY_ENUM_INFO, + crate::empty_enums::EMPTY_ENUMS_INFO, crate::empty_line_after::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::empty_line_after::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::empty_with_brackets::EMPTY_ENUM_VARIANTS_WITH_BRACKETS_INFO, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index f087a894d76ae..c6016a863afcc 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -85,6 +85,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ ("clippy::drop_copy", "dropping_copy_types"), #[clippy::version = ""] ("clippy::drop_ref", "dropping_references"), + #[clippy::version = "1.92.0"] + ("clippy::empty_enum", "clippy::empty_enums"), #[clippy::version = ""] ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), #[clippy::version = "1.53.0"] diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enums.rs similarity index 95% rename from clippy_lints/src/empty_enum.rs rename to clippy_lints/src/empty_enums.rs index 19871ac5c50fc..f96854411fe6f 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enums.rs @@ -50,14 +50,14 @@ declare_clippy_lint! { /// [newtype]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction /// [visibility]: https://doc.rust-lang.org/reference/visibility-and-privacy.html #[clippy::version = "pre 1.29.0"] - pub EMPTY_ENUM, + pub EMPTY_ENUMS, pedantic, "enum with no variants" } -declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); +declare_lint_pass!(EmptyEnums => [EMPTY_ENUMS]); -impl LateLintPass<'_> for EmptyEnum { +impl LateLintPass<'_> for EmptyEnums { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Enum(.., def) = item.kind && def.variants.is_empty() @@ -67,7 +67,7 @@ impl LateLintPass<'_> for EmptyEnum { { span_lint_and_help( cx, - EMPTY_ENUM, + EMPTY_ENUMS, item.span, "enum with no variants", None, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 96077d0bd00f6..e6f34b436d9e6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -117,7 +117,7 @@ mod drop_forget_ref; mod duplicate_mod; mod else_if_without_else; mod empty_drop; -mod empty_enum; +mod empty_enums; mod empty_line_after; mod empty_with_brackets; mod endian_bytes; @@ -541,7 +541,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(derive::Derive)); store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(conf))); store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef)); - store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); + store.register_late_pass(|_| Box::new(empty_enums::EmptyEnums)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))); diff --git a/tests/ui/empty_enum.rs b/tests/ui/empty_enums.rs similarity index 88% rename from tests/ui/empty_enum.rs rename to tests/ui/empty_enums.rs index 009d8c430490f..0deb0f57b0e4a 100644 --- a/tests/ui/empty_enum.rs +++ b/tests/ui/empty_enums.rs @@ -1,9 +1,9 @@ -#![warn(clippy::empty_enum)] +#![warn(clippy::empty_enums)] // Enable never type to test empty enum lint #![feature(never_type)] enum Empty {} -//~^ empty_enum +//~^ empty_enums mod issue15910 { enum NotReallyEmpty { diff --git a/tests/ui/empty_enum.stderr b/tests/ui/empty_enums.stderr similarity index 75% rename from tests/ui/empty_enum.stderr rename to tests/ui/empty_enums.stderr index 6a1ded9298ed7..5aa2347b4ae08 100644 --- a/tests/ui/empty_enum.stderr +++ b/tests/ui/empty_enums.stderr @@ -1,12 +1,12 @@ error: enum with no variants - --> tests/ui/empty_enum.rs:5:1 + --> tests/ui/empty_enums.rs:5:1 | LL | enum Empty {} | ^^^^^^^^^^^^^ | = help: consider using the uninhabited type `!` (never type) or a wrapper around it to introduce a type which can't be instantiated - = note: `-D clippy::empty-enum` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::empty_enum)]` + = note: `-D clippy::empty-enums` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::empty_enums)]` error: aborting due to 1 previous error diff --git a/tests/ui/empty_enum_without_never_type.rs b/tests/ui/empty_enums_without_never_type.rs similarity index 77% rename from tests/ui/empty_enum_without_never_type.rs rename to tests/ui/empty_enums_without_never_type.rs index cec53c5552466..17ccac83ce98e 100644 --- a/tests/ui/empty_enum_without_never_type.rs +++ b/tests/ui/empty_enums_without_never_type.rs @@ -1,6 +1,6 @@ //@ check-pass -#![warn(clippy::empty_enum)] +#![warn(clippy::empty_enums)] // `never_type` is not enabled; this test has no stderr file enum Empty {} diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index fdd851414746b..ff7130a71cd48 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -19,6 +19,7 @@ #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(clippy::empty_enums)] #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::manual_filter_map)] #![allow(clippy::manual_find_map)] @@ -83,6 +84,7 @@ #![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` #![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` #![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` +#![warn(clippy::empty_enums)] //~ ERROR: lint `clippy::empty_enum` #![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence` #![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map` #![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 591c8ca53ac22..86338a25ded36 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -19,6 +19,7 @@ #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(clippy::empty_enums)] #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::manual_filter_map)] #![allow(clippy::manual_find_map)] @@ -83,6 +84,7 @@ #![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` #![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` #![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` +#![warn(clippy::empty_enum)] //~ ERROR: lint `clippy::empty_enum` #![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence` #![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map` #![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index b54fec8c57949..6e36e34d4033f 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:68:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,442 +8,448 @@ 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:69:9 + --> tests/ui/rename.rs:70: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:70:9 + --> tests/ui/rename.rs:71: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:71:9 + --> tests/ui/rename.rs:72: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:72:9 + --> tests/ui/rename.rs:73: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:73:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:75: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:75:9 + --> tests/ui/rename.rs:76: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:76:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:78: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:78:9 + --> tests/ui/rename.rs:79: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:79:9 + --> tests/ui/rename.rs:80: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:80:9 + --> tests/ui/rename.rs:81: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:81:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::double_neg` has been renamed to `double_negations` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:83: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:83:9 + --> tests/ui/rename.rs:84: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:84:9 + --> tests/ui/rename.rs:85: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:85:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` +error: lint `clippy::empty_enum` has been renamed to `clippy::empty_enums` + --> tests/ui/rename.rs:87:9 + | +LL | #![warn(clippy::empty_enum)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::empty_enums` + error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::fn_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:92: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:91:9 + --> tests/ui/rename.rs:93: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:92:9 + --> tests/ui/rename.rs:94: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:93:9 + --> tests/ui/rename.rs:95: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:94:9 + --> tests/ui/rename.rs:96: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:95:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:98: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:97:9 + --> tests/ui/rename.rs:99: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:98:9 + --> tests/ui/rename.rs:100: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:99:9 + --> tests/ui/rename.rs:101: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:100:9 + --> tests/ui/rename.rs:102: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:101:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:104: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:103:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::invalid_null_ptr_usage)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:107: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:106:9 + --> tests/ui/rename.rs:108: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:107:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:111: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:110:9 + --> tests/ui/rename.rs:112: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:111:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:114: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:113:9 + --> tests/ui/rename.rs:115: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:114:9 + --> tests/ui/rename.rs:116: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:115:9 + --> tests/ui/rename.rs:117: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:116:9 + --> tests/ui/rename.rs:118: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:117:9 + --> tests/ui/rename.rs:119: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:118:9 + --> tests/ui/rename.rs:120:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:119:9 + --> tests/ui/rename.rs:121: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:120:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:121:9 + --> tests/ui/rename.rs:123: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:122:9 + --> tests/ui/rename.rs:124: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:123:9 + --> tests/ui/rename.rs:125: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:124:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:128: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:127:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:128: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::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:131: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:130:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:133:9 | LL | #![warn(clippy::transmute_float_to_int)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:134:9 | LL | #![warn(clippy::transmute_int_to_char)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:133:9 + --> tests/ui/rename.rs:135:9 | LL | #![warn(clippy::transmute_int_to_float)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:134:9 + --> tests/ui/rename.rs:136:9 | LL | #![warn(clippy::transmute_num_to_bytes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::unchecked_duration_subtraction` has been renamed to `clippy::unchecked_time_subtraction` - --> tests/ui/rename.rs:135:9 + --> tests/ui/rename.rs:137:9 | LL | #![warn(clippy::unchecked_duration_subtraction)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unchecked_time_subtraction` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:136:9 + --> tests/ui/rename.rs:138: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:137:9 + --> tests/ui/rename.rs:139: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:138:9 + --> tests/ui/rename.rs:140:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:139:9 + --> tests/ui/rename.rs:141:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:140:9 + --> tests/ui/rename.rs:142:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:141:9 + --> tests/ui/rename.rs:143:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` -error: aborting due to 74 previous errors +error: aborting due to 75 previous errors From 4e3fa960d4595ffc66e9932a09ab32e769988404 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 20 Oct 2025 15:50:47 +0200 Subject: [PATCH 032/108] refactor(option_as_ref_cloned): move the `method_call` call out of the `check` --- clippy_lints/src/methods/mod.rs | 6 ++++- .../src/methods/option_as_ref_cloned.rs | 26 +++++++++++-------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c9066be51c44b..fefd46f35c611 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5050,7 +5050,11 @@ impl Methods { (sym::bytes, []) => unbuffered_bytes::check(cx, expr, recv), (sym::cloned, []) => { cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv); - option_as_ref_cloned::check(cx, recv, span); + if let Some((method @ (sym::as_ref | sym::as_mut), as_ref_recv, [], as_ref_ident_span, _)) = + method_call(recv) + { + option_as_ref_cloned::check(cx, span, method, as_ref_recv, as_ref_ident_span); + } }, (sym::collect, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); diff --git a/clippy_lints/src/methods/option_as_ref_cloned.rs b/clippy_lints/src/methods/option_as_ref_cloned.rs index 591f6aacaef87..156c624488eba 100644 --- a/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -4,24 +4,28 @@ use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; -use super::{OPTION_AS_REF_CLONED, method_call}; +use super::OPTION_AS_REF_CLONED; -pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { - if let Some((method @ (sym::as_ref | sym::as_mut), as_ref_recv, [], as_ref_ident_span, _)) = - method_call(cloned_recv) - && cx - .typeck_results() - .expr_ty(as_ref_recv) - .peel_refs() - .is_diag_item(cx, sym::Option) +pub(super) fn check( + cx: &LateContext<'_>, + cloned_ident_span: Span, + as_ref_method: Symbol, + as_ref_recv: &Expr<'_>, + as_ref_ident_span: Span, +) { + if cx + .typeck_results() + .expr_ty(as_ref_recv) + .peel_refs() + .is_diag_item(cx, sym::Option) { span_lint_and_sugg( cx, OPTION_AS_REF_CLONED, as_ref_ident_span.to(cloned_ident_span), - format!("cloning an `Option<_>` using `.{method}().cloned()`"), + format!("cloning an `Option<_>` using `.{as_ref_method}().cloned()`"), "this can be written more concisely by cloning the `Option<_>` directly", "clone".into(), Applicability::MachineApplicable, From 0702934c9896879b678eeacd3d59e14be0291979 Mon Sep 17 00:00:00 2001 From: Teodoro Freund Date: Mon, 20 Oct 2025 16:22:12 +0100 Subject: [PATCH 033/108] `manual_let_else` fix when expression finishes with '}' --- clippy_lints/src/manual_let_else.rs | 8 +++++- tests/ui/manual_let_else.rs | 25 +++++++++++++++++++ tests/ui/manual_let_else.stderr | 38 ++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 298bf10754897..0f3d8b3366751 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -183,7 +183,13 @@ fn emit_manual_let_else( format!("{{ {sn_else} }}") }; let sn_bl = replace_in_pattern(cx, span, ident_map, pat, &mut app, true); - let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};"); + let sugg = if sn_expr.ends_with('}') { + // let-else statement expressions are not allowed to end with `}` + // https://rust-lang.github.io/rfcs/3137-let-else.html#let-pattern--if--else--else- + format!("let {sn_bl} = ({sn_expr}) else {else_bl};") + } else { + format!("let {sn_bl} = {sn_expr} else {else_bl};") + }; diag.span_suggestion(span, "consider writing", sugg, app); }, ); diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index 3781ba1676f5e..160e321d9a1e7 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -546,3 +546,28 @@ mod issue14598 { todo!() } } + +mod issue15914 { + // https://github.com/rust-lang/rust-clippy/issues/15914 + unsafe fn something_unsafe() -> Option { + None + } + + fn foo() { + let value = if let Some(value) = unsafe { something_unsafe() } { + //~^ manual_let_else + value + } else { + return; + }; + + let some_flag = true; + + let value = if let Some(value) = if some_flag { None } else { Some(3) } { + //~^ manual_let_else + value + } else { + return; + }; + } +} diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index a1eea04192919..f4b1644c44baa 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -549,5 +549,41 @@ LL | | Some(x) => x, LL | | }; | |__________^ help: consider writing: `let Some(v) = w else { return Err("abc") };` -error: aborting due to 35 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:557:9 + | +LL | / let value = if let Some(value) = unsafe { something_unsafe() } { +LL | | +LL | | value +LL | | } else { +LL | | return; +LL | | }; + | |__________^ + | +help: consider writing + | +LL ~ let Some(value) = (unsafe { something_unsafe() }) else { +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:566:9 + | +LL | / let value = if let Some(value) = if some_flag { None } else { Some(3) } { +LL | | +LL | | value +LL | | } else { +LL | | return; +LL | | }; + | |__________^ + | +help: consider writing + | +LL ~ let Some(value) = (if some_flag { None } else { Some(3) }) else { +LL + return; +LL + }; + | + +error: aborting due to 37 previous errors From b5c3ed048cd56d2a6d9d0d6dd31c98dedf11c776 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Tue, 3 Jun 2025 19:27:03 -0600 Subject: [PATCH 034/108] refactor: Move to anstream + anstyle for styling --- clippy_lints/src/doc/needless_doctest_main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/doc/needless_doctest_main.rs b/clippy_lints/src/doc/needless_doctest_main.rs index 43bb972355500..0d00a6caeeb70 100644 --- a/clippy_lints/src/doc/needless_doctest_main.rs +++ b/clippy_lints/src/doc/needless_doctest_main.rs @@ -6,7 +6,7 @@ use crate::doc::{NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST}; use clippy_utils::diagnostics::span_lint; use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind}; use rustc_errors::emitter::HumanEmitter; -use rustc_errors::{Diag, DiagCtxt}; +use rustc_errors::{AutoStream, Diag, DiagCtxt}; use rustc_lint::LateContext; use rustc_parse::lexer::StripTokens; use rustc_parse::new_parser_from_source_str; @@ -44,7 +44,7 @@ pub fn check( let filename = FileName::anon_source_code(&code); let translator = rustc_driver::default_translator(); - let emitter = HumanEmitter::new(Box::new(io::sink()), translator); + let emitter = HumanEmitter::new(AutoStream::never(Box::new(io::sink())), translator); let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); #[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); From 960c01f4dd3a1e3a2eff183971cbdd7405acd8d6 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 24 Jan 2025 12:19:17 +0000 Subject: [PATCH 035/108] Add not-null pointer patterns to pattern types --- clippy_utils/src/hir_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index a6d821c99c1b5..974596a81a409 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1123,7 +1123,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty_pat(variant); } }, - TyPatKind::Err(_) => {}, + TyPatKind::NotNull | TyPatKind::Err(_) => {}, } } From d9ecbb73e8afdc5d276520731b5086d6e4cfc51e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 20 Oct 2025 23:11:01 +0200 Subject: [PATCH 036/108] clean-up --- clippy_lints/src/lines_filter_map_ok.rs | 24 ++++++++++-------------- tests/ui/lines_filter_map_ok.fixed | 18 ++++++++---------- tests/ui/lines_filter_map_ok.rs | 18 ++++++++---------- tests/ui/lines_filter_map_ok.stderr | 20 ++++++++++---------- 4 files changed, 36 insertions(+), 44 deletions(-) diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index 65e922ac07d36..f49ce2848abfb 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -87,11 +87,13 @@ impl LateLintPass<'_> for LinesFilterMapOk { cx, LINES_FILTER_MAP_OK, fm_span, - format!("`{fm_method_name}()` will run forever if the iterator repeatedly produces an `Err`",), + format!("`{fm_method_name}()` will run forever if the iterator repeatedly produces an `Err`"), |diag| { diag.span_note( fm_receiver.span, - "this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error"); + "this expression returning a `std::io::Lines` may produce \ + an infinite number of `Err` in case of a read error", + ); diag.span_suggestion( fm_span, "replace with", @@ -108,27 +110,21 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> match args { [] => method_name == sym::flatten, [fm_arg] => { - match &fm_arg.kind { + match fm_arg.kind { // Detect `Result::ok` - ExprKind::Path(qpath) => cx + ExprKind::Path(ref qpath) => cx .qpath_res(qpath, fm_arg.hir_id) - .opt_def_id() - .is_some_and(|did| cx.tcx.is_diagnostic_item(sym::result_ok_method, did)), + .is_diag_item(cx, sym::result_ok_method), // Detect `|x| x.ok()` - ExprKind::Closure(Closure { body, .. }) => { + ExprKind::Closure(&Closure { body, .. }) => { if let Body { params: [param], value, .. - } = cx.tcx.hir_body(*body) + } = cx.tcx.hir_body(body) && let ExprKind::MethodCall(method, receiver, [], _) = value.kind { method.ident.name == sym::ok && receiver.res_local_id() == Some(param.pat.hir_id) - && cx - .typeck_results() - .type_dependent_def_id(value.hir_id) - .opt_parent(cx) - .opt_impl_ty(cx) - .is_diag_item(cx, sym::Result) + && cx.ty_based_def(*value).is_diag_item(cx, sym::result_ok_method) } else { false } diff --git a/tests/ui/lines_filter_map_ok.fixed b/tests/ui/lines_filter_map_ok.fixed index 977e31c744a2c..0617f06fafb7c 100644 --- a/tests/ui/lines_filter_map_ok.fixed +++ b/tests/ui/lines_filter_map_ok.fixed @@ -1,39 +1,37 @@ -#![allow(unused, clippy::map_identity)] +#![allow(clippy::map_identity)] #![warn(clippy::lines_filter_map_ok)] use std::io::{self, BufRead, BufReader}; fn main() -> io::Result<()> { + // Lint: + let f = std::fs::File::open("/")?; - // Lint BufReader::new(f).lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint let f = std::fs::File::open("/")?; BufReader::new(f).lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint let f = std::fs::File::open("/")?; BufReader::new(f).lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - let s = "foo\nbar\nbaz\n"; - // Lint io::stdin().lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint io::stdin().lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint io::stdin().lines().map_while(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Do not lint (not a `Lines` iterator) + + // Do not lint: + + // not a `Lines` iterator io::stdin() .lines() .map(std::convert::identity) .filter_map(|x| x.ok()) .for_each(|_| ()); - // Do not lint (not a `Result::ok()` extractor) + // not a `Result::ok()` extractor io::stdin().lines().filter_map(|x| x.err()).for_each(|_| ()); Ok(()) } diff --git a/tests/ui/lines_filter_map_ok.rs b/tests/ui/lines_filter_map_ok.rs index 2196075bc4455..dfd1ec431a52f 100644 --- a/tests/ui/lines_filter_map_ok.rs +++ b/tests/ui/lines_filter_map_ok.rs @@ -1,39 +1,37 @@ -#![allow(unused, clippy::map_identity)] +#![allow(clippy::map_identity)] #![warn(clippy::lines_filter_map_ok)] use std::io::{self, BufRead, BufReader}; fn main() -> io::Result<()> { + // Lint: + let f = std::fs::File::open("/")?; - // Lint BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint let f = std::fs::File::open("/")?; BufReader::new(f).lines().flat_map(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint let f = std::fs::File::open("/")?; BufReader::new(f).lines().flatten().for_each(|_| ()); //~^ lines_filter_map_ok - let s = "foo\nbar\nbaz\n"; - // Lint io::stdin().lines().filter_map(Result::ok).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ()); //~^ lines_filter_map_ok - // Lint io::stdin().lines().flatten().for_each(|_| ()); //~^ lines_filter_map_ok - // Do not lint (not a `Lines` iterator) + + // Do not lint: + + // not a `Lines` iterator io::stdin() .lines() .map(std::convert::identity) .filter_map(|x| x.ok()) .for_each(|_| ()); - // Do not lint (not a `Result::ok()` extractor) + // not a `Result::ok()` extractor io::stdin().lines().filter_map(|x| x.err()).for_each(|_| ()); Ok(()) } diff --git a/tests/ui/lines_filter_map_ok.stderr b/tests/ui/lines_filter_map_ok.stderr index f9038eec9fb21..c05dadd1b5f29 100644 --- a/tests/ui/lines_filter_map_ok.stderr +++ b/tests/ui/lines_filter_map_ok.stderr @@ -1,11 +1,11 @@ error: `filter_map()` will run forever if the iterator repeatedly produces an `Err` - --> tests/ui/lines_filter_map_ok.rs:9:31 + --> tests/ui/lines_filter_map_ok.rs:10:31 | LL | BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)` | note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error - --> tests/ui/lines_filter_map_ok.rs:9:5 + --> tests/ui/lines_filter_map_ok.rs:10:5 | LL | BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,49 +25,49 @@ LL | BufReader::new(f).lines().flat_map(Result::ok).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `flatten()` will run forever if the iterator repeatedly produces an `Err` - --> tests/ui/lines_filter_map_ok.rs:17:31 + --> tests/ui/lines_filter_map_ok.rs:16:31 | LL | BufReader::new(f).lines().flatten().for_each(|_| ()); | ^^^^^^^^^ help: replace with: `map_while(Result::ok)` | note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error - --> tests/ui/lines_filter_map_ok.rs:17:5 + --> tests/ui/lines_filter_map_ok.rs:16:5 | LL | BufReader::new(f).lines().flatten().for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `filter_map()` will run forever if the iterator repeatedly produces an `Err` - --> tests/ui/lines_filter_map_ok.rs:22:25 + --> tests/ui/lines_filter_map_ok.rs:19:25 | LL | io::stdin().lines().filter_map(Result::ok).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)` | note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error - --> tests/ui/lines_filter_map_ok.rs:22:5 + --> tests/ui/lines_filter_map_ok.rs:19:5 | LL | io::stdin().lines().filter_map(Result::ok).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^ error: `filter_map()` will run forever if the iterator repeatedly produces an `Err` - --> tests/ui/lines_filter_map_ok.rs:25:25 + --> tests/ui/lines_filter_map_ok.rs:21:25 | LL | io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)` | note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error - --> tests/ui/lines_filter_map_ok.rs:25:5 + --> tests/ui/lines_filter_map_ok.rs:21:5 | LL | io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^ error: `flatten()` will run forever if the iterator repeatedly produces an `Err` - --> tests/ui/lines_filter_map_ok.rs:28:25 + --> tests/ui/lines_filter_map_ok.rs:23:25 | LL | io::stdin().lines().flatten().for_each(|_| ()); | ^^^^^^^^^ help: replace with: `map_while(Result::ok)` | note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error - --> tests/ui/lines_filter_map_ok.rs:28:5 + --> tests/ui/lines_filter_map_ok.rs:23:5 | LL | io::stdin().lines().flatten().for_each(|_| ()); | ^^^^^^^^^^^^^^^^^^^ From c5215b6d8465f1b9ea2e9b4ba53a2126d115538a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 15:36:06 +0200 Subject: [PATCH 037/108] clean-up a bit --- clippy_lints/src/manual_option_as_slice.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 63f6d89f2ad74..59b5de6e7e0bf 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -7,7 +7,6 @@ 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}; @@ -124,8 +123,7 @@ fn check_map(cx: &LateContext<'_>, map: &Expr<'_>, span: Span, msrv: Msrv) { fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span, msrv: Msrv) { if let ExprKind::MethodCall(seg, callee, [], _) = expr.kind && seg.ident.name == sym::as_ref - && let ty::Adt(adtdef, ..) = cx.typeck_results().expr_ty(callee).kind() - && cx.tcx.is_diagnostic_item(sym::Option, adtdef.did()) + && cx.typeck_results().expr_ty(callee).is_diag_item(cx, sym::Option) && msrv.meets( cx, if clippy_utils::is_in_const_context(cx) { From a8fa9e1627244d11d5fe5bb9e1d7a7d245103386 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 16:38:50 +0200 Subject: [PATCH 038/108] clean-up --- clippy_lints/src/matches/match_as_ref.rs | 76 ++++++++++++------------ 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 156118be347f9..684b65564dbc4 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::option_arg_ty; use clippy_utils::{is_none_arm, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; @@ -10,54 +11,53 @@ use rustc_middle::ty; use super::MATCH_AS_REF; pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { - if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() { - let arm_ref_mut = if is_none_arm(cx, &arms[0]) { - is_ref_some_arm(cx, &arms[1]) - } else if is_none_arm(cx, &arms[1]) { - is_ref_some_arm(cx, &arms[0]) + if let [arm1, arm2] = arms + && arm1.guard.is_none() + && arm2.guard.is_none() + && let Some(arm_ref_mutbl) = if is_none_arm(cx, arm1) { + as_ref_some_arm(cx, arm2) + } else if is_none_arm(cx, arm2) { + as_ref_some_arm(cx, arm1) } else { None + } + { + let method = match arm_ref_mutbl { + Mutability::Not => "as_ref", + Mutability::Mut => "as_mut", }; - if let Some(rb) = arm_ref_mut { - let suggestion = match rb { - Mutability::Not => "as_ref", - Mutability::Mut => "as_mut", - }; - let output_ty = cx.typeck_results().expr_ty(expr); - let input_ty = cx.typeck_results().expr_ty(ex); + let output_ty = cx.typeck_results().expr_ty(expr); + let input_ty = cx.typeck_results().expr_ty(ex); - let cast = if let ty::Adt(_, args) = input_ty.kind() - && let input_ty = args.type_at(0) - && let ty::Adt(_, args) = output_ty.kind() - && let output_ty = args.type_at(0) - && let ty::Ref(_, output_ty, _) = *output_ty.kind() - && input_ty != output_ty - { - ".map(|x| x as _)" - } else { - "" - }; + let cast = if let Some(input_ty) = option_arg_ty(cx, input_ty) + && let Some(output_ty) = option_arg_ty(cx, output_ty) + && let ty::Ref(_, output_ty, _) = *output_ty.kind() + && input_ty != output_ty + { + ".map(|x| x as _)" + } else { + "" + }; - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - MATCH_AS_REF, - expr.span, - format!("use `{suggestion}()` instead"), - "try", - format!( - "{}.{suggestion}(){cast}", - snippet_with_applicability(cx, ex.span, "_", &mut applicability), - ), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MATCH_AS_REF, + expr.span, + format!("use `{method}()` instead"), + "try", + format!( + "{}.{method}(){cast}", + snippet_with_applicability(cx, ex.span, "_", &mut applicability), + ), + applicability, + ); } } // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) -fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { +fn as_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind && cx .qpath_res(qpath, arm.pat.hir_id) From 91dbaae9adaa768872e6c86f2b81763839c01406 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 16:41:48 +0200 Subject: [PATCH 039/108] improve diagnostics --- clippy_lints/src/matches/match_as_ref.rs | 23 +++++++------ tests/ui/match_as_ref.stderr | 41 ++++++++++++++++++++---- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 684b65564dbc4..20f5b2b0e8f51 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::option_arg_ty; @@ -41,17 +41,22 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: }; let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( + span_lint_and_then( cx, MATCH_AS_REF, expr.span, - format!("use `{method}()` instead"), - "try", - format!( - "{}.{method}(){cast}", - snippet_with_applicability(cx, ex.span, "_", &mut applicability), - ), - applicability, + format!("manual implementation of `Option::{method}`"), + |diag| { + diag.span_suggestion_verbose( + expr.span, + format!("use `Option::{method}()` directly"), + format!( + "{}.{method}(){cast}", + snippet_with_applicability(cx, ex.span, "_", &mut applicability), + ), + applicability, + ); + }, ); } } diff --git a/tests/ui/match_as_ref.stderr b/tests/ui/match_as_ref.stderr index 7b40cb80dc6ed..80898b10bc2f9 100644 --- a/tests/ui/match_as_ref.stderr +++ b/tests/ui/match_as_ref.stderr @@ -1,4 +1,4 @@ -error: use `as_ref()` instead +error: manual implementation of `Option::as_ref` --> tests/ui/match_as_ref.rs:6:33 | LL | let borrowed: Option<&()> = match owned { @@ -7,12 +7,21 @@ LL | | LL | | None => None, LL | | Some(ref v) => Some(v), LL | | }; - | |_____^ help: try: `owned.as_ref()` + | |_____^ | = note: `-D clippy::match-as-ref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_as_ref)]` +help: use `Option::as_ref()` directly + | +LL - let borrowed: Option<&()> = match owned { +LL - +LL - None => None, +LL - Some(ref v) => Some(v), +LL - }; +LL + let borrowed: Option<&()> = owned.as_ref(); + | -error: use `as_mut()` instead +error: manual implementation of `Option::as_mut` --> tests/ui/match_as_ref.rs:13:39 | LL | let borrow_mut: Option<&mut ()> = match mut_owned { @@ -21,9 +30,19 @@ LL | | LL | | None => None, LL | | Some(ref mut v) => Some(v), LL | | }; - | |_____^ help: try: `mut_owned.as_mut()` + | |_____^ + | +help: use `Option::as_mut()` directly + | +LL - let borrow_mut: Option<&mut ()> = match mut_owned { +LL - +LL - None => None, +LL - Some(ref mut v) => Some(v), +LL - }; +LL + let borrow_mut: Option<&mut ()> = mut_owned.as_mut(); + | -error: use `as_ref()` instead +error: manual implementation of `Option::as_ref` --> tests/ui/match_as_ref.rs:32:13 | LL | / match self.source { @@ -31,7 +50,17 @@ LL | | LL | | Some(ref s) => Some(s), LL | | None => None, LL | | } - | |_____________^ help: try: `self.source.as_ref().map(|x| x as _)` + | |_____________^ + | +help: use `Option::as_ref()` directly + | +LL - match self.source { +LL - +LL - Some(ref s) => Some(s), +LL - None => None, +LL - } +LL + self.source.as_ref().map(|x| x as _) + | error: aborting due to 3 previous errors From cb33ccd3fefba6a9ef452b0b5538d90ec5320a02 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 15:43:17 +0200 Subject: [PATCH 040/108] improve diagnostics --- clippy_lints/src/manual_option_as_slice.rs | 32 +++++---- tests/ui/manual_option_as_slice.stderr | 79 ++++++++++++++++++---- 2 files changed, 83 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 59b5de6e7e0bf..3342bf8347859 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,7 +1,8 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; +use clippy_utils::source::snippet_with_context; use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -133,19 +134,22 @@ fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span, msrv: Msrv) { }, ) { - if let Some(snippet) = clippy_utils::source::snippet_opt(cx, callee.span) { - span_lint_and_sugg( - cx, - MANUAL_OPTION_AS_SLICE, - span, - "use `Option::as_slice`", - "use", - format!("{snippet}.as_slice()"), - Applicability::MachineApplicable, - ); - } else { - span_lint(cx, MANUAL_OPTION_AS_SLICE, span, "use `Option_as_slice`"); - } + span_lint_and_then( + cx, + MANUAL_OPTION_AS_SLICE, + span, + "manual implementation of `Option::as_slice`", + |diag| { + let mut app = Applicability::MachineApplicable; + let callee = snippet_with_context(cx, callee.span, expr.span.ctxt(), "_", &mut app).0; + diag.span_suggestion_verbose( + span, + "use `Option::as_slice` directly", + format!("{callee}.as_slice()"), + app, + ); + }, + ); } } diff --git a/tests/ui/manual_option_as_slice.stderr b/tests/ui/manual_option_as_slice.stderr index e240ae8eb7d90..37113e9eafe1b 100644 --- a/tests/ui/manual_option_as_slice.stderr +++ b/tests/ui/manual_option_as_slice.stderr @@ -1,4 +1,4 @@ -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:5:9 | LL | _ = match x.as_ref() { @@ -7,12 +7,21 @@ LL | | LL | | Some(f) => std::slice::from_ref(f), LL | | None => &[], LL | | }; - | |_____^ help: use: `x.as_slice()` + | |_____^ | = note: `-D clippy::manual-option-as-slice` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_option_as_slice)]` +help: use `Option::as_slice` directly + | +LL - _ = match x.as_ref() { +LL - +LL - Some(f) => std::slice::from_ref(f), +LL - None => &[], +LL - }; +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:11:9 | LL | _ = if let Some(f) = x.as_ref() { @@ -23,37 +32,79 @@ LL | | std::slice::from_ref(f) LL | | } else { LL | | &[] LL | | }; - | |_____^ help: use: `x.as_slice()` + | |_____^ + | +help: use `Option::as_slice` directly + | +LL - _ = if let Some(f) = x.as_ref() { +LL - +LL - +LL - std::slice::from_ref(f) +LL - } else { +LL - &[] +LL - }; +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:19:9 | LL | _ = x.as_ref().map_or(&[][..], std::slice::from_ref); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `Option::as_slice` directly + | +LL - _ = x.as_ref().map_or(&[][..], std::slice::from_ref); +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:22:9 | LL | _ = x.as_ref().map_or_else(Default::default, std::slice::from_ref); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `Option::as_slice` directly + | +LL - _ = x.as_ref().map_or_else(Default::default, std::slice::from_ref); +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:25:9 | LL | _ = x.as_ref().map(std::slice::from_ref).unwrap_or_default(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `Option::as_slice` directly + | +LL - _ = x.as_ref().map(std::slice::from_ref).unwrap_or_default(); +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:28:9 | LL | _ = x.as_ref().map_or_else(|| &[42][..0], std::slice::from_ref); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `Option::as_slice` directly + | +LL - _ = x.as_ref().map_or_else(|| &[42][..0], std::slice::from_ref); +LL + _ = x.as_slice(); + | -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:33:13 | LL | _ = x.as_ref().map_or_else(<&[_]>::default, from_ref); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `Option::as_slice` directly + | +LL - _ = x.as_ref().map_or_else(<&[_]>::default, from_ref); +LL + _ = x.as_slice(); + | error: aborting due to 7 previous errors From 97d60490df4244f90b74665831e1878382237265 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 10 Sep 2025 16:06:58 -0500 Subject: [PATCH 041/108] Mark range expr with desugaring --- clippy_lints/src/loops/manual_memcpy.rs | 1 + clippy_lints/src/loops/manual_slice_fill.rs | 1 + clippy_lints/src/loops/needless_range_loop.rs | 9 +++++---- clippy_lints/src/manual_is_ascii_check.rs | 1 + clippy_lints/src/methods/iter_next_slice.rs | 1 + .../map_with_unused_argument_over_ranges.rs | 4 ++-- clippy_lints/src/methods/mod.rs | 4 ++-- clippy_lints/src/no_effect.rs | 6 +++--- clippy_lints/src/ranges.rs | 16 +++++++++------- .../src/unnecessary_struct_initialization.rs | 19 ++++++++++++++++--- clippy_utils/src/higher.rs | 8 ++++++++ clippy_utils/src/lib.rs | 2 +- clippy_utils/src/source.rs | 12 ++++++++++-- tests/ui/range_plus_minus_one.stderr | 16 ++++++++-------- tests/ui/reversed_empty_ranges_fixable.fixed | 4 ++-- tests/ui/reversed_empty_ranges_fixable.stderr | 12 ++++++------ .../reversed_empty_ranges_loops_fixable.fixed | 2 +- ...reversed_empty_ranges_loops_fixable.stderr | 6 +++--- 18 files changed, 80 insertions(+), 44 deletions(-) diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index a2da43c2ca245..7e48fe28011a8 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -28,6 +28,7 @@ pub(super) fn check<'tcx>( start: Some(start), end: Some(end), limits, + span: _, }) = higher::Range::hir(arg) // the var must be a single name && let PatKind::Binding(_, canonical_id, _, _) = pat.kind diff --git a/clippy_lints/src/loops/manual_slice_fill.rs b/clippy_lints/src/loops/manual_slice_fill.rs index 15c656cc7bc76..94cddb4a15069 100644 --- a/clippy_lints/src/loops/manual_slice_fill.rs +++ b/clippy_lints/src/loops/manual_slice_fill.rs @@ -31,6 +31,7 @@ pub(super) fn check<'tcx>( start: Some(start), end: Some(end), limits: RangeLimits::HalfOpen, + span: _, }) = higher::Range::hir(arg) && let ExprKind::Lit(Spanned { node: LitKind::Int(Pu128(0), _), diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 11edb929d70b2..7d890621d47c0 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -30,6 +30,7 @@ pub(super) fn check<'tcx>( start: Some(start), ref end, limits, + span, }) = higher::Range::hir(arg) // the var must be a single name && let PatKind::Binding(_, canonical_id, ident, _) = pat.kind @@ -149,7 +150,7 @@ pub(super) fn check<'tcx>( span_lint_and_then( cx, NEEDLESS_RANGE_LOOP, - arg.span, + span, format!("the loop variable `{}` is used to index `{indexed}`", ident.name), |diag| { diag.multipart_suggestion( @@ -157,7 +158,7 @@ pub(super) fn check<'tcx>( vec![ (pat.span, format!("({}, )", ident.name)), ( - arg.span, + span, format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), ), ], @@ -175,12 +176,12 @@ pub(super) fn check<'tcx>( span_lint_and_then( cx, NEEDLESS_RANGE_LOOP, - arg.span, + span, format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), |diag| { diag.multipart_suggestion( "consider using an iterator", - vec![(pat.span, "".to_string()), (arg.span, repl)], + vec![(pat.span, "".to_string()), (span, repl)], Applicability::HasPlaceholders, ); }, diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 8c6abbeac4489..456b488080907 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -110,6 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { start: Some(start), end: Some(end), limits: RangeLimits::Closed, + span: _, }) = higher::Range::hir(receiver) && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index 01f35ff02d44a..f37b15371c259 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -30,6 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen, + span: _, }) = higher::Range::hir(index_expr) && let hir::ExprKind::Lit(start_lit) = &start_expr.kind && let ast::LitKind::Int(start_idx, _) = start_lit.node diff --git a/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs index a2a522a60687d..38ca15cb1334c 100644 --- a/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs +++ b/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -63,7 +63,7 @@ pub(super) fn check( receiver: &Expr<'_>, arg: &Expr<'_>, msrv: Msrv, - method_call_span: Span, + method_name_span: Span, ) { let mut applicability = Applicability::MaybeIncorrect; if let Some(range) = higher::Range::hir(receiver) @@ -105,7 +105,7 @@ pub(super) fn check( // collate all our parts here and then remove those that are empty. let mut parts = vec![ ( - receiver.span.to(method_call_span), + ex.span.with_hi(method_name_span.hi()), format!("{exec_context}::iter::{method_to_use_name}"), ), new_span, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c9066be51c44b..48fa5f7bdc6ef 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4854,8 +4854,8 @@ impl_lint_pass!(Methods => [ /// come from expansion. pub fn method_call<'tcx>(recv: &'tcx Expr<'tcx>) -> Option<(Symbol, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind - && !args.iter().any(|e| e.span.from_expansion()) - && !receiver.span.from_expansion() + && !args.iter().any(|e| e.range_span().unwrap_or(e.span).from_expansion()) + && !receiver.range_span().unwrap_or(receiver.span).from_expansion() { Some((path.ident.name, receiver, args, path.ident.span, call_span)) } else { diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 701923cf6efc4..fdb8e1b475c1d 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -122,7 +122,7 @@ impl NoEffect { return true; } - if expr.span.from_expansion() { + if expr.range_span().unwrap_or(expr.span).from_expansion() { return false; } let expr = peel_blocks(expr); @@ -273,7 +273,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if let StmtKind::Semi(expr) = stmt.kind && !stmt.span.in_external_macro(cx.sess().source_map()) && let ctxt = stmt.span.ctxt() - && expr.span.ctxt() == ctxt + && expr.range_span().unwrap_or(expr.span).ctxt() == ctxt && let Some(reduced) = reduce_expression(cx, expr) && reduced.iter().all(|e| e.span.ctxt() == ctxt) { @@ -330,7 +330,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option>> { - if expr.span.from_expansion() { + if expr.range_span().unwrap_or(expr.span).from_expansion() { return None; } match expr.kind { diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index e4c91b7efd2bd..ac2cc11d30206 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -501,17 +501,18 @@ fn check_range_switch<'tcx>( msg: &'static str, operator: &str, ) { - if expr.span.can_be_used_for_suggestions() - && let Some(higher::Range { + if let Some(range) = higher::Range::hir(expr) + && let higher::Range { start, end: Some(end), limits, - }) = higher::Range::hir(expr) + span, + } = range + && span.can_be_used_for_suggestions() && limits == kind && let Some(y) = predicate(cx, end) && can_switch_ranges(cx, expr, kind, cx.typeck_results().expr_ty(y)) { - let span = expr.span; span_lint_and_then(cx, lint, span, msg, |diag| { let mut app = Applicability::MachineApplicable; let start = start.map_or(String::new(), |x| { @@ -567,6 +568,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { start: Some(start), end: Some(end), limits, + span, }) = higher::Range::hir(expr) && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() @@ -582,7 +584,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { span_lint( cx, REVERSED_EMPTY_RANGES, - expr.span, + span, "this range is reversed and using it to index a slice will panic at run-time", ); } @@ -591,7 +593,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { span_lint_and_then( cx, REVERSED_EMPTY_RANGES, - expr.span, + span, "this range is empty so it will yield no values", |diag| { if ordering != Ordering::Equal { @@ -603,7 +605,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { }; diag.span_suggestion( - expr.span, + span, "consider using the following if you are attempting to iterate over this \ range in reverse", format!("({end_snippet}{dots}{start_snippet}).rev()"), diff --git a/clippy_lints/src/unnecessary_struct_initialization.rs b/clippy_lints/src/unnecessary_struct_initialization.rs index 51596859e4c7b..51397accefeae 100644 --- a/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/clippy_lints/src/unnecessary_struct_initialization.rs @@ -6,6 +6,7 @@ use clippy_utils::{get_parent_expr, is_mutable}; use rustc_hir::{Expr, ExprField, ExprKind, Path, QPath, StructTailExpr, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::{DesugaringKind, Ident}; declare_clippy_lint! { /// ### What it does @@ -52,7 +53,8 @@ impl LateLintPass<'_> for UnnecessaryStruct { return; }; - if expr.span.from_expansion() { + let expr_span = expr.range_span().unwrap_or(expr.span); + if expr_span.from_expansion() { // Prevent lint from hitting inside macro code return; } @@ -80,7 +82,7 @@ impl LateLintPass<'_> for UnnecessaryStruct { span_lint_and_sugg( cx, UNNECESSARY_STRUCT_INITIALIZATION, - expr.span, + expr_span, "unnecessary struct building", "replace with", snippet(cx, sugg, "..").into_owned(), @@ -130,7 +132,7 @@ fn same_path_in_all_fields<'tcx>( // expression type matches && ty == cx.typeck_results().expr_ty(src_expr) // field name matches - && f.ident == ident + && ident_without_range_desugaring(f.ident) == ident // assigned from a path expression && let ExprKind::Path(QPath::Resolved(None, src_path)) = src_expr.kind { @@ -197,3 +199,14 @@ fn path_matches_base(path: &Path<'_>, base: &Expr<'_>) -> bool { }; path.res == base_path.res } + +fn ident_without_range_desugaring(ident: Ident) -> Ident { + if ident.span.desugaring_kind() == Some(DesugaringKind::RangeExpr) { + Ident { + span: ident.span.parent_callsite().unwrap(), + ..ident + } + } else { + ident + } +} diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 3383e66fe3688..9d895c9aa649a 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -209,12 +209,14 @@ pub struct Range<'a> { pub end: Option<&'a Expr<'a>>, /// Whether the interval is open or closed. pub limits: ast::RangeLimits, + pub span: Span } impl<'a> Range<'a> { /// Higher a `hir` range to something similar to `ast::ExprKind::Range`. #[expect(clippy::similar_names)] pub fn hir(expr: &'a Expr<'_>) -> Option> { + let span = expr.range_span()?; match expr.kind { ExprKind::Call(path, [arg1, arg2]) if matches!( @@ -226,6 +228,7 @@ impl<'a> Range<'a> { start: Some(arg1), end: Some(arg2), limits: ast::RangeLimits::Closed, + span, }) }, ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) { @@ -233,12 +236,14 @@ impl<'a> Range<'a> { start: None, end: None, limits: ast::RangeLimits::HalfOpen, + span, }), (QPath::LangItem(hir::LangItem::RangeFrom, ..), [field]) if field.ident.name == sym::start => { Some(Range { start: Some(field.expr), end: None, limits: ast::RangeLimits::HalfOpen, + span, }) }, (QPath::LangItem(hir::LangItem::Range, ..), [field1, field2]) => { @@ -251,6 +256,7 @@ impl<'a> Range<'a> { start: Some(start), end: Some(end), limits: ast::RangeLimits::HalfOpen, + span, }) }, (QPath::LangItem(hir::LangItem::RangeToInclusive, ..), [field]) if field.ident.name == sym::end => { @@ -258,12 +264,14 @@ impl<'a> Range<'a> { start: None, end: Some(field.expr), limits: ast::RangeLimits::Closed, + span, }) }, (QPath::LangItem(hir::LangItem::RangeTo, ..), [field]) if field.ident.name == sym::end => Some(Range { start: None, end: Some(field.expr), limits: ast::RangeLimits::HalfOpen, + span, }), _ => None, }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index c8943523e1798..6ee991eae137f 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1282,7 +1282,7 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { /// If the given `Expr` is not some kind of range, the function returns `false`. pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - if let Some(Range { start, end, limits }) = Range::hir(expr) { + if let Some(Range { start, end, limits, .. }) = Range::hir(expr) { let start_is_none_or_min = start.is_none_or(|start| { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 83d7f6bc565d7..954a71f6c320e 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -13,8 +13,8 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::source_map::{SourceMap, original_sp}; use rustc_span::{ - BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, Span, SpanData, - SyntaxContext, hygiene, + BytePos, DesugaringKind, DUMMY_SP, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, + Span, SpanData, SyntaxContext, hygiene, }; use std::borrow::Cow; use std::fmt; @@ -670,6 +670,14 @@ fn snippet_with_context_sess<'a>( default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { + // If it is just range desugaring, use the desugaring span since it may include parenthesis. + if span.desugaring_kind() == Some(DesugaringKind::RangeExpr) && span.parent_callsite().unwrap().ctxt() == outer { + return ( + snippet_with_applicability_sess(sess, span, default, applicability), + false, + ) + } + let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else( || { // The span is from a macro argument, and the outer context is the macro using the argument diff --git a/tests/ui/range_plus_minus_one.stderr b/tests/ui/range_plus_minus_one.stderr index a419d935bd62f..60abe50efa10a 100644 --- a/tests/ui/range_plus_minus_one.stderr +++ b/tests/ui/range_plus_minus_one.stderr @@ -32,22 +32,22 @@ LL | for _ in 1..ONE + ONE {} | ^^^^^^^^^^^^ help: use: `1..=ONE` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:70:5 + --> tests/ui/range_plus_minus_one.rs:70:6 | LL | (1..10 + 1).for_each(|_| {}); - | ^^^^^^^^^^^ help: use: `(1..=10)` + | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:75:5 + --> tests/ui/range_plus_minus_one.rs:75:6 | LL | (1..10 + 1).into_iter().for_each(|_| {}); - | ^^^^^^^^^^^ help: use: `(1..=10)` + | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:80:17 + --> tests/ui/range_plus_minus_one.rs:80:18 | LL | let _ = (1..10 + 1).start_bound(); - | ^^^^^^^^^^^ help: use: `(1..=10)` + | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable --> tests/ui/range_plus_minus_one.rs:86:16 @@ -80,10 +80,10 @@ LL | a[0..2 + 1][0] = 1; | ^^^^^^^^ help: use: `0..=2` error: an exclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:180:5 + --> tests/ui/range_plus_minus_one.rs:180:6 | LL | (1..=n - 1).sum() - | ^^^^^^^^^^^ help: use: `(1..n)` + | ^^^^^^^^^ help: use: `1..n` | = note: `-D clippy::range-minus-one` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::range_minus_one)]` diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed index ba5059bbaa37e..6c866aceed4f1 100644 --- a/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -6,9 +6,9 @@ const ANSWER: i32 = 42; fn main() { // These should be linted: - (21..=42).rev().for_each(|x| println!("{}", x)); + ((21..=42).rev()).for_each(|x| println!("{}", x)); //~^ reversed_empty_ranges - let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); + let _ = ((21..ANSWER).rev()).filter(|x| x % 2 == 0).take(10).collect::>(); //~^ reversed_empty_ranges for _ in (-42..=-21).rev() {} diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 3fadc4c169f13..f3d3ac298cec8 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,27 +1,27 @@ error: this range is empty so it will yield no values - --> tests/ui/reversed_empty_ranges_fixable.rs:9:5 + --> tests/ui/reversed_empty_ranges_fixable.rs:9:6 | LL | (42..=21).for_each(|x| println!("{}", x)); - | ^^^^^^^^^ + | ^^^^^^^ | = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::reversed_empty_ranges)]` help: consider using the following if you are attempting to iterate over this range in reverse | LL - (42..=21).for_each(|x| println!("{}", x)); -LL + (21..=42).rev().for_each(|x| println!("{}", x)); +LL + ((21..=42).rev()).for_each(|x| println!("{}", x)); | error: this range is empty so it will yield no values - --> tests/ui/reversed_empty_ranges_fixable.rs:11:13 + --> tests/ui/reversed_empty_ranges_fixable.rs:11:14 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); - | ^^^^^^^^^^^^ + | ^^^^^^^^^^ | help: consider using the following if you are attempting to iterate over this range in reverse | LL - let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); -LL + let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); +LL + let _ = ((21..ANSWER).rev()).filter(|x| x % 2 == 0).take(10).collect::>(); | error: this range is empty so it will yield no values diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.fixed b/tests/ui/reversed_empty_ranges_loops_fixable.fixed index 55080da8a1377..9fb46c9533cdd 100644 --- a/tests/ui/reversed_empty_ranges_loops_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_loops_fixable.fixed @@ -34,7 +34,7 @@ fn main() { println!("{}", i); } - for i in (0..10).rev().map(|x| x * 2) { + for i in ((0..10).rev()).map(|x| x * 2) { //~^ reversed_empty_ranges println!("{}", i); } diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.stderr b/tests/ui/reversed_empty_ranges_loops_fixable.stderr index eadd9d3675e1c..775d109296a1b 100644 --- a/tests/ui/reversed_empty_ranges_loops_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_loops_fixable.stderr @@ -37,15 +37,15 @@ LL + for i in (0..MAX_LEN).rev() { | error: this range is empty so it will yield no values - --> tests/ui/reversed_empty_ranges_loops_fixable.rs:37:14 + --> tests/ui/reversed_empty_ranges_loops_fixable.rs:37:15 | LL | for i in (10..0).map(|x| x * 2) { - | ^^^^^^^ + | ^^^^^ | help: consider using the following if you are attempting to iterate over this range in reverse | LL - for i in (10..0).map(|x| x * 2) { -LL + for i in (0..10).rev().map(|x| x * 2) { +LL + for i in ((0..10).rev()).map(|x| x * 2) { | error: this range is empty so it will yield no values From a6e5159c7d4c5cf8d922fd350d2c033cf90c9ecc Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 18:11:39 +0200 Subject: [PATCH 042/108] fix: parenthesise the receiver if needed --- clippy_lints/src/matches/match_as_ref.rs | 4 ++-- tests/ui/match_as_ref.fixed | 13 +++++++++++++ tests/ui/match_as_ref.rs | 17 +++++++++++++++++ tests/ui/match_as_ref.stderr | 23 ++++++++++++++++++++++- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 20f5b2b0e8f51..2c4d08639f61c 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeQPath}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::option_arg_ty; use clippy_utils::{is_none_arm, peel_blocks}; use rustc_errors::Applicability; @@ -52,7 +52,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: format!("use `Option::{method}()` directly"), format!( "{}.{method}(){cast}", - snippet_with_applicability(cx, ex.span, "_", &mut applicability), + Sugg::hir_with_applicability(cx, ex, "_", &mut applicability).maybe_paren(), ), applicability, ); diff --git a/tests/ui/match_as_ref.fixed b/tests/ui/match_as_ref.fixed index a39f0c9299bd5..3653b81eda221 100644 --- a/tests/ui/match_as_ref.fixed +++ b/tests/ui/match_as_ref.fixed @@ -71,3 +71,16 @@ mod issue15691 { }; } } + +fn recv_requiring_parens() { + struct S; + + impl std::ops::Not for S { + type Output = Option; + fn not(self) -> Self::Output { + None + } + } + + let _ = (!S).as_ref(); +} diff --git a/tests/ui/match_as_ref.rs b/tests/ui/match_as_ref.rs index 049928167901a..d58cfd81aeab4 100644 --- a/tests/ui/match_as_ref.rs +++ b/tests/ui/match_as_ref.rs @@ -83,3 +83,20 @@ mod issue15691 { }; } } + +fn recv_requiring_parens() { + struct S; + + impl std::ops::Not for S { + type Output = Option; + fn not(self) -> Self::Output { + None + } + } + + let _ = match !S { + //~^ match_as_ref + None => None, + Some(ref v) => Some(v), + }; +} diff --git a/tests/ui/match_as_ref.stderr b/tests/ui/match_as_ref.stderr index 80898b10bc2f9..226655970ab8f 100644 --- a/tests/ui/match_as_ref.stderr +++ b/tests/ui/match_as_ref.stderr @@ -62,5 +62,26 @@ LL - } LL + self.source.as_ref().map(|x| x as _) | -error: aborting due to 3 previous errors +error: manual implementation of `Option::as_ref` + --> tests/ui/match_as_ref.rs:97:13 + | +LL | let _ = match !S { + | _____________^ +LL | | +LL | | None => None, +LL | | Some(ref v) => Some(v), +LL | | }; + | |_____^ + | +help: use `Option::as_ref()` directly + | +LL - let _ = match !S { +LL - +LL - None => None, +LL - Some(ref v) => Some(v), +LL - }; +LL + let _ = (!S).as_ref(); + | + +error: aborting due to 4 previous errors From 37ebd4c97e845871ab2b8d583a88a3d62540a14d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 22:12:20 +0200 Subject: [PATCH 043/108] move the lint file to under `methods/` WARNING: this commit won't compile --- clippy_lints/src/{ => methods}/lines_filter_map_ok.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clippy_lints/src/{ => methods}/lines_filter_map_ok.rs (100%) diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/methods/lines_filter_map_ok.rs similarity index 100% rename from clippy_lints/src/lines_filter_map_ok.rs rename to clippy_lints/src/methods/lines_filter_map_ok.rs From 81d23008621fa7c712831dfae2ff29e81ba34aa1 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 14:40:14 +0200 Subject: [PATCH 044/108] move to `methods/`, for real this time --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 - .../src/methods/lines_filter_map_ok.rs | 188 +++++++----------- clippy_lints/src/methods/mod.rs | 92 +++++++-- 4 files changed, 148 insertions(+), 136 deletions(-) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 9d2532f8e47f8..af829d0862a14 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -258,7 +258,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::lifetimes::ELIDABLE_LIFETIME_NAMES_INFO, crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, crate::lifetimes::NEEDLESS_LIFETIMES_INFO, - crate::lines_filter_map_ok::LINES_FILTER_MAP_OK_INFO, crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO, crate::literal_representation::LARGE_DIGIT_GROUPS_INFO, @@ -403,6 +402,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::ITER_SKIP_ZERO_INFO, crate::methods::ITER_WITH_DRAIN_INFO, crate::methods::JOIN_ABSOLUTE_PATHS_INFO, + crate::methods::LINES_FILTER_MAP_OK_INFO, crate::methods::MANUAL_CONTAINS_INFO, crate::methods::MANUAL_C_STR_LITERALS_INFO, crate::methods::MANUAL_FILTER_MAP_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 96077d0bd00f6..2334e195957c2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -191,7 +191,6 @@ mod let_if_seq; mod let_underscore; mod let_with_type_underscore; mod lifetimes; -mod lines_filter_map_ok; mod literal_representation; mod literal_string_with_formatting_args; mod loops; @@ -742,7 +741,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))); store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); - store.register_late_pass(move |_| Box::new(lines_filter_map_ok::LinesFilterMapOk::new(conf))); store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))); store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))); diff --git a/clippy_lints/src/methods/lines_filter_map_ok.rs b/clippy_lints/src/methods/lines_filter_map_ok.rs index f49ce2848abfb..baf5b6f93f637 100644 --- a/clippy_lints/src/methods/lines_filter_map_ok.rs +++ b/clippy_lints/src/methods/lines_filter_map_ok.rs @@ -1,137 +1,85 @@ -use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::impl_lint_pass; -use rustc_span::Symbol; +use rustc_lint::LateContext; +use rustc_span::Span; -pub struct LinesFilterMapOk { - msrv: Msrv, -} +use super::LINES_FILTER_MAP_OK; -impl LinesFilterMapOk { - pub fn new(conf: &Conf) -> Self { - Self { msrv: conf.msrv } +pub(super) fn check_flatten(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, call_span: Span, msrv: Msrv) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx + .typeck_results() + .expr_ty_adjusted(recv) + .is_diag_item(cx, sym::IoLines) + && msrv.meets(cx, msrvs::MAP_WHILE) + { + emit(cx, recv, "flatten", call_span); } } -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)` - /// when `lines` has type `std::io::Lines`. - /// - /// ### Why is this bad? - /// `Lines` instances might produce a never-ending stream of `Err`, in which case - /// `filter_map(Result::ok)` will enter an infinite loop while waiting for an - /// `Ok` variant. Calling `next()` once is sufficient to enter the infinite loop, - /// even in the absence of explicit loops in the user code. - /// - /// This situation can arise when working with user-provided paths. On some platforms, - /// `std::fs::File::open(path)` might return `Ok(fs)` even when `path` is a directory, - /// but any later attempt to read from `fs` will return an error. - /// - /// ### Known problems - /// This lint suggests replacing `filter_map()` or `flat_map()` applied to a `Lines` - /// instance in all cases. There are two cases where the suggestion might not be - /// appropriate or necessary: - /// - /// - If the `Lines` instance can never produce any error, or if an error is produced - /// only once just before terminating the iterator, using `map_while()` is not - /// necessary but will not do any harm. - /// - If the `Lines` instance can produce intermittent errors then recover and produce - /// successful results, using `map_while()` would stop at the first error. - /// - /// ### Example - /// ```no_run - /// # use std::{fs::File, io::{self, BufRead, BufReader}}; - /// # let _ = || -> io::Result<()> { - /// let mut lines = BufReader::new(File::open("some-path")?).lines().filter_map(Result::ok); - /// // If "some-path" points to a directory, the next statement never terminates: - /// let first_line: Option = lines.next(); - /// # Ok(()) }; - /// ``` - /// Use instead: - /// ```no_run - /// # use std::{fs::File, io::{self, BufRead, BufReader}}; - /// # let _ = || -> io::Result<()> { - /// let mut lines = BufReader::new(File::open("some-path")?).lines().map_while(Result::ok); - /// let first_line: Option = lines.next(); - /// # Ok(()) }; - /// ``` - #[clippy::version = "1.70.0"] - pub LINES_FILTER_MAP_OK, - suspicious, - "filtering `std::io::Lines` with `filter_map()`, `flat_map()`, or `flatten()` might cause an infinite loop" -} - -impl_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]); - -impl LateLintPass<'_> for LinesFilterMapOk { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind - && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) - && let fm_method_name = fm_method.ident.name - && matches!(fm_method_name, sym::filter_map | sym::flat_map | sym::flatten) - && cx - .typeck_results() - .expr_ty_adjusted(fm_receiver) - .is_diag_item(cx, sym::IoLines) - && should_lint(cx, fm_args, fm_method_name) - && self.msrv.meets(cx, msrvs::MAP_WHILE) - { - span_lint_and_then( - cx, - LINES_FILTER_MAP_OK, - fm_span, - format!("`{fm_method_name}()` will run forever if the iterator repeatedly produces an `Err`"), - |diag| { - diag.span_note( - fm_receiver.span, - "this expression returning a `std::io::Lines` may produce \ - an infinite number of `Err` in case of a read error", - ); - diag.span_suggestion( - fm_span, - "replace with", - "map_while(Result::ok)", - Applicability::MaybeIncorrect, - ); - }, - ); +pub(super) fn check_filter_or_flat_map( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + method_name: &'static str, + method_arg: &Expr<'_>, + call_span: Span, + msrv: Msrv, +) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx + .typeck_results() + .expr_ty_adjusted(recv) + .is_diag_item(cx, sym::IoLines) + && match method_arg.kind { + // Detect `Result::ok` + ExprKind::Path(ref qpath) => cx + .qpath_res(qpath, method_arg.hir_id) + .is_diag_item(cx, sym::result_ok_method), + // Detect `|x| x.ok()` + ExprKind::Closure(&Closure { body, .. }) => { + if let Body { + params: [param], value, .. + } = cx.tcx.hir_body(body) + && let ExprKind::MethodCall(method, receiver, [], _) = value.kind + { + method.ident.name == sym::ok + && receiver.res_local_id() == Some(param.pat.hir_id) + && cx.ty_based_def(*value).is_diag_item(cx, sym::result_ok_method) + } else { + false + } + }, + _ => false, } + && msrv.meets(cx, msrvs::MAP_WHILE) + { + emit(cx, recv, method_name, call_span); } } -fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> bool { - match args { - [] => method_name == sym::flatten, - [fm_arg] => { - match fm_arg.kind { - // Detect `Result::ok` - ExprKind::Path(ref qpath) => cx - .qpath_res(qpath, fm_arg.hir_id) - .is_diag_item(cx, sym::result_ok_method), - // Detect `|x| x.ok()` - ExprKind::Closure(&Closure { body, .. }) => { - if let Body { - params: [param], value, .. - } = cx.tcx.hir_body(body) - && let ExprKind::MethodCall(method, receiver, [], _) = value.kind - { - method.ident.name == sym::ok - && receiver.res_local_id() == Some(param.pat.hir_id) - && cx.ty_based_def(*value).is_diag_item(cx, sym::result_ok_method) - } else { - false - } - }, - _ => false, - } +fn emit(cx: &LateContext<'_>, recv: &Expr<'_>, method_name: &'static str, call_span: Span) { + span_lint_and_then( + cx, + LINES_FILTER_MAP_OK, + call_span, + format!("`{method_name}()` will run forever if the iterator repeatedly produces an `Err`"), + |diag| { + diag.span_note( + recv.span, + "this expression returning a `std::io::Lines` may produce \ + an infinite number of `Err` in case of a read error", + ); + diag.span_suggestion( + call_span, + "replace with", + "map_while(Result::ok)", + Applicability::MaybeIncorrect, + ); }, - _ => false, - } + ); } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c9066be51c44b..54b04a8fff056 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -56,6 +56,7 @@ mod iter_with_drain; mod iterator_step_by_zero; mod join_absolute_paths; mod lib; +mod lines_filter_map_ok; mod manual_c_str_literals; mod manual_contains; mod manual_inspect; @@ -4665,6 +4666,55 @@ declare_clippy_lint! { "making no use of the \"map closure\" when calling `.map_or_else(|| 2 * k, |n| n)`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)` + /// when `lines` has type `std::io::Lines`. + /// + /// ### Why is this bad? + /// `Lines` instances might produce a never-ending stream of `Err`, in which case + /// `filter_map(Result::ok)` will enter an infinite loop while waiting for an + /// `Ok` variant. Calling `next()` once is sufficient to enter the infinite loop, + /// even in the absence of explicit loops in the user code. + /// + /// This situation can arise when working with user-provided paths. On some platforms, + /// `std::fs::File::open(path)` might return `Ok(fs)` even when `path` is a directory, + /// but any later attempt to read from `fs` will return an error. + /// + /// ### Known problems + /// This lint suggests replacing `filter_map()` or `flat_map()` applied to a `Lines` + /// instance in all cases. There are two cases where the suggestion might not be + /// appropriate or necessary: + /// + /// - If the `Lines` instance can never produce any error, or if an error is produced + /// only once just before terminating the iterator, using `map_while()` is not + /// necessary but will not do any harm. + /// - If the `Lines` instance can produce intermittent errors then recover and produce + /// successful results, using `map_while()` would stop at the first error. + /// + /// ### Example + /// ```no_run + /// # use std::{fs::File, io::{self, BufRead, BufReader}}; + /// # let _ = || -> io::Result<()> { + /// let mut lines = BufReader::new(File::open("some-path")?).lines().filter_map(Result::ok); + /// // If "some-path" points to a directory, the next statement never terminates: + /// let first_line: Option = lines.next(); + /// # Ok(()) }; + /// ``` + /// Use instead: + /// ```no_run + /// # use std::{fs::File, io::{self, BufRead, BufReader}}; + /// # let _ = || -> io::Result<()> { + /// let mut lines = BufReader::new(File::open("some-path")?).lines().map_while(Result::ok); + /// let first_line: Option = lines.next(); + /// # Ok(()) }; + /// ``` + #[clippy::version = "1.70.0"] + pub LINES_FILTER_MAP_OK, + suspicious, + "filtering `std::io::Lines` with `filter_map()`, `flat_map()`, or `flatten()` might cause an infinite loop" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4847,6 +4897,7 @@ impl_lint_pass!(Methods => [ IP_CONSTANT, REDUNDANT_ITER_CLONED, UNNECESSARY_OPTION_MAP_OR_ELSE, + LINES_FILTER_MAP_OK, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -5165,6 +5216,15 @@ impl Methods { unnecessary_filter_map::check(cx, expr, arg, name); filter_map_bool_then::check(cx, expr, arg, call_span); filter_map_identity::check(cx, expr, arg, span); + lines_filter_map_ok::check_filter_or_flat_map( + cx, + expr, + recv, + "filter_map", + arg, + call_span, + self.msrv, + ); }, (sym::find_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); @@ -5174,20 +5234,26 @@ impl Methods { unused_enumerate_index::check(cx, expr, recv, arg); flat_map_identity::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span); + lines_filter_map_ok::check_filter_or_flat_map( + cx, expr, recv, "flat_map", arg, call_span, self.msrv, + ); }, - (sym::flatten, []) => match method_call(recv) { - Some((sym::map, recv, [map_arg], map_span, _)) => { - map_flatten::check(cx, expr, recv, map_arg, map_span); - }, - Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( - cx, - expr, - recv, - recv2, - iter_overeager_cloned::Op::LaterCloned, - true, - ), - _ => {}, + (sym::flatten, []) => { + match method_call(recv) { + Some((sym::map, recv, [map_arg], map_span, _)) => { + map_flatten::check(cx, expr, recv, map_arg, map_span); + }, + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::LaterCloned, + true, + ), + _ => {}, + } + lines_filter_map_ok::check_flatten(cx, expr, recv, call_span, self.msrv); }, (sym::fold, [init, acc]) => { manual_try_fold::check(cx, expr, init, acc, call_span, self.msrv); From 17f194238d696fb7bcb8f293fe70bcab1cf01469 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 19 Oct 2025 18:32:06 +0200 Subject: [PATCH 045/108] Adapt the logic from `extra_unused_type_parameters` .. and add some docs to both of them --- .../src/extra_unused_type_parameters.rs | 5 ++++ clippy_lints/src/lifetimes.rs | 25 ++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index c0b0fd88d9e16..fb98cb30ae902 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -157,6 +157,11 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { let spans = if explicit_params.len() == extra_params.len() { vec![self.generics.span] // Remove the entire list of generics } else { + // 1. Start from the last extra param + // 2. While the params preceding it are also extra, construct spans going from the current param to + // the comma before it + // 3. Once this chain of extra params stops, switch to constructing spans going from the current + // param to the comma _after_ it let mut end: Option = None; extra_params .iter() diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 2779052e17a25..727e9b172a872 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -863,20 +863,33 @@ fn elision_suggestions( // ^^^^ vec![(generics.span, String::new())] } else { + // 1. Start from the last elidable lifetime + // 2. While the lifetimes preceding it are also elidable, construct spans going from the current + // lifetime to the comma before it + // 3. Once this chain of elidable lifetimes stops, switch to constructing spans going from the + // current lifetime to the comma _after_ it + let mut end: Option = None; elidable_lts .iter() + .rev() .map(|&id| { - let pos = explicit_params.iter().position(|param| param.def_id == id)?; - let param = explicit_params.get(pos)?; + let (idx, param) = explicit_params.iter().find_position(|param| param.def_id == id)?; - let span = if let Some(next) = explicit_params.get(pos + 1) { + let span = if let Some(next) = explicit_params.get(idx + 1) + && end != Some(next.def_id) + { + // Extend the current span forward, up until the next param in the list. // fn x<'prev, 'a, 'next>() {} // ^^^^ param.span.until(next.span) } else { - // `pos` should be at least 1 here, because the param in position 0 would either have a `next` - // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. - let prev = explicit_params.get(pos - 1)?; + // Extend the current span back to include the comma following the previous + // param. If the span of the next param in the list has already been + // extended, we continue the chain. This is why we're iterating in reverse. + end = Some(param.def_id); + + // `idx` will never be 0, else we'd be removing the entire list of generics + let prev = explicit_params.get(idx - 1)?; // fn x<'prev, 'a>() {} // ^^^^ From bab7519d5b68595e6fb35c01646c39559ebfe95e Mon Sep 17 00:00:00 2001 From: Zylos <217501196+ZylosLumen@users.noreply.github.com> Date: Tue, 21 Oct 2025 21:43:34 -0400 Subject: [PATCH 046/108] Update mod.rs --- clippy_lints/src/operators/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index f2aae8ca458d3..75a6d57efb3c5 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -464,7 +464,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for statements of the form `(a - b) < f32::EPSILON` or - /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`. + /// `(a - b) < f64::EPSILON`. Note the missing `.abs()`. /// /// ### Why is this bad? /// The code without `.abs()` is more likely to have a bug. @@ -617,7 +617,7 @@ declare_clippy_lint! { /// println!("{within_tolerance}"); // true /// ``` /// - /// NB! Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is + /// NOTE: Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is /// a different use of the term that is not suitable for floating point equality comparison. /// Indeed, for the example above using `f64::EPSILON` as the allowed error would return `false`. /// @@ -680,7 +680,7 @@ declare_clippy_lint! { /// println!("{within_tolerance}"); // true /// ``` /// - /// NB! Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is + /// NOTE: Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is /// a different use of the term that is not suitable for floating point equality comparison. /// Indeed, for the example above using `f64::EPSILON` as the allowed error would return `false`. /// From 5ff804e9b239f33fe1aab98c896248546a523b71 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 22 Oct 2025 11:46:37 +0200 Subject: [PATCH 047/108] misc: move infallible stuff out of the way --- clippy_lints/src/matches/match_as_ref.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 2c4d08639f61c..7b23cdabf2e8c 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -21,24 +21,18 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: } else { None } + && let output_ty = cx.typeck_results().expr_ty(expr) + && let input_ty = cx.typeck_results().expr_ty(ex) + && let Some(input_ty) = option_arg_ty(cx, input_ty) + && let Some(output_ty) = option_arg_ty(cx, output_ty) + && let ty::Ref(_, output_ty, _) = *output_ty.kind() { let method = match arm_ref_mutbl { Mutability::Not => "as_ref", Mutability::Mut => "as_mut", }; - let output_ty = cx.typeck_results().expr_ty(expr); - let input_ty = cx.typeck_results().expr_ty(ex); - - let cast = if let Some(input_ty) = option_arg_ty(cx, input_ty) - && let Some(output_ty) = option_arg_ty(cx, output_ty) - && let ty::Ref(_, output_ty, _) = *output_ty.kind() - && input_ty != output_ty - { - ".map(|x| x as _)" - } else { - "" - }; + let cast = if input_ty == output_ty { "" } else { ".map(|x| x as _)" }; let mut applicability = Applicability::MachineApplicable; span_lint_and_then( From 22bdd9f7c9bef81a3ebccffeb6621c9959172fe9 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 22 Oct 2025 01:03:58 +0200 Subject: [PATCH 048/108] fix(match_as_ref): suggest `as_ref` when the reference needs to be cast --- clippy_lints/src/matches/match_as_ref.rs | 45 +++++++++++++++++------ tests/ui/match_as_ref.fixed | 6 ++++ tests/ui/match_as_ref.rs | 14 ++++++++ tests/ui/match_as_ref.stderr | 46 +++++++++++++++++++++++- 4 files changed, 100 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 7b23cdabf2e8c..7ac57fae690cb 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -25,13 +25,25 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: && let input_ty = cx.typeck_results().expr_ty(ex) && let Some(input_ty) = option_arg_ty(cx, input_ty) && let Some(output_ty) = option_arg_ty(cx, output_ty) - && let ty::Ref(_, output_ty, _) = *output_ty.kind() + && let ty::Ref(_, output_ty, output_mutbl) = *output_ty.kind() { let method = match arm_ref_mutbl { Mutability::Not => "as_ref", Mutability::Mut => "as_mut", }; + // ``` + // let _: Option<&T> = match opt { + // Some(ref mut t) => Some(t), + // None => None, + // }; + // ``` + // We need to suggest `t.as_ref()` in order downcast the reference from `&mut` to `&`. + // We may or may not need to cast the type as well, for which we'd need `.map()`, and that could + // theoretically take care of the reference downcasting as well, but we chose to keep these two + // operations separate + let need_as_ref = arm_ref_mutbl == Mutability::Mut && output_mutbl == Mutability::Not; + let cast = if input_ty == output_ty { "" } else { ".map(|x| x as _)" }; let mut applicability = Applicability::MachineApplicable; @@ -41,15 +53,28 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: expr.span, format!("manual implementation of `Option::{method}`"), |diag| { - diag.span_suggestion_verbose( - expr.span, - format!("use `Option::{method}()` directly"), - format!( - "{}.{method}(){cast}", - Sugg::hir_with_applicability(cx, ex, "_", &mut applicability).maybe_paren(), - ), - applicability, - ); + if need_as_ref { + diag.note("but the type is coerced to a non-mutable reference, and so `as_ref` can used instead"); + diag.span_suggestion_verbose( + expr.span, + "use `Option::as_ref()`", + format!( + "{}.as_ref(){cast}", + Sugg::hir_with_applicability(cx, ex, "_", &mut applicability).maybe_paren(), + ), + applicability, + ); + } else { + diag.span_suggestion_verbose( + expr.span, + format!("use `Option::{method}()` directly"), + format!( + "{}.{method}(){cast}", + Sugg::hir_with_applicability(cx, ex, "_", &mut applicability).maybe_paren(), + ), + applicability, + ); + } }, ); } diff --git a/tests/ui/match_as_ref.fixed b/tests/ui/match_as_ref.fixed index 3653b81eda221..09a6ed1693905 100644 --- a/tests/ui/match_as_ref.fixed +++ b/tests/ui/match_as_ref.fixed @@ -84,3 +84,9 @@ fn recv_requiring_parens() { let _ = (!S).as_ref(); } + +fn issue15932() { + let _: Option<&u32> = Some(0).as_ref(); + + let _: Option<&dyn std::fmt::Debug> = Some(0).as_ref().map(|x| x as _); +} diff --git a/tests/ui/match_as_ref.rs b/tests/ui/match_as_ref.rs index d58cfd81aeab4..347b6d1868879 100644 --- a/tests/ui/match_as_ref.rs +++ b/tests/ui/match_as_ref.rs @@ -100,3 +100,17 @@ fn recv_requiring_parens() { Some(ref v) => Some(v), }; } + +fn issue15932() { + let _: Option<&u32> = match Some(0) { + //~^ match_as_ref + None => None, + Some(ref mut v) => Some(v), + }; + + let _: Option<&dyn std::fmt::Debug> = match Some(0) { + //~^ match_as_ref + None => None, + Some(ref mut v) => Some(v), + }; +} diff --git a/tests/ui/match_as_ref.stderr b/tests/ui/match_as_ref.stderr index 226655970ab8f..df06e358f296e 100644 --- a/tests/ui/match_as_ref.stderr +++ b/tests/ui/match_as_ref.stderr @@ -83,5 +83,49 @@ LL - }; LL + let _ = (!S).as_ref(); | -error: aborting due to 4 previous errors +error: manual implementation of `Option::as_mut` + --> tests/ui/match_as_ref.rs:105:27 + | +LL | let _: Option<&u32> = match Some(0) { + | ___________________________^ +LL | | +LL | | None => None, +LL | | Some(ref mut v) => Some(v), +LL | | }; + | |_____^ + | + = note: but the type is coerced to a non-mutable reference, and so `as_ref` can used instead +help: use `Option::as_ref()` + | +LL - let _: Option<&u32> = match Some(0) { +LL - +LL - None => None, +LL - Some(ref mut v) => Some(v), +LL - }; +LL + let _: Option<&u32> = Some(0).as_ref(); + | + +error: manual implementation of `Option::as_mut` + --> tests/ui/match_as_ref.rs:111:43 + | +LL | let _: Option<&dyn std::fmt::Debug> = match Some(0) { + | ___________________________________________^ +LL | | +LL | | None => None, +LL | | Some(ref mut v) => Some(v), +LL | | }; + | |_____^ + | + = note: but the type is coerced to a non-mutable reference, and so `as_ref` can used instead +help: use `Option::as_ref()` + | +LL - let _: Option<&dyn std::fmt::Debug> = match Some(0) { +LL - +LL - None => None, +LL - Some(ref mut v) => Some(v), +LL - }; +LL + let _: Option<&dyn std::fmt::Debug> = Some(0).as_ref().map(|x| x as _); + | + +error: aborting due to 6 previous errors From de63d25a379fd8d8a46b75e134a4cf4367b4bbd1 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Wed, 22 Oct 2025 09:58:23 -0600 Subject: [PATCH 049/108] fix: Don't add diff symbol to unchanged lines --- tests/ui/derivable_impls.stderr | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/ui/derivable_impls.stderr b/tests/ui/derivable_impls.stderr index cd46414cb4a8a..19c7d09512a22 100644 --- a/tests/ui/derivable_impls.stderr +++ b/tests/ui/derivable_impls.stderr @@ -14,7 +14,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct FooDefault<'a> { +LL | struct FooDefault<'a> { | error: this `impl` can be derived @@ -31,7 +31,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct TupleDefault(bool, i32, u64); +LL | struct TupleDefault(bool, i32, u64); | error: this `impl` can be derived @@ -48,7 +48,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct StrDefault<'a>(&'a str); +LL | struct StrDefault<'a>(&'a str); | error: this `impl` can be derived @@ -65,7 +65,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct Y(u32); +LL | struct Y(u32); | error: this `impl` can be derived @@ -82,7 +82,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct WithoutSelfCurly { +LL | struct WithoutSelfCurly { | error: this `impl` can be derived @@ -99,7 +99,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ struct WithoutSelfParan(bool); +LL | struct WithoutSelfParan(bool); | error: this `impl` can be derived @@ -115,7 +115,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ pub struct DirectDefaultDefaultCall { +LL | pub struct DirectDefaultDefaultCall { | error: this `impl` can be derived @@ -131,7 +131,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ pub struct EquivalentToDefaultDefaultCallVec { +LL | pub struct EquivalentToDefaultDefaultCallVec { | error: this `impl` can be derived @@ -147,7 +147,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ pub struct EquivalentToDefaultDefaultCallLocal { +LL | pub struct EquivalentToDefaultDefaultCallLocal { | error: this `impl` can be derived @@ -164,7 +164,7 @@ LL | | } help: replace the manual implementation with a derive attribute | LL + #[derive(Default)] -LL ~ pub struct RepeatDefault1 { +LL | pub struct RepeatDefault1 { | error: this `impl` can be derived @@ -181,7 +181,7 @@ LL | | } help: replace the manual implementation with a derive attribute and mark the default variant | LL + #[derive(Default)] -LL ~ pub enum SimpleEnum { +LL | pub enum SimpleEnum { LL | Foo, LL ~ #[default] LL ~ Bar, From fcfab5fe4b33b9e9c76cd07c88b30502bd082a74 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 10 Oct 2025 17:04:01 -0400 Subject: [PATCH 050/108] `clippy_dev`: Move all parsing within a parse context. --- clippy_dev/src/deprecate_lint.rs | 8 +- clippy_dev/src/lib.rs | 5 +- clippy_dev/src/main.rs | 27 +++-- clippy_dev/src/parse.rs | 190 ++++++++++++++++--------------- clippy_dev/src/rename_lint.rs | 8 +- clippy_dev/src/update_lints.rs | 8 +- clippy_dev/src/utils.rs | 20 ++++ 7 files changed, 151 insertions(+), 115 deletions(-) diff --git a/clippy_dev/src/deprecate_lint.rs b/clippy_dev/src/deprecate_lint.rs index 4d99eb91e6f94..e2602f57f953b 100644 --- a/clippy_dev/src/deprecate_lint.rs +++ b/clippy_dev/src/deprecate_lint.rs @@ -1,4 +1,4 @@ -use crate::parse::{DeprecatedLint, Lint, find_lint_decls, read_deprecated_lints}; +use crate::parse::{DeprecatedLint, Lint, ParseCx}; use crate::update_lints::generate_lint_files; use crate::utils::{UpdateMode, Version}; use std::ffi::OsStr; @@ -14,9 +14,9 @@ use std::{fs, io}; /// # Panics /// /// If a file path could not read from or written to -pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { - let mut lints = find_lint_decls(); - let (mut deprecated_lints, renamed_lints) = read_deprecated_lints(); +pub fn deprecate<'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'cx str, reason: &'cx str) { + let mut lints = cx.find_lint_decls(); + let (mut deprecated_lints, renamed_lints) = cx.read_deprecated_lints(); let Some(lint) = lints.iter().find(|l| l.name == name) else { eprintln!("error: failed to find lint `{name}`"); diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index fb8b2e1c91c1f..16db6fa43dbe7 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -34,7 +34,8 @@ pub mod setup; pub mod sync; pub mod update_lints; +mod parse; mod utils; -pub use utils::{ClippyInfo, UpdateMode}; -mod parse; +pub use self::parse::{ParseCx, new_parse_cx}; +pub use self::utils::{ClippyInfo, UpdateMode}; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 78fb44d7ad1b5..392c3aabf1937 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -4,8 +4,8 @@ use clap::{Args, Parser, Subcommand}; use clippy_dev::{ - ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, - update_lints, + ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, new_parse_cx, release, rename_lint, serve, + setup, sync, update_lints, }; use std::env; @@ -27,7 +27,7 @@ fn main() { allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), DevCommand::Fmt { check } => fmt::run(UpdateMode::from_check(check)), - DevCommand::UpdateLints { check } => update_lints::update(UpdateMode::from_check(check)), + DevCommand::UpdateLints { check } => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::from_check(check))), DevCommand::NewLint { pass, name, @@ -35,7 +35,7 @@ fn main() { r#type, msrv, } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) { - Ok(()) => update_lints::update(UpdateMode::Change), + Ok(()) => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::Change)), Err(e) => eprintln!("Unable to create lint: {e}"), }, DevCommand::Setup(SetupCommand { subcommand }) => match subcommand { @@ -78,13 +78,18 @@ fn main() { old_name, new_name, uplift, - } => rename_lint::rename( - clippy.version, - &old_name, - new_name.as_ref().unwrap_or(&old_name), - uplift, - ), - DevCommand::Deprecate { name, reason } => deprecate_lint::deprecate(clippy.version, &name, &reason), + } => new_parse_cx(|cx| { + rename_lint::rename( + cx, + clippy.version, + &old_name, + new_name.as_ref().unwrap_or(&old_name), + uplift, + ); + }), + DevCommand::Deprecate { name, reason } => { + new_parse_cx(|cx| deprecate_lint::deprecate(cx, clippy.version, &name, &reason)); + }, DevCommand::Sync(SyncCommand { subcommand }) => match subcommand { SyncSubcommand::UpdateNightly => sync::update_nightly(), }, diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index 5cea73d34af55..5c89f83dbd6af 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -1,12 +1,20 @@ pub mod cursor; use self::cursor::{Capture, Cursor}; -use crate::utils::{ErrAction, File, expect_action}; +use crate::utils::{ErrAction, File, Scoped, expect_action}; use core::range::Range; use std::fs; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; +pub struct ParseCxImpl; +pub type ParseCx<'cx> = &'cx mut ParseCxImpl; + +/// Calls the given function inside a newly created parsing context. +pub fn new_parse_cx<'env, T>(f: impl for<'cx> FnOnce(&'cx mut Scoped<'cx, 'env, ParseCxImpl>) -> T) -> T { + f(&mut Scoped::new(ParseCxImpl)) +} + pub struct Lint { pub name: String, pub group: String, @@ -27,33 +35,101 @@ pub struct RenamedLint { pub version: String, } -/// Finds all lint declarations (`declare_clippy_lint!`) -#[must_use] -pub fn find_lint_decls() -> Vec { - let mut lints = Vec::with_capacity(1000); - let mut contents = String::new(); - for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { - let e = expect_action(e, ErrAction::Read, "."); - if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() { - continue; +impl ParseCxImpl { + /// Finds all lint declarations (`declare_clippy_lint!`) + #[must_use] + pub fn find_lint_decls(&mut self) -> Vec { + let mut lints = Vec::with_capacity(1000); + let mut contents = String::new(); + for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { + let e = expect_action(e, ErrAction::Read, "."); + if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() { + continue; + } + let Ok(mut name) = e.file_name().into_string() else { + continue; + }; + if name.starts_with("clippy_lints") && name != "clippy_lints_internal" { + name.push_str("/src"); + for (file, module) in read_src_with_module(name.as_ref()) { + parse_clippy_lint_decls( + file.path(), + File::open_read_to_cleared_string(file.path(), &mut contents), + &module, + &mut lints, + ); + } + } } - let Ok(mut name) = e.file_name().into_string() else { - continue; - }; - if name.starts_with("clippy_lints") && name != "clippy_lints_internal" { - name.push_str("/src"); - for (file, module) in read_src_with_module(name.as_ref()) { - parse_clippy_lint_decls( - file.path(), - File::open_read_to_cleared_string(file.path(), &mut contents), - &module, - &mut lints, - ); + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + lints + } + + #[must_use] + pub fn read_deprecated_lints(&mut self) -> (Vec, Vec) { + #[allow(clippy::enum_glob_use)] + use cursor::Pat::*; + #[rustfmt::skip] + static DECL_TOKENS: &[cursor::Pat<'_>] = &[ + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, CloseBracket, + // ("first", "second"), + OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma, + ]; + #[rustfmt::skip] + static DEPRECATED_TOKENS: &[cursor::Pat<'_>] = &[ + // !{ DEPRECATED(DEPRECATED_VERSION) = [ + Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + #[rustfmt::skip] + static RENAMED_TOKENS: &[cursor::Pat<'_>] = &[ + // !{ RENAMED(RENAMED_VERSION) = [ + Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + + let path = "clippy_lints/src/deprecated_lints.rs"; + let mut deprecated = Vec::with_capacity(30); + let mut renamed = Vec::with_capacity(80); + let mut contents = String::new(); + File::open_read_to_cleared_string(path, &mut contents); + + let mut cursor = Cursor::new(&contents); + let mut captures = [Capture::EMPTY; 3]; + + // First instance is the macro definition. + assert!( + cursor.find_ident("declare_with_version").is_some(), + "error reading deprecated lints" + ); + + if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(DEPRECATED_TOKENS, &mut []) { + while cursor.match_all(DECL_TOKENS, &mut captures) { + deprecated.push(DeprecatedLint { + name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), + reason: parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), + }); } + } else { + panic!("error reading deprecated lints"); } + + if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(RENAMED_TOKENS, &mut []) { + while cursor.match_all(DECL_TOKENS, &mut captures) { + renamed.push(RenamedLint { + old_name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), + new_name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), + }); + } + } else { + panic!("error reading renamed lints"); + } + + deprecated.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(&rhs.old_name)); + (deprecated, renamed) } - lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); - lints } /// Reads the source files from the given root directory @@ -116,72 +192,6 @@ fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mu } } -#[must_use] -pub fn read_deprecated_lints() -> (Vec, Vec) { - #[allow(clippy::enum_glob_use)] - use cursor::Pat::*; - #[rustfmt::skip] - static DECL_TOKENS: &[cursor::Pat<'_>] = &[ - // #[clippy::version = "version"] - Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, CloseBracket, - // ("first", "second"), - OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma, - ]; - #[rustfmt::skip] - static DEPRECATED_TOKENS: &[cursor::Pat<'_>] = &[ - // !{ DEPRECATED(DEPRECATED_VERSION) = [ - Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket, - ]; - #[rustfmt::skip] - static RENAMED_TOKENS: &[cursor::Pat<'_>] = &[ - // !{ RENAMED(RENAMED_VERSION) = [ - Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket, - ]; - - let path = "clippy_lints/src/deprecated_lints.rs"; - let mut deprecated = Vec::with_capacity(30); - let mut renamed = Vec::with_capacity(80); - let mut contents = String::new(); - File::open_read_to_cleared_string(path, &mut contents); - - let mut cursor = Cursor::new(&contents); - let mut captures = [Capture::EMPTY; 3]; - - // First instance is the macro definition. - assert!( - cursor.find_ident("declare_with_version").is_some(), - "error reading deprecated lints" - ); - - if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(DEPRECATED_TOKENS, &mut []) { - while cursor.match_all(DECL_TOKENS, &mut captures) { - deprecated.push(DeprecatedLint { - name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), - reason: parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), - version: parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), - }); - } - } else { - panic!("error reading deprecated lints"); - } - - if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(RENAMED_TOKENS, &mut []) { - while cursor.match_all(DECL_TOKENS, &mut captures) { - renamed.push(RenamedLint { - old_name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), - new_name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), - version: parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), - }); - } - } else { - panic!("error reading renamed lints"); - } - - deprecated.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); - renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(&rhs.old_name)); - (deprecated, renamed) -} - /// Removes the line splices and surrounding quotes from a string literal fn parse_str_lit(s: &str) -> String { let (s, is_raw) = if let Some(s) = s.strip_prefix("r") { diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index 207c9b2ff5966..04942cd639d64 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -1,5 +1,5 @@ use crate::parse::cursor::{self, Capture, Cursor}; -use crate::parse::{RenamedLint, find_lint_decls, read_deprecated_lints}; +use crate::parse::{ParseCx, RenamedLint}; use crate::update_lints::generate_lint_files; use crate::utils::{ ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, @@ -26,10 +26,10 @@ use std::path::Path; /// * If `old_name` doesn't name an existing lint. /// * If `old_name` names a deprecated or renamed lint. #[expect(clippy::too_many_lines)] -pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: bool) { +pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str, new_name: &'cx str, uplift: bool) { let mut updater = FileUpdater::default(); - let mut lints = find_lint_decls(); - let (deprecated_lints, mut renamed_lints) = read_deprecated_lints(); + let mut lints = cx.find_lint_decls(); + let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); let Ok(lint_idx) = lints.binary_search_by(|x| x.name.as_str().cmp(old_name)) else { panic!("could not find lint `{old_name}`"); diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index ef841891721d7..5d0de45a2e265 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,5 +1,5 @@ use crate::parse::cursor::Cursor; -use crate::parse::{DeprecatedLint, Lint, RenamedLint, find_lint_decls, read_deprecated_lints}; +use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint}; use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; use itertools::Itertools; use std::collections::HashSet; @@ -21,9 +21,9 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht /// # Panics /// /// Panics if a file path could not read from or then written to -pub fn update(update_mode: UpdateMode) { - let lints = find_lint_decls(); - let (deprecated, renamed) = read_deprecated_lints(); +pub fn update(cx: ParseCx<'_>, update_mode: UpdateMode) { + let lints = cx.find_lint_decls(); + let (deprecated, renamed) = cx.read_deprecated_lints(); generate_lint_files(update_mode, &lints, &deprecated, &renamed); } diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 526613a53c77b..c01b05ec17208 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -1,5 +1,7 @@ use core::fmt::{self, Display}; +use core::marker::PhantomData; use core::num::NonZero; +use core::ops::{Deref, DerefMut}; use core::range::Range; use core::str::FromStr; use std::ffi::OsStr; @@ -10,6 +12,24 @@ use std::process::{self, Command, Stdio}; use std::{env, thread}; use walkdir::WalkDir; +pub struct Scoped<'inner, 'outer: 'inner, T>(T, PhantomData<&'inner mut T>, PhantomData<&'outer mut ()>); +impl Scoped<'_, '_, T> { + pub fn new(value: T) -> Self { + Self(value, PhantomData, PhantomData) + } +} +impl Deref for Scoped<'_, '_, T> { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for Scoped<'_, '_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + #[derive(Clone, Copy)] pub enum ErrAction { Open, From 7579e71c1fdecd326cb82d8372b8708a7cceca9a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 11 Oct 2025 11:35:08 -0400 Subject: [PATCH 051/108] `clippy_dev`: Inline and simplify `read_src_with_module`. --- clippy_dev/src/fmt.rs | 2 +- clippy_dev/src/parse.rs | 74 +++++++++++++++-------------------- clippy_dev/src/rename_lint.rs | 2 +- clippy_dev/src/utils.rs | 4 +- 4 files changed, 35 insertions(+), 47 deletions(-) diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 2b2138d3108df..781e37e6144ef 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -268,7 +268,7 @@ fn run_rustfmt(update_mode: UpdateMode) { .expect("invalid rustfmt path"); rustfmt_path.truncate(rustfmt_path.trim_end().len()); - let args: Vec<_> = walk_dir_no_dot_or_target() + let args: Vec<_> = walk_dir_no_dot_or_target(".") .filter_map(|e| { let e = expect_action(e, ErrAction::Read, "."); e.path() diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index 5c89f83dbd6af..f151a3c8fee97 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -1,11 +1,10 @@ pub mod cursor; use self::cursor::{Capture, Cursor}; -use crate::utils::{ErrAction, File, Scoped, expect_action}; +use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_target}; use core::range::Range; use std::fs; -use std::path::{Path, PathBuf}; -use walkdir::{DirEntry, WalkDir}; +use std::path::{self, Path, PathBuf}; pub struct ParseCxImpl; pub type ParseCx<'cx> = &'cx mut ParseCxImpl; @@ -43,18 +42,38 @@ impl ParseCxImpl { let mut contents = String::new(); for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { let e = expect_action(e, ErrAction::Read, "."); - if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() { - continue; - } - let Ok(mut name) = e.file_name().into_string() else { + + // Skip if this isn't a lint crate's directory. + let mut crate_path = if expect_action(e.file_type(), ErrAction::Read, ".").is_dir() + && let Ok(crate_path) = e.file_name().into_string() + && crate_path.starts_with("clippy_lints") + && crate_path != "clippy_lints_internal" + { + crate_path + } else { continue; }; - if name.starts_with("clippy_lints") && name != "clippy_lints_internal" { - name.push_str("/src"); - for (file, module) in read_src_with_module(name.as_ref()) { + + crate_path.push(path::MAIN_SEPARATOR); + crate_path.push_str("src"); + for e in walk_dir_no_dot_or_target(&crate_path) { + let e = expect_action(e, ErrAction::Read, &crate_path); + if let Some(path) = e.path().to_str() + && let Some(path) = path.strip_suffix(".rs") + && let Some(path) = path.get(crate_path.len() + 1..) + { + let module = if path == "lib" { + String::new() + } else { + let path = path + .strip_suffix("mod") + .and_then(|x| x.strip_suffix(path::MAIN_SEPARATOR)) + .unwrap_or(path); + path.replace(['/', '\\'], "::") + }; parse_clippy_lint_decls( - file.path(), - File::open_read_to_cleared_string(file.path(), &mut contents), + e.path(), + File::open_read_to_cleared_string(e.path(), &mut contents), &module, &mut lints, ); @@ -132,37 +151,6 @@ impl ParseCxImpl { } } -/// 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 = expect_action(e, ErrAction::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(src_root.as_os_str().len() + 1..) - { - 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 - } - }) -} - /// Parse a source file looking for `declare_clippy_lint` macro invocations. fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mut Vec) { #[allow(clippy::enum_glob_use)] diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index 04942cd639d64..bbe5baa41cce3 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -127,7 +127,7 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str } let mut update_fn = file_update_fn(old_name, new_name, mod_edit); - for e in walk_dir_no_dot_or_target() { + for e in walk_dir_no_dot_or_target(".") { let e = expect_action(e, ErrAction::Read, "."); if e.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { updater.update_file(e.path(), &mut update_fn); diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index c01b05ec17208..52452dd86b490 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -593,8 +593,8 @@ pub fn delete_dir_if_exists(path: &Path) { } /// Walks all items excluding top-level dot files/directories and any target directories. -pub fn walk_dir_no_dot_or_target() -> impl Iterator> { - WalkDir::new(".").into_iter().filter_entry(|e| { +pub fn walk_dir_no_dot_or_target(p: impl AsRef) -> impl Iterator> { + WalkDir::new(p).into_iter().filter_entry(|e| { e.path() .file_name() .is_none_or(|x| x != "target" && x.as_encoded_bytes().first().copied() != Some(b'.')) From bae625fed002328b1aff4dc8ae60b26d5c643d83 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 11 Oct 2025 14:59:59 -0400 Subject: [PATCH 052/108] `clippy_dev`: Allocate onto an arena when parsing. --- clippy_dev/src/deprecate_lint.rs | 15 +- clippy_dev/src/lib.rs | 2 + clippy_dev/src/parse.rs | 251 ++++++++++++++++++++----------- clippy_dev/src/rename_lint.rs | 40 ++--- clippy_dev/src/update_lints.rs | 14 +- 5 files changed, 200 insertions(+), 122 deletions(-) diff --git a/clippy_dev/src/deprecate_lint.rs b/clippy_dev/src/deprecate_lint.rs index e2602f57f953b..0401cfda7080c 100644 --- a/clippy_dev/src/deprecate_lint.rs +++ b/clippy_dev/src/deprecate_lint.rs @@ -23,8 +23,11 @@ pub fn deprecate<'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'cx str, return; }; - let prefixed_name = String::from_iter(["clippy::", name]); - match deprecated_lints.binary_search_by(|x| x.name.cmp(&prefixed_name)) { + let prefixed_name = cx.str_buf.with(|buf| { + buf.extend(["clippy::", name]); + cx.arena.alloc_str(buf) + }); + match deprecated_lints.binary_search_by(|x| x.name.cmp(prefixed_name)) { Ok(_) => { println!("`{name}` is already deprecated"); return; @@ -33,8 +36,8 @@ pub fn deprecate<'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'cx str, idx, DeprecatedLint { name: prefixed_name, - reason: reason.into(), - version: clippy_version.rust_display().to_string(), + reason, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), }, ), } @@ -58,8 +61,8 @@ pub fn deprecate<'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'cx str, } } -fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io::Result { - fn remove_lint(name: &str, lints: &mut Vec) { +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)); } diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 16db6fa43dbe7..dcca08aee7e64 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -5,6 +5,7 @@ new_range_api, os_str_slice, os_string_truncate, + pattern, rustc_private, slice_split_once )] @@ -17,6 +18,7 @@ )] #![allow(clippy::missing_panics_doc)] +extern crate rustc_arena; #[expect(unused_extern_crates, reason = "required to link to rustc crates")] extern crate rustc_driver; extern crate rustc_lexer; diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index f151a3c8fee97..de5caf4e1ef65 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -2,42 +2,109 @@ pub mod cursor; use self::cursor::{Capture, Cursor}; use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_target}; +use core::fmt::{Display, Write as _}; use core::range::Range; +use rustc_arena::DroplessArena; use std::fs; use std::path::{self, Path, PathBuf}; +use std::str::pattern::Pattern; -pub struct ParseCxImpl; -pub type ParseCx<'cx> = &'cx mut ParseCxImpl; +pub struct ParseCxImpl<'cx> { + pub arena: &'cx DroplessArena, + pub str_buf: StrBuf, +} +pub type ParseCx<'cx> = &'cx mut ParseCxImpl<'cx>; /// Calls the given function inside a newly created parsing context. -pub fn new_parse_cx<'env, T>(f: impl for<'cx> FnOnce(&'cx mut Scoped<'cx, 'env, ParseCxImpl>) -> T) -> T { - f(&mut Scoped::new(ParseCxImpl)) +pub fn new_parse_cx<'env, T>(f: impl for<'cx> FnOnce(&'cx mut Scoped<'cx, 'env, ParseCxImpl<'cx>>) -> T) -> T { + let arena = DroplessArena::default(); + f(&mut Scoped::new(ParseCxImpl { + arena: &arena, + str_buf: StrBuf::with_capacity(128), + })) } -pub struct Lint { - pub name: String, - pub group: String, - pub module: String, +/// A string used as a temporary buffer used to avoid allocating for short lived strings. +pub struct StrBuf(String); +impl StrBuf { + /// Creates a new buffer with the specified initial capacity. + pub fn with_capacity(cap: usize) -> Self { + Self(String::with_capacity(cap)) + } + + /// Allocates the result of formatting the given value onto the arena. + pub fn alloc_display<'cx>(&mut self, arena: &'cx DroplessArena, value: impl Display) -> &'cx str { + self.0.clear(); + write!(self.0, "{value}").expect("`Display` impl returned an error"); + arena.alloc_str(&self.0) + } + + /// Allocates the string onto the arena with all ascii characters converted to + /// lowercase. + pub fn alloc_ascii_lower<'cx>(&mut self, arena: &'cx DroplessArena, s: &str) -> &'cx str { + self.0.clear(); + self.0.push_str(s); + self.0.make_ascii_lowercase(); + arena.alloc_str(&self.0) + } + + /// Allocates the result of replacing all instances the pattern with the given string + /// onto the arena. + pub fn alloc_replaced<'cx>( + &mut self, + arena: &'cx DroplessArena, + s: &str, + pat: impl Pattern, + replacement: &str, + ) -> &'cx str { + let mut parts = s.split(pat); + let Some(first) = parts.next() else { + return ""; + }; + self.0.clear(); + self.0.push_str(first); + for part in parts { + self.0.push_str(replacement); + self.0.push_str(part); + } + if self.0.is_empty() { + "" + } else { + arena.alloc_str(&self.0) + } + } + + /// Performs an operation with the freshly cleared buffer. + pub fn with(&mut self, f: impl FnOnce(&mut String) -> T) -> T { + self.0.clear(); + f(&mut self.0) + } +} + +pub struct Lint<'cx> { + pub name: &'cx str, + pub group: &'cx str, + pub module: &'cx str, pub path: PathBuf, pub declaration_range: Range, } -pub struct DeprecatedLint { - pub name: String, - pub reason: String, - pub version: String, +pub struct DeprecatedLint<'cx> { + pub name: &'cx str, + pub reason: &'cx str, + pub version: &'cx str, } -pub struct RenamedLint { - pub old_name: String, - pub new_name: String, - pub version: String, +pub struct RenamedLint<'cx> { + pub old_name: &'cx str, + pub new_name: &'cx str, + pub version: &'cx str, } -impl ParseCxImpl { +impl<'cx> ParseCxImpl<'cx> { /// Finds all lint declarations (`declare_clippy_lint!`) #[must_use] - pub fn find_lint_decls(&mut self) -> Vec { + pub fn find_lint_decls(&mut self) -> Vec> { let mut lints = Vec::with_capacity(1000); let mut contents = String::new(); for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { @@ -63,29 +130,59 @@ impl ParseCxImpl { && let Some(path) = path.get(crate_path.len() + 1..) { let module = if path == "lib" { - String::new() + "" } else { let path = path .strip_suffix("mod") .and_then(|x| x.strip_suffix(path::MAIN_SEPARATOR)) .unwrap_or(path); - path.replace(['/', '\\'], "::") + self.str_buf + .alloc_replaced(self.arena, path, path::MAIN_SEPARATOR, "::") }; - parse_clippy_lint_decls( + self.parse_clippy_lint_decls( e.path(), File::open_read_to_cleared_string(e.path(), &mut contents), - &module, + module, &mut lints, ); } } } - lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); lints } + /// Parse a source file looking for `declare_clippy_lint` macro invocations. + fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx str, lints: &mut Vec>) { + #[allow(clippy::enum_glob_use)] + use cursor::Pat::*; + #[rustfmt::skip] + static DECL_TOKENS: &[cursor::Pat<'_>] = &[ + // !{ /// docs + Bang, OpenBrace, AnyComment, + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, + // pub NAME, GROUP, + Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma, + ]; + + let mut cursor = Cursor::new(contents); + let mut captures = [Capture::EMPTY; 2]; + while let Some(start) = cursor.find_ident("declare_clippy_lint") { + if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) { + lints.push(Lint { + name: self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])), + group: self.arena.alloc_str(cursor.get_text(captures[1])), + module, + path: path.into(), + declaration_range: start as usize..cursor.pos() as usize, + }); + } + } + } + #[must_use] - pub fn read_deprecated_lints(&mut self) -> (Vec, Vec) { + pub fn read_deprecated_lints(&mut self) -> (Vec>, Vec>) { #[allow(clippy::enum_glob_use)] use cursor::Pat::*; #[rustfmt::skip] @@ -124,9 +221,9 @@ impl ParseCxImpl { if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(DEPRECATED_TOKENS, &mut []) { while cursor.match_all(DECL_TOKENS, &mut captures) { deprecated.push(DeprecatedLint { - name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), - reason: parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), - version: parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), + name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), + reason: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), }); } } else { @@ -136,81 +233,53 @@ impl ParseCxImpl { if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(RENAMED_TOKENS, &mut []) { while cursor.match_all(DECL_TOKENS, &mut captures) { renamed.push(RenamedLint { - old_name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), - new_name: parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), - version: parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), + old_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), + new_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), }); } } else { panic!("error reading renamed lints"); } - deprecated.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); - renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(&rhs.old_name)); + deprecated.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); + renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(rhs.old_name)); (deprecated, renamed) } -} -/// Parse a source file looking for `declare_clippy_lint` macro invocations. -fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mut Vec) { - #[allow(clippy::enum_glob_use)] - use cursor::Pat::*; - #[rustfmt::skip] - static DECL_TOKENS: &[cursor::Pat<'_>] = &[ - // !{ /// docs - Bang, OpenBrace, AnyComment, - // #[clippy::version = "version"] - Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, - // pub NAME, GROUP, - Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma, - ]; - - let mut cursor = Cursor::new(contents); - let mut captures = [Capture::EMPTY; 2]; - while let Some(start) = cursor.find_ident("declare_clippy_lint") { - if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) { - lints.push(Lint { - name: cursor.get_text(captures[0]).to_lowercase(), - group: cursor.get_text(captures[1]).into(), - module: module.into(), - path: path.into(), - declaration_range: start as usize..cursor.pos() as usize, - }); + /// Removes the line splices and surrounding quotes from a string literal + fn parse_str_lit(&mut self, s: &str) -> &'cx str { + let (s, is_raw) = if let Some(s) = s.strip_prefix("r") { + (s.trim_matches('#'), true) + } else { + (s, false) + }; + let s = s + .strip_prefix('"') + .and_then(|s| s.strip_suffix('"')) + .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); + + if is_raw { + if s.is_empty() { "" } else { self.arena.alloc_str(s) } + } else { + self.str_buf.with(|buf| { + rustc_literal_escaper::unescape_str(s, &mut |_, ch| { + if let Ok(ch) = ch { + buf.push(ch); + } + }); + if buf.is_empty() { "" } else { self.arena.alloc_str(buf) } + }) } } -} -/// Removes the line splices and surrounding quotes from a string literal -fn parse_str_lit(s: &str) -> String { - let (s, is_raw) = if let Some(s) = s.strip_prefix("r") { - (s.trim_matches('#'), true) - } else { - (s, false) - }; - let s = s - .strip_prefix('"') - .and_then(|s| s.strip_suffix('"')) - .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); - - if is_raw { - s.into() - } else { - let mut res = String::with_capacity(s.len()); - rustc_literal_escaper::unescape_str(s, &mut |_, ch| { - if let Ok(ch) = ch { - res.push(ch); - } - }); - res + fn parse_str_single_line(&mut self, path: &Path, s: &str) -> &'cx str { + let value = self.parse_str_lit(s); + assert!( + !value.contains('\n'), + "error parsing `{}`: `{s}` should be a single line string", + path.display(), + ); + value } } - -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 -} diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index bbe5baa41cce3..8e30eb7ce95b3 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -31,24 +31,30 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str let mut lints = cx.find_lint_decls(); let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); - let Ok(lint_idx) = lints.binary_search_by(|x| x.name.as_str().cmp(old_name)) else { + let Ok(lint_idx) = lints.binary_search_by(|x| x.name.cmp(old_name)) else { panic!("could not find lint `{old_name}`"); }; let lint = &lints[lint_idx]; - let old_name_prefixed = String::from_iter(["clippy::", old_name]); + let old_name_prefixed = cx.str_buf.with(|buf| { + buf.extend(["clippy::", old_name]); + cx.arena.alloc_str(buf) + }); let new_name_prefixed = if uplift { - new_name.to_owned() + new_name } else { - String::from_iter(["clippy::", new_name]) + cx.str_buf.with(|buf| { + buf.extend(["clippy::", new_name]); + cx.arena.alloc_str(buf) + }) }; for lint in &mut renamed_lints { if lint.new_name == old_name_prefixed { - lint.new_name.clone_from(&new_name_prefixed); + lint.new_name = new_name_prefixed; } } - match renamed_lints.binary_search_by(|x| x.old_name.cmp(&old_name_prefixed)) { + match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { Ok(_) => { println!("`{old_name}` already has a rename registered"); return; @@ -58,12 +64,8 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str idx, RenamedLint { old_name: old_name_prefixed, - new_name: if uplift { - new_name.to_owned() - } else { - String::from_iter(["clippy::", new_name]) - }, - version: clippy_version.rust_display().to_string(), + new_name: new_name_prefixed, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), }, ); }, @@ -99,7 +101,7 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str } delete_test_files(old_name, change_prefixed_tests); lints.remove(lint_idx); - } else if lints.binary_search_by(|x| x.name.as_str().cmp(new_name)).is_err() { + } else if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() { let lint = &mut lints[lint_idx]; if lint.module.ends_with(old_name) && lint @@ -113,13 +115,15 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str mod_edit = ModEdit::Rename; } - let mod_len = lint.module.len(); - lint.module.truncate(mod_len - old_name.len()); - lint.module.push_str(new_name); + lint.module = cx.str_buf.with(|buf| { + buf.push_str(&lint.module[..lint.module.len() - old_name.len()]); + buf.push_str(new_name); + cx.arena.alloc_str(buf) + }); } rename_test_files(old_name, new_name, change_prefixed_tests); - new_name.clone_into(&mut lints[lint_idx].name); - lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + lints[lint_idx].name = new_name; + lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); } else { println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); println!("Since `{new_name}` already exists the existing code has not been changed"); diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 5d0de45a2e265..3d0da68461144 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -30,9 +30,9 @@ pub fn update(cx: ParseCx<'_>, update_mode: UpdateMode) { #[expect(clippy::too_many_lines)] pub fn generate_lint_files( update_mode: UpdateMode, - lints: &[Lint], - deprecated: &[DeprecatedLint], - renamed: &[RenamedLint], + lints: &[Lint<'_>], + deprecated: &[DeprecatedLint<'_>], + renamed: &[RenamedLint<'_>], ) { let mut updater = FileUpdater::default(); updater.update_file_checked( @@ -61,7 +61,7 @@ pub fn generate_lint_files( |dst| { for lint in lints .iter() - .map(|l| &*l.name) + .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() @@ -132,13 +132,13 @@ pub fn generate_lint_files( dst.push_str(GENERATED_FILE_COMMENT); dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); for lint in renamed { - if seen_lints.insert(&lint.new_name) { + if seen_lints.insert(lint.new_name) { writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); } } seen_lints.clear(); for lint in renamed { - if seen_lints.insert(&lint.old_name) { + if seen_lints.insert(lint.old_name) { writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); } } @@ -164,7 +164,7 @@ pub fn generate_lint_files( for lint_mod in lints .iter() .filter(|l| !l.module.is_empty()) - .map(|l| l.module.split_once("::").map_or(&*l.module, |x| x.0)) + .map(|l| l.module.split_once("::").map_or(l.module, |x| x.0)) .sorted() .dedup() { From 6026cc9d110ef6ee8970545f36baba3fe1044aa5 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 16 Oct 2025 21:23:43 +0000 Subject: [PATCH 053/108] Replace NullOp::SizeOf and NullOp::AlignOf by lang items. --- clippy_utils/src/qualify_min_const_fn.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 7722bebdbbe0d..90ea2616890a4 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -194,10 +194,7 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, - _, - ) + Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, _) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, cx.tcx); From 26b12a97565e106f72c8c500f2de6b88df5760c1 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Wed, 15 Oct 2025 04:04:36 +0000 Subject: [PATCH 054/108] Retire ast::TyAliasWhereClauses. --- clippy_utils/src/ast_utils/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index b01e160e22972..22b74ab16d615 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -562,7 +562,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { defaultness: ld, ident: li, generics: lg, - where_clauses: _, + after_where_clause: lw, bounds: lb, ty: lt, }), @@ -570,7 +570,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { defaultness: rd, ident: ri, generics: rg, - where_clauses: _, + after_where_clause: rw, bounds: rb, ty: rt, }), @@ -578,6 +578,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { eq_defaultness(*ld, *rd) && eq_id(*li, *ri) && eq_generics(lg, rg) + && over(&lw.predicates, &rw.predicates, eq_where_predicate) && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) }, @@ -645,7 +646,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { defaultness: ld, ident: li, generics: lg, - where_clauses: _, + after_where_clause: lw, bounds: lb, ty: lt, }), @@ -653,7 +654,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { defaultness: rd, ident: ri, generics: rg, - where_clauses: _, + after_where_clause: rw, bounds: rb, ty: rt, }), @@ -661,6 +662,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { eq_defaultness(*ld, *rd) && eq_id(*li, *ri) && eq_generics(lg, rg) + && over(&lw.predicates, &rw.predicates, eq_where_predicate) && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) }, From 9fd359b53e3507bfdd231a7d278482809435c8f8 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 17:54:50 +0200 Subject: [PATCH 055/108] clean-up - introduce `Kind`: a bit more type-safe and hopefully a bit faster --- clippy_lints/src/methods/mod.rs | 4 +- .../src/methods/unnecessary_filter_map.rs | 95 +++++++++++-------- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c9066be51c44b..6415ed177d35a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5162,13 +5162,13 @@ impl Methods { }, (sym::filter_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); - unnecessary_filter_map::check(cx, expr, arg, name); + unnecessary_filter_map::check(cx, expr, arg, unnecessary_filter_map::Kind::FilterMap); filter_map_bool_then::check(cx, expr, arg, call_span); filter_map_identity::check(cx, expr, arg, span); }, (sym::find_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); - unnecessary_filter_map::check(cx, expr, arg, name); + unnecessary_filter_map::check(cx, expr, arg, unnecessary_filter_map::Kind::FindMap); }, (sym::flat_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index d9d642015063e..9d491b3eecccb 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -2,24 +2,18 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; use clippy_utils::sym; -use clippy_utils::ty::is_copy; +use clippy_utils::ty::{is_copy, option_arg_ty}; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; 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::Symbol; +use std::fmt::Display; use super::{UNNECESSARY_FILTER_MAP, UNNECESSARY_FIND_MAP}; -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - arg: &'tcx hir::Expr<'tcx>, - name: Symbol, -) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, kind: Kind) { if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } @@ -45,10 +39,10 @@ pub(super) fn check<'tcx>( let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); let sugg = if !found_filtering { // Check if the closure is .filter_map(|x| Some(x)) - if name == sym::filter_map - && let hir::ExprKind::Call(expr, args) = body.value.kind + if kind.is_filter_map() + && let hir::ExprKind::Call(expr, [arg]) = body.value.kind && expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) - && let hir::ExprKind::Path(_) = args[0].kind + && let hir::ExprKind::Path(_) = arg.kind { span_lint( cx, @@ -58,48 +52,75 @@ pub(super) fn check<'tcx>( ); return; } - if name == sym::filter_map { - "map(..)" - } else { - "map(..).next()" + match kind { + Kind::FilterMap => "map(..)", + Kind::FindMap => "map(..).next()", } } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) { - match cx.typeck_results().expr_ty(body.value).kind() { - ty::Adt(adt, subst) - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) => - { - if name == sym::filter_map { - "filter(..)" - } else { - "find(..)" - } - }, - _ => return, + let ty = cx.typeck_results().expr_ty(body.value); + if option_arg_ty(cx, ty).is_some_and(|t| t == in_ty) { + match kind { + Kind::FilterMap => "filter(..)", + Kind::FindMap => "find(..)", + } + } else { + return; } } else { return; }; span_lint( cx, - if name == sym::filter_map { - UNNECESSARY_FILTER_MAP - } else { - UNNECESSARY_FIND_MAP + match kind { + Kind::FilterMap => UNNECESSARY_FILTER_MAP, + Kind::FindMap => UNNECESSARY_FIND_MAP, }, expr.span, - format!("this `.{name}(..)` can be written more simply using `.{sugg}`"), + format!("this `.{kind}(..)` can be written more simply using `.{sugg}`"), ); } } +#[derive(Clone, Copy)] +pub(super) enum Kind { + FilterMap, + FindMap, +} + +impl Kind { + fn is_filter_map(self) -> bool { + matches!(self, Self::FilterMap) + } +} + +impl Display for Kind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::FilterMap => f.write_str("filter_map"), + Self::FindMap => f.write_str("find_map"), + } + } +} + // returns (found_mapping, found_filtering) fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match expr.kind { + hir::ExprKind::Path(ref path) + if cx + .qpath_res(path, expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { + // None + (false, true) + }, hir::ExprKind::Call(func, args) => { if func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) { if args[0].res_local_id() == Some(arg_id) { + // Some(arg_id) return (false, false); } + // Some(not arg_id) return (true, false); } (true, true) @@ -109,8 +130,10 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc && cx.typeck_results().expr_ty(recv).is_bool() && arg.res_local_id() == Some(arg_id) { + // bool.then_some(arg_id) (false, true) } else { + // bool.then_some(not arg_id) (true, true) } }, @@ -134,14 +157,6 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) }, - hir::ExprKind::Path(ref path) - if cx - .qpath_res(path, expr.hir_id) - .ctor_parent(cx) - .is_lang_item(cx, OptionNone) => - { - (false, true) - }, _ => (true, true), } } From 9145cee02599463c5a4ebf273fd5332eb8c34853 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Oct 2025 17:49:11 +0200 Subject: [PATCH 056/108] make the spans more precise --- clippy_lints/src/methods/mod.rs | 4 ++-- .../src/methods/unnecessary_filter_map.rs | 13 +++++++--- tests/ui/unnecessary_filter_map.stderr | 24 +++++++++---------- tests/ui/unnecessary_find_map.rs | 2 -- tests/ui/unnecessary_find_map.stderr | 20 ++++++++-------- 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6415ed177d35a..2ec375021eb97 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5162,13 +5162,13 @@ impl Methods { }, (sym::filter_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); - unnecessary_filter_map::check(cx, expr, arg, unnecessary_filter_map::Kind::FilterMap); + unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FilterMap); filter_map_bool_then::check(cx, expr, arg, call_span); filter_map_identity::check(cx, expr, arg, span); }, (sym::find_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); - unnecessary_filter_map::check(cx, expr, arg, unnecessary_filter_map::Kind::FindMap); + unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FindMap); }, (sym::flat_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 9d491b3eecccb..7f729ac7ca94e 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -9,11 +9,18 @@ use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; +use rustc_span::Span; use std::fmt::Display; use super::{UNNECESSARY_FILTER_MAP, UNNECESSARY_FIND_MAP}; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, kind: Kind) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + arg: &'tcx hir::Expr<'tcx>, + call_span: Span, + kind: Kind, +) { if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } @@ -47,7 +54,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a span_lint( cx, UNNECESSARY_FILTER_MAP, - expr.span, + call_span, String::from("this call to `.filter_map(..)` is unnecessary"), ); return; @@ -75,7 +82,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a Kind::FilterMap => UNNECESSARY_FILTER_MAP, Kind::FindMap => UNNECESSARY_FIND_MAP, }, - expr.span, + call_span, format!("this `.{kind}(..)` can be written more simply using `.{sugg}`"), ); } diff --git a/tests/ui/unnecessary_filter_map.stderr b/tests/ui/unnecessary_filter_map.stderr index a879633e10f2a..8c33c08c267d5 100644 --- a/tests/ui/unnecessary_filter_map.stderr +++ b/tests/ui/unnecessary_filter_map.stderr @@ -1,17 +1,17 @@ error: this `.filter_map(..)` can be written more simply using `.filter(..)` - --> tests/ui/unnecessary_filter_map.rs:4:13 + --> tests/ui/unnecessary_filter_map.rs:4:20 | LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_filter_map)]` error: this `.filter_map(..)` can be written more simply using `.filter(..)` - --> tests/ui/unnecessary_filter_map.rs:7:13 + --> tests/ui/unnecessary_filter_map.rs:7:20 | LL | let _ = (0..4).filter_map(|x| { - | _____________^ + | ____________________^ LL | | LL | | LL | | if x > 1 { @@ -21,10 +21,10 @@ LL | | }); | |______^ error: this `.filter_map(..)` can be written more simply using `.filter(..)` - --> tests/ui/unnecessary_filter_map.rs:15:13 + --> tests/ui/unnecessary_filter_map.rs:15:20 | LL | let _ = (0..4).filter_map(|x| match x { - | _____________^ + | ____________________^ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), @@ -32,22 +32,22 @@ LL | | }); | |______^ error: this `.filter_map(..)` can be written more simply using `.map(..)` - --> tests/ui/unnecessary_filter_map.rs:21:13 + --> tests/ui/unnecessary_filter_map.rs:21:20 | LL | let _ = (0..4).filter_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call to `.filter_map(..)` is unnecessary - --> tests/ui/unnecessary_filter_map.rs:28:61 + --> tests/ui/unnecessary_filter_map.rs:28:46 | LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - | ^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `.filter_map(..)` can be written more simply using `.filter(..)` - --> tests/ui/unnecessary_filter_map.rs:166:14 + --> tests/ui/unnecessary_filter_map.rs:166:33 | LL | let _x = std::iter::once(1).filter_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/tests/ui/unnecessary_find_map.rs b/tests/ui/unnecessary_find_map.rs index 33ba7074d623b..c5a8937488d98 100644 --- a/tests/ui/unnecessary_find_map.rs +++ b/tests/ui/unnecessary_find_map.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - fn main() { let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None }); //~^ unnecessary_find_map diff --git a/tests/ui/unnecessary_find_map.stderr b/tests/ui/unnecessary_find_map.stderr index 3754a3d99538e..8a269119df220 100644 --- a/tests/ui/unnecessary_find_map.stderr +++ b/tests/ui/unnecessary_find_map.stderr @@ -1,17 +1,17 @@ error: this `.find_map(..)` can be written more simply using `.find(..)` - --> tests/ui/unnecessary_find_map.rs:4:13 + --> tests/ui/unnecessary_find_map.rs:2:20 | LL | let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-find-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_find_map)]` error: this `.find_map(..)` can be written more simply using `.find(..)` - --> tests/ui/unnecessary_find_map.rs:7:13 + --> tests/ui/unnecessary_find_map.rs:5:20 | LL | let _ = (0..4).find_map(|x| { - | _____________^ + | ____________________^ LL | | LL | | LL | | if x > 1 { @@ -21,10 +21,10 @@ LL | | }); | |______^ error: this `.find_map(..)` can be written more simply using `.find(..)` - --> tests/ui/unnecessary_find_map.rs:15:13 + --> tests/ui/unnecessary_find_map.rs:13:20 | LL | let _ = (0..4).find_map(|x| match x { - | _____________^ + | ____________________^ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), @@ -32,16 +32,16 @@ LL | | }); | |______^ error: this `.find_map(..)` can be written more simply using `.map(..).next()` - --> tests/ui/unnecessary_find_map.rs:21:13 + --> tests/ui/unnecessary_find_map.rs:19:20 | LL | let _ = (0..4).find_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `.find_map(..)` can be written more simply using `.find(..)` - --> tests/ui/unnecessary_find_map.rs:33:14 + --> tests/ui/unnecessary_find_map.rs:31:33 | LL | let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 5 previous errors From c238924dad846c34d7345939986490549052f130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 23 Oct 2025 16:10:53 +0000 Subject: [PATCH 057/108] Revert "Auto merge of #146121 - Muscraft:filter-suggestion-parts, r=petrochenkov" This reverts commit 99317ef14d0be42fa4039eea7c5ce50cb4e9aee7, reversing changes made to 9cd272dc85320e85a8c83a1a338870de52c005f3. --- tests/ui/bool_assert_comparison.stderr | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr index 72aa6303a2026..f823f08f31dca 100644 --- a/tests/ui/bool_assert_comparison.stderr +++ b/tests/ui/bool_assert_comparison.stderr @@ -272,8 +272,10 @@ LL | assert_eq!(a!(), true); | help: replace it with `assert!(..)` | -LL - assert_eq!(a!(), true); -LL + assert!(a!()); +LL | true +... +LL | +LL ~ assert!(a!()); | error: used `assert_eq!` with a literal bool @@ -284,8 +286,10 @@ LL | assert_eq!(true, b!()); | help: replace it with `assert!(..)` | -LL - assert_eq!(true, b!()); -LL + assert!(b!()); +LL | true +... +LL | +LL ~ assert!(b!()); | error: used `debug_assert_eq!` with a literal bool From 0cc159480e14bf53b02a0f12f2e26de486fca9c7 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 23 Oct 2025 22:48:27 +0200 Subject: [PATCH 058/108] Fix trait method checking in book --- book/src/development/method_checking.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/book/src/development/method_checking.md b/book/src/development/method_checking.md index cca6d6ae7bf34..f3912f81d859f 100644 --- a/book/src/development/method_checking.md +++ b/book/src/development/method_checking.md @@ -21,14 +21,13 @@ use clippy_utils::sym; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // Check our expr is calling a method with pattern matching - if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind + if let hir::ExprKind::MethodCall(path, _, _, _) = &expr.kind // Check if the name of this method is `our_fancy_method` && path.ident.name == sym::our_fancy_method - // We can check the type of the self argument whenever necessary. - // (It's necessary if we want to check that method is specifically belonging to a specific trait, - // for example, a `map` method could belong to user-defined trait instead of to `Iterator`) + // Check if the method belongs to the `sym::OurFancyTrait` trait. + // (for example, a `map` method could belong to user-defined trait instead of to `Iterator`) // See the next section for more information. - && cx.ty_based_def(self_arg).opt_parent(cx).is_diag_item(cx, sym::OurFancyTrait) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::OurFancyTrait) { println!("`expr` is a method call for `our_fancy_method`"); } @@ -45,6 +44,12 @@ New symbols such as `our_fancy_method` need to be added to the `clippy_utils::sy This module extends the list of symbols already provided by the compiler crates in `rustc_span::sym`. +If a trait defines only one method (such as the `std::ops::Deref` trait, which only has the `deref()` method), +one might be tempted to omit the method name check. This would work, but is not always advisable because: +- If a new method (possibly with a default implementation) were to be added to the trait, there would be a risk of + matching the wrong method. +- Comparing symbols is very cheap and might prevent a more expensive lookup. + ## Checking if a `impl` block implements a method While sometimes we want to check whether a method is being called or not, other From 9eba2cccd897814923b21a1c8665c462334eaaa2 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Thu, 16 Oct 2025 23:52:12 +0800 Subject: [PATCH 059/108] fix: `len_zero` FP on unstable methods --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/len_zero.rs | 228 +++++++++++++++++------------- clippy_lints/src/lib.rs | 2 +- tests/ui/len_zero.fixed | 4 + tests/ui/len_zero.rs | 4 + tests/ui/len_zero_unstable.fixed | 7 + tests/ui/len_zero_unstable.rs | 7 + tests/ui/len_zero_unstable.stderr | 11 ++ 9 files changed, 169 insertions(+), 96 deletions(-) create mode 100644 tests/ui/len_zero_unstable.fixed create mode 100644 tests/ui/len_zero_unstable.rs create mode 100644 tests/ui/len_zero_unstable.stderr diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index b9ecff1fa3643..6569bdabf115a 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -859,6 +859,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`io_other_error`](https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error) * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) * [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants) +* [`len_zero`](https://rust-lang.github.io/rust-clippy/master/index.html#len_zero) * [`lines_filter_map_ok`](https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok) * [`manual_abs_diff`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff) * [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 9993765b4bdc4..2a042e6c3d853 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -748,6 +748,7 @@ define_Conf! { io_other_error, iter_kv_map, legacy_numeric_constants, + len_zero, lines_filter_map_ok, manual_abs_diff, manual_bits, diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 04a8e4739b853..877bd34a732bc 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,4 +1,6 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; @@ -10,12 +12,12 @@ use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, HirId, ImplItem, ImplItemKind, ImplicitSelfKind, - Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy, QPath, TraitItemId, - TyKind, + Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy, QPath, RustcVersion, + StabilityLevel, StableSince, TraitItemId, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, FnSig, Ty}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::kw; use rustc_span::{Ident, Span, Symbol}; @@ -120,7 +122,17 @@ declare_clippy_lint! { "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead" } -declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]); +pub struct LenZero { + msrv: Msrv, +} + +impl_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]); + +impl LenZero { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for LenZero { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -184,7 +196,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { _ => false, } && !expr.span.from_expansion() - && has_is_empty(cx, lt.init) + && has_is_empty(cx, lt.init, self.msrv) { let mut applicability = Applicability::MachineApplicable; @@ -206,7 +218,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::PartialEq) && !expr.span.from_expansion() { - check_empty_expr( + self.check_empty_expr( cx, expr.span, lhs_expr, @@ -226,29 +238,110 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { let actual_span = span_without_enclosing_paren(cx, expr.span); match cmp { BinOpKind::Eq => { - check_cmp(cx, actual_span, left, right, "", 0); // len == 0 - check_cmp(cx, actual_span, right, left, "", 0); // 0 == len + self.check_cmp(cx, actual_span, left, right, "", 0); // len == 0 + self.check_cmp(cx, actual_span, right, left, "", 0); // 0 == len }, BinOpKind::Ne => { - check_cmp(cx, actual_span, left, right, "!", 0); // len != 0 - check_cmp(cx, actual_span, right, left, "!", 0); // 0 != len + self.check_cmp(cx, actual_span, left, right, "!", 0); // len != 0 + self.check_cmp(cx, actual_span, right, left, "!", 0); // 0 != len }, BinOpKind::Gt => { - check_cmp(cx, actual_span, left, right, "!", 0); // len > 0 - check_cmp(cx, actual_span, right, left, "", 1); // 1 > len + self.check_cmp(cx, actual_span, left, right, "!", 0); // len > 0 + self.check_cmp(cx, actual_span, right, left, "", 1); // 1 > len }, BinOpKind::Lt => { - check_cmp(cx, actual_span, left, right, "", 1); // len < 1 - check_cmp(cx, actual_span, right, left, "!", 0); // 0 < len + self.check_cmp(cx, actual_span, left, right, "", 1); // len < 1 + self.check_cmp(cx, actual_span, right, left, "!", 0); // 0 < len }, - BinOpKind::Ge => check_cmp(cx, actual_span, left, right, "!", 1), // len >= 1 - BinOpKind::Le => check_cmp(cx, actual_span, right, left, "!", 1), // 1 <= len + BinOpKind::Ge => self.check_cmp(cx, actual_span, left, right, "!", 1), // len >= 1 + BinOpKind::Le => self.check_cmp(cx, actual_span, right, left, "!", 1), // 1 <= len _ => (), } } } } +impl LenZero { + fn check_cmp( + &self, + cx: &LateContext<'_>, + span: Span, + method: &Expr<'_>, + lit: &Expr<'_>, + op: &str, + compare_to: u32, + ) { + if method.span.from_expansion() { + return; + } + + if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { + // check if we are in an is_empty() method + if parent_item_name(cx, method) == Some(sym::is_empty) { + return; + } + + self.check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to); + } else { + self.check_empty_expr(cx, span, method, lit, op); + } + } + + #[expect(clippy::too_many_arguments)] + fn check_len( + &self, + cx: &LateContext<'_>, + span: Span, + method_name: Symbol, + receiver: &Expr<'_>, + lit: &LitKind, + op: &str, + compare_to: u32, + ) { + if let LitKind::Int(lit, _) = *lit { + // check if length is compared to the specified number + if lit != u128::from(compare_to) { + return; + } + + if method_name == sym::len && has_is_empty(cx, receiver, self.msrv) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + LEN_ZERO, + span, + format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), + format!("using `{op}is_empty` is clearer and more explicit"), + format!( + "{op}{}.is_empty()", + snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0, + ), + applicability, + ); + } + } + } + + fn check_empty_expr(&self, cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) { + if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1, self.msrv) { + let mut applicability = Applicability::MachineApplicable; + + let lit1 = peel_ref_operators(cx, lit1); + let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_paren(); + + span_lint_and_sugg( + cx, + COMPARISON_TO_EMPTY, + span, + "comparison to empty slice", + format!("using `{op}is_empty` is clearer and more explicit"), + format!("{op}{lit_str}.is_empty()"), + applicability, + ); + } + } +} + fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span { let Some(snippet) = span.get_source_text(cx) else { return span; @@ -513,75 +606,6 @@ fn check_for_is_empty( } } -fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { - if method.span.from_expansion() { - return; - } - - if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { - // check if we are in an is_empty() method - if parent_item_name(cx, method) == Some(sym::is_empty) { - return; - } - - check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to); - } else { - check_empty_expr(cx, span, method, lit, op); - } -} - -fn check_len( - cx: &LateContext<'_>, - span: Span, - method_name: Symbol, - receiver: &Expr<'_>, - lit: &LitKind, - op: &str, - compare_to: u32, -) { - if let LitKind::Int(lit, _) = *lit { - // check if length is compared to the specified number - if lit != u128::from(compare_to) { - return; - } - - if method_name == sym::len && has_is_empty(cx, receiver) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - LEN_ZERO, - span, - format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), - format!("using `{op}is_empty` is clearer and more explicit"), - format!( - "{op}{}.is_empty()", - snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0, - ), - applicability, - ); - } - } -} - -fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) { - if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { - let mut applicability = Applicability::MachineApplicable; - - let lit1 = peel_ref_operators(cx, lit1); - let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_paren(); - - span_lint_and_sugg( - cx, - COMPARISON_TO_EMPTY, - span, - "comparison to empty slice", - format!("using `{op}is_empty` is clearer and more explicit"), - format!("{op}{lit_str}.is_empty()"), - applicability, - ); - } -} - fn is_empty_string(expr: &Expr<'_>) -> bool { if let ExprKind::Lit(lit) = expr.kind && let LitKind::Str(lit, _) = lit.node @@ -600,45 +624,59 @@ fn is_empty_array(expr: &Expr<'_>) -> bool { } /// Checks if this type has an `is_empty` method. -fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { +fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) -> bool { /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. - fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { + fn is_is_empty_and_stable(cx: &LateContext<'_>, item: &ty::AssocItem, msrv: Msrv) -> bool { if item.is_fn() { let sig = cx.tcx.fn_sig(item.def_id).skip_binder(); let ty = sig.skip_binder(); ty.inputs().len() == 1 + && cx.tcx.lookup_stability(item.def_id).is_none_or(|stability| { + if let StabilityLevel::Stable { since, .. } = stability.level { + let version = match since { + StableSince::Version(version) => version, + StableSince::Current => RustcVersion::CURRENT, + StableSince::Err(_) => return false, + }; + + msrv.meets(cx, version) + } else { + // Unstable fn, check if the feature is enabled. + cx.tcx.features().enabled(stability.feature) && msrv.current(cx).is_none() + } + }) } else { false } } /// Checks the inherent impl's items for an `is_empty(self)` method. - fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { + fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId, msrv: Msrv) -> bool { cx.tcx.inherent_impls(id).iter().any(|imp| { cx.tcx .associated_items(*imp) .filter_by_name_unhygienic(sym::is_empty) - .any(|item| is_is_empty(cx, item)) + .any(|item| is_is_empty_and_stable(cx, item, msrv)) }) } - fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize) -> bool { + fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize, msrv: Msrv) -> bool { match ty.kind() { ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { cx.tcx .associated_items(principal.def_id()) .filter_by_name_unhygienic(sym::is_empty) - .any(|item| is_is_empty(cx, item)) + .any(|item| is_is_empty_and_stable(cx, item, msrv)) }), - ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), + ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id, msrv), ty::Adt(id, _) => { - has_is_empty_impl(cx, id.did()) + has_is_empty_impl(cx, id.did(), msrv) || (cx.tcx.recursion_limit().value_within_limit(depth) && cx.tcx.get_diagnostic_item(sym::Deref).is_some_and(|deref_id| { implements_trait(cx, ty, deref_id, &[]) && cx .get_associated_type(ty, deref_id, sym::Target) - .is_some_and(|deref_ty| ty_has_is_empty(cx, deref_ty, depth + 1)) + .is_some_and(|deref_ty| ty_has_is_empty(cx, deref_ty, depth + 1, msrv)) })) }, ty::Array(..) | ty::Slice(..) | ty::Str => true, @@ -646,5 +684,5 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } - ty_has_is_empty(cx, cx.typeck_results().expr_ty(expr).peel_refs(), 0) + ty_has_is_empty(cx, cx.typeck_results().expr_ty(expr).peel_refs(), 0, msrv) } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a4ad9424b3eb1..aabeee54c308a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -481,7 +481,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(mut_mut::MutMut::default())); store.register_late_pass(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)); store.register_late_pass(|_| Box::>::default()); - store.register_late_pass(|_| Box::new(len_zero::LenZero)); + store.register_late_pass(move |_| Box::new(len_zero::LenZero::new(conf))); store.register_late_pass(move |_| Box::new(attrs::Attributes::new(conf))); store.register_late_pass(|_| Box::new(blocks_in_conditions::BlocksInConditions)); store.register_late_pass(|_| Box::new(unicode::Unicode)); diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index b8573ef13b0e8..679414f5ea4c9 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -275,3 +275,7 @@ fn no_infinite_recursion() -> bool { // Do not crash while checking if S implements `.is_empty()` S == "" } + +fn issue15890(vertices: &mut dyn ExactSizeIterator) -> bool { + vertices.len() == 0 +} diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index ef3c49c1ab309..a019ea7ca6542 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -275,3 +275,7 @@ fn no_infinite_recursion() -> bool { // Do not crash while checking if S implements `.is_empty()` S == "" } + +fn issue15890(vertices: &mut dyn ExactSizeIterator) -> bool { + vertices.len() == 0 +} diff --git a/tests/ui/len_zero_unstable.fixed b/tests/ui/len_zero_unstable.fixed new file mode 100644 index 0000000000000..8d4e6c2cc006f --- /dev/null +++ b/tests/ui/len_zero_unstable.fixed @@ -0,0 +1,7 @@ +#![warn(clippy::len_zero)] +#![feature(exact_size_is_empty)] + +fn issue15890(vertices: &mut dyn ExactSizeIterator) -> bool { + vertices.is_empty() + //~^ len_zero +} diff --git a/tests/ui/len_zero_unstable.rs b/tests/ui/len_zero_unstable.rs new file mode 100644 index 0000000000000..f59056c5c55b1 --- /dev/null +++ b/tests/ui/len_zero_unstable.rs @@ -0,0 +1,7 @@ +#![warn(clippy::len_zero)] +#![feature(exact_size_is_empty)] + +fn issue15890(vertices: &mut dyn ExactSizeIterator) -> bool { + vertices.len() == 0 + //~^ len_zero +} diff --git a/tests/ui/len_zero_unstable.stderr b/tests/ui/len_zero_unstable.stderr new file mode 100644 index 0000000000000..103ccf3dcbf5a --- /dev/null +++ b/tests/ui/len_zero_unstable.stderr @@ -0,0 +1,11 @@ +error: length comparison to zero + --> tests/ui/len_zero_unstable.rs:5:5 + | +LL | vertices.len() == 0 + | ^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `vertices.is_empty()` + | + = note: `-D clippy::len-zero` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::len_zero)]` + +error: aborting due to 1 previous error + From 21d3b9dad91e0e0340636fb4420a2aba9e704af1 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 24 Oct 2025 20:03:35 +0200 Subject: [PATCH 060/108] split test files for `use_debug` and `print_stdout` --- tests/ui/print.stderr | 56 ----------------------------- tests/ui/print_stdout.rs | 23 ++++++++++++ tests/ui/print_stdout.stderr | 35 ++++++++++++++++++ tests/ui/{print.rs => use_debug.rs} | 19 ++-------- tests/ui/use_debug.stderr | 23 ++++++++++++ 5 files changed, 84 insertions(+), 72 deletions(-) delete mode 100644 tests/ui/print.stderr create mode 100644 tests/ui/print_stdout.rs create mode 100644 tests/ui/print_stdout.stderr rename tests/ui/{print.rs => use_debug.rs} (59%) create mode 100644 tests/ui/use_debug.stderr diff --git a/tests/ui/print.stderr b/tests/ui/print.stderr deleted file mode 100644 index 9dd216bd1449d..0000000000000 --- a/tests/ui/print.stderr +++ /dev/null @@ -1,56 +0,0 @@ -error: use of `Debug`-based formatting - --> tests/ui/print.rs:11:20 - | -LL | write!(f, "{:?}", 43.1415) - | ^^^^ - | - = note: `-D clippy::use-debug` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::use_debug)]` - -error: use of `println!` - --> tests/ui/print.rs:24:5 - | -LL | println!("Hello"); - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::print-stdout` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::print_stdout)]` - -error: use of `print!` - --> tests/ui/print.rs:27:5 - | -LL | print!("Hello"); - | ^^^^^^^^^^^^^^^ - -error: use of `print!` - --> tests/ui/print.rs:30:5 - | -LL | print!("Hello {}", "World"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: use of `print!` - --> tests/ui/print.rs:33:5 - | -LL | print!("Hello {:?}", "World"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: use of `Debug`-based formatting - --> tests/ui/print.rs:33:19 - | -LL | print!("Hello {:?}", "World"); - | ^^^^ - -error: use of `print!` - --> tests/ui/print.rs:37:5 - | -LL | print!("Hello {:#?}", "#orld"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: use of `Debug`-based formatting - --> tests/ui/print.rs:37:19 - | -LL | print!("Hello {:#?}", "#orld"); - | ^^^^^ - -error: aborting due to 8 previous errors - diff --git a/tests/ui/print_stdout.rs b/tests/ui/print_stdout.rs new file mode 100644 index 0000000000000..a379457f18696 --- /dev/null +++ b/tests/ui/print_stdout.rs @@ -0,0 +1,23 @@ +#![expect(clippy::print_literal)] +#![warn(clippy::print_stdout)] + +fn main() { + println!("Hello"); + //~^ print_stdout + + print!("Hello"); + //~^ print_stdout + + print!("Hello {}", "World"); + //~^ print_stdout + + print!("Hello {:?}", "World"); + //~^ print_stdout + + print!("Hello {:#?}", "#orld"); + //~^ print_stdout + + assert_eq!(42, 1337); + + vec![1, 2]; +} diff --git a/tests/ui/print_stdout.stderr b/tests/ui/print_stdout.stderr new file mode 100644 index 0000000000000..e1360e59b4126 --- /dev/null +++ b/tests/ui/print_stdout.stderr @@ -0,0 +1,35 @@ +error: use of `println!` + --> tests/ui/print_stdout.rs:5:5 + | +LL | println!("Hello"); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stdout` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::print_stdout)]` + +error: use of `print!` + --> tests/ui/print_stdout.rs:8:5 + | +LL | print!("Hello"); + | ^^^^^^^^^^^^^^^ + +error: use of `print!` + --> tests/ui/print_stdout.rs:11:5 + | +LL | print!("Hello {}", "World"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `print!` + --> tests/ui/print_stdout.rs:14:5 + | +LL | print!("Hello {:?}", "World"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `print!` + --> tests/ui/print_stdout.rs:17:5 + | +LL | print!("Hello {:#?}", "#orld"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/print.rs b/tests/ui/use_debug.rs similarity index 59% rename from tests/ui/print.rs rename to tests/ui/use_debug.rs index ee3d9dc0de037..ffd744339c850 100644 --- a/tests/ui/print.rs +++ b/tests/ui/use_debug.rs @@ -1,9 +1,7 @@ -#![allow(clippy::print_literal, clippy::write_literal)] -#![warn(clippy::print_stdout, clippy::use_debug)] +#![warn(clippy::use_debug)] use std::fmt::{Debug, Display, Formatter, Result}; -#[allow(dead_code)] struct Foo; impl Display for Foo { @@ -21,22 +19,11 @@ impl Debug for Foo { } fn main() { - println!("Hello"); - //~^ print_stdout - - print!("Hello"); - //~^ print_stdout - - print!("Hello {}", "World"); - //~^ print_stdout - print!("Hello {:?}", "World"); - //~^ print_stdout - //~| use_debug + //~^ use_debug print!("Hello {:#?}", "#orld"); - //~^ print_stdout - //~| use_debug + //~^ use_debug assert_eq!(42, 1337); diff --git a/tests/ui/use_debug.stderr b/tests/ui/use_debug.stderr new file mode 100644 index 0000000000000..85168d3bc3416 --- /dev/null +++ b/tests/ui/use_debug.stderr @@ -0,0 +1,23 @@ +error: use of `Debug`-based formatting + --> tests/ui/use_debug.rs:9:20 + | +LL | write!(f, "{:?}", 43.1415) + | ^^^^ + | + = note: `-D clippy::use-debug` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_debug)]` + +error: use of `Debug`-based formatting + --> tests/ui/use_debug.rs:22:19 + | +LL | print!("Hello {:?}", "World"); + | ^^^^ + +error: use of `Debug`-based formatting + --> tests/ui/use_debug.rs:25:19 + | +LL | print!("Hello {:#?}", "#orld"); + | ^^^^^ + +error: aborting due to 3 previous errors + From 622a77d288c90989541a33a4cb2d85d9db208dd0 Mon Sep 17 00:00:00 2001 From: ceptontech <> Date: Thu, 16 Oct 2025 13:48:13 -0700 Subject: [PATCH 061/108] feat(search_is_some): Fix when the closure spans multiple lines Previously the program only fixed the code when the closure supplied to the method contained only 1 line. This patch removes the restriction. The code already works. This patch only removes the extra check that causes the restriction. The test cases that can now be fixed are moved into the files containing tests cases that can be fixed. The unnecessary check has survived in the code this way. - In Dec 2015, patch a6bd2d06227f, pull request 524. The lint was first added. The program did not support fixing code automatically yet. So the suggested fix was printed as a part of the diagnostic message. When the original code contained multiple lines, the suggested fix was omitted in order to keep the diagnostic message concise. - In May 2019, patch bd0b75f6c3a8, pull request 4049. Logic was added to strip the reference in the closure when the suggested replacement method required it. Because the fix was still only printed when the code contained a single line, the new transformation was only done when the code contained a single line. - In Aug 2019, patch 945d4cf69f7a, pull request 4454. The lint was updated to fix code automatically. Because the fixed code had only been printed in the diagnostic message for a single line, the fix was only added for a single line. - In Nov 2021, patch 092fe209a656, pull request 7463. The logic for transforming the closure was moved into another file. A comment was added saying that it was only good for a single line because it had only been used for a single line. changelog: [`search_is_some`] now fixes code spanning multiple lines --- clippy_lints/src/methods/search_is_some.rs | 122 +++++++-------- clippy_utils/src/sugg.rs | 2 +- tests/ui/search_is_some.rs | 65 -------- tests/ui/search_is_some.stderr | 102 +------------ tests/ui/search_is_some_fixable_none.fixed | 18 +++ tests/ui/search_is_some_fixable_none.rs | 24 +++ tests/ui/search_is_some_fixable_none.stderr | 158 ++++++++++++++------ tests/ui/search_is_some_fixable_some.fixed | 31 ++++ tests/ui/search_is_some_fixable_some.rs | 35 +++++ tests/ui/search_is_some_fixable_some.stderr | 156 ++++++++++++++----- 10 files changed, 398 insertions(+), 315 deletions(-) diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index c9c75f3f38e2d..8732eba6d4e87 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; @@ -34,79 +34,67 @@ pub(super) fn check<'tcx>( { let msg = format!("called `{option_check_method}()` after searching an `Iterator` with `{search_method}`"); let search_snippet = snippet(cx, search_arg.span, ".."); - if search_snippet.lines().count() <= 1 { - // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` - // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` - let mut applicability = Applicability::MachineApplicable; - let any_search_snippet = if search_method == sym::find - && let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind - && let closure_body = cx.tcx.hir_body(body) - && let Some(closure_arg) = closure_body.params.first() - { - if let PatKind::Ref(..) = closure_arg.pat.kind { - Some(search_snippet.replacen('&', "", 1)) - } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind { - // `find()` provides a reference to the item, but `any` does not, - // so we should fix item usages for suggestion - if let Some(closure_sugg) = deref_closure_args(cx, search_arg) { - applicability = closure_sugg.applicability; - Some(closure_sugg.suggestion) - } else { - Some(search_snippet.to_string()) - } + // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` + // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` + let mut applicability = Applicability::MachineApplicable; + let any_search_snippet = if search_method == sym::find + && let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind + && let closure_body = cx.tcx.hir_body(body) + && let Some(closure_arg) = closure_body.params.first() + { + if let PatKind::Ref(..) = closure_arg.pat.kind { + Some(search_snippet.replacen('&', "", 1)) + } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind { + // `find()` provides a reference to the item, but `any` does not, + // so we should fix item usages for suggestion + if let Some(closure_sugg) = deref_closure_args(cx, search_arg) { + applicability = closure_sugg.applicability; + Some(closure_sugg.suggestion) } else { - None + Some(search_snippet.to_string()) } } else { None - }; - // add note if not multi-line - if is_some { - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - msg, - "consider using", - format!( - "any({})", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ), - applicability, - ); - } else { - let iter = snippet(cx, search_recv.span, ".."); - let sugg = if is_receiver_of_method_call(cx, expr) { - format!( - "(!{iter}.any({}))", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ) - } else { - format!( - "!{iter}.any({})", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ) - }; - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - expr.span, - msg, - "consider using", - sugg, - applicability, - ); } } else { - let hint = format!( - "this is more succinctly expressed by calling `any()`{}", - if option_check_method == "is_none" { - " with negation" - } else { - "" - } + None + }; + // add note if not multi-line + if is_some { + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + msg, + "consider using", + format!( + "any({})", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + applicability, + ); + } else { + let iter = snippet(cx, search_recv.span, ".."); + let sugg = if is_receiver_of_method_call(cx, expr) { + format!( + "(!{iter}.any({}))", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ) + } else { + format!( + "!{iter}.any({})", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ) + }; + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + expr.span, + msg, + "consider using", + sugg, + applicability, ); - span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, msg, None, hint); } } // lint if `find()` is called by `String` or `&str` diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 581c2b02839dc..4995bc2d13f74 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -765,7 +765,7 @@ pub struct DerefClosure { /// such as explicit deref and borrowing cases. /// Returns `None` if no such use cases have been triggered in closure body /// -/// note: this only works on single line immutable closures with exactly one input parameter. +/// note: This only works on immutable closures with exactly one input parameter. pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option { if let ExprKind::Closure(&Closure { fn_decl, def_id, body, .. diff --git a/tests/ui/search_is_some.rs b/tests/ui/search_is_some.rs index 802d27449abf6..0a1e0b07d1eb6 100644 --- a/tests/ui/search_is_some.rs +++ b/tests/ui/search_is_some.rs @@ -8,31 +8,6 @@ use option_helpers::IteratorFalsePositives; //@no-rustfix #[rustfmt::skip] fn main() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - let y = &&42; - - - // Check `find().is_some()`, multi-line case. - let _ = v.iter().find(|&x| { - //~^ search_is_some - *x < 0 - } - ).is_some(); - - // Check `position().is_some()`, multi-line case. - let _ = v.iter().position(|&x| { - //~^ search_is_some - x < 0 - } - ).is_some(); - - // Check `rposition().is_some()`, multi-line case. - let _ = v.iter().rposition(|&x| { - //~^ search_is_some - x < 0 - } - ).is_some(); - // Check that we don't lint if the caller is not an `Iterator` or string let falsepos = IteratorFalsePositives { foo: 0 }; let _ = falsepos.find().is_some(); @@ -49,31 +24,6 @@ fn main() { #[rustfmt::skip] fn is_none() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - let y = &&42; - - - // Check `find().is_none()`, multi-line case. - let _ = v.iter().find(|&x| { - //~^ search_is_some - *x < 0 - } - ).is_none(); - - // Check `position().is_none()`, multi-line case. - let _ = v.iter().position(|&x| { - //~^ search_is_some - x < 0 - } - ).is_none(); - - // Check `rposition().is_none()`, multi-line case. - let _ = v.iter().rposition(|&x| { - //~^ search_is_some - x < 0 - } - ).is_none(); - // Check that we don't lint if the caller is not an `Iterator` or string let falsepos = IteratorFalsePositives { foo: 0 }; let _ = falsepos.find().is_none(); @@ -87,18 +37,3 @@ fn is_none() { let _ = (0..1).find(some_closure).is_none(); //~^ search_is_some } - -#[allow(clippy::match_like_matches_macro)] -fn issue15102() { - let values = [None, Some(3)]; - let has_even = values - //~^ search_is_some - .iter() - .find(|v| match v { - Some(x) if x % 2 == 0 => true, - _ => false, - }) - .is_some(); - - println!("{has_even}"); -} diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr index d5412f9011107..ccbab5320ee24 100644 --- a/tests/ui/search_is_some.stderr +++ b/tests/ui/search_is_some.stderr @@ -1,109 +1,17 @@ error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:16:13 - | -LL | let _ = v.iter().find(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` - = note: `-D clippy::search-is-some` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` - -error: called `is_some()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some.rs:23:13 - | -LL | let _ = v.iter().position(|&x| { - | _____________^ -LL | | -LL | | x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` - -error: called `is_some()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some.rs:30:13 - | -LL | let _ = v.iter().rposition(|&x| { - | _____________^ -LL | | -LL | | x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` - -error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:46:20 + --> tests/ui/search_is_some.rs:21:20 | LL | let _ = (0..1).find(some_closure).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(some_closure)` - -error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:57:13 | -LL | let _ = v.iter().find(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).is_none(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` with negation - -error: called `is_none()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some.rs:64:13 - | -LL | let _ = v.iter().position(|&x| { - | _____________^ -LL | | -LL | | x < 0 -LL | | } -LL | | ).is_none(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` with negation - -error: called `is_none()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some.rs:71:13 - | -LL | let _ = v.iter().rposition(|&x| { - | _____________^ -LL | | -LL | | x < 0 -LL | | } -LL | | ).is_none(); - | |______________________________^ - | - = help: this is more succinctly expressed by calling `any()` with negation + = note: `-D clippy::search-is-some` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:87:13 + --> tests/ui/search_is_some.rs:37:13 | LL | let _ = (0..1).find(some_closure).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(some_closure)` -error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:94:20 - | -LL | let has_even = values - | ____________________^ -LL | | -LL | | .iter() -LL | | .find(|v| match v { -... | -LL | | }) -LL | | .is_some(); - | |__________________^ - | - = help: this is more succinctly expressed by calling `any()` - -error: aborting due to 9 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/search_is_some_fixable_none.fixed b/tests/ui/search_is_some_fixable_none.fixed index cc4dbc919d81d..720ed385fb480 100644 --- a/tests/ui/search_is_some_fixable_none.fixed +++ b/tests/ui/search_is_some_fixable_none.fixed @@ -24,14 +24,32 @@ fn main() { let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0); //~^ search_is_some let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1); + // Check `find().is_none()`, multi-line case. + let _ = !v + //~^ search_is_some + .iter().any(|x| { + *x < 0 // + }); // Check `position().is_none()`, single-line case. let _ = !v.iter().any(|&x| x < 0); //~^ search_is_some + // Check `position().is_none()`, multi-line case. + let _ = !v + //~^ search_is_some + .iter().any(|&x| { + x < 0 // + }); // Check `rposition().is_none()`, single-line case. let _ = !v.iter().any(|&x| x < 0); //~^ search_is_some + // Check `rposition().is_none()`, multi-line case. + let _ = !v + //~^ search_is_some + .iter().any(|&x| { + x < 0 // + }); let s1 = String::from("hello world"); let s2 = String::from("world"); diff --git a/tests/ui/search_is_some_fixable_none.rs b/tests/ui/search_is_some_fixable_none.rs index fa31a9ddedc66..1cb5f9c399590 100644 --- a/tests/ui/search_is_some_fixable_none.rs +++ b/tests/ui/search_is_some_fixable_none.rs @@ -27,14 +27,38 @@ fn main() { //~^ search_is_some .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1) .is_none(); + // Check `find().is_none()`, multi-line case. + let _ = v + //~^ search_is_some + .iter() + .find(|&x| { + *x < 0 // + }) + .is_none(); // Check `position().is_none()`, single-line case. let _ = v.iter().position(|&x| x < 0).is_none(); //~^ search_is_some + // Check `position().is_none()`, multi-line case. + let _ = v + //~^ search_is_some + .iter() + .position(|&x| { + x < 0 // + }) + .is_none(); // Check `rposition().is_none()`, single-line case. let _ = v.iter().rposition(|&x| x < 0).is_none(); //~^ search_is_some + // Check `rposition().is_none()`, multi-line case. + let _ = v + //~^ search_is_some + .iter() + .rposition(|&x| { + x < 0 // + }) + .is_none(); let s1 = String::from("hello world"); let s2 = String::from("world"); diff --git a/tests/ui/search_is_some_fixable_none.stderr b/tests/ui/search_is_some_fixable_none.stderr index b079cf7ea361b..909248357169f 100644 --- a/tests/ui/search_is_some_fixable_none.stderr +++ b/tests/ui/search_is_some_fixable_none.stderr @@ -59,92 +59,158 @@ LL | | .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains( LL | | .is_none(); | |__________________^ help: consider using: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` +error: called `is_none()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_none.rs:31:13 + | +LL | let _ = v + | _____________^ +LL | | +LL | | .iter() +LL | | .find(|&x| { +LL | | *x < 0 // +LL | | }) +LL | | .is_none(); + | |__________________^ + | +help: consider using + | +LL ~ let _ = !v +LL + +LL + .iter().any(|x| { +LL + *x < 0 // +LL ~ }); + | + error: called `is_none()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some_fixable_none.rs:32:13 + --> tests/ui/search_is_some_fixable_none.rs:40:13 | LL | let _ = v.iter().position(|&x| x < 0).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|&x| x < 0)` +error: called `is_none()` after searching an `Iterator` with `position` + --> tests/ui/search_is_some_fixable_none.rs:43:13 + | +LL | let _ = v + | _____________^ +LL | | +LL | | .iter() +LL | | .position(|&x| { +LL | | x < 0 // +LL | | }) +LL | | .is_none(); + | |__________________^ + | +help: consider using + | +LL ~ let _ = !v +LL + +LL + .iter().any(|&x| { +LL + x < 0 // +LL ~ }); + | + error: called `is_none()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some_fixable_none.rs:36:13 + --> tests/ui/search_is_some_fixable_none.rs:52:13 | LL | let _ = v.iter().rposition(|&x| x < 0).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|&x| x < 0)` +error: called `is_none()` after searching an `Iterator` with `rposition` + --> tests/ui/search_is_some_fixable_none.rs:55:13 + | +LL | let _ = v + | _____________^ +LL | | +LL | | .iter() +LL | | .rposition(|&x| { +LL | | x < 0 // +LL | | }) +LL | | .is_none(); + | |__________________^ + | +help: consider using + | +LL ~ let _ = !v +LL + +LL + .iter().any(|&x| { +LL + x < 0 // +LL ~ }); + | + error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:43:13 + --> tests/ui/search_is_some_fixable_none.rs:67:13 | LL | let _ = "hello world".find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:45:13 + --> tests/ui/search_is_some_fixable_none.rs:69:13 | LL | let _ = "hello world".find(&s2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains(&s2)` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:47:13 + --> tests/ui/search_is_some_fixable_none.rs:71:13 | LL | let _ = "hello world".find(&s2[2..]).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains(&s2[2..])` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:50:13 + --> tests/ui/search_is_some_fixable_none.rs:74:13 | LL | let _ = s1.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:52:13 + --> tests/ui/search_is_some_fixable_none.rs:76:13 | LL | let _ = s1.find(&s2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains(&s2)` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:54:13 + --> tests/ui/search_is_some_fixable_none.rs:78:13 | LL | let _ = s1.find(&s2[2..]).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains(&s2[2..])` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:57:13 + --> tests/ui/search_is_some_fixable_none.rs:81:13 | LL | let _ = s1[2..].find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:59:13 + --> tests/ui/search_is_some_fixable_none.rs:83:13 | LL | let _ = s1[2..].find(&s2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains(&s2)` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:61:13 + --> tests/ui/search_is_some_fixable_none.rs:85:13 | LL | let _ = s1[2..].find(&s2[2..]).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains(&s2[2..])` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:78:25 + --> tests/ui/search_is_some_fixable_none.rs:102:25 | LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!filter_hand.iter().any(|cc| c == &cc)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:95:30 + --> tests/ui/search_is_some_fixable_none.rs:119:30 | LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!filter_hand.iter().any(|cc| c == cc)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:107:17 + --> tests/ui/search_is_some_fixable_none.rs:131:17 | LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.foo == 1 && v.bar == 2)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:111:17 + --> tests/ui/search_is_some_fixable_none.rs:135:17 | LL | let _ = vfoo | _________________^ @@ -162,55 +228,55 @@ LL ~ .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2); | error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:120:17 + --> tests/ui/search_is_some_fixable_none.rs:144:17 | LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|a| a[0] == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:127:17 + --> tests/ui/search_is_some_fixable_none.rs:151:17 | LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|sub| sub[1..4].len() == 3)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:146:17 + --> tests/ui/search_is_some_fixable_none.rs:170:17 | LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![ppx].iter().any(|ppp_x: &&u32| please(ppp_x))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:148:17 + --> tests/ui/search_is_some_fixable_none.rs:172:17 | LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![String::from("Hey hey")].iter().any(|s| s.len() == 2)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:152:17 + --> tests/ui/search_is_some_fixable_none.rs:176:17 | LL | let _ = v.iter().find(|x| deref_enough(**x)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| deref_enough(*x))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:154:17 + --> tests/ui/search_is_some_fixable_none.rs:178:17 | LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x: &u32| deref_enough(*x))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:158:17 + --> tests/ui/search_is_some_fixable_none.rs:182:17 | LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| arg_no_deref(&x))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:161:17 + --> tests/ui/search_is_some_fixable_none.rs:185:17 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x: &u32| arg_no_deref(&x))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:182:17 + --> tests/ui/search_is_some_fixable_none.rs:206:17 | LL | let _ = vfoo | _________________^ @@ -228,25 +294,25 @@ LL ~ .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] | error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:199:17 + --> tests/ui/search_is_some_fixable_none.rs:223:17 | LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.inner[0].bar == 2)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:205:17 + --> tests/ui/search_is_some_fixable_none.rs:229:17 | LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|x| (**x)[0] == 9)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:219:17 + --> tests/ui/search_is_some_fixable_none.rs:243:17 | LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.by_ref(&v.bar))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:224:17 + --> tests/ui/search_is_some_fixable_none.rs:248:17 | LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] | _________________^ @@ -264,106 +330,106 @@ LL ~ .iter().any(|&&(&x, ref y)| x == *y); | error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:247:17 + --> tests/ui/search_is_some_fixable_none.rs:271:17 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| s[0].is_empty())` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:249:17 + --> tests/ui/search_is_some_fixable_none.rs:273:17 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| test_string_1(&s[0]))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:259:17 + --> tests/ui/search_is_some_fixable_none.rs:283:17 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| fp.field.is_power_of_two())` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:261:17 + --> tests/ui/search_is_some_fixable_none.rs:285:17 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_1(fp.field))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:263:17 + --> tests/ui/search_is_some_fixable_none.rs:287:17 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_2(*fp.field))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:280:17 + --> tests/ui/search_is_some_fixable_none.rs:304:17 | LL | let _ = v.iter().find(|x| **x == 42).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:282:17 + --> tests/ui/search_is_some_fixable_none.rs:306:17 | LL | Foo.bar(v.iter().find(|x| **x == 42).is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:288:9 + --> tests/ui/search_is_some_fixable_none.rs:312:9 | LL | v.iter().find(|x| **x == 42).is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:294:9 + --> tests/ui/search_is_some_fixable_none.rs:318:9 | LL | v.iter().find(|x| **x == 42).is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:300:17 + --> tests/ui/search_is_some_fixable_none.rs:324:17 | LL | let _ = s.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:302:17 + --> tests/ui/search_is_some_fixable_none.rs:326:17 | LL | Foo.bar(s.find("world").is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:305:17 + --> tests/ui/search_is_some_fixable_none.rs:329:17 | LL | let _ = s.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:307:17 + --> tests/ui/search_is_some_fixable_none.rs:331:17 | LL | Foo.bar(s.find("world").is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:313:17 + --> tests/ui/search_is_some_fixable_none.rs:337:17 | LL | let _ = s.find("world").is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:316:17 + --> tests/ui/search_is_some_fixable_none.rs:340:17 | LL | let _ = s.find("world").is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:322:17 + --> tests/ui/search_is_some_fixable_none.rs:346:17 | LL | let _ = s.find("world").is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:325:17 + --> tests/ui/search_is_some_fixable_none.rs:349:17 | LL | let _ = s.find("world").is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` -error: aborting due to 54 previous errors +error: aborting due to 57 previous errors diff --git a/tests/ui/search_is_some_fixable_some.fixed b/tests/ui/search_is_some_fixable_some.fixed index c7a4422f37378..1213fdcf61197 100644 --- a/tests/ui/search_is_some_fixable_some.fixed +++ b/tests/ui/search_is_some_fixable_some.fixed @@ -25,14 +25,35 @@ fn main() { //~^ search_is_some let _ = (1..3) .any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1); + // Check `find().is_some()`, multi-line case. + let _ = v + .iter() + .any(|x| { + //~^ search_is_some + *x < 0 + }); // Check `position().is_some()`, single-line case. let _ = v.iter().any(|&x| x < 0); //~^ search_is_some + // Check `position().is_some()`, multi-line case. + let _ = v + .iter() + .any(|&x| { + //~^ search_is_some + x < 0 + }); // Check `rposition().is_some()`, single-line case. let _ = v.iter().any(|&x| x < 0); //~^ search_is_some + // Check `rposition().is_some()`, multi-line case. + let _ = v + .iter() + .any(|&x| { + //~^ search_is_some + x < 0 + }); let s1 = String::from("hello world"); let s2 = String::from("world"); @@ -290,9 +311,19 @@ mod issue9120 { } } +#[allow(clippy::match_like_matches_macro)] fn issue15102() { let values = [None, Some(3)]; let has_even = values.iter().any(|v| matches!(&v, Some(x) if x % 2 == 0)); //~^ search_is_some println!("{has_even}"); + + let has_even = values + .iter() + .any(|v| match &v { + //~^ search_is_some + Some(x) if x % 2 == 0 => true, + _ => false, + }); + println!("{has_even}"); } diff --git a/tests/ui/search_is_some_fixable_some.rs b/tests/ui/search_is_some_fixable_some.rs index d6b1c67c9718c..4294a39333f20 100644 --- a/tests/ui/search_is_some_fixable_some.rs +++ b/tests/ui/search_is_some_fixable_some.rs @@ -27,14 +27,38 @@ fn main() { .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1) //~^ search_is_some .is_some(); + // Check `find().is_some()`, multi-line case. + let _ = v + .iter() + .find(|&x| { + //~^ search_is_some + *x < 0 + }) + .is_some(); // Check `position().is_some()`, single-line case. let _ = v.iter().position(|&x| x < 0).is_some(); //~^ search_is_some + // Check `position().is_some()`, multi-line case. + let _ = v + .iter() + .position(|&x| { + //~^ search_is_some + x < 0 + }) + .is_some(); // Check `rposition().is_some()`, single-line case. let _ = v.iter().rposition(|&x| x < 0).is_some(); //~^ search_is_some + // Check `rposition().is_some()`, multi-line case. + let _ = v + .iter() + .rposition(|&x| { + //~^ search_is_some + x < 0 + }) + .is_some(); let s1 = String::from("hello world"); let s2 = String::from("world"); @@ -298,9 +322,20 @@ mod issue9120 { } } +#[allow(clippy::match_like_matches_macro)] fn issue15102() { let values = [None, Some(3)]; let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some(); //~^ search_is_some println!("{has_even}"); + + let has_even = values + .iter() + .find(|v| match v { + //~^ search_is_some + Some(x) if x % 2 == 0 => true, + _ => false, + }) + .is_some(); + println!("{has_even}"); } diff --git a/tests/ui/search_is_some_fixable_some.stderr b/tests/ui/search_is_some_fixable_some.stderr index 551a670d937f2..cee1eb08876ba 100644 --- a/tests/ui/search_is_some_fixable_some.stderr +++ b/tests/ui/search_is_some_fixable_some.stderr @@ -58,92 +58,149 @@ LL | | LL | | .is_some(); | |__________________^ help: consider using: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some.rs:33:10 + | +LL | .find(|&x| { + | __________^ +LL | | +LL | | *x < 0 +LL | | }) +LL | | .is_some(); + | |__________________^ + | +help: consider using + | +LL ~ .any(|x| { +LL + +LL + *x < 0 +LL ~ }); + | + error: called `is_some()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some_fixable_some.rs:32:22 + --> tests/ui/search_is_some_fixable_some.rs:40:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)` +error: called `is_some()` after searching an `Iterator` with `position` + --> tests/ui/search_is_some_fixable_some.rs:45:10 + | +LL | .position(|&x| { + | __________^ +LL | | +LL | | x < 0 +LL | | }) +LL | | .is_some(); + | |__________________^ + | +help: consider using + | +LL ~ .any(|&x| { +LL + +LL + x < 0 +LL ~ }); + | + error: called `is_some()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some_fixable_some.rs:36:22 + --> tests/ui/search_is_some_fixable_some.rs:52:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)` +error: called `is_some()` after searching an `Iterator` with `rposition` + --> tests/ui/search_is_some_fixable_some.rs:57:10 + | +LL | .rposition(|&x| { + | __________^ +LL | | +LL | | x < 0 +LL | | }) +LL | | .is_some(); + | |__________________^ + | +help: consider using + | +LL ~ .any(|&x| { +LL + +LL + x < 0 +LL ~ }); + | + error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:42:27 + --> tests/ui/search_is_some_fixable_some.rs:66:27 | LL | let _ = "hello world".find("world").is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:44:27 + --> tests/ui/search_is_some_fixable_some.rs:68:27 | LL | let _ = "hello world".find(&s2).is_some(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:46:27 + --> tests/ui/search_is_some_fixable_some.rs:70:27 | LL | let _ = "hello world".find(&s2[2..]).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:49:16 + --> tests/ui/search_is_some_fixable_some.rs:73:16 | LL | let _ = s1.find("world").is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:51:16 + --> tests/ui/search_is_some_fixable_some.rs:75:16 | LL | let _ = s1.find(&s2).is_some(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:53:16 + --> tests/ui/search_is_some_fixable_some.rs:77:16 | LL | let _ = s1.find(&s2[2..]).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:56:21 + --> tests/ui/search_is_some_fixable_some.rs:80:21 | LL | let _ = s1[2..].find("world").is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:58:21 + --> tests/ui/search_is_some_fixable_some.rs:82:21 | LL | let _ = s1[2..].find(&s2).is_some(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_some.rs:60:21 + --> tests/ui/search_is_some_fixable_some.rs:84:21 | LL | let _ = s1[2..].find(&s2[2..]).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:77:44 + --> tests/ui/search_is_some_fixable_some.rs:101:44 | LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == &cc)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:94:49 + --> tests/ui/search_is_some_fixable_some.rs:118:49 | LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == cc)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:106:29 + --> tests/ui/search_is_some_fixable_some.rs:130:29 | LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.foo == 1 && v.bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:112:14 + --> tests/ui/search_is_some_fixable_some.rs:136:14 | LL | .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2) | ______________^ @@ -152,55 +209,55 @@ LL | | .is_some(); | |______________________^ help: consider using: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:119:29 + --> tests/ui/search_is_some_fixable_some.rs:143:29 | LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|a| a[0] == 42)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:126:29 + --> tests/ui/search_is_some_fixable_some.rs:150:29 | LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|sub| sub[1..4].len() == 3)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:145:30 + --> tests/ui/search_is_some_fixable_some.rs:169:30 | LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|ppp_x: &&u32| please(ppp_x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:147:50 + --> tests/ui/search_is_some_fixable_some.rs:171:50 | LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s.len() == 2)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:151:26 + --> tests/ui/search_is_some_fixable_some.rs:175:26 | LL | let _ = v.iter().find(|x| deref_enough(**x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| deref_enough(*x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:153:26 + --> tests/ui/search_is_some_fixable_some.rs:177:26 | LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| deref_enough(*x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:157:26 + --> tests/ui/search_is_some_fixable_some.rs:181:26 | LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| arg_no_deref(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:160:26 + --> tests/ui/search_is_some_fixable_some.rs:184:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:183:14 + --> tests/ui/search_is_some_fixable_some.rs:207:14 | LL | .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2) | ______________^ @@ -209,25 +266,25 @@ LL | | .is_some(); | |______________________^ help: consider using: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:198:29 + --> tests/ui/search_is_some_fixable_some.rs:222:29 | LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.inner[0].bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:204:29 + --> tests/ui/search_is_some_fixable_some.rs:228:29 | LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| (**x)[0] == 9)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:218:29 + --> tests/ui/search_is_some_fixable_some.rs:242:29 | LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.by_ref(&v.bar))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:225:14 + --> tests/ui/search_is_some_fixable_some.rs:249:14 | LL | .find(|&&&(&x, ref y)| x == *y) | ______________^ @@ -236,64 +293,85 @@ LL | | .is_some(); | |______________________^ help: consider using: `any(|&&(&x, ref y)| x == *y)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:246:26 + --> tests/ui/search_is_some_fixable_some.rs:270:26 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s[0].is_empty())` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:248:26 + --> tests/ui/search_is_some_fixable_some.rs:272:26 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| test_string_1(&s[0]))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:258:26 + --> tests/ui/search_is_some_fixable_some.rs:282:26 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| fp.field.is_power_of_two())` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:260:26 + --> tests/ui/search_is_some_fixable_some.rs:284:26 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_1(fp.field))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:262:26 + --> tests/ui/search_is_some_fixable_some.rs:286:26 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_2(*fp.field))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:278:18 + --> tests/ui/search_is_some_fixable_some.rs:302:18 | LL | v.iter().find(|x: &&u32| func(x)).is_some() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| func(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:288:26 + --> tests/ui/search_is_some_fixable_some.rs:312:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_impl(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:292:26 + --> tests/ui/search_is_some_fixable_some.rs:316:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_dyn(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:296:26 + --> tests/ui/search_is_some_fixable_some.rs:320:26 | LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:303:34 + --> tests/ui/search_is_some_fixable_some.rs:328:34 | LL | let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| matches!(&v, Some(x) if x % 2 == 0))` -error: aborting due to 47 previous errors +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some.rs:334:10 + | +LL | .find(|v| match v { + | __________^ +LL | | +LL | | Some(x) if x % 2 == 0 => true, +LL | | _ => false, +LL | | }) +LL | | .is_some(); + | |__________________^ + | +help: consider using + | +LL ~ .any(|v| match &v { +LL + +LL + Some(x) if x % 2 == 0 => true, +LL + _ => false, +LL ~ }); + | + +error: aborting due to 51 previous errors From aae10d749f98507590c947611e588b0e8f22b05b Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 26 Oct 2025 10:48:34 +0100 Subject: [PATCH 062/108] refactor(double_comparison): clean-up, simplify lint logic - use let-chains - rm macro - add comments explaining linted cases --- .../src/operators/double_comparison.rs | 88 ++++++++++--------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/clippy_lints/src/operators/double_comparison.rs b/clippy_lints/src/operators/double_comparison.rs index 54f50f11e0349..71982023779e0 100644 --- a/clippy_lints/src/operators/double_comparison.rs +++ b/clippy_lints/src/operators/double_comparison.rs @@ -8,46 +8,52 @@ use rustc_span::Span; use super::DOUBLE_COMPARISONS; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) { - let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) { - (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => { - (lb.node, llhs, lrhs, rb.node, rlhs, rrhs) - }, - _ => return, - }; - if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) { - return; - } - macro_rules! lint_double_comparison { - ($op:tt) => {{ - let mut applicability = Applicability::MachineApplicable; - let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability); - let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability); - let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str); - span_lint_and_sugg( - cx, - DOUBLE_COMPARISONS, - span, - "this binary expression can be simplified", - "try", - sugg, - applicability, - ); - }}; - } - match (op, lkind, rkind) { - (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => { - lint_double_comparison!(<=); - }, - (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => { - lint_double_comparison!(>=); - }, - (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => { - lint_double_comparison!(!=); - }, - (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => { - lint_double_comparison!(==); - }, - _ => (), +pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>, span: Span) { + if let ExprKind::Binary(lop, llhs, lrhs) = lhs.kind + && let ExprKind::Binary(rop, rlhs, rrhs) = rhs.kind + && eq_expr_value(cx, llhs, rlhs) + && eq_expr_value(cx, lrhs, rrhs) + { + let op = match (op, lop.node, rop.node) { + // x == y || x < y => x <= y + (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) + // x < y || x == y => x <= y + | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => { + "<=" + }, + // x == y || x > y => x >= y + (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) + // x > y || x == y => x >= y + | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => { + ">=" + }, + // x < y || x > y => x != y + (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) + // x > y || x < y => x != y + | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => { + "!=" + }, + // x <= y && x >= y => x == y + (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) + // x >= y && x <= y => x == y + | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => { + "==" + }, + _ => return, + }; + + let mut applicability = Applicability::MachineApplicable; + let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability); + let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability); + let sugg = format!("{lhs_str} {op} {rhs_str}"); + span_lint_and_sugg( + cx, + DOUBLE_COMPARISONS, + span, + "this binary expression can be simplified", + "try", + sugg, + applicability, + ); } } From ce2ef31ed6c66afd77b8758a53bddff3f6878013 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 26 Oct 2025 15:34:23 +0100 Subject: [PATCH 063/108] fix(needless_if): don't expand macro invocations in the suggestion --- clippy_lints/src/needless_if.rs | 5 +++-- tests/ui/needless_if.fixed | 9 +++++++++ tests/ui/needless_if.rs | 9 +++++++++ tests/ui/needless_if.stderr | 14 +++++++++++++- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/needless_if.rs b/clippy_lints/src/needless_if.rs index c90019f6ee161..c550700757bf2 100644 --- a/clippy_lints/src/needless_if.rs +++ b/clippy_lints/src/needless_if.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::If; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::{SpanRangeExt, walk_span_to_context}; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -56,7 +56,8 @@ impl LateLintPass<'_> for NeedlessIf { src.bytes() .all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace()) }) - && let Some(cond_snippet) = cond.span.get_source_text(cx) + && let Some(cond_span) = walk_span_to_context(cond.span, expr.span.ctxt()) + && let Some(cond_snippet) = cond_span.get_source_text(cx) && !is_from_proc_macro(cx, expr) { span_lint_and_sugg( diff --git a/tests/ui/needless_if.fixed b/tests/ui/needless_if.fixed index c839156bed9bd..e371c64638547 100644 --- a/tests/ui/needless_if.fixed +++ b/tests/ui/needless_if.fixed @@ -104,3 +104,12 @@ fn main() { let () = if maybe_side_effect() {}; } + +fn issue15960() -> i32 { + matches!(2, 3); + //~^ needless_if + matches!(2, 3) == (2 * 2 == 5); + //~^ needless_if + + 1 // put something here so that `if` is a statement not an expression +} diff --git a/tests/ui/needless_if.rs b/tests/ui/needless_if.rs index 11103af5c5598..9d73d9104dd92 100644 --- a/tests/ui/needless_if.rs +++ b/tests/ui/needless_if.rs @@ -105,3 +105,12 @@ fn main() { let () = if maybe_side_effect() {}; } + +fn issue15960() -> i32 { + if matches!(2, 3) {} + //~^ needless_if + if matches!(2, 3) == (2 * 2 == 5) {} + //~^ needless_if + + 1 // put something here so that `if` is a statement not an expression +} diff --git a/tests/ui/needless_if.stderr b/tests/ui/needless_if.stderr index 4b56843bd5229..b81a3890afa13 100644 --- a/tests/ui/needless_if.stderr +++ b/tests/ui/needless_if.stderr @@ -74,5 +74,17 @@ error: this `if` branch is empty LL | if true {} | ^^^^^^^^^^ help: you can remove it: `true;` -error: aborting due to 7 previous errors +error: this `if` branch is empty + --> tests/ui/needless_if.rs:110:5 + | +LL | if matches!(2, 3) {} + | ^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `matches!(2, 3);` + +error: this `if` branch is empty + --> tests/ui/needless_if.rs:112:5 + | +LL | if matches!(2, 3) == (2 * 2 == 5) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `matches!(2, 3) == (2 * 2 == 5);` + +error: aborting due to 9 previous errors From 0ba9ba7db86b3f231ee53ca2bcd4f8496baa2acd Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 26 Oct 2025 14:46:12 -0700 Subject: [PATCH 064/108] Prepare for mdbook 0.5 The `multilingual` field has been removed in mdbook 0.5. Additionally this removes the unnecessary `src` value which defaults to the same value. --- book/book.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/book/book.toml b/book/book.toml index c918aadf83c4b..ae5fd07487ced 100644 --- a/book/book.toml +++ b/book/book.toml @@ -1,8 +1,6 @@ [book] authors = ["The Rust Clippy Developers"] language = "en" -multilingual = false -src = "src" title = "Clippy Documentation" [rust] From 12b011b809ceb1ae52d0658d3e17b4afbec599d8 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 16 Mar 2025 21:54:14 +0100 Subject: [PATCH 065/108] Lint precedence possible ambiguity between closure and method call `|x: String| {x}.clone()` applies `clone()` to {x}, while `|x: String| -> String {x}.clone()` applies `.clone()` to the closure because a closure with an explicit return type requires a block. This extends the `precedence` lint to suggest using parentheses around the closure definition when the closure would be cloned, as in `(|x: String| -> String {x}).clone()`. --- clippy_lints/src/precedence.rs | 18 ++++++++++++++-- tests/ui/precedence.fixed | 32 +++++++++++++++++++++++++--- tests/ui/precedence.rs | 32 +++++++++++++++++++++++++--- tests/ui/precedence.stderr | 38 +++++++++++++++++++++++++++------- 4 files changed, 104 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index ec6835db897ed..034fe8edc715d 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use rustc_ast::ast::BinOpKind::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub}; use rustc_ast::ast::{BinOpKind, Expr, ExprKind}; @@ -10,7 +10,8 @@ use rustc_span::source_map::Spanned; declare_clippy_lint! { /// ### What it does /// Checks for operations where precedence may be unclear and suggests to add parentheses. - /// It catches a mixed usage of arithmetic and bit shifting/combining operators without parentheses + /// It catches a mixed usage of arithmetic and bit shifting/combining operators, + /// as well as method calls applied to closures. /// /// ### Why is this bad? /// Not everyone knows the precedence of those operators by @@ -109,6 +110,19 @@ impl EarlyLintPass for Precedence { }, _ => (), } + } else if let ExprKind::MethodCall(method_call) = &expr.kind + && let ExprKind::Closure(closure) = &method_call.receiver.kind + { + span_lint_and_then(cx, PRECEDENCE, expr.span, "precedence might not be obvious", |diag| { + diag.multipart_suggestion( + "consider parenthesizing the closure", + vec![ + (closure.fn_decl_span.shrink_to_lo(), String::from("(")), + (closure.body.span.shrink_to_hi(), String::from(")")), + ], + Applicability::MachineApplicable, + ); + }); } } } diff --git a/tests/ui/precedence.fixed b/tests/ui/precedence.fixed index cbb78bff68bff..f0252c4484a5a 100644 --- a/tests/ui/precedence.fixed +++ b/tests/ui/precedence.fixed @@ -1,7 +1,12 @@ #![warn(clippy::precedence)] -#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)] -#![allow(clippy::identity_op)] -#![allow(clippy::eq_op)] +#![allow( + unused_must_use, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::clone_on_copy, + clippy::identity_op, + clippy::eq_op +)] macro_rules! trip { ($a:expr) => { @@ -35,3 +40,24 @@ fn main() { let b = 3; trip!(b * 8); } + +struct W(u8); +impl Clone for W { + fn clone(&self) -> Self { + W(1) + } +} + +fn closure_method_call() { + // Do not lint when the method call is applied to the block, both inside the closure + let f = |x: W| { x }.clone(); + assert!(matches!(f(W(0)), W(1))); + + let f = (|x: W| -> _ { x }).clone(); + assert!(matches!(f(W(0)), W(0))); + //~^^ precedence + + let f = (move |x: W| -> _ { x }).clone(); + assert!(matches!(f(W(0)), W(0))); + //~^^ precedence +} diff --git a/tests/ui/precedence.rs b/tests/ui/precedence.rs index c73a4020e2e51..5d47462114b85 100644 --- a/tests/ui/precedence.rs +++ b/tests/ui/precedence.rs @@ -1,7 +1,12 @@ #![warn(clippy::precedence)] -#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)] -#![allow(clippy::identity_op)] -#![allow(clippy::eq_op)] +#![allow( + unused_must_use, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::clone_on_copy, + clippy::identity_op, + clippy::eq_op +)] macro_rules! trip { ($a:expr) => { @@ -35,3 +40,24 @@ fn main() { let b = 3; trip!(b * 8); } + +struct W(u8); +impl Clone for W { + fn clone(&self) -> Self { + W(1) + } +} + +fn closure_method_call() { + // Do not lint when the method call is applied to the block, both inside the closure + let f = |x: W| { x }.clone(); + assert!(matches!(f(W(0)), W(1))); + + let f = |x: W| -> _ { x }.clone(); + assert!(matches!(f(W(0)), W(0))); + //~^^ precedence + + let f = move |x: W| -> _ { x }.clone(); + assert!(matches!(f(W(0)), W(0))); + //~^^ precedence +} diff --git a/tests/ui/precedence.stderr b/tests/ui/precedence.stderr index 50cd8f4b81464..f086cfe028904 100644 --- a/tests/ui/precedence.stderr +++ b/tests/ui/precedence.stderr @@ -1,5 +1,5 @@ error: operator precedence might not be obvious - --> tests/ui/precedence.rs:16:5 + --> tests/ui/precedence.rs:21:5 | LL | 1 << 2 + 3; | ^^^^^^^^^^ help: consider parenthesizing your expression: `1 << (2 + 3)` @@ -8,40 +8,62 @@ LL | 1 << 2 + 3; = help: to override `-D warnings` add `#[allow(clippy::precedence)]` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:18:5 + --> tests/ui/precedence.rs:23:5 | LL | 1 + 2 << 3; | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 2) << 3` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:20:5 + --> tests/ui/precedence.rs:25:5 | LL | 4 >> 1 + 1; | ^^^^^^^^^^ help: consider parenthesizing your expression: `4 >> (1 + 1)` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:22:5 + --> tests/ui/precedence.rs:27:5 | LL | 1 + 3 >> 2; | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 3) >> 2` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:24:5 + --> tests/ui/precedence.rs:29:5 | LL | 1 ^ 1 - 1; | ^^^^^^^^^ help: consider parenthesizing your expression: `1 ^ (1 - 1)` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:26:5 + --> tests/ui/precedence.rs:31:5 | LL | 3 | 2 - 1; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 | (2 - 1)` error: operator precedence might not be obvious - --> tests/ui/precedence.rs:28:5 + --> tests/ui/precedence.rs:33:5 | LL | 3 & 5 - 2; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)` -error: aborting due to 7 previous errors +error: precedence might not be obvious + --> tests/ui/precedence.rs:56:13 + | +LL | let f = |x: W| -> _ { x }.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider parenthesizing the closure + | +LL | let f = (|x: W| -> _ { x }).clone(); + | + + + +error: precedence might not be obvious + --> tests/ui/precedence.rs:60:13 + | +LL | let f = move |x: W| -> _ { x }.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider parenthesizing the closure + | +LL | let f = (move |x: W| -> _ { x }).clone(); + | + + + +error: aborting due to 9 previous errors From 640e1188f0763e8349e1baa25c9bf8eba4bca727 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 26 Oct 2025 15:47:28 +0100 Subject: [PATCH 066/108] chore(needless_if): rename to `needless_ifs` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/deprecated_lints.rs | 2 + clippy_lints/src/lib.rs | 4 +- .../src/{needless_if.rs => needless_ifs.rs} | 8 +- .../excessive_nesting/excessive_nesting.rs | 2 +- tests/ui/auxiliary/proc_macros.rs | 2 +- tests/ui/blocks_in_conditions.fixed | 2 +- tests/ui/blocks_in_conditions.rs | 2 +- tests/ui/bool_comparison.fixed | 2 +- tests/ui/bool_comparison.rs | 2 +- .../ui/cmp_owned/asymmetric_partial_eq.fixed | 2 +- tests/ui/cmp_owned/asymmetric_partial_eq.rs | 2 +- tests/ui/collapsible_else_if.fixed | 2 +- tests/ui/collapsible_else_if.rs | 2 +- tests/ui/collapsible_if.fixed | 2 +- tests/ui/collapsible_if.rs | 2 +- tests/ui/comparison_to_empty.fixed | 2 +- tests/ui/comparison_to_empty.rs | 2 +- tests/ui/crashes/ice-7169.fixed | 2 +- tests/ui/crashes/ice-7169.rs | 2 +- tests/ui/disallowed_names.rs | 2 +- tests/ui/double_comparison.fixed | 2 +- tests/ui/double_comparison.rs | 2 +- tests/ui/equatable_if_let.fixed | 2 +- tests/ui/equatable_if_let.rs | 2 +- tests/ui/expect_tool_lint_rfc_2383.rs | 2 +- tests/ui/filetype_is_file.rs | 2 +- tests/ui/if_same_then_else2.rs | 2 +- tests/ui/ifs_same_cond.rs | 2 +- tests/ui/len_zero.fixed | 2 +- tests/ui/len_zero.rs | 2 +- tests/ui/manual_float_methods.rs | 2 +- tests/ui/manual_let_else.rs | 2 +- tests/ui/match_overlapping_arm.rs | 2 +- tests/ui/needless_bool/fixable.fixed | 2 +- tests/ui/needless_bool/fixable.rs | 2 +- tests/ui/needless_borrowed_ref.fixed | 2 +- tests/ui/needless_borrowed_ref.rs | 2 +- tests/ui/needless_collect.fixed | 2 +- tests/ui/needless_collect.rs | 2 +- tests/ui/needless_collect_indirect.rs | 2 +- .../{needless_if.fixed => needless_ifs.fixed} | 20 +-- tests/ui/{needless_if.rs => needless_ifs.rs} | 20 +-- ...needless_if.stderr => needless_ifs.stderr} | 22 +-- tests/ui/nonminimal_bool.rs | 2 +- tests/ui/nonminimal_bool_methods.fixed | 2 +- tests/ui/nonminimal_bool_methods.rs | 2 +- tests/ui/op_ref.fixed | 2 +- tests/ui/op_ref.rs | 2 +- tests/ui/panicking_overflow_checks.rs | 2 +- tests/ui/partialeq_to_none.fixed | 2 +- tests/ui/partialeq_to_none.rs | 2 +- ...edundant_pattern_matching_drop_order.fixed | 2 +- .../redundant_pattern_matching_drop_order.rs | 2 +- ...dundant_pattern_matching_if_let_true.fixed | 2 +- .../redundant_pattern_matching_if_let_true.rs | 2 +- .../redundant_pattern_matching_ipaddr.fixed | 2 +- tests/ui/redundant_pattern_matching_ipaddr.rs | 2 +- .../redundant_pattern_matching_option.fixed | 2 +- tests/ui/redundant_pattern_matching_option.rs | 2 +- .../ui/redundant_pattern_matching_poll.fixed | 2 +- tests/ui/redundant_pattern_matching_poll.rs | 2 +- .../redundant_pattern_matching_result.fixed | 2 +- tests/ui/redundant_pattern_matching_result.rs | 2 +- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 158 +++++++++--------- tests/ui/shadow.rs | 2 +- tests/ui/single_match.fixed | 2 +- tests/ui/single_match.rs | 2 +- .../ui/single_match_else_deref_patterns.fixed | 2 +- tests/ui/single_match_else_deref_patterns.rs | 2 +- tests/ui/starts_ends_with.fixed | 2 +- tests/ui/starts_ends_with.rs | 2 +- tests/ui/suspicious_else_formatting.rs | 2 +- tests/ui/suspicious_unary_op_formatting.rs | 2 +- tests/ui/unit_cmp.rs | 2 +- tests/ui/unnecessary_safety_comment.rs | 2 +- tests/ui/unneeded_wildcard_pattern.fixed | 2 +- tests/ui/unneeded_wildcard_pattern.rs | 2 +- tests/ui/unnested_or_patterns.fixed | 2 +- tests/ui/unnested_or_patterns.rs | 2 +- tests/ui/unnested_or_patterns2.fixed | 2 +- tests/ui/unnested_or_patterns2.rs | 2 +- tests/ui/useless_conversion.fixed | 2 +- tests/ui/useless_conversion.rs | 2 +- tests/ui/useless_conversion_try.rs | 2 +- 88 files changed, 204 insertions(+), 191 deletions(-) rename clippy_lints/src/{needless_if.rs => needless_ifs.rs} (95%) rename tests/ui/{needless_if.fixed => needless_ifs.fixed} (89%) rename tests/ui/{needless_if.rs => needless_ifs.rs} (89%) rename tests/ui/{needless_if.stderr => needless_ifs.stderr} (81%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9279267ebf0a2..c04735a495a76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6584,6 +6584,7 @@ Released 2018-09-13 [`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each [`needless_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_if +[`needless_ifs`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_ifs [`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 4af81eb8913bf..67e5b1de17a73 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -544,7 +544,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::needless_continue::NEEDLESS_CONTINUE_INFO, crate::needless_else::NEEDLESS_ELSE_INFO, crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, - crate::needless_if::NEEDLESS_IF_INFO, + crate::needless_ifs::NEEDLESS_IFS_INFO, crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, crate::needless_maybe_sized::NEEDLESS_MAYBE_SIZED_INFO, crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index c6016a863afcc..f010d17917f9e 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -139,6 +139,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), #[clippy::version = "1.80.0"] ("clippy::mismatched_target_os", "unexpected_cfgs"), + #[clippy::version = "1.92.0"] + ("clippy::needless_if", "clippy::needless_ifs"), #[clippy::version = ""] ("clippy::new_without_default_derive", "clippy::new_without_default"), #[clippy::version = ""] diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9821f419b6b00..1d0ebff26e69a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -253,7 +253,7 @@ mod needless_borrows_for_generic_args; mod needless_continue; mod needless_else; mod needless_for_each; -mod needless_if; +mod needless_ifs; mod needless_late_init; mod needless_maybe_sized; mod needless_parens_on_range_literals; @@ -752,7 +752,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(endian_bytes::EndianBytes)); store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations)); store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync)); - store.register_late_pass(|_| Box::new(needless_if::NeedlessIf)); + store.register_late_pass(|_| Box::new(needless_ifs::NeedlessIfs)); store.register_late_pass(move |_| Box::new(min_ident_chars::MinIdentChars::new(conf))); store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))); store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)); diff --git a/clippy_lints/src/needless_if.rs b/clippy_lints/src/needless_ifs.rs similarity index 95% rename from clippy_lints/src/needless_if.rs rename to clippy_lints/src/needless_ifs.rs index c550700757bf2..8ec7e47ccc5bc 100644 --- a/clippy_lints/src/needless_if.rs +++ b/clippy_lints/src/needless_ifs.rs @@ -29,13 +29,13 @@ declare_clippy_lint! { /// really_expensive_condition_with_side_effects(&mut i); /// ``` #[clippy::version = "1.72.0"] - pub NEEDLESS_IF, + pub NEEDLESS_IFS, complexity, "checks for empty if branches" } -declare_lint_pass!(NeedlessIf => [NEEDLESS_IF]); +declare_lint_pass!(NeedlessIfs => [NEEDLESS_IFS]); -impl LateLintPass<'_> for NeedlessIf { +impl LateLintPass<'_> for NeedlessIfs { fn check_stmt<'tcx>(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'tcx>) { if let StmtKind::Expr(expr) = stmt.kind && let Some(If { @@ -62,7 +62,7 @@ impl LateLintPass<'_> for NeedlessIf { { span_lint_and_sugg( cx, - NEEDLESS_IF, + NEEDLESS_IFS, stmt.span, "this `if` branch is empty", "you can remove it", diff --git a/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/tests/ui-toml/excessive_nesting/excessive_nesting.rs index 205cd8ba4ee88..001a6ceb1b17d 100644 --- a/tests/ui-toml/excessive_nesting/excessive_nesting.rs +++ b/tests/ui-toml/excessive_nesting/excessive_nesting.rs @@ -9,7 +9,7 @@ clippy::no_effect, clippy::unnecessary_operation, clippy::never_loop, - clippy::needless_if, + clippy::needless_ifs, clippy::collapsible_if, clippy::blocks_in_conditions, clippy::single_match, diff --git a/tests/ui/auxiliary/proc_macros.rs b/tests/ui/auxiliary/proc_macros.rs index bb55539617fc5..a7c20a787519f 100644 --- a/tests/ui/auxiliary/proc_macros.rs +++ b/tests/ui/auxiliary/proc_macros.rs @@ -1,5 +1,5 @@ #![feature(proc_macro_span)] -#![allow(clippy::needless_if, dead_code)] +#![allow(clippy::needless_ifs, dead_code)] extern crate proc_macro; diff --git a/tests/ui/blocks_in_conditions.fixed b/tests/ui/blocks_in_conditions.fixed index 6ae5b0cb2f041..417ed370e7e5b 100644 --- a/tests/ui/blocks_in_conditions.fixed +++ b/tests/ui/blocks_in_conditions.fixed @@ -4,7 +4,7 @@ #![allow( unused, unnecessary_transmutes, - clippy::needless_if, + clippy::needless_ifs, clippy::missing_transmute_annotations )] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/blocks_in_conditions.rs b/tests/ui/blocks_in_conditions.rs index 3fd060620728f..fd67ad372114d 100644 --- a/tests/ui/blocks_in_conditions.rs +++ b/tests/ui/blocks_in_conditions.rs @@ -4,7 +4,7 @@ #![allow( unused, unnecessary_transmutes, - clippy::needless_if, + clippy::needless_ifs, clippy::missing_transmute_annotations )] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/bool_comparison.fixed b/tests/ui/bool_comparison.fixed index b0b60104c0b91..52564c1cb0922 100644 --- a/tests/ui/bool_comparison.fixed +++ b/tests/ui/bool_comparison.fixed @@ -1,4 +1,4 @@ -#![allow(non_local_definitions, clippy::needless_if)] +#![allow(non_local_definitions, clippy::needless_ifs)] #![warn(clippy::bool_comparison)] #![allow(clippy::non_canonical_partial_ord_impl)] diff --git a/tests/ui/bool_comparison.rs b/tests/ui/bool_comparison.rs index 1b1108d6ce504..a78cf167e84b2 100644 --- a/tests/ui/bool_comparison.rs +++ b/tests/ui/bool_comparison.rs @@ -1,4 +1,4 @@ -#![allow(non_local_definitions, clippy::needless_if)] +#![allow(non_local_definitions, clippy::needless_ifs)] #![warn(clippy::bool_comparison)] #![allow(clippy::non_canonical_partial_ord_impl)] diff --git a/tests/ui/cmp_owned/asymmetric_partial_eq.fixed b/tests/ui/cmp_owned/asymmetric_partial_eq.fixed index 8d543b0422009..2e57078d8e169 100644 --- a/tests/ui/cmp_owned/asymmetric_partial_eq.fixed +++ b/tests/ui/cmp_owned/asymmetric_partial_eq.fixed @@ -1,6 +1,6 @@ #![allow( unused, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_clone, clippy::derive_partial_eq_without_eq )] // See #5700 diff --git a/tests/ui/cmp_owned/asymmetric_partial_eq.rs b/tests/ui/cmp_owned/asymmetric_partial_eq.rs index 6da311c50ee7b..7040d8a41030d 100644 --- a/tests/ui/cmp_owned/asymmetric_partial_eq.rs +++ b/tests/ui/cmp_owned/asymmetric_partial_eq.rs @@ -1,6 +1,6 @@ #![allow( unused, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_clone, clippy::derive_partial_eq_without_eq )] // See #5700 diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index da958f76a5ca0..e7439beef186c 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)] +#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_ifs)] #![warn(clippy::collapsible_if, clippy::collapsible_else_if)] #[rustfmt::skip] diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 06af49f2f6f3b..434ba3654f983 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -1,4 +1,4 @@ -#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)] +#![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_ifs)] #![warn(clippy::collapsible_if, clippy::collapsible_else_if)] #[rustfmt::skip] diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index ca9d02ff2d4f8..6e3fd0f78ddfd 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -1,7 +1,7 @@ #![allow( clippy::assertions_on_constants, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::nonminimal_bool, clippy::eq_op, clippy::redundant_pattern_matching diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index 9ac68ecd4cac0..666252a526547 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -1,7 +1,7 @@ #![allow( clippy::assertions_on_constants, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::nonminimal_bool, clippy::eq_op, clippy::redundant_pattern_matching diff --git a/tests/ui/comparison_to_empty.fixed b/tests/ui/comparison_to_empty.fixed index 7a71829dd62cc..4c3b6004e8006 100644 --- a/tests/ui/comparison_to_empty.fixed +++ b/tests/ui/comparison_to_empty.fixed @@ -1,5 +1,5 @@ #![warn(clippy::comparison_to_empty)] -#![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] +#![allow(clippy::borrow_deref_ref, clippy::needless_ifs, clippy::useless_vec)] fn main() { // Disallow comparisons to empty diff --git a/tests/ui/comparison_to_empty.rs b/tests/ui/comparison_to_empty.rs index 5d213a09e8126..0d4bbe4abf8a0 100644 --- a/tests/ui/comparison_to_empty.rs +++ b/tests/ui/comparison_to_empty.rs @@ -1,5 +1,5 @@ #![warn(clippy::comparison_to_empty)] -#![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] +#![allow(clippy::borrow_deref_ref, clippy::needless_ifs, clippy::useless_vec)] fn main() { // Disallow comparisons to empty diff --git a/tests/ui/crashes/ice-7169.fixed b/tests/ui/crashes/ice-7169.fixed index 71a40ad7de71a..4392e5d4c028b 100644 --- a/tests/ui/crashes/ice-7169.fixed +++ b/tests/ui/crashes/ice-7169.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #[derive(Default)] struct A { diff --git a/tests/ui/crashes/ice-7169.rs b/tests/ui/crashes/ice-7169.rs index d43e2cc164d75..a2aa1bdd5275e 100644 --- a/tests/ui/crashes/ice-7169.rs +++ b/tests/ui/crashes/ice-7169.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #[derive(Default)] struct A { diff --git a/tests/ui/disallowed_names.rs b/tests/ui/disallowed_names.rs index 15bb67349976d..331cccef9f932 100644 --- a/tests/ui/disallowed_names.rs +++ b/tests/ui/disallowed_names.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![allow( dead_code, - clippy::needless_if, + clippy::needless_ifs, clippy::similar_names, clippy::single_match, clippy::toplevel_ref_arg, diff --git a/tests/ui/double_comparison.fixed b/tests/ui/double_comparison.fixed index 685e3319bf9af..0680eb35ef974 100644 --- a/tests/ui/double_comparison.fixed +++ b/tests/ui/double_comparison.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] fn main() { let x = 1; diff --git a/tests/ui/double_comparison.rs b/tests/ui/double_comparison.rs index 3670a050e88d1..18ab7d2c42546 100644 --- a/tests/ui/double_comparison.rs +++ b/tests/ui/double_comparison.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] fn main() { let x = 1; diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index ce8b67f9ca7b0..58fbad64a78da 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -4,7 +4,7 @@ unused_variables, dead_code, clippy::derive_partial_eq_without_eq, - clippy::needless_if + clippy::needless_ifs )] #![warn(clippy::equatable_if_let)] diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index ff09533f26519..cca97c76b5094 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -4,7 +4,7 @@ unused_variables, dead_code, clippy::derive_partial_eq_without_eq, - clippy::needless_if + clippy::needless_ifs )] #![warn(clippy::equatable_if_let)] diff --git a/tests/ui/expect_tool_lint_rfc_2383.rs b/tests/ui/expect_tool_lint_rfc_2383.rs index 2295691c81272..82ac4db172d88 100644 --- a/tests/ui/expect_tool_lint_rfc_2383.rs +++ b/tests/ui/expect_tool_lint_rfc_2383.rs @@ -10,7 +10,7 @@ //! This test can't cover every lint from Clippy, rustdoc and potentially other //! tools that will be developed. This therefore only tests a small subset of lints #![expect(rustdoc::missing_crate_level_docs)] -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] mod rustc_ok { //! See diff --git a/tests/ui/filetype_is_file.rs b/tests/ui/filetype_is_file.rs index 8ca01b91210fb..2ec5b3b81446d 100644 --- a/tests/ui/filetype_is_file.rs +++ b/tests/ui/filetype_is_file.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #![warn(clippy::filetype_is_file)] fn main() -> std::io::Result<()> { diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index 5b74aecdacbe5..6ac5fe6e7b543 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -5,7 +5,7 @@ clippy::equatable_if_let, clippy::collapsible_if, clippy::ifs_same_cond, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_return, clippy::single_element_loop, clippy::branches_sharing_code diff --git a/tests/ui/ifs_same_cond.rs b/tests/ui/ifs_same_cond.rs index 7067434953d6e..486903f653de6 100644 --- a/tests/ui/ifs_same_cond.rs +++ b/tests/ui/ifs_same_cond.rs @@ -1,5 +1,5 @@ #![warn(clippy::ifs_same_cond)] -#![allow(clippy::if_same_then_else, clippy::needless_if, clippy::needless_else)] // all empty blocks +#![allow(clippy::if_same_then_else, clippy::needless_ifs, clippy::needless_else)] // all empty blocks fn ifs_same_cond() { let a = 0; diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index 679414f5ea4c9..50b96f7712d71 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -2,7 +2,7 @@ #![allow( dead_code, unused, - clippy::needless_if, + clippy::needless_ifs, clippy::len_without_is_empty, clippy::const_is_empty )] diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index a019ea7ca6542..a6acc2be714bc 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -2,7 +2,7 @@ #![allow( dead_code, unused, - clippy::needless_if, + clippy::needless_ifs, clippy::len_without_is_empty, clippy::const_is_empty )] diff --git a/tests/ui/manual_float_methods.rs b/tests/ui/manual_float_methods.rs index 4b496a4932833..3cd469fd43a0d 100644 --- a/tests/ui/manual_float_methods.rs +++ b/tests/ui/manual_float_methods.rs @@ -1,6 +1,6 @@ //@no-rustfix: overlapping suggestions //@aux-build:proc_macros.rs -#![allow(clippy::needless_if, unused)] +#![allow(clippy::needless_ifs, unused)] #![warn(clippy::manual_is_infinite, clippy::manual_is_finite)] // FIXME(f16_f128): add tests for these types once constants are available diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index 160e321d9a1e7..4523edec3c764 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -6,7 +6,7 @@ clippy::let_unit_value, clippy::match_single_binding, clippy::never_loop, - clippy::needless_if, + clippy::needless_ifs, clippy::diverging_sub_expression, clippy::single_match, clippy::manual_unwrap_or_default diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 176287e3d2e0c..224cac055f251 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -1,6 +1,6 @@ #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] -#![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_if)] +#![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_ifs)] fn overlapping() { const FOO: u64 = 2; diff --git a/tests/ui/needless_bool/fixable.fixed b/tests/ui/needless_bool/fixable.fixed index 0664abf0944dc..2589819f019b8 100644 --- a/tests/ui/needless_bool/fixable.fixed +++ b/tests/ui/needless_bool/fixable.fixed @@ -5,7 +5,7 @@ clippy::no_effect, clippy::if_same_then_else, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_return, clippy::self_named_constructors, clippy::struct_field_names diff --git a/tests/ui/needless_bool/fixable.rs b/tests/ui/needless_bool/fixable.rs index 7507a6af408bb..f9cc0122f5e7b 100644 --- a/tests/ui/needless_bool/fixable.rs +++ b/tests/ui/needless_bool/fixable.rs @@ -5,7 +5,7 @@ clippy::no_effect, clippy::if_same_then_else, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_return, clippy::self_named_constructors, clippy::struct_field_names diff --git a/tests/ui/needless_borrowed_ref.fixed b/tests/ui/needless_borrowed_ref.fixed index 84924cac62d52..6d74899218124 100644 --- a/tests/ui/needless_borrowed_ref.fixed +++ b/tests/ui/needless_borrowed_ref.fixed @@ -4,7 +4,7 @@ irrefutable_let_patterns, non_shorthand_field_patterns, clippy::needless_borrow, - clippy::needless_if + clippy::needless_ifs )] fn main() {} diff --git a/tests/ui/needless_borrowed_ref.rs b/tests/ui/needless_borrowed_ref.rs index 280cef43340cc..a4cb899231647 100644 --- a/tests/ui/needless_borrowed_ref.rs +++ b/tests/ui/needless_borrowed_ref.rs @@ -4,7 +4,7 @@ irrefutable_let_patterns, non_shorthand_field_patterns, clippy::needless_borrow, - clippy::needless_if + clippy::needless_ifs )] fn main() {} diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index b09efe9888f50..842d77dbc8c50 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -1,6 +1,6 @@ #![allow( unused, - clippy::needless_if, + clippy::needless_ifs, clippy::suspicious_map, clippy::iter_count, clippy::manual_contains diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index da4182966bb17..98d8d27321d24 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -1,6 +1,6 @@ #![allow( unused, - clippy::needless_if, + clippy::needless_ifs, clippy::suspicious_map, clippy::iter_count, clippy::manual_contains diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index fff6d2f34b8e3..69764becfe666 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args, clippy::useless_vec, clippy::needless_if)] +#![allow(clippy::uninlined_format_args, clippy::useless_vec, clippy::needless_ifs)] #![warn(clippy::needless_collect)] //@no-rustfix use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; diff --git a/tests/ui/needless_if.fixed b/tests/ui/needless_ifs.fixed similarity index 89% rename from tests/ui/needless_if.fixed rename to tests/ui/needless_ifs.fixed index e371c64638547..0e0b0fa39c9b7 100644 --- a/tests/ui/needless_if.fixed +++ b/tests/ui/needless_ifs.fixed @@ -12,7 +12,7 @@ clippy::redundant_pattern_matching, unused )] -#![warn(clippy::needless_if)] +#![warn(clippy::needless_ifs)] extern crate proc_macros; use proc_macros::{external, with_span}; @@ -24,16 +24,16 @@ fn maybe_side_effect() -> bool { fn main() { // Lint - //~^ needless_if + //~^ needless_ifs // Do not remove the condition maybe_side_effect(); - //~^ needless_if + //~^ needless_ifs // Do not lint if (true) { } else { } ({ - //~^ needless_if + //~^ needless_ifs return; }); // Do not lint if `else if` is present @@ -48,7 +48,7 @@ fn main() { if true && let true = true {} // Can lint nested `if let`s ({ - //~^ needless_if + //~^ needless_ifs if let true = true && true { @@ -92,24 +92,24 @@ fn main() { // Must be placed into an expression context to not be interpreted as a block ({ maybe_side_effect() }); - //~^ needless_if + //~^ needless_ifs // Would be a block followed by `&&true` - a double reference to `true` ({ maybe_side_effect() } && true); - //~^ needless_if + //~^ needless_ifs // Don't leave trailing attributes #[allow(unused)] true; - //~^ needless_if + //~^ needless_ifs let () = if maybe_side_effect() {}; } fn issue15960() -> i32 { matches!(2, 3); - //~^ needless_if + //~^ needless_ifs matches!(2, 3) == (2 * 2 == 5); - //~^ needless_if + //~^ needless_ifs 1 // put something here so that `if` is a statement not an expression } diff --git a/tests/ui/needless_if.rs b/tests/ui/needless_ifs.rs similarity index 89% rename from tests/ui/needless_if.rs rename to tests/ui/needless_ifs.rs index 9d73d9104dd92..fb0ee5c9cc832 100644 --- a/tests/ui/needless_if.rs +++ b/tests/ui/needless_ifs.rs @@ -12,7 +12,7 @@ clippy::redundant_pattern_matching, unused )] -#![warn(clippy::needless_if)] +#![warn(clippy::needless_ifs)] extern crate proc_macros; use proc_macros::{external, with_span}; @@ -24,16 +24,16 @@ fn maybe_side_effect() -> bool { fn main() { // Lint if (true) {} - //~^ needless_if + //~^ needless_ifs // Do not remove the condition if maybe_side_effect() {} - //~^ needless_if + //~^ needless_ifs // Do not lint if (true) { } else { } if { - //~^ needless_if + //~^ needless_ifs return; } {} // Do not lint if `else if` is present @@ -48,7 +48,7 @@ fn main() { if true && let true = true {} // Can lint nested `if let`s if { - //~^ needless_if + //~^ needless_ifs if let true = true && true { @@ -93,24 +93,24 @@ fn main() { // Must be placed into an expression context to not be interpreted as a block if { maybe_side_effect() } {} - //~^ needless_if + //~^ needless_ifs // Would be a block followed by `&&true` - a double reference to `true` if { maybe_side_effect() } && true {} - //~^ needless_if + //~^ needless_ifs // Don't leave trailing attributes #[allow(unused)] if true {} - //~^ needless_if + //~^ needless_ifs let () = if maybe_side_effect() {}; } fn issue15960() -> i32 { if matches!(2, 3) {} - //~^ needless_if + //~^ needless_ifs if matches!(2, 3) == (2 * 2 == 5) {} - //~^ needless_if + //~^ needless_ifs 1 // put something here so that `if` is a statement not an expression } diff --git a/tests/ui/needless_if.stderr b/tests/ui/needless_ifs.stderr similarity index 81% rename from tests/ui/needless_if.stderr rename to tests/ui/needless_ifs.stderr index b81a3890afa13..8684ec217a0a4 100644 --- a/tests/ui/needless_if.stderr +++ b/tests/ui/needless_ifs.stderr @@ -1,20 +1,20 @@ error: this `if` branch is empty - --> tests/ui/needless_if.rs:26:5 + --> tests/ui/needless_ifs.rs:26:5 | LL | if (true) {} | ^^^^^^^^^^^^ help: you can remove it | - = note: `-D clippy::needless-if` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_if)]` + = note: `-D clippy::needless-ifs` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_ifs)]` error: this `if` branch is empty - --> tests/ui/needless_if.rs:29:5 + --> tests/ui/needless_ifs.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:35:5 + --> tests/ui/needless_ifs.rs:35:5 | LL | / if { LL | | @@ -31,7 +31,7 @@ LL + }); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:50:5 + --> tests/ui/needless_ifs.rs:50:5 | LL | / if { LL | | @@ -57,31 +57,31 @@ LL + } && true); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:95:5 + --> tests/ui/needless_ifs.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:98:5 + --> tests/ui/needless_ifs.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:103:5 + --> tests/ui/needless_ifs.rs:103:5 | LL | if true {} | ^^^^^^^^^^ help: you can remove it: `true;` error: this `if` branch is empty - --> tests/ui/needless_if.rs:110:5 + --> tests/ui/needless_ifs.rs:110:5 | LL | if matches!(2, 3) {} | ^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `matches!(2, 3);` error: this `if` branch is empty - --> tests/ui/needless_if.rs:112:5 + --> tests/ui/needless_ifs.rs:112:5 | LL | if matches!(2, 3) == (2 * 2 == 5) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `matches!(2, 3) == (2 * 2 == 5);` diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index f03f74dfafe21..d040ba6ee83cb 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -2,7 +2,7 @@ #![allow( unused, clippy::diverging_sub_expression, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_pattern_matching )] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/nonminimal_bool_methods.fixed b/tests/ui/nonminimal_bool_methods.fixed index c2377491f25b1..f50af147c60cf 100644 --- a/tests/ui/nonminimal_bool_methods.fixed +++ b/tests/ui/nonminimal_bool_methods.fixed @@ -1,4 +1,4 @@ -#![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)] +#![allow(unused, clippy::diverging_sub_expression, clippy::needless_ifs)] #![warn(clippy::nonminimal_bool)] fn methods_with_negation() { diff --git a/tests/ui/nonminimal_bool_methods.rs b/tests/ui/nonminimal_bool_methods.rs index 1ae0f0064c6b6..0ecd4775035bf 100644 --- a/tests/ui/nonminimal_bool_methods.rs +++ b/tests/ui/nonminimal_bool_methods.rs @@ -1,4 +1,4 @@ -#![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)] +#![allow(unused, clippy::diverging_sub_expression, clippy::needless_ifs)] #![warn(clippy::nonminimal_bool)] fn methods_with_negation() { diff --git a/tests/ui/op_ref.fixed b/tests/ui/op_ref.fixed index 4bf4b91888c8c..fe4a48989b0fb 100644 --- a/tests/ui/op_ref.fixed +++ b/tests/ui/op_ref.fixed @@ -111,7 +111,7 @@ mod issue_2597 { } } -#[allow(clippy::needless_if)] +#[allow(clippy::needless_ifs)] fn issue15063() { use std::ops::BitAnd; diff --git a/tests/ui/op_ref.rs b/tests/ui/op_ref.rs index 9a192661aafcd..cd0d231497abc 100644 --- a/tests/ui/op_ref.rs +++ b/tests/ui/op_ref.rs @@ -111,7 +111,7 @@ mod issue_2597 { } } -#[allow(clippy::needless_if)] +#[allow(clippy::needless_ifs)] fn issue15063() { use std::ops::BitAnd; diff --git a/tests/ui/panicking_overflow_checks.rs b/tests/ui/panicking_overflow_checks.rs index 29789c9497567..61dfca8b37286 100644 --- a/tests/ui/panicking_overflow_checks.rs +++ b/tests/ui/panicking_overflow_checks.rs @@ -1,5 +1,5 @@ #![warn(clippy::panicking_overflow_checks)] -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] fn test(a: u32, b: u32, c: u32) { if a + b < a {} diff --git a/tests/ui/partialeq_to_none.fixed b/tests/ui/partialeq_to_none.fixed index e700cc56349f2..9288529423dd9 100644 --- a/tests/ui/partialeq_to_none.fixed +++ b/tests/ui/partialeq_to_none.fixed @@ -1,5 +1,5 @@ #![warn(clippy::partialeq_to_none)] -#![allow(clippy::eq_op, clippy::needless_if)] +#![allow(clippy::eq_op, clippy::needless_ifs)] struct Foobar; diff --git a/tests/ui/partialeq_to_none.rs b/tests/ui/partialeq_to_none.rs index 1bae076dd3377..7940251c18a2b 100644 --- a/tests/ui/partialeq_to_none.rs +++ b/tests/ui/partialeq_to_none.rs @@ -1,5 +1,5 @@ #![warn(clippy::partialeq_to_none)] -#![allow(clippy::eq_op, clippy::needless_if)] +#![allow(clippy::eq_op, clippy::needless_ifs)] struct Foobar; diff --git a/tests/ui/redundant_pattern_matching_drop_order.fixed b/tests/ui/redundant_pattern_matching_drop_order.fixed index 1141b5db3ebf4..490948442e11f 100644 --- a/tests/ui/redundant_pattern_matching_drop_order.fixed +++ b/tests/ui/redundant_pattern_matching_drop_order.fixed @@ -3,7 +3,7 @@ #![allow( clippy::if_same_then_else, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_else )] use std::task::Poll::{Pending, Ready}; diff --git a/tests/ui/redundant_pattern_matching_drop_order.rs b/tests/ui/redundant_pattern_matching_drop_order.rs index f60ddf4683096..d40fe84693b00 100644 --- a/tests/ui/redundant_pattern_matching_drop_order.rs +++ b/tests/ui/redundant_pattern_matching_drop_order.rs @@ -3,7 +3,7 @@ #![allow( clippy::if_same_then_else, clippy::equatable_if_let, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_else )] use std::task::Poll::{Pending, Ready}; diff --git a/tests/ui/redundant_pattern_matching_if_let_true.fixed b/tests/ui/redundant_pattern_matching_if_let_true.fixed index 980455da2ee7e..b746bfd1c3584 100644 --- a/tests/ui/redundant_pattern_matching_if_let_true.fixed +++ b/tests/ui/redundant_pattern_matching_if_let_true.fixed @@ -1,5 +1,5 @@ #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::needless_if, clippy::no_effect, clippy::nonminimal_bool)] +#![allow(clippy::needless_ifs, clippy::no_effect, clippy::nonminimal_bool)] macro_rules! condition { () => { diff --git a/tests/ui/redundant_pattern_matching_if_let_true.rs b/tests/ui/redundant_pattern_matching_if_let_true.rs index 9cb0fe2e65f63..7be9f31e56bd6 100644 --- a/tests/ui/redundant_pattern_matching_if_let_true.rs +++ b/tests/ui/redundant_pattern_matching_if_let_true.rs @@ -1,5 +1,5 @@ #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::needless_if, clippy::no_effect, clippy::nonminimal_bool)] +#![allow(clippy::needless_ifs, clippy::no_effect, clippy::nonminimal_bool)] macro_rules! condition { () => { diff --git a/tests/ui/redundant_pattern_matching_ipaddr.fixed b/tests/ui/redundant_pattern_matching_ipaddr.fixed index 1cec19ab8c99d..7c05eca1b70e5 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.fixed +++ b/tests/ui/redundant_pattern_matching_ipaddr.fixed @@ -2,7 +2,7 @@ #![allow( clippy::match_like_matches_macro, clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::uninlined_format_args )] diff --git a/tests/ui/redundant_pattern_matching_ipaddr.rs b/tests/ui/redundant_pattern_matching_ipaddr.rs index 123573a8602b6..1d4abca492813 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.rs +++ b/tests/ui/redundant_pattern_matching_ipaddr.rs @@ -2,7 +2,7 @@ #![allow( clippy::match_like_matches_macro, clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::uninlined_format_args )] diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index dc9d6491691f3..a54a4ce13d53f 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -2,7 +2,7 @@ #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index 2e9714ad8e75f..7252fce8cce6d 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -2,7 +2,7 @@ #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else diff --git a/tests/ui/redundant_pattern_matching_poll.fixed b/tests/ui/redundant_pattern_matching_poll.fixed index 800889b5fda0a..735c444bb45b5 100644 --- a/tests/ui/redundant_pattern_matching_poll.fixed +++ b/tests/ui/redundant_pattern_matching_poll.fixed @@ -1,7 +1,7 @@ #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else diff --git a/tests/ui/redundant_pattern_matching_poll.rs b/tests/ui/redundant_pattern_matching_poll.rs index 1668c2ff2bbac..eeca51e8be760 100644 --- a/tests/ui/redundant_pattern_matching_poll.rs +++ b/tests/ui/redundant_pattern_matching_poll.rs @@ -1,7 +1,7 @@ #![warn(clippy::redundant_pattern_matching)] #![allow( clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed index dab816716d598..261d82fc35c87 100644 --- a/tests/ui/redundant_pattern_matching_result.fixed +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -4,7 +4,7 @@ clippy::if_same_then_else, clippy::match_like_matches_macro, clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::uninlined_format_args, clippy::unnecessary_wraps )] diff --git a/tests/ui/redundant_pattern_matching_result.rs b/tests/ui/redundant_pattern_matching_result.rs index 3fd70515d0847..6cae4cc4b6b07 100644 --- a/tests/ui/redundant_pattern_matching_result.rs +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -4,7 +4,7 @@ clippy::if_same_then_else, clippy::match_like_matches_macro, clippy::needless_bool, - clippy::needless_if, + clippy::needless_ifs, clippy::uninlined_format_args, clippy::unnecessary_wraps )] diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index ff7130a71cd48..c08819b21ff01 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -43,6 +43,7 @@ #![allow(clippy::overly_complex_bool_expr)] #![allow(unexpected_cfgs)] #![allow(enum_intrinsics_non_enums)] +#![allow(clippy::needless_ifs)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] @@ -111,6 +112,7 @@ #![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` #![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` #![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` +#![warn(clippy::needless_ifs)] //~ ERROR: lint `clippy::needless_if` #![warn(clippy::new_without_default)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::bind_instead_of_map)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::option_expect_used` diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 86338a25ded36..0f6b58e144d95 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -43,6 +43,7 @@ #![allow(clippy::overly_complex_bool_expr)] #![allow(unexpected_cfgs)] #![allow(enum_intrinsics_non_enums)] +#![allow(clippy::needless_ifs)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] @@ -111,6 +112,7 @@ #![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` #![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` #![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` +#![warn(clippy::needless_if)] //~ ERROR: lint `clippy::needless_if` #![warn(clippy::new_without_default_derive)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::option_and_then_some)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::option_expect_used)] //~ ERROR: lint `clippy::option_expect_used` diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 6e36e34d4033f..4a7799be37d71 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:69:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,448 +8,454 @@ 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:70:9 + --> tests/ui/rename.rs:71: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:71:9 + --> tests/ui/rename.rs:72: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:72:9 + --> tests/ui/rename.rs:73: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:73:9 + --> tests/ui/rename.rs:74: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:74:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:76: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:76:9 + --> tests/ui/rename.rs:77: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:77:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:79: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:79:9 + --> tests/ui/rename.rs:80: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:80:9 + --> tests/ui/rename.rs:81: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:81:9 + --> tests/ui/rename.rs:82: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:82:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::double_neg` has been renamed to `double_negations` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:84: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:84:9 + --> tests/ui/rename.rs:85: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:85:9 + --> tests/ui/rename.rs:86: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:86:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::empty_enum` has been renamed to `clippy::empty_enums` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::empty_enum)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::empty_enums` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::fn_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:93: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:93:9 + --> tests/ui/rename.rs:94: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:94:9 + --> tests/ui/rename.rs:95: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:95:9 + --> tests/ui/rename.rs:96: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:96:9 + --> tests/ui/rename.rs:97: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:97:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:99: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:99:9 + --> tests/ui/rename.rs:100: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:100:9 + --> tests/ui/rename.rs:101: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:101:9 + --> tests/ui/rename.rs:102: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:102:9 + --> tests/ui/rename.rs:103: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:103:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:105: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:105:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::invalid_null_ptr_usage)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:108: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:108:9 + --> tests/ui/rename.rs:109: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:109:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:112: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:112:9 + --> tests/ui/rename.rs:113: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:113:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` +error: lint `clippy::needless_if` has been renamed to `clippy::needless_ifs` + --> tests/ui/rename.rs:115:9 + | +LL | #![warn(clippy::needless_if)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_ifs` + error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:116: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:115:9 + --> tests/ui/rename.rs:117: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:116:9 + --> tests/ui/rename.rs:118: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:117:9 + --> tests/ui/rename.rs:119: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:118:9 + --> tests/ui/rename.rs:120: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:119:9 + --> tests/ui/rename.rs:121: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:120:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:121:9 + --> tests/ui/rename.rs:123: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:122:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:123:9 + --> tests/ui/rename.rs:125: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:124:9 + --> tests/ui/rename.rs:126: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:125:9 + --> tests/ui/rename.rs:127: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:126:9 + --> tests/ui/rename.rs:128:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:130: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:129:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:133: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:132:9 + --> tests/ui/rename.rs:134:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:133:9 + --> tests/ui/rename.rs:135:9 | LL | #![warn(clippy::transmute_float_to_int)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:134:9 + --> tests/ui/rename.rs:136:9 | LL | #![warn(clippy::transmute_int_to_char)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:135:9 + --> tests/ui/rename.rs:137:9 | LL | #![warn(clippy::transmute_int_to_float)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:136:9 + --> tests/ui/rename.rs:138:9 | LL | #![warn(clippy::transmute_num_to_bytes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::unchecked_duration_subtraction` has been renamed to `clippy::unchecked_time_subtraction` - --> tests/ui/rename.rs:137:9 + --> tests/ui/rename.rs:139:9 | LL | #![warn(clippy::unchecked_duration_subtraction)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unchecked_time_subtraction` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:138:9 + --> tests/ui/rename.rs:140: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:139:9 + --> tests/ui/rename.rs:141: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:140:9 + --> tests/ui/rename.rs:142:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:141:9 + --> tests/ui/rename.rs:143:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:142:9 + --> tests/ui/rename.rs:144:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:143:9 + --> tests/ui/rename.rs:145:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` -error: aborting due to 75 previous errors +error: aborting due to 76 previous errors diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index 05009b2ddd416..d589bbc4affde 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -3,7 +3,7 @@ #![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)] #![allow( clippy::let_unit_value, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_guards, clippy::redundant_locals )] diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index 03982b069e675..fe28674ec18e5 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -3,7 +3,7 @@ #![allow( unused, clippy::uninlined_format_args, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_guards, clippy::redundant_pattern_matching, clippy::manual_unwrap_or_default diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index e28128e35adab..0f558cb5f786c 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -3,7 +3,7 @@ #![allow( unused, clippy::uninlined_format_args, - clippy::needless_if, + clippy::needless_ifs, clippy::redundant_guards, clippy::redundant_pattern_matching, clippy::manual_unwrap_or_default diff --git a/tests/ui/single_match_else_deref_patterns.fixed b/tests/ui/single_match_else_deref_patterns.fixed index 7a9f806309645..1743ae6bf5a7d 100644 --- a/tests/ui/single_match_else_deref_patterns.fixed +++ b/tests/ui/single_match_else_deref_patterns.fixed @@ -5,7 +5,7 @@ clippy::op_ref, clippy::deref_addrof, clippy::borrow_deref_ref, - clippy::needless_if + clippy::needless_ifs )] #![deny(clippy::single_match_else)] diff --git a/tests/ui/single_match_else_deref_patterns.rs b/tests/ui/single_match_else_deref_patterns.rs index ef19c7cbde2ba..d5cb8a24a609c 100644 --- a/tests/ui/single_match_else_deref_patterns.rs +++ b/tests/ui/single_match_else_deref_patterns.rs @@ -5,7 +5,7 @@ clippy::op_ref, clippy::deref_addrof, clippy::borrow_deref_ref, - clippy::needless_if + clippy::needless_ifs )] #![deny(clippy::single_match_else)] diff --git a/tests/ui/starts_ends_with.fixed b/tests/ui/starts_ends_with.fixed index b4f9d5867703a..f2fab9217604b 100644 --- a/tests/ui/starts_ends_with.fixed +++ b/tests/ui/starts_ends_with.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] +#![allow(clippy::needless_ifs, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/tests/ui/starts_ends_with.rs b/tests/ui/starts_ends_with.rs index dae04ac4a62d5..1460d77a094dd 100644 --- a/tests/ui/starts_ends_with.rs +++ b/tests/ui/starts_ends_with.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] +#![allow(clippy::needless_ifs, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs index 072e7b27b0d0a..8574cf1c20a1a 100644 --- a/tests/ui/suspicious_else_formatting.rs +++ b/tests/ui/suspicious_else_formatting.rs @@ -4,7 +4,7 @@ #![allow( clippy::if_same_then_else, clippy::let_unit_value, - clippy::needless_if, + clippy::needless_ifs, clippy::needless_else )] diff --git a/tests/ui/suspicious_unary_op_formatting.rs b/tests/ui/suspicious_unary_op_formatting.rs index ab9bdde6cf902..19f8b231925b7 100644 --- a/tests/ui/suspicious_unary_op_formatting.rs +++ b/tests/ui/suspicious_unary_op_formatting.rs @@ -1,5 +1,5 @@ #![warn(clippy::suspicious_unary_op_formatting)] -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #[rustfmt::skip] fn main() { diff --git a/tests/ui/unit_cmp.rs b/tests/ui/unit_cmp.rs index 0a0fe3a1990b0..839987a474cd0 100644 --- a/tests/ui/unit_cmp.rs +++ b/tests/ui/unit_cmp.rs @@ -3,7 +3,7 @@ clippy::no_effect, clippy::unnecessary_operation, clippy::derive_partial_eq_without_eq, - clippy::needless_if + clippy::needless_ifs )] #[derive(PartialEq)] diff --git a/tests/ui/unnecessary_safety_comment.rs b/tests/ui/unnecessary_safety_comment.rs index 4440089b3633b..d82a7b969080b 100644 --- a/tests/ui/unnecessary_safety_comment.rs +++ b/tests/ui/unnecessary_safety_comment.rs @@ -1,5 +1,5 @@ #![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] -#![allow(clippy::let_unit_value, clippy::missing_safety_doc, clippy::needless_if)] +#![allow(clippy::let_unit_value, clippy::missing_safety_doc, clippy::needless_ifs)] mod unsafe_items_invalid_comment { // SAFETY: diff --git a/tests/ui/unneeded_wildcard_pattern.fixed b/tests/ui/unneeded_wildcard_pattern.fixed index 7e6a493b86c12..32494435fff5e 100644 --- a/tests/ui/unneeded_wildcard_pattern.fixed +++ b/tests/ui/unneeded_wildcard_pattern.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![feature(stmt_expr_attributes)] #![deny(clippy::unneeded_wildcard_pattern)] -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #[macro_use] extern crate proc_macros; diff --git a/tests/ui/unneeded_wildcard_pattern.rs b/tests/ui/unneeded_wildcard_pattern.rs index c91383e2cca5f..b3a0fb6098d61 100644 --- a/tests/ui/unneeded_wildcard_pattern.rs +++ b/tests/ui/unneeded_wildcard_pattern.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![feature(stmt_expr_attributes)] #![deny(clippy::unneeded_wildcard_pattern)] -#![allow(clippy::needless_if)] +#![allow(clippy::needless_ifs)] #[macro_use] extern crate proc_macros; diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index 339d4a95084ae..0391fb19b1f6f 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -4,7 +4,7 @@ clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms, - clippy::needless_if, + clippy::needless_ifs, clippy::manual_range_patterns )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index f5c99183b0c51..f0702668fa917 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -4,7 +4,7 @@ clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms, - clippy::needless_if, + clippy::needless_ifs, clippy::manual_range_patterns )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] diff --git a/tests/ui/unnested_or_patterns2.fixed b/tests/ui/unnested_or_patterns2.fixed index 6d601ea5e57fe..2c794463457fb 100644 --- a/tests/ui/unnested_or_patterns2.fixed +++ b/tests/ui/unnested_or_patterns2.fixed @@ -3,7 +3,7 @@ #![allow( clippy::cognitive_complexity, clippy::match_ref_pats, - clippy::needless_if, + clippy::needless_ifs, clippy::manual_range_patterns )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] diff --git a/tests/ui/unnested_or_patterns2.rs b/tests/ui/unnested_or_patterns2.rs index 7e5ea0161bffd..12f3d3fca464e 100644 --- a/tests/ui/unnested_or_patterns2.rs +++ b/tests/ui/unnested_or_patterns2.rs @@ -3,7 +3,7 @@ #![allow( clippy::cognitive_complexity, clippy::match_ref_pats, - clippy::needless_if, + clippy::needless_ifs, clippy::manual_range_patterns )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 2942f64741e91..9de7d2c67149d 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -1,5 +1,5 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps, unused)] +#![allow(clippy::needless_ifs, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index f2da414e9f652..38cd1175aa485 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -1,5 +1,5 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps, unused)] +#![allow(clippy::needless_ifs, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs index d16506d94aa3c..157c236a214cd 100644 --- a/tests/ui/useless_conversion_try.rs +++ b/tests/ui/useless_conversion_try.rs @@ -1,6 +1,6 @@ #![deny(clippy::useless_conversion)] #![allow( - clippy::needless_if, + clippy::needless_ifs, clippy::unnecessary_fallible_conversions, clippy::manual_unwrap_or_default )] From d349b208efd3078db4b29ae0a8261090c29fa943 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 24 Oct 2025 20:08:41 +0200 Subject: [PATCH 067/108] fix(use_debug): don't get confused by nested `Debug` impls --- clippy_lints/src/write.rs | 25 ++++++++++++++++--------- tests/ui/use_debug.rs | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index c55c5ec2f51ae..c39e4a4cc9562 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -9,7 +9,7 @@ use rustc_ast::{ FormatPlaceholder, FormatTrait, }; use rustc_errors::Applicability; -use rustc_hir::{Expr, Impl, Item, ItemKind}; +use rustc_hir::{Expr, Impl, Item, ItemKind, OwnerId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Span}; @@ -240,7 +240,8 @@ declare_clippy_lint! { pub struct Write { format_args: FormatArgsStorage, - in_debug_impl: bool, + // The outermost `impl Debug` we're currently in. While we're in one, `USE_DEBUG` is deactivated + outermost_debug_impl: Option, allow_print_in_tests: bool, } @@ -248,10 +249,14 @@ impl Write { pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - in_debug_impl: false, + outermost_debug_impl: None, allow_print_in_tests: conf.allow_print_in_tests, } } + + fn in_debug_impl(&self) -> bool { + self.outermost_debug_impl.is_some() + } } impl_lint_pass!(Write => [ @@ -268,14 +273,16 @@ impl_lint_pass!(Write => [ impl<'tcx> LateLintPass<'tcx> for Write { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_debug_impl(cx, item) { - self.in_debug_impl = true; + // Only check for `impl Debug`s if we're not already in one + if self.outermost_debug_impl.is_none() && is_debug_impl(cx, item) { + self.outermost_debug_impl = Some(item.owner_id); } } - fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_debug_impl(cx, item) { - self.in_debug_impl = false; + fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { + // Only clear `self.outermost_debug_impl` if we're escaping the _outermost_ debug impl + if self.outermost_debug_impl == Some(item.owner_id) { + self.outermost_debug_impl = None; } } @@ -329,7 +336,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { check_literal(cx, format_args, name); - if !self.in_debug_impl { + if !self.in_debug_impl() { for piece in &format_args.template { if let &FormatArgsPiece::Placeholder(FormatPlaceholder { span: Some(span), diff --git a/tests/ui/use_debug.rs b/tests/ui/use_debug.rs index ffd744339c850..89c127a12faf9 100644 --- a/tests/ui/use_debug.rs +++ b/tests/ui/use_debug.rs @@ -29,3 +29,22 @@ fn main() { vec![1, 2]; } + +// don't get confused by nested impls +fn issue15942() { + struct Bar; + impl Debug for Bar { + fn fmt(&self, f: &mut Formatter) -> Result { + struct Baz; + impl Debug for Baz { + fn fmt(&self, f: &mut Formatter) -> Result { + // ok, we can use `Debug` formatting in `Debug` implementations + write!(f, "{:?}", 42.718) + } + } + + // ok, we can use `Debug` formatting in `Debug` implementations + write!(f, "{:?}", 42.718) + } + } +} From a5b3c445da0fbe2aa9e9e2af06bda588cd3e9900 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 14 Sep 2025 09:02:15 +0200 Subject: [PATCH 068/108] Consider labels of inline asm as conditionally executed Since an `asm!()` statement is mostly unknown, as we do not know what it does, consider all labels' `break` as being conditionally executed only. --- clippy_lints/src/loops/never_loop.rs | 19 ++++++- tests/ui/never_loop.rs | 38 ++++++++++--- tests/ui/never_loop.stderr | 80 ++++++++++++++++------------ 3 files changed, 94 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 528cc64fa7bc5..0d37be17689a5 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -7,7 +7,7 @@ use clippy_utils::source::snippet; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use rustc_errors::Applicability; use rustc_hir::{ - Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Node, Pat, Stmt, StmtKind, StructTailExpr, + Block, Destination, Expr, ExprKind, HirId, InlineAsm, InlineAsmOperand, Node, Pat, Stmt, StmtKind, StructTailExpr, }; use rustc_lint::LateContext; use rustc_span::{BytePos, Span, sym}; @@ -75,12 +75,19 @@ pub(super) fn check<'tcx>( fn contains_any_break_or_continue(block: &Block<'_>) -> bool { for_each_expr_without_closures(block, |e| match e.kind { ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()), + ExprKind::InlineAsm(asm) if contains_label(asm) => ControlFlow::Break(()), ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), _ => ControlFlow::Continue(Descend::Yes), }) .is_some() } +fn contains_label(asm: &InlineAsm<'_>) -> bool { + asm.operands + .iter() + .any(|(op, _span)| matches!(op, InlineAsmOperand::Label { .. })) +} + /// The `never_loop` analysis keeps track of three things: /// /// * Has any (reachable) code path hit a `continue` of the main loop? @@ -378,7 +385,15 @@ fn never_loop_expr<'tcx>( InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => { NeverLoopResult::Normal }, - InlineAsmOperand::Label { block } => never_loop_block(cx, block, local_labels, main_loop_id), + InlineAsmOperand::Label { block } => + // We do not know whether the label will be executed or not, so `Diverging` must be + // downgraded to `Normal`. + { + match never_loop_block(cx, block, local_labels, main_loop_id) { + NeverLoopResult::Diverging { .. } => NeverLoopResult::Normal, + result => result, + } + }, })), ExprKind::OffsetOf(_, _) | ExprKind::Yield(_, _) diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 01db64a446c0f..9769ee0c3a37d 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -1,12 +1,9 @@ #![feature(try_blocks)] -#![allow( - clippy::eq_op, - clippy::single_match, - unused_assignments, - unused_variables, - clippy::while_immutable_condition -)] +#![expect(clippy::eq_op, clippy::single_match, clippy::while_immutable_condition)] //@no-rustfix + +use std::arch::asm; + fn test1() { let mut x = 0; loop { @@ -522,3 +519,30 @@ fn issue15350() { } } } + +fn issue15673() { + loop { + unsafe { + // No lint since we don't analyze the inside of the asm + asm! { + "/* {} */", + label { + break; + } + } + } + } + + //~v never_loop + loop { + unsafe { + asm! { + "/* {} */", + label { + break; + } + } + } + return; + } +} diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 4fda06cff4ab9..49392d971ee32 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -1,5 +1,5 @@ error: this loop never actually loops - --> tests/ui/never_loop.rs:12:5 + --> tests/ui/never_loop.rs:9:5 | LL | / loop { ... | @@ -10,7 +10,7 @@ LL | | } = note: `#[deny(clippy::never_loop)]` on by default error: this loop never actually loops - --> tests/ui/never_loop.rs:36:5 + --> tests/ui/never_loop.rs:33:5 | LL | / loop { ... | @@ -19,7 +19,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:58:5 + --> tests/ui/never_loop.rs:55:5 | LL | / loop { ... | @@ -28,7 +28,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:62:9 + --> tests/ui/never_loop.rs:59:9 | LL | / while i == 0 { ... | @@ -36,7 +36,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:76:9 + --> tests/ui/never_loop.rs:73:9 | LL | / loop { ... | @@ -45,7 +45,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:114:5 + --> tests/ui/never_loop.rs:111:5 | LL | / while let Some(y) = x { ... | @@ -53,7 +53,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:123:5 + --> tests/ui/never_loop.rs:120:5 | LL | / for x in 0..10 { ... | @@ -67,7 +67,7 @@ LL + if let Some(x) = (0..10).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:173:5 + --> tests/ui/never_loop.rs:170:5 | LL | / 'outer: while a { ... | @@ -76,7 +76,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:190:9 + --> tests/ui/never_loop.rs:187:9 | LL | / while false { LL | | @@ -86,7 +86,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:243:13 + --> tests/ui/never_loop.rs:240:13 | LL | let _ = loop { | _____________^ @@ -99,7 +99,7 @@ LL | | }; | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:266:5 + --> tests/ui/never_loop.rs:263:5 | LL | / 'a: loop { LL | | @@ -110,7 +110,7 @@ LL | | } | |_____^ error: sub-expression diverges - --> tests/ui/never_loop.rs:271:17 + --> tests/ui/never_loop.rs:268:17 | LL | break 'a; | ^^^^^^^^ @@ -119,7 +119,7 @@ LL | break 'a; = help: to override `-D warnings` add `#[allow(clippy::diverging_sub_expression)]` error: this loop never actually loops - --> tests/ui/never_loop.rs:303:13 + --> tests/ui/never_loop.rs:300:13 | LL | / for _ in 0..20 { LL | | @@ -135,7 +135,7 @@ LL + if let Some(_) = (0..20).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:388:13 + --> tests/ui/never_loop.rs:385:13 | LL | / 'c: loop { LL | | @@ -145,7 +145,7 @@ LL | | } | |_____________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:400:5 + --> tests/ui/never_loop.rs:397:5 | LL | / loop { LL | | @@ -155,7 +155,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:405:5 + --> tests/ui/never_loop.rs:402:5 | LL | / loop { LL | | @@ -165,7 +165,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:426:5 + --> tests/ui/never_loop.rs:423:5 | LL | / for v in 0..10 { LL | | @@ -183,7 +183,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:434:5 + --> tests/ui/never_loop.rs:431:5 | LL | / 'outer: for v in 0..10 { LL | | @@ -194,7 +194,7 @@ LL | | } | |_____^ | help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:436:9 + --> tests/ui/never_loop.rs:433:9 | LL | / loop { LL | | @@ -202,7 +202,7 @@ LL | | break 'outer; LL | | } | |_________^ help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:440:9 + --> tests/ui/never_loop.rs:437:9 | LL | return; | ^^^^^^^ @@ -213,7 +213,7 @@ LL + if let Some(v) = (0..10).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:436:9 + --> tests/ui/never_loop.rs:433:9 | LL | / loop { LL | | @@ -222,7 +222,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:443:5 + --> tests/ui/never_loop.rs:440:5 | LL | / for v in 0..10 { LL | | @@ -239,7 +239,7 @@ LL + if let Some(v) = (0..10).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:445:9 + --> tests/ui/never_loop.rs:442:9 | LL | / 'inner: loop { LL | | @@ -248,7 +248,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:471:5 + --> tests/ui/never_loop.rs:468:5 | LL | / 'a: for _ in 0..1 { LL | | @@ -264,7 +264,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:477:5 + --> tests/ui/never_loop.rs:474:5 | LL | / 'a: for i in 0..1 { LL | | @@ -288,7 +288,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:492:5 + --> tests/ui/never_loop.rs:489:5 | LL | / for v in 0..10 { LL | | @@ -311,7 +311,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:503:5 + --> tests/ui/never_loop.rs:500:5 | LL | / 'bar: for _ in 0..100 { LL | | @@ -321,7 +321,7 @@ LL | | } | |_____^ | help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:505:9 + --> tests/ui/never_loop.rs:502:9 | LL | / loop { LL | | @@ -336,7 +336,7 @@ LL + if let Some(_) = (0..100).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:505:9 + --> tests/ui/never_loop.rs:502:9 | LL | / loop { LL | | @@ -346,7 +346,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:512:5 + --> tests/ui/never_loop.rs:509:5 | LL | / 'foo: for _ in 0..100 { LL | | @@ -356,7 +356,7 @@ LL | | } | |_____^ | help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:514:9 + --> tests/ui/never_loop.rs:511:9 | LL | / loop { LL | | @@ -372,7 +372,7 @@ LL + if let Some(_) = (0..100).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:514:9 + --> tests/ui/never_loop.rs:511:9 | LL | / loop { LL | | @@ -383,7 +383,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:517:13 + --> tests/ui/never_loop.rs:514:13 | LL | / loop { LL | | @@ -392,5 +392,17 @@ LL | | break 'foo; LL | | } | |_____________^ -error: aborting due to 29 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:537:5 + | +LL | / loop { +LL | | unsafe { +LL | | asm! { +LL | | "/* {} */", +... | +LL | | return; +LL | | } + | |_____^ + +error: aborting due to 30 previous errors From 0ff3a380e1b76c41f71adf42f965f8b288b3e83d Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 27 Oct 2025 19:04:22 +0000 Subject: [PATCH 069/108] Improve doc comment code language tag parsing, don't use a full parser --- clippy_lints/src/doc/mod.rs | 117 ++++++---- clippy_lints/src/doc/needless_doctest_main.rs | 203 +++++------------- clippy_lints/src/doc/test_attr_in_doctest.rs | 34 +++ clippy_lints/src/lib.rs | 2 - tests/ui/doc/needless_doctest_main.rs | 28 +-- tests/ui/doc/needless_doctest_main.stderr | 33 +-- tests/ui/explicit_write_in_test.stderr | 0 tests/ui/needless_doc_main.rs | 15 +- tests/ui/needless_doc_main.stderr | 35 +-- tests/ui/test_attr_in_doctest.rs | 1 - tests/ui/test_attr_in_doctest.stderr | 28 +-- 11 files changed, 198 insertions(+), 298 deletions(-) create mode 100644 clippy_lints/src/doc/test_attr_in_doctest.rs delete mode 100644 tests/ui/explicit_write_in_test.stderr diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 2a3fb82946117..1e1d6e69cc91c 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -22,7 +22,6 @@ use rustc_resolve::rustdoc::{ }; use rustc_session::impl_lint_pass; use rustc_span::Span; -use rustc_span::edition::Edition; use std::ops::Range; use url::Url; @@ -36,6 +35,7 @@ mod markdown; mod missing_headers; mod needless_doctest_main; mod suspicious_doc_comments; +mod test_attr_in_doctest; mod too_long_first_doc_paragraph; declare_clippy_lint! { @@ -900,8 +900,6 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ )) } -const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; - enum Container { Blockquote, List(usize), @@ -966,6 +964,70 @@ fn check_for_code_clusters<'a, Events: Iterator Self { + Self { + no_run: false, + ignore: false, + compile_fail: false, + + rust: true, + } + } +} + +impl CodeTags { + /// Based on + fn parse(lang: &str) -> Self { + let mut tags = Self::default(); + + let mut seen_rust_tags = false; + let mut seen_other_tags = false; + for item in lang.split([',', ' ', '\t']) { + match item.trim() { + "" => {}, + "rust" => { + tags.rust = true; + seen_rust_tags = true; + }, + "ignore" => { + tags.ignore = true; + seen_rust_tags = !seen_other_tags; + }, + "no_run" => { + tags.no_run = true; + seen_rust_tags = !seen_other_tags; + }, + "should_panic" => seen_rust_tags = !seen_other_tags, + "compile_fail" => { + tags.compile_fail = true; + seen_rust_tags = !seen_other_tags || seen_rust_tags; + }, + "test_harness" | "standalone_crate" => { + seen_rust_tags = !seen_other_tags || seen_rust_tags; + }, + _ if item.starts_with("ignore-") => seen_rust_tags = true, + _ if item.starts_with("edition") => {}, + _ => seen_other_tags = true, + } + } + + tags.rust &= seen_rust_tags || !seen_other_tags; + + tags + } +} + /// Checks parsed documentation. /// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`, /// so lints here will generally access that information. @@ -981,14 +1043,10 @@ fn check_doc<'a, Events: Iterator, Range DocHeaders { // true if a safety header was found let mut headers = DocHeaders::default(); - let mut in_code = false; + let mut code = None; let mut in_link = None; let mut in_heading = false; let mut in_footnote_definition = false; - let mut is_rust = false; - let mut no_test = false; - let mut ignore = false; - let mut edition = None; let mut ticks_unbalanced = false; let mut text_to_check: Vec<(CowStr<'_>, Range, isize)> = Vec::new(); let mut paragraph_range = 0..0; @@ -1048,31 +1106,12 @@ fn check_doc<'a, Events: Iterator, Range { - in_code = true; - if let CodeBlockKind::Fenced(lang) = kind { - for item in lang.split(',') { - if item == "ignore" { - is_rust = false; - break; - } else if item == "no_test" { - no_test = true; - } else if item == "no_run" || item == "compile_fail" { - ignore = true; - } - if let Some(stripped) = item.strip_prefix("edition") { - is_rust = true; - edition = stripped.parse::().ok(); - } else if item.is_empty() || RUST_CODE.contains(&item) { - is_rust = true; - } - } - } - }, - End(TagEnd::CodeBlock) => { - in_code = false; - is_rust = false; - ignore = false; + code = Some(match kind { + CodeBlockKind::Indented => CodeTags::default(), + CodeBlockKind::Fenced(lang) => CodeTags::parse(lang), + }); }, + End(TagEnd::CodeBlock) => code = None, Start(Link { dest_url, .. }) => in_link = Some(dest_url), End(TagEnd::Link) => in_link = None, Start(Heading { .. } | Paragraph | Item) => { @@ -1182,7 +1221,7 @@ fn check_doc<'a, Events: Iterator, Range, Range>) { - test_attr_spans.extend( - item.attrs - .iter() - .find(|attr| attr.has_name(sym::test)) - .map(|attr| attr.span.lo().to_usize()..ident.span.hi().to_usize()), - ); -} - -pub fn check( - cx: &LateContext<'_>, - text: &str, - edition: Edition, - range: Range, - fragments: Fragments<'_>, - ignore: bool, -) { - // return whether the code contains a needless `fn main` plus a vector of byte position ranges - // of all `#[test]` attributes in not ignored code examples - fn check_code_sample(code: String, edition: Edition, ignore: bool) -> (bool, Vec>) { - rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_globals_then(edition, &[], None, || { - let mut test_attr_spans = vec![]; - let filename = FileName::anon_source_code(&code); - - let translator = rustc_driver::default_translator(); - let emitter = HumanEmitter::new(Box::new(io::sink()), translator); - let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); - #[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx - let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); - let psess = ParseSess::with_dcx(dcx, sm); - - let mut parser = - match new_parser_from_source_str(&psess, filename, code, StripTokens::ShebangAndFrontmatter) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(Diag::cancel); - return (false, test_attr_spans); - }, - }; - - let mut relevant_main_found = false; - let mut eligible = true; - loop { - match parser.parse_item(ForceCollect::No) { - Ok(Some(item)) => match &item.kind { - ItemKind::Fn(box Fn { - ident, - sig, - body: Some(block), - .. - }) if ident.name == sym::main => { - if !ignore { - get_test_spans(&item, *ident, &mut test_attr_spans); - } - - let is_async = matches!(sig.header.coroutine_kind, Some(CoroutineKind::Async { .. })); - let returns_nothing = match &sig.decl.output { - FnRetTy::Default(..) => true, - FnRetTy::Ty(ty) if ty.kind.is_unit() => true, - FnRetTy::Ty(_) => false, - }; - - if returns_nothing && !is_async && !block.stmts.is_empty() { - // This main function should be linted, but only if there are no other functions - relevant_main_found = true; - } else { - // This main function should not be linted, we're done - eligible = false; - } - }, - // Another function was found; this case is ignored for needless_doctest_main - ItemKind::Fn(fn_) => { - eligible = false; - if ignore { - // If ignore is active invalidating one lint, - // and we already found another function thus - // invalidating the other one, we have no - // business continuing. - return (false, test_attr_spans); - } - get_test_spans(&item, fn_.ident, &mut test_attr_spans); - }, - // Tests with one of these items are ignored - ItemKind::Static(..) - | ItemKind::Const(..) - | ItemKind::ExternCrate(..) - | ItemKind::ForeignMod(..) => { - eligible = false; - }, - _ => {}, - }, - Ok(None) => break, - Err(e) => { - // See issue #15041. When calling `.cancel()` on the `Diag`, Clippy will unexpectedly panic - // when the `Diag` is unwinded. Meanwhile, we can just call `.emit()`, since the `DiagCtxt` - // is just a sink, nothing will be printed. - e.emit(); - return (false, test_attr_spans); - }, - } - } - - (relevant_main_found & eligible, test_attr_spans) - }) - }) - .ok() - .unwrap_or_default() +use rustc_span::InnerSpan; + +fn returns_unit<'a>(mut tokens: impl Iterator) -> bool { + let mut next = || tokens.next().map_or(TokenKind::Whitespace, |(kind, ..)| kind); + + match next() { + // { + TokenKind::OpenBrace => true, + // - > ( ) { + TokenKind::Minus => { + next() == TokenKind::Gt + && next() == TokenKind::OpenParen + && next() == TokenKind::CloseParen + && next() == TokenKind::OpenBrace + }, + _ => false, } +} - let trailing_whitespace = text.len() - text.trim_end().len(); - - // We currently only test for "fn main". Checking for the real - // entrypoint (with tcx.entry_fn(())) in each block would be unnecessarily - // expensive, as those are probably intended and relevant. Same goes for - // macros and other weird ways of declaring a main function. - // - // Also, as we only check for attribute names and don't do macro expansion, - // we can check only for #[test] - - if !((text.contains("main") && text.contains("fn")) || text.contains("#[test]")) { +pub fn check(cx: &LateContext<'_>, text: &str, offset: usize, fragments: Fragments<'_>) { + if !text.contains("main") { return; } - // Because of the global session, we need to create a new session in a different thread with - // the edition we need. - let text = text.to_owned(); - let (has_main, test_attr_spans) = thread::spawn(move || check_code_sample(text, edition, ignore)) - .join() - .expect("thread::spawn failed"); - if has_main && let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace) { - span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); - } - for span in test_attr_spans { - let span = (range.start + span.start)..(range.start + span.end); - if let Some(span) = fragments.span(cx, span) { - span_lint(cx, TEST_ATTR_IN_DOCTEST, span, "unit tests in doctest are not executed"); + let mut tokens = tokenize_with_text(text).filter(|&(kind, ..)| { + !matches!( + kind, + TokenKind::Whitespace | TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } + ) + }); + if let Some((TokenKind::Ident, "fn", fn_span)) = tokens.next() + && let Some((TokenKind::Ident, "main", main_span)) = tokens.next() + && let Some((TokenKind::OpenParen, ..)) = tokens.next() + && let Some((TokenKind::CloseParen, ..)) = tokens.next() + && returns_unit(&mut tokens) + { + let mut depth = 1; + for (kind, ..) in &mut tokens { + match kind { + TokenKind::OpenBrace => depth += 1, + TokenKind::CloseBrace => { + depth -= 1; + if depth == 0 { + break; + } + }, + _ => {}, + } + } + + if tokens.next().is_none() + && let Some(span) = fragments.span(cx, fn_span.start + offset..main_span.end + offset) + { + span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } } diff --git a/clippy_lints/src/doc/test_attr_in_doctest.rs b/clippy_lints/src/doc/test_attr_in_doctest.rs new file mode 100644 index 0000000000000..65738434ac286 --- /dev/null +++ b/clippy_lints/src/doc/test_attr_in_doctest.rs @@ -0,0 +1,34 @@ +use super::Fragments; +use crate::doc::TEST_ATTR_IN_DOCTEST; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::tokenize_with_text; +use rustc_lexer::TokenKind; +use rustc_lint::LateContext; + +pub fn check(cx: &LateContext<'_>, text: &str, offset: usize, fragments: Fragments<'_>) { + if !text.contains("#[test]") { + return; + } + + let mut spans = Vec::new(); + let mut tokens = tokenize_with_text(text).filter(|&(kind, ..)| kind != TokenKind::Whitespace); + while let Some(token) = tokens.next() { + if let (TokenKind::Pound, _, pound_span) = token + && let Some((TokenKind::OpenBracket, ..)) = tokens.next() + && let Some((TokenKind::Ident, "test", _)) = tokens.next() + && let Some((TokenKind::CloseBracket, _, close_span)) = tokens.next() + && let Some(span) = fragments.span(cx, pound_span.start + offset..close_span.end + offset) + { + spans.push(span); + } + } + + if !spans.is_empty() { + span_lint( + cx, + TEST_ATTR_IN_DOCTEST, + spans, + "unit tests in doctest are not executed", + ); + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1d0ebff26e69a..384dd29642955 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -32,7 +32,6 @@ extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_data_structures; -extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_analysis; @@ -43,7 +42,6 @@ extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; -extern crate rustc_parse; extern crate rustc_parse_format; extern crate rustc_resolve; extern crate rustc_session; diff --git a/tests/ui/doc/needless_doctest_main.rs b/tests/ui/doc/needless_doctest_main.rs index 8c3217624d443..0fbf29365ecfe 100644 --- a/tests/ui/doc/needless_doctest_main.rs +++ b/tests/ui/doc/needless_doctest_main.rs @@ -1,33 +1,14 @@ #![warn(clippy::needless_doctest_main)] -//! issue 10491: -//! ```rust,no_test -//! use std::collections::HashMap; -//! -//! fn main() { -//! let mut m = HashMap::new(); -//! m.insert(1u32, 2u32); -//! } -//! ``` -/// some description here -/// ```rust,no_test -/// fn main() { -/// foo() -/// } -/// ``` -fn foo() {} - -#[rustfmt::skip] /// Description /// ```rust /// fn main() { -//~^ error: needless `fn main` in doctest +//~^ needless_doctest_main /// let a = 0; /// } /// ``` fn mulpipulpi() {} -#[rustfmt::skip] /// With a `#[no_main]` /// ```rust /// #[no_main] @@ -45,7 +26,6 @@ fn pulpimulpi() {} /// ``` fn plumilupi() {} -#[rustfmt::skip] /// Additional function, shouldn't trigger /// ```rust /// fn additional_function() { @@ -58,7 +38,6 @@ fn plumilupi() {} /// ``` fn mlupipupi() {} -#[rustfmt::skip] /// Additional function AFTER main, shouldn't trigger /// ```rust /// fn main() { @@ -71,22 +50,19 @@ fn mlupipupi() {} /// ``` fn lumpimupli() {} -#[rustfmt::skip] /// Ignore code block, should not lint at all /// ```rust, ignore /// fn main() { -//~^ error: needless `fn main` in doctest /// // Hi! /// let _ = 0; /// } /// ``` fn mpulpilumi() {} -#[rustfmt::skip] /// Spaces in weird positions (including an \u{A0} after `main`) /// ```rust /// fn main (){ -//~^ error: needless `fn main` in doctest +//~^ needless_doctest_main /// let _ = 0; /// } /// ``` diff --git a/tests/ui/doc/needless_doctest_main.stderr b/tests/ui/doc/needless_doctest_main.stderr index dd5474ccb85af..693cc22fba2a7 100644 --- a/tests/ui/doc/needless_doctest_main.stderr +++ b/tests/ui/doc/needless_doctest_main.stderr @@ -1,36 +1,17 @@ error: needless `fn main` in doctest - --> tests/ui/doc/needless_doctest_main.rs:23:5 + --> tests/ui/doc/needless_doctest_main.rs:5:5 | -LL | /// fn main() { - | _____^ -LL | | -LL | | /// let a = 0; -LL | | /// } - | |_____^ +LL | /// fn main() { + | ^^^^^^^ | = note: `-D clippy::needless-doctest-main` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_doctest_main)]` error: needless `fn main` in doctest - --> tests/ui/doc/needless_doctest_main.rs:77:5 + --> tests/ui/doc/needless_doctest_main.rs:64:5 | -LL | /// fn main() { - | _____^ -LL | | -LL | | /// // Hi! -LL | | /// let _ = 0; -LL | | /// } - | |_____^ +LL | /// fn main (){ + | ^^^^^^^^^^^ -error: needless `fn main` in doctest - --> tests/ui/doc/needless_doctest_main.rs:88:5 - | -LL | /// fn main (){ - | _____^ -LL | | -LL | | /// let _ = 0; -LL | | /// } - | |_____^ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/explicit_write_in_test.stderr b/tests/ui/explicit_write_in_test.stderr deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 8894ab0285d4c..afecd4b47f5eb 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -5,7 +5,7 @@ /// This should lint /// ``` /// fn main() { -//~^ ERROR: needless `fn main` in doctest +//~^ needless_doctest_main /// unimplemented!(); /// } /// ``` @@ -13,7 +13,7 @@ /// With an explicit return type it should lint too /// ```edition2015 /// fn main() -> () { -//~^ ERROR: needless `fn main` in doctest +//~^ needless_doctest_main /// unimplemented!(); /// } /// ``` @@ -21,7 +21,7 @@ /// This should, too. /// ```rust /// fn main() { -//~^ ERROR: needless `fn main` in doctest +//~^ needless_doctest_main /// unimplemented!(); /// } /// ``` @@ -29,8 +29,8 @@ /// This one too. /// ```no_run /// // the fn is not always the first line -//~^ ERROR: needless `fn main` in doctest /// fn main() { +//~^ needless_doctest_main /// unimplemented!(); /// } /// ``` @@ -38,12 +38,7 @@ fn bad_doctests() {} /// # Examples /// -/// This shouldn't lint, because the `main` is empty: -/// ``` -/// fn main(){} -/// ``` -/// -/// This shouldn't lint either, because main is async: +/// This shouldn't lint because main is async: /// ```edition2018 /// async fn main() { /// assert_eq!(42, ANSWER); diff --git a/tests/ui/needless_doc_main.stderr b/tests/ui/needless_doc_main.stderr index 9ba2ad306dac2..79763b0f22487 100644 --- a/tests/ui/needless_doc_main.stderr +++ b/tests/ui/needless_doc_main.stderr @@ -1,12 +1,8 @@ error: needless `fn main` in doctest --> tests/ui/needless_doc_main.rs:7:5 | -LL | /// fn main() { - | _____^ -LL | | -LL | | /// unimplemented!(); -LL | | /// } - | |_____^ +LL | /// fn main() { + | ^^^^^^^ | = note: `-D clippy::needless-doctest-main` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_doctest_main)]` @@ -14,33 +10,20 @@ LL | | /// } error: needless `fn main` in doctest --> tests/ui/needless_doc_main.rs:15:5 | -LL | /// fn main() -> () { - | _____^ -LL | | -LL | | /// unimplemented!(); -LL | | /// } - | |_____^ +LL | /// fn main() -> () { + | ^^^^^^^ error: needless `fn main` in doctest --> tests/ui/needless_doc_main.rs:23:5 | -LL | /// fn main() { - | _____^ -LL | | -LL | | /// unimplemented!(); -LL | | /// } - | |_____^ +LL | /// fn main() { + | ^^^^^^^ error: needless `fn main` in doctest - --> tests/ui/needless_doc_main.rs:31:5 + --> tests/ui/needless_doc_main.rs:32:5 | -LL | /// // the fn is not always the first line - | _____^ -LL | | -LL | | /// fn main() { -LL | | /// unimplemented!(); -LL | | /// } - | |_____^ +LL | /// fn main() { + | ^^^^^^^ error: aborting due to 4 previous errors diff --git a/tests/ui/test_attr_in_doctest.rs b/tests/ui/test_attr_in_doctest.rs index 6cffd813b8358..7d1a09024895b 100644 --- a/tests/ui/test_attr_in_doctest.rs +++ b/tests/ui/test_attr_in_doctest.rs @@ -21,7 +21,6 @@ /// } /// /// #[test] -//~^ test_attr_in_doctest /// fn should_be_linted_too() { /// assert_eq!("#[test]", " /// #[test] diff --git a/tests/ui/test_attr_in_doctest.stderr b/tests/ui/test_attr_in_doctest.stderr index 166b9b417b626..a8fa53034036b 100644 --- a/tests/ui/test_attr_in_doctest.stderr +++ b/tests/ui/test_attr_in_doctest.stderr @@ -1,11 +1,8 @@ error: unit tests in doctest are not executed --> tests/ui/test_attr_in_doctest.rs:6:5 | -LL | /// #[test] - | _____^ -LL | | -LL | | /// fn should_be_linted() { - | |_______________________^ +LL | /// #[test] + | ^^^^^^^ | = note: `-D clippy::test-attr-in-doctest` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::test_attr_in_doctest)]` @@ -13,20 +10,11 @@ LL | | /// fn should_be_linted() { error: unit tests in doctest are not executed --> tests/ui/test_attr_in_doctest.rs:16:5 | -LL | /// #[test] - | _____^ -LL | | -LL | | /// fn should_also_be_linted() { - | |____________________________^ +LL | /// #[test] + | ^^^^^^^ +... +LL | /// #[test] + | ^^^^^^^ -error: unit tests in doctest are not executed - --> tests/ui/test_attr_in_doctest.rs:23:5 - | -LL | /// #[test] - | _____^ -LL | | -LL | | /// fn should_be_linted_too() { - | |___________________________^ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors From bc5bd29ce6fd5daec2aa75ec740f0ac48c0634e2 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 1 Sep 2025 14:47:40 -0500 Subject: [PATCH 070/108] Remove QPath::LangItem from for loops --- clippy_lints/src/ranges.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index ac2cc11d30206..5c1a906599d34 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::{MaybeQPath, MaybeResPath}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; @@ -15,7 +15,7 @@ use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::ty::{self, ClauseKind, GenericArgKind, PredicatePolarity, Ty}; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::{Span, sym}; +use rustc_span::{DesugaringKind, Span, sym}; use std::cmp::Ordering; declare_clippy_lint! { @@ -368,7 +368,9 @@ fn can_switch_ranges<'tcx>( // Check if `expr` is the argument of a compiler-generated `IntoIter::into_iter(expr)` if let ExprKind::Call(func, [arg]) = parent_expr.kind && arg.hir_id == use_ctxt.child_id - && func.opt_lang_path() == Some(LangItem::IntoIterIntoIter) + && let ExprKind::Path(qpath) = func.kind + && cx.tcx.qpath_is_lang_item(qpath, LangItem::IntoIterIntoIter) + && parent_expr.span.is_desugaring(DesugaringKind::ForLoop) { return true; } From ae9d202f8c36d529aa6f144f99e660b66f62ebf4 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 17 Aug 2025 07:43:01 -0500 Subject: [PATCH 071/108] Remove QPath::LangItem from ranges --- clippy_lints/src/indexing_slicing.rs | 2 +- clippy_lints/src/infinite_iter.rs | 2 +- .../src/loops/char_indices_as_byte_indices.rs | 2 +- clippy_lints/src/loops/manual_memcpy.rs | 2 +- clippy_lints/src/loops/manual_slice_fill.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 2 +- clippy_lints/src/loops/needless_range_loop.rs | 2 +- clippy_lints/src/loops/single_element_loop.rs | 2 +- clippy_lints/src/manual_is_ascii_check.rs | 2 +- clippy_lints/src/manual_option_as_slice.rs | 2 +- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/methods/iter_next_slice.rs | 2 +- .../map_with_unused_argument_over_ranges.rs | 2 +- .../src/methods/range_zip_with_len.rs | 2 +- .../src/missing_asserts_for_indexing.rs | 6 +++--- .../src/needless_parens_on_range_literals.rs | 2 +- clippy_lints/src/ranges.rs | 4 ++-- clippy_lints/src/single_range_in_vec_init.rs | 6 +++--- clippy_lints/src/strings.rs | 5 +++-- clippy_utils/src/higher.rs | 20 +++++++++---------- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/sugg.rs | 10 +++++----- tests/ui/author/loop.stdout | 9 ++++++--- tests/ui/author/macro_in_loop.stdout | 3 ++- 24 files changed, 49 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 99a393b4d53af..a2fcdb4a54b43 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); let allowed_in_tests = self.allow_indexing_slicing_in_tests && is_in_test(cx.tcx, expr.hir_id); - if let Some(range) = higher::Range::hir(index) { + if let Some(range) = higher::Range::hir(cx, index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] if let ty::Array(_, s) = ty.kind() { let size: u128 = if let Some(size) = s.try_to_target_usize(cx.tcx) { diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index f193f065e68d5..8f6de9fc60bdd 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -178,7 +178,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { Finite } }, - ExprKind::Struct(..) => higher::Range::hir(expr).is_some_and(|r| r.end.is_none()).into(), + ExprKind::Struct(..) => higher::Range::hir(cx, expr).is_some_and(|r| r.end.is_none()).into(), _ => Finite, } } diff --git a/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/clippy_lints/src/loops/char_indices_as_byte_indices.rs index 7acf2a5462220..4aae602ea0516 100644 --- a/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -131,7 +131,7 @@ fn check_index_usage<'tcx>( fn index_consumed_at<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) { match node { - Node::Expr(expr) if higher::Range::hir(expr).is_some() => {}, + Node::Expr(expr) if higher::Range::hir(cx, expr).is_some() => {}, Node::ExprField(_) => {}, Node::Expr(expr) => return Some(expr), _ => break, diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 7e48fe28011a8..e5c37377e857b 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( end: Some(end), limits, span: _, - }) = higher::Range::hir(arg) + }) = higher::Range::hir(cx, arg) // the var must be a single name && let PatKind::Binding(_, canonical_id, _, _) = pat.kind { diff --git a/clippy_lints/src/loops/manual_slice_fill.rs b/clippy_lints/src/loops/manual_slice_fill.rs index 94cddb4a15069..a2ff60a3d8ac8 100644 --- a/clippy_lints/src/loops/manual_slice_fill.rs +++ b/clippy_lints/src/loops/manual_slice_fill.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( end: Some(end), limits: RangeLimits::HalfOpen, span: _, - }) = higher::Range::hir(arg) + }) = higher::Range::hir(cx, arg) && let ExprKind::Lit(Spanned { node: LitKind::Int(Pu128(0), _), .. diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index daeda227f670c..6ab7bb628eca5 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -16,7 +16,7 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { start: Some(start), end: Some(end), .. - }) = higher::Range::hir(arg) + }) = higher::Range::hir(cx, arg) && let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end)) && (mut_id_start.is_some() || mut_id_end.is_some()) { diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 7d890621d47c0..c974d9cca7d23 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>( ref end, limits, span, - }) = higher::Range::hir(arg) + }) = higher::Range::hir(cx, arg) // the var must be a single name && let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index d66771a8b5bec..edbf6e63e659f 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -90,7 +90,7 @@ pub(super) fn check<'tcx>( arg_snip = format!("({arg_snip})").into(); } - if clippy_utils::higher::Range::hir(arg_expression).is_some() { + if clippy_utils::higher::Range::hir(cx, arg_expression).is_some() { let range_expr = snippet(cx, arg_expression.span, "?").to_string(); let sugg = snippet(cx, arg_expression.span, ".."); diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 456b488080907..a0f946d22fa61 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { end: Some(end), limits: RangeLimits::Closed, span: _, - }) = higher::Range::hir(receiver) + }) = higher::Range::hir(cx, receiver) && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { let arg = peel_ref_operators(cx, arg); diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 63f6d89f2ad74..86f0eff9cd869 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -207,7 +207,7 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { ExprKind::Index(arr, range, _) => match arr.kind { ExprKind::Array([]) => is_range_literal(range), ExprKind::Array(_) => { - let Some(range) = clippy_utils::higher::Range::hir(range) else { + let Some(range) = clippy_utils::higher::Range::hir(cx, range) else { return false; }; range.end.is_some_and(|e| clippy_utils::is_integer_const(cx, e, 0)) diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index f5d15310879a3..b668f391d67a2 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -237,7 +237,7 @@ fn find_stripping<'tcx>( if is_ref_str(self.cx, ex) && let unref = peel_ref(ex) && let ExprKind::Index(indexed, index, _) = &unref.kind - && let Some(higher::Range { start, end, .. }) = higher::Range::hir(index) + && let Some(higher::Range { start, end, .. }) = higher::Range::hir(self.cx, index) && let ExprKind::Path(path) = &indexed.kind && self.cx.qpath_res(path, ex.hir_id) == self.target { diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index f37b15371c259..8c8c8c1dd8557 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal end: None, limits: ast::RangeLimits::HalfOpen, span: _, - }) = higher::Range::hir(index_expr) + }) = higher::Range::hir(cx, index_expr) && let hir::ExprKind::Lit(start_lit) = &start_expr.kind && let ast::LitKind::Int(start_idx, _) = start_lit.node { diff --git a/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs index 38ca15cb1334c..f60387fe86f71 100644 --- a/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs +++ b/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -66,7 +66,7 @@ pub(super) fn check( method_name_span: Span, ) { let mut applicability = Applicability::MaybeIncorrect; - if let Some(range) = higher::Range::hir(receiver) + if let Some(range) = higher::Range::hir(cx, receiver) && let ExprKind::Closure(Closure { body, .. }) = arg.kind && let body_hir = cx.tcx.hir_body(*body) && let Body { diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index de4207c2abf45..7ece83ba7ca39 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -11,7 +11,7 @@ use super::RANGE_ZIP_WITH_LEN; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) { if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // range expression in `.zip()` call: `0..x.len()` - && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg) + && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(cx, zip_arg) && is_integer_const(cx, start, 0) // `.len()` call && let ExprKind::MethodCall(len_path, len_recv, [], _) = end.kind diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index cf0c85990b150..35d06780bcb82 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -220,14 +220,14 @@ impl<'hir> IndexEntry<'hir> { /// /// E.g. for `5` this returns `Some(5)`, for `..5` this returns `Some(4)`, /// for `..=5` this returns `Some(5)` -fn upper_index_expr(expr: &Expr<'_>) -> Option { +fn upper_index_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let ExprKind::Lit(lit) = &expr.kind && let LitKind::Int(Pu128(index), _) = lit.node { Some(index as usize) } else if let Some(higher::Range { end: Some(end), limits, .. - }) = higher::Range::hir(expr) + }) = higher::Range::hir(cx, expr) && let ExprKind::Lit(lit) = &end.kind && let LitKind::Int(Pu128(index @ 1..), _) = lit.node { @@ -244,7 +244,7 @@ fn upper_index_expr(expr: &Expr<'_>) -> Option { fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap>>) { if let ExprKind::Index(slice, index_lit, _) = expr.kind && cx.typeck_results().expr_ty_adjusted(slice).peel_refs().is_slice() - && let Some(index) = upper_index_expr(index_lit) + && let Some(index) = upper_index_expr(cx, index_lit) { let hash = hash_expr(cx, slice); diff --git a/clippy_lints/src/needless_parens_on_range_literals.rs b/clippy_lints/src/needless_parens_on_range_literals.rs index 021a11593f3a7..f270ba7277cb6 100644 --- a/clippy_lints/src/needless_parens_on_range_literals.rs +++ b/clippy_lints/src/needless_parens_on_range_literals.rs @@ -74,7 +74,7 @@ fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) { impl<'tcx> LateLintPass<'tcx> for NeedlessParensOnRangeLiterals { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::Range { start, end, .. }) = higher::Range::hir(expr) { + if let Some(higher::Range { start, end, .. }) = higher::Range::hir(cx, expr) { if let Some(start) = start { check_for_parens(cx, start, true); } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 5c1a906599d34..ca0d41fca5248 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -503,7 +503,7 @@ fn check_range_switch<'tcx>( msg: &'static str, operator: &str, ) { - if let Some(range) = higher::Range::hir(expr) + if let Some(range) = higher::Range::hir(cx, expr) && let higher::Range { start, end: Some(end), @@ -571,7 +571,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { end: Some(end), limits, span, - }) = higher::Range::hir(expr) + }) = higher::Range::hir(cx, expr) && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() && let ecx = ConstEvalCtxt::new(cx) diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs index dda2f8cc1d00b..960edc6b02803 100644 --- a/clippy_lints/src/single_range_in_vec_init.rs +++ b/clippy_lints/src/single_range_in_vec_init.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::implements_trait; use clippy_utils::{is_no_std_crate, sym}; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr}; +use rustc_hir::{Expr, ExprKind, LangItem, StructTailExpr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use std::fmt::{self, Display, Formatter}; @@ -86,12 +86,12 @@ impl LateLintPass<'_> for SingleRangeInVecInit { return; }; - let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], StructTailExpr::None) = inner_expr.kind + let ExprKind::Struct(&qpath, [start, end], StructTailExpr::None) = inner_expr.kind else { return; }; - if matches!(lang_item, LangItem::Range) + if cx.tcx.qpath_is_lang_item(qpath, LangItem::Range) && let ty = cx.typeck_results().expr_ty(start.expr) && let Some(snippet) = span.get_source_text(cx) // `is_from_proc_macro` will skip any `vec![]`. Let's not! diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 47306949a6990..1d0efa46a14c1 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -6,7 +6,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node, QPath}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::declare_lint_pass; @@ -266,7 +266,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { && expressions[0].1.is_empty() // Check for slicer - && let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), _, _) = right.kind + && let ExprKind::Struct(&qpath, _, _) = right.kind + && cx.tcx.qpath_is_lang_item(qpath, LangItem::Range) { let mut applicability = Applicability::MachineApplicable; let string_expression = &expressions[0].0; diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 9d895c9aa649a..3904818cc9b6f 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -215,14 +215,12 @@ pub struct Range<'a> { impl<'a> Range<'a> { /// Higher a `hir` range to something similar to `ast::ExprKind::Range`. #[expect(clippy::similar_names)] - pub fn hir(expr: &'a Expr<'_>) -> Option> { + pub fn hir(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option> { let span = expr.range_span()?; match expr.kind { ExprKind::Call(path, [arg1, arg2]) - if matches!( - path.kind, - ExprKind::Path(QPath::LangItem(hir::LangItem::RangeInclusiveNew, ..)) - ) => + if let ExprKind::Path(qpath) = path.kind + && cx.tcx.qpath_is_lang_item(qpath, hir::LangItem::RangeInclusiveNew) => { Some(Range { start: Some(arg1), @@ -231,14 +229,14 @@ impl<'a> Range<'a> { span, }) }, - ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) { - (QPath::LangItem(hir::LangItem::RangeFull, ..), []) => Some(Range { + ExprKind::Struct(&qpath, fields, StructTailExpr::None) => match (cx.tcx.qpath_lang_item(qpath)?, fields) { + (hir::LangItem::RangeFull, []) => Some(Range { start: None, end: None, limits: ast::RangeLimits::HalfOpen, span, }), - (QPath::LangItem(hir::LangItem::RangeFrom, ..), [field]) if field.ident.name == sym::start => { + (hir::LangItem::RangeFrom, [field]) if field.ident.name == sym::start => { Some(Range { start: Some(field.expr), end: None, @@ -246,7 +244,7 @@ impl<'a> Range<'a> { span, }) }, - (QPath::LangItem(hir::LangItem::Range, ..), [field1, field2]) => { + (hir::LangItem::Range, [field1, field2]) => { let (start, end) = match (field1.ident.name, field2.ident.name) { (sym::start, sym::end) => (field1.expr, field2.expr), (sym::end, sym::start) => (field2.expr, field1.expr), @@ -259,7 +257,7 @@ impl<'a> Range<'a> { span, }) }, - (QPath::LangItem(hir::LangItem::RangeToInclusive, ..), [field]) if field.ident.name == sym::end => { + (hir::LangItem::RangeToInclusive, [field]) if field.ident.name == sym::end => { Some(Range { start: None, end: Some(field.expr), @@ -267,7 +265,7 @@ impl<'a> Range<'a> { span, }) }, - (QPath::LangItem(hir::LangItem::RangeTo, ..), [field]) if field.ident.name == sym::end => Some(Range { + (hir::LangItem::RangeTo, [field]) if field.ident.name == sym::end => Some(Range { start: None, end: Some(field.expr), limits: ast::RangeLimits::HalfOpen, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 6ee991eae137f..75247d8fa751b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1282,7 +1282,7 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { /// If the given `Expr` is not some kind of range, the function returns `false`. pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - if let Some(Range { start, end, limits, .. }) = Range::hir(expr) { + if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) { let start_is_none_or_min = start.is_none_or(|start| { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 581c2b02839dc..5945a0c7b5587 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -59,7 +59,7 @@ impl<'a> Sugg<'a> { pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { let ctxt = expr.span.ctxt(); let get_snippet = |span| snippet_with_context(cx, span, ctxt, "", &mut Applicability::Unspecified).0; - snippet_opt(cx, expr.span).map(|_| Self::hir_from_snippet(expr, get_snippet)) + snippet_opt(cx, expr.span).map(|_| Self::hir_from_snippet(cx, expr, get_snippet)) } /// Convenience function around `hir_opt` for suggestions with a default @@ -115,7 +115,7 @@ impl<'a> Sugg<'a> { Box::new(Self::hir_with_context(cx, inner, ctxt, default, applicability)), ) } else { - Self::hir_from_snippet(expr, |span| { + Self::hir_from_snippet(cx, expr, |span| { snippet_with_context(cx, span, ctxt, default, applicability).0 }) } @@ -127,8 +127,8 @@ impl<'a> Sugg<'a> { /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*` /// function variants of `Sugg`, since these use different snippet functions. - fn hir_from_snippet(expr: &hir::Expr<'_>, mut get_snippet: impl FnMut(Span) -> Cow<'a, str>) -> Self { - if let Some(range) = higher::Range::hir(expr) { + fn hir_from_snippet(cx: &LateContext<'_>, expr: &hir::Expr<'_>, mut get_snippet: impl FnMut(Span) -> Cow<'a, str>) -> Self { + if let Some(range) = higher::Range::hir(cx, expr) { let op = AssocOp::Range(range.limits); let start = range.start.map_or("".into(), |expr| get_snippet(expr.span)); let end = range.end.map_or("".into(), |expr| get_snippet(expr.span)); @@ -166,7 +166,7 @@ impl<'a> Sugg<'a> { | ExprKind::Use(..) | ExprKind::Err(_) | ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(get_snippet(expr.span)), - ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet), + ExprKind::DropTemps(inner) => Self::hir_from_snippet(cx, inner, get_snippet), ExprKind::Assign(lhs, rhs, _) => { Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span)) }, diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout index 79794cec92698..6a3159939dd1e 100644 --- a/tests/ui/author/loop.stdout +++ b/tests/ui/author/loop.stdout @@ -2,7 +2,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && let PatKind::Binding(BindingMode::NONE, _, name, None) = pat.kind && name.as_str() == "y" && let ExprKind::Struct(qpath, fields, None) = arg.kind - && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() + && paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed && fields.len() == 2 && fields[0].ident.as_str() == "start" && let ExprKind::Lit(ref lit) = fields[0].expr.kind @@ -23,7 +24,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) && let PatKind::Wild = pat.kind && let ExprKind::Struct(qpath, fields, None) = arg.kind - && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() + && paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed && fields.len() == 2 && fields[0].ident.as_str() == "start" && let ExprKind::Lit(ref lit) = fields[0].expr.kind @@ -43,7 +45,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) && let PatKind::Wild = pat.kind && let ExprKind::Struct(qpath, fields, None) = arg.kind - && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() + && paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed && fields.len() == 2 && fields[0].ident.as_str() == "start" && let ExprKind::Lit(ref lit) = fields[0].expr.kind diff --git a/tests/ui/author/macro_in_loop.stdout b/tests/ui/author/macro_in_loop.stdout index 80717900b5255..ba3b7e2442043 100644 --- a/tests/ui/author/macro_in_loop.stdout +++ b/tests/ui/author/macro_in_loop.stdout @@ -2,7 +2,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && let PatKind::Binding(BindingMode::NONE, _, name, None) = pat.kind && name.as_str() == "i" && let ExprKind::Struct(qpath, fields, None) = arg.kind - && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() + && paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed && fields.len() == 2 && fields[0].ident.as_str() == "start" && let ExprKind::Lit(ref lit) = fields[0].expr.kind From 930dfc889035926d4b36906dd2219db4b9f72d9e Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 17 Aug 2025 07:44:53 -0500 Subject: [PATCH 072/108] Remove QPath::LangItem from async --- clippy_lints/src/large_futures.rs | 5 +++-- clippy_lints/src/unused_io_amount.rs | 10 ++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/large_futures.rs b/clippy_lints/src/large_futures.rs index fd7965d564d59..b11e89a9b566c 100644 --- a/clippy_lints/src/large_futures.rs +++ b/clippy_lints/src/large_futures.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use clippy_utils::ty::implements_trait; use rustc_abi::Size; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; +use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -58,7 +58,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeFuture { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Match(scrutinee, _, MatchSource::AwaitDesugar) = expr.kind && let ExprKind::Call(func, [arg]) = scrutinee.kind - && let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind + && let ExprKind::Path(qpath) = func.kind + && cx.tcx.qpath_is_lang_item(qpath, LangItem::IntoFutureIntoFuture) && !expr.span.from_expansion() && let ty = cx.typeck_results().expr_ty(arg) && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 2884bbd9280d2..755f9454e9016 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -194,7 +194,7 @@ fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option inner = unpack_match(inner); inner = unpack_try(inner); inner = unpack_call_chain(inner); - inner = unpack_await(inner); + inner = unpack_await(cx, inner); // we type-check it to get whether it's a read/write or their vectorized forms // and keep only the ones that are produce io amount check_io_mode(cx, inner) @@ -277,13 +277,11 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { /// If `expr` is an (e).await, return the inner expression "e" that's being /// waited on. Otherwise return None. -fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { +fn unpack_await<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind && let ExprKind::Call(func, [arg_0]) = expr.kind - && matches!( - func.kind, - ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) - ) + && let ExprKind::Path(qpath) = func.kind + && cx.tcx.qpath_is_lang_item(qpath, hir::LangItem::IntoFutureIntoFuture) { return arg_0; } From e2b3508d190b32c3f994deca48f12c30be1ff029 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 1 Sep 2025 15:08:46 -0500 Subject: [PATCH 073/108] Remove QPath::LangItem from try --- clippy_lints/src/matches/try_err.rs | 6 +++--- clippy_lints/src/methods/clone_on_copy.rs | 5 +++-- clippy_lints/src/methods/str_splitn.rs | 8 ++++---- clippy_lints/src/needless_question_mark.rs | 5 +++-- clippy_lints/src/question_mark.rs | 14 ++++++++------ clippy_lints/src/returns/needless_return.rs | 5 +++-- clippy_lints/src/unused_io_amount.rs | 10 ++++------ 7 files changed, 28 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index 7358628f0f7e6..401b8468135bc 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -5,7 +5,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::option_arg_ty; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; -use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; +use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use rustc_span::{hygiene, sym}; @@ -23,8 +23,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine // val, // }; if let ExprKind::Call(match_fun, [try_arg]) = scrutinee.kind - && let ExprKind::Path(ref match_fun_path) = match_fun.kind - && matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)) + && let ExprKind::Path(match_fun_path) = match_fun.kind + && cx.tcx.qpath_is_lang_item(match_fun_path, LangItem::TryTraitBranch) && let ExprKind::Call(err_fun, [err_arg]) = try_arg.kind && err_fun.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && let Some(return_ty) = find_return_type(cx, &expr.kind) diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 2a0ae14a4b088..8980a22ad6131 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_copy; use rustc_errors::Applicability; -use rustc_hir::{BindingMode, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath}; +use rustc_hir::{BindingMode, ByRef, Expr, ExprKind, MatchSource, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; @@ -47,7 +47,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) // ? is a Call, makes sure not to rec *x?, but rather (*x)? ExprKind::Call(hir_callee, [_]) => matches!( hir_callee.kind, - ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, ..)) + ExprKind::Path(qpath) + if cx.tcx.qpath_is_lang_item(qpath, rustc_hir::LangItem::TryTraitBranch) ), ExprKind::MethodCall(_, self_arg, ..) if expr.hir_id == self_arg.hir_id => true, ExprKind::Match(_, _, MatchSource::TryDesugar(_) | MatchSource::AwaitDesugar) diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index eee7fb0c5a813..fff203296bce9 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -9,7 +9,7 @@ use clippy_utils::{paths, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ - BindingMode, Expr, ExprKind, HirId, LangItem, LetStmt, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, + BindingMode, Expr, ExprKind, HirId, LangItem, LetStmt, MatchSource, Node, Pat, PatKind, Stmt, StmtKind, }; use rustc_lint::LateContext; use rustc_middle::ty; @@ -332,12 +332,12 @@ fn parse_iter_usage<'tcx>( let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() { match e.kind { ExprKind::Call( - Expr { - kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)), + &Expr { + kind: ExprKind::Path(qpath), .. }, [_], - ) => { + ) if cx.tcx.qpath_is_lang_item(qpath, LangItem::TryTraitBranch) => { let parent_span = e.span.parent_callsite().unwrap(); if parent_span.ctxt() == ctxt { (Some(UnwrapKind::QuestionMark), parent_span) diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 986a827c59c55..29c45168b61e5 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::MaybeQPath; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Block, Body, Expr, ExprKind, LangItem, MatchSource, QPath}; +use rustc_hir::{Block, Body, Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -105,7 +105,8 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { } && let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar(_)) = &arg.kind && let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind - && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind + && let ExprKind::Path(qpath) = called.kind + && cx.tcx.qpath_is_lang_item(qpath, LangItem::TryTraitBranch) && expr.span.eq_ctxt(inner_expr.span) && let expr_ty = cx.typeck_results().expr_ty(expr) && let inner_ty = cx.typeck_results().expr_ty(inner_expr) diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index e67ea1f5e370d..1a6165d0af835 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -530,11 +530,13 @@ impl QuestionMark { } } -fn is_try_block(bl: &Block<'_>) -> bool { +fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool { if let Some(expr) = bl.expr && let ExprKind::Call(callee, [_]) = expr.kind + && let ExprKind::Path(qpath) = callee.kind + && cx.tcx.qpath_is_lang_item(qpath, LangItem::TryTraitFromOutput) { - callee.opt_lang_path() == Some(LangItem::TryTraitFromOutput) + true } else { false } @@ -590,8 +592,8 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { } } - fn check_block(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if is_try_block(block) { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if is_try_block(cx, block) { *self .try_block_depth_stack .last_mut() @@ -607,8 +609,8 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { self.try_block_depth_stack.pop(); } - fn check_block_post(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if is_try_block(block) { + fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if is_try_block(cx, block) { *self .try_block_depth_stack .last_mut() diff --git a/clippy_lints/src/returns/needless_return.rs b/clippy_lints/src/returns/needless_return.rs index 04739fc1b22ad..7d836b610e5f8 100644 --- a/clippy_lints/src/returns/needless_return.rs +++ b/clippy_lints/src/returns/needless_return.rs @@ -7,7 +7,7 @@ use clippy_utils::{ use rustc_ast::MetaItemInner; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, Expr, ExprKind, HirId, LangItem, MatchSource, QPath, StmtKind}; +use rustc_hir::{Body, Expr, ExprKind, HirId, LangItem, MatchSource, StmtKind}; use rustc_lint::{LateContext, Level, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_span::{BytePos, Pos, Span}; @@ -134,7 +134,8 @@ fn check_final_expr<'tcx>( let replacement = if let Some(inner_expr) = inner { // if desugar of `do yeet`, don't lint if let ExprKind::Call(path_expr, [_]) = inner_expr.kind - && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind + && let ExprKind::Path(qpath) = path_expr.kind + && cx.tcx.qpath_is_lang_item(qpath, LangItem::TryTraitFromYeet) { return; } diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 755f9454e9016..ffce03cb5dbea 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -192,7 +192,7 @@ fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option { inner = unpack_match(inner); - inner = unpack_try(inner); + inner = unpack_try(cx, inner); inner = unpack_call_chain(inner); inner = unpack_await(cx, inner); // we type-check it to get whether it's a read/write or their vectorized forms @@ -256,12 +256,10 @@ fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { expr } -fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { +fn unpack_try<'a>(cx: &LateContext<'_>, mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { while let ExprKind::Call(func, [arg_0]) = expr.kind - && matches!( - func.kind, - ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..)) - ) + && let ExprKind::Path(qpath) = func.kind + && cx.tcx.qpath_is_lang_item(qpath, hir::LangItem::TryTraitBranch) { expr = arg_0; } From a2d89559f73f57f639291d0687b4f51144d37dd3 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 1 Sep 2025 14:54:03 -0500 Subject: [PATCH 074/108] Remove QPath::LangItem --- .../src/arbitrary_source_item_ordering.rs | 1 - clippy_lints/src/incompatible_msrv.rs | 9 +++----- .../unnecessary_fallible_conversions.rs | 1 - .../src/redundant_type_annotations.rs | 1 - clippy_lints/src/types/mod.rs | 1 - clippy_lints/src/unconditional_recursion.rs | 2 -- .../src/unnecessary_map_on_constructor.rs | 1 - clippy_lints/src/utils/author.rs | 4 +--- clippy_utils/src/check_proc_macro.rs | 1 - clippy_utils/src/eager_or_lazy.rs | 1 - clippy_utils/src/hir_utils.rs | 4 ---- clippy_utils/src/lib.rs | 1 - clippy_utils/src/res.rs | 21 ++++++------------- clippy_utils/src/ty/type_certainty/mod.rs | 13 ------------ 14 files changed, 10 insertions(+), 51 deletions(-) diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index b8bf8b25b3233..454026e80ab30 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -550,7 +550,6 @@ fn get_item_name(item: &Item<'_>) -> Option { // This case doesn't exist in the clippy tests codebase. None }, - QPath::LangItem(_, _) => None, } } else { // Impls for anything that isn't a named type can be skipped. diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 89988be587588..bfe7a6355c00d 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -4,7 +4,7 @@ use clippy_utils::msrvs::Msrv; use clippy_utils::{is_in_const_context, is_in_test}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; -use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, QPath, RustcVersion, StabilityLevel, StableSince}; +use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; @@ -193,10 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { self.emit_lint_if_under_msrv(cx, method_did, expr.hir_id, span); } }, - // Desugaring into function calls by the compiler will use `QPath::LangItem` variants. Those should - // not be linted as they will not be generated in older compilers if the function is not available, - // and the compiler is allowed to call unstable functions. - ExprKind::Path(qpath @ (QPath::Resolved(..) | QPath::TypeRelative(..))) => { + ExprKind::Path(qpath) => { if let Some(path_def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id() { self.emit_lint_if_under_msrv(cx, path_def_id, expr.hir_id, expr.span); } @@ -206,7 +203,7 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { } fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &'tcx hir::Ty<'tcx, AmbigArg>) { - if let hir::TyKind::Path(qpath @ (QPath::Resolved(..) | QPath::TypeRelative(..))) = hir_ty.kind + if let hir::TyKind::Path(qpath) = hir_ty.kind && let Some(ty_def_id) = cx.qpath_res(&qpath, hir_ty.hir_id).opt_def_id() // `CStr` and `CString` have been moved around but have been available since Rust 1.0.0 && !matches!(cx.tcx.get_diagnostic_name(ty_def_id), Some(sym::cstr_type | sym::cstring_type)) diff --git a/clippy_lints/src/methods/unnecessary_fallible_conversions.rs b/clippy_lints/src/methods/unnecessary_fallible_conversions.rs index 0ec2d8b4fc312..23546cad0af70 100644 --- a/clippy_lints/src/methods/unnecessary_fallible_conversions.rs +++ b/clippy_lints/src/methods/unnecessary_fallible_conversions.rs @@ -181,7 +181,6 @@ pub(super) fn check_function(cx: &LateContext<'_>, expr: &Expr<'_>, callee: &Exp QPath::TypeRelative(_, seg) => Some(SpansKind::Fn { fn_span: seg.ident.span, }), - QPath::LangItem(_, _) => unreachable!("`TryFrom` and `TryInto` are not lang items"), }; check( diff --git a/clippy_lints/src/redundant_type_annotations.rs b/clippy_lints/src/redundant_type_annotations.rs index 7bd4d6e993b47..e298fa55a2b69 100644 --- a/clippy_lints/src/redundant_type_annotations.rs +++ b/clippy_lints/src/redundant_type_annotations.rs @@ -97,7 +97,6 @@ fn extract_fn_ty<'tcx>( // let a: String = String::new(); // let a: String = String::get_string(); hir::QPath::TypeRelative(..) => func_hir_id_to_func_ty(cx, call.hir_id), - hir::QPath::LangItem(..) => None, } } diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index ccb027f77bf5f..7018146f184b2 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -655,7 +655,6 @@ impl Types { } } }, - QPath::LangItem(..) => {}, } }, TyKind::Path(ref qpath) => { diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index e6663182fec53..48f1cda9ee7b9 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -106,7 +106,6 @@ fn get_hir_ty_def_id<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: rustc_hir::Ty<'tcx>) -> Op _ => None, } }, - QPath::LangItem(..) => None, } } @@ -291,7 +290,6 @@ fn is_default_method_on_current_ty<'tcx>(tcx: TyCtxt<'tcx>, qpath: QPath<'tcx>, } get_hir_ty_def_id(tcx, *ty) == Some(implemented_ty_id) }, - QPath::LangItem(..) => false, } } diff --git a/clippy_lints/src/unnecessary_map_on_constructor.rs b/clippy_lints/src/unnecessary_map_on_constructor.rs index 94b1a34455ff0..af9f291f5deb7 100644 --- a/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -60,7 +60,6 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { } }, hir::QPath::TypeRelative(_, path) => path.ident.name, - hir::QPath::LangItem(..) => return, }; match constructor_symbol { sym::Some | sym::Ok if path.ident.name == sym::map => (), diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 68e51dace2db2..222427cd3075f 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -270,9 +270,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn qpath(&self, qpath: &Binding<&QPath<'_>>, hir_id_binding: &str, hir_id: HirId) { - if let QPath::LangItem(lang_item, ..) = *qpath.value { - chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); - } else if let Some(def_id) = self.cx.qpath_res(qpath.value, hir_id).opt_def_id() + if let Some(def_id) = self.cx.qpath_res(qpath.value, hir_id).opt_def_id() && !def_id.is_local() { bind!(self, def_id); diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index ff3e7b94f03bf..544218e09930a 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -121,7 +121,6 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) { (start, end) }, QPath::TypeRelative(_, name) => (Pat::Str(""), Pat::Sym(name.ident.name)), - QPath::LangItem(..) => (Pat::Str(""), Pat::Str("")), } } diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index eb3f442ac754b..6f0e57bd3ab1c 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -167,7 +167,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS QPath::TypeRelative(_, name) => { self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, !args.is_empty()); }, - QPath::LangItem(..) => self.eagerness = Lazy, }, _ => self.eagerness = Lazy, }, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 974596a81a409..710b88e92154e 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -555,7 +555,6 @@ impl HirEqInterExpr<'_, '_, '_> { (QPath::TypeRelative(lty, lseg), QPath::TypeRelative(rty, rseg)) => { self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg) }, - (QPath::LangItem(llang_item, ..), QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item, _ => false, } } @@ -1092,9 +1091,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { QPath::TypeRelative(_, path) => { self.hash_name(path.ident.name); }, - QPath::LangItem(lang_item, ..) => { - std::mem::discriminant(lang_item).hash(&mut self.s); - }, } // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 75247d8fa751b..11220a8c4c017 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -361,7 +361,6 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { match *path { QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"), QPath::TypeRelative(_, seg) => seg, - QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"), } } diff --git a/clippy_utils/src/res.rs b/clippy_utils/src/res.rs index 90b9529278964..d1f6ebf35ce24 100644 --- a/clippy_utils/src/res.rs +++ b/clippy_utils/src/res.rs @@ -99,15 +99,6 @@ pub trait MaybeQPath<'a>: Copy { /// use for type dependant lookup. fn opt_qpath(self) -> Option>; - /// If this node is a `QPath::LangItem` gets the item it resolves to. - #[inline] - fn opt_lang_path(self) -> Option { - match self.opt_qpath() { - Some((&QPath::LangItem(item, _), _)) => Some(item), - _ => None, - } - } - /// If this is a path gets its resolution. Returns `Res::Err` otherwise. #[inline] #[cfg_attr(debug_assertions, track_caller)] @@ -116,10 +107,10 @@ pub trait MaybeQPath<'a>: Copy { fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res { match *qpath { QPath::Resolved(_, p) => p.res, - QPath::TypeRelative(..) | QPath::LangItem(..) if let Some((kind, id)) = typeck.ty_based_def(id) => { + QPath::TypeRelative(..) if let Some((kind, id)) = typeck.ty_based_def(id) => { Res::Def(kind, id) }, - QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + QPath::TypeRelative(..) => Res::Err, } } match self.opt_qpath() { @@ -148,7 +139,7 @@ pub trait MaybeQPath<'a>: Copy { { Res::Def(kind, id) }, - QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + QPath::Resolved(..) | QPath::TypeRelative(..) => Res::Err, } } match self.opt_qpath() { @@ -168,7 +159,7 @@ pub trait MaybeQPath<'a>: Copy { QPath::TypeRelative(_, seg) if let Some((kind, id)) = typeck.ty_based_def(id) => { (Res::Def(kind, id), Some(seg)) }, - QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => (Res::Err, None), + QPath::Resolved(..) | QPath::TypeRelative(..) => (Res::Err, None), } } match self.opt_qpath() { @@ -202,7 +193,7 @@ pub trait MaybeQPath<'a>: Copy { }, _, ) if let Some((kind, id)) = typeck.ty_based_def(id) => Res::Def(kind, id), - QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + QPath::Resolved(..) | QPath::TypeRelative(..) => Res::Err, } } match self.opt_qpath() { @@ -244,7 +235,7 @@ pub trait MaybeQPath<'a>: Copy { { Res::Def(kind, id) }, - QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + QPath::Resolved(..) | QPath::TypeRelative(..) => Res::Err, } } match self.opt_qpath() { diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index d46c7bdcd4c1d..eadb07a11be02 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -197,19 +197,6 @@ fn qpath_certainty(cx: &LateContext<'_>, qpath: &QPath<'_>, resolves_to_type: bo QPath::TypeRelative(ty, path_segment) => { path_segment_certainty(cx, type_certainty(cx, ty), path_segment, resolves_to_type) }, - - QPath::LangItem(lang_item, ..) => cx - .tcx - .lang_items() - .get(*lang_item) - .map_or(Certainty::Uncertain, |def_id| { - let generics = cx.tcx.generics_of(def_id); - if generics.is_empty() { - Certainty::Certain(if resolves_to_type { Some(def_id) } else { None }) - } else { - Certainty::Uncertain - } - }), }; debug_assert!(resolves_to_type || certainty.to_def_id().is_none()); certainty From 02e4516252f4eb0821e9e009fd46e55d087e7d05 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 23 Oct 2025 15:44:41 +0200 Subject: [PATCH 075/108] fix(double_parens): don't lint in proc-macros --- clippy_lints/src/double_parens.rs | 37 ++++++++++++-- tests/ui/auxiliary/macro_rules.rs | 12 +++++ tests/ui/auxiliary/proc_macro_derive.rs | 11 +++++ tests/ui/double_parens.fixed | 65 +++++++++++++++++++++++++ tests/ui/double_parens.rs | 65 +++++++++++++++++++++++++ tests/ui/double_parens.stderr | 55 ++++++++++++++++----- 6 files changed, 229 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index bddf4702fb340..8defbeeaa5f22 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{HasSession, snippet_with_applicability, snippet_with_context}; +use clippy_utils::source::{HasSession, SpanRangeExt, snippet_with_applicability, snippet_with_context}; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -47,8 +47,12 @@ impl EarlyLintPass for DoubleParens { // ^^^^^^ expr // ^^^^ inner ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => { - // suggest removing the outer parens - if expr.span.eq_ctxt(inner.span) { + if expr.span.eq_ctxt(inner.span) + && !expr.span.in_external_macro(cx.sess().source_map()) + && check_source(cx, inner) + { + // suggest removing the outer parens + let mut applicability = Applicability::MachineApplicable; // We don't need to use `snippet_with_context` here, because: // - if `inner`'s `ctxt` is from macro, we don't lint in the first place (see the check above) @@ -74,8 +78,12 @@ impl EarlyLintPass for DoubleParens { if let [arg] = &**args && let ExprKind::Paren(inner) = &arg.kind => { - // suggest removing the inner parens - if expr.span.eq_ctxt(arg.span) { + if expr.span.eq_ctxt(arg.span) + && !arg.span.in_external_macro(cx.sess().source_map()) + && check_source(cx, arg) + { + // suggest removing the inner parens + let mut applicability = Applicability::MachineApplicable; let sugg = snippet_with_context(cx.sess(), inner.span, arg.span.ctxt(), "_", &mut applicability).0; span_lint_and_sugg( @@ -93,3 +101,22 @@ impl EarlyLintPass for DoubleParens { } } } + +/// Check that the span does indeed look like `( (..) )` +fn check_source(cx: &EarlyContext<'_>, inner: &Expr) -> bool { + if let Some(sfr) = inner.span.get_source_range(cx) + // this is the same as `SourceFileRange::as_str`, but doesn't apply the range right away, because + // we're interested in the source code outside it + && let Some(src) = sfr.sf.src.as_ref().map(|src| src.as_str()) + && let Some((start, outer_after_inner)) = src.split_at_checked(sfr.range.end) + && let Some((outer_before_inner, inner)) = start.split_at_checked(sfr.range.start) + && outer_before_inner.trim_end().ends_with('(') + && inner.starts_with('(') + && inner.ends_with(')') + && outer_after_inner.trim_start().starts_with(')') + { + true + } else { + false + } +} diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 9efbb39084976..1f1afe4ea3a53 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -57,3 +57,15 @@ macro_rules! bad_transmute { std::mem::transmute($e) }; } + +#[macro_export] +#[rustfmt::skip] +macro_rules! double_parens { + ($a:expr, $b:expr, $c:expr, $d:expr) => {{ + let a = ($a); + let a = (()); + let b = ((5)); + let c = std::convert::identity((5)); + InterruptMask((($a.union($b).union($c).union($d)).into_bits()) as u32) + }}; +} diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 5465092287179..629a500ff6f56 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -230,3 +230,14 @@ pub fn allow_lint_same_span_derive(input: TokenStream) -> TokenStream { span_help(Group::new(Delimiter::Brace, TokenStream::new()).into()), ]) } + +#[proc_macro_derive(DoubleParens)] +pub fn derive_double_parens(_: TokenStream) -> TokenStream { + quote! { + fn foo() { + let a = (()); + let b = ((5)); + let c = std::convert::identity((5)); + } + } +} diff --git a/tests/ui/double_parens.fixed b/tests/ui/double_parens.fixed index dedc513438d11..024af68401327 100644 --- a/tests/ui/double_parens.fixed +++ b/tests/ui/double_parens.fixed @@ -1,8 +1,13 @@ +//@aux-build:proc_macros.rs +//@aux-build:proc_macro_derive.rs +//@aux-build:macro_rules.rs #![warn(clippy::double_parens)] #![expect(clippy::eq_op, clippy::no_effect)] #![feature(custom_inner_attributes)] #![rustfmt::skip] +use proc_macros::{external, with_span}; + fn dummy_fn(_: T) {} struct DummyStruct; @@ -96,4 +101,64 @@ fn issue9000(x: DummyStruct) { //~^ double_parens } +fn issue15892() { + use macro_rules::double_parens as double_parens_external; + + macro_rules! double_parens{ + ($a:expr, $b:expr, $c:expr, $d:expr) => {{ + let a = ($a); + let a = (); + //~^ double_parens + let b = (5); + //~^ double_parens + let c = std::convert::identity(5); + //~^ double_parens + InterruptMask((($a.union($b).union($c).union($d)).into_bits()) as u32) + }}; + } + + // Don't lint: external macro + (external!((5))); + external!(((5))); + + #[repr(transparent)] + #[derive(Clone, Copy, PartialEq, Eq)] + pub struct InterruptMask(u32); + + impl InterruptMask { + pub const OE: InterruptMask = InterruptMask(1 << 10); + pub const BE: InterruptMask = InterruptMask(1 << 9); + pub const PE: InterruptMask = InterruptMask(1 << 8); + pub const FE: InterruptMask = InterruptMask(1 << 7); + // Lint: internal macro + pub const E: InterruptMask = double_parens!((Self::OE), Self::BE, Self::PE, Self::FE); + // Don't lint: external macro + pub const F: InterruptMask = double_parens_external!((Self::OE), Self::BE, Self::PE, Self::FE); + #[allow(clippy::unnecessary_cast)] + pub const G: InterruptMask = external!( + InterruptMask((((Self::OE.union(Self::BE).union(Self::PE).union(Self::FE))).into_bits()) as u32) + ); + #[allow(clippy::unnecessary_cast)] + // Don't lint: external proc-macro + pub const H: InterruptMask = with_span!(span + InterruptMask((((Self::OE.union(Self::BE).union(Self::PE).union(Self::FE))).into_bits()) as u32) + ); + pub const fn into_bits(self) -> u32 { + self.0 + } + #[must_use] + pub const fn union(self, rhs: Self) -> Self { + InterruptMask(self.0 | rhs.0) + } + } +} + +fn issue15940() { + use proc_macro_derive::DoubleParens; + + #[derive(DoubleParens)] + // Don't lint: external derive macro + pub struct Person; +} + fn main() {} diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 27f252485b714..8a76f2837f353 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -1,8 +1,13 @@ +//@aux-build:proc_macros.rs +//@aux-build:proc_macro_derive.rs +//@aux-build:macro_rules.rs #![warn(clippy::double_parens)] #![expect(clippy::eq_op, clippy::no_effect)] #![feature(custom_inner_attributes)] #![rustfmt::skip] +use proc_macros::{external, with_span}; + fn dummy_fn(_: T) {} struct DummyStruct; @@ -96,4 +101,64 @@ fn issue9000(x: DummyStruct) { //~^ double_parens } +fn issue15892() { + use macro_rules::double_parens as double_parens_external; + + macro_rules! double_parens{ + ($a:expr, $b:expr, $c:expr, $d:expr) => {{ + let a = ($a); + let a = (()); + //~^ double_parens + let b = ((5)); + //~^ double_parens + let c = std::convert::identity((5)); + //~^ double_parens + InterruptMask((($a.union($b).union($c).union($d)).into_bits()) as u32) + }}; + } + + // Don't lint: external macro + (external!((5))); + external!(((5))); + + #[repr(transparent)] + #[derive(Clone, Copy, PartialEq, Eq)] + pub struct InterruptMask(u32); + + impl InterruptMask { + pub const OE: InterruptMask = InterruptMask(1 << 10); + pub const BE: InterruptMask = InterruptMask(1 << 9); + pub const PE: InterruptMask = InterruptMask(1 << 8); + pub const FE: InterruptMask = InterruptMask(1 << 7); + // Lint: internal macro + pub const E: InterruptMask = double_parens!((Self::OE), Self::BE, Self::PE, Self::FE); + // Don't lint: external macro + pub const F: InterruptMask = double_parens_external!((Self::OE), Self::BE, Self::PE, Self::FE); + #[allow(clippy::unnecessary_cast)] + pub const G: InterruptMask = external!( + InterruptMask((((Self::OE.union(Self::BE).union(Self::PE).union(Self::FE))).into_bits()) as u32) + ); + #[allow(clippy::unnecessary_cast)] + // Don't lint: external proc-macro + pub const H: InterruptMask = with_span!(span + InterruptMask((((Self::OE.union(Self::BE).union(Self::PE).union(Self::FE))).into_bits()) as u32) + ); + pub const fn into_bits(self) -> u32 { + self.0 + } + #[must_use] + pub const fn union(self, rhs: Self) -> Self { + InterruptMask(self.0 | rhs.0) + } + } +} + +fn issue15940() { + use proc_macro_derive::DoubleParens; + + #[derive(DoubleParens)] + // Don't lint: external derive macro + pub struct Person; +} + fn main() {} diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index 3a740e44cacf3..51b5c6279b211 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -1,5 +1,5 @@ error: unnecessary parentheses - --> tests/ui/double_parens.rs:15:5 + --> tests/ui/double_parens.rs:20:5 | LL | ((0)) | ^^^^^ help: remove them: `(0)` @@ -8,37 +8,37 @@ LL | ((0)) = help: to override `-D warnings` add `#[allow(clippy::double_parens)]` error: unnecessary parentheses - --> tests/ui/double_parens.rs:20:14 + --> tests/ui/double_parens.rs:25:14 | LL | dummy_fn((0)); | ^^^ help: remove them: `0` error: unnecessary parentheses - --> tests/ui/double_parens.rs:25:20 + --> tests/ui/double_parens.rs:30:20 | LL | x.dummy_method((0)); | ^^^ help: remove them: `0` error: unnecessary parentheses - --> tests/ui/double_parens.rs:30:5 + --> tests/ui/double_parens.rs:35:5 | LL | ((1, 2)) | ^^^^^^^^ help: remove them: `(1, 2)` error: unnecessary parentheses - --> tests/ui/double_parens.rs:36:5 + --> tests/ui/double_parens.rs:41:5 | LL | (()) | ^^^^ help: remove them: `()` error: unnecessary parentheses - --> tests/ui/double_parens.rs:59:16 + --> tests/ui/double_parens.rs:64:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); | ^^^^^^^^ help: remove them: `(1, 2)` error: unnecessary parentheses - --> tests/ui/double_parens.rs:84:16 + --> tests/ui/double_parens.rs:89:16 | LL | () => {((100))} | ^^^^^^^ help: remove them: `(100)` @@ -49,22 +49,55 @@ LL | bar!(); = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary parentheses - --> tests/ui/double_parens.rs:91:5 + --> tests/ui/double_parens.rs:96:5 | LL | ((vec![1, 2])); | ^^^^^^^^^^^^^^ help: remove them: `(vec![1, 2])` error: unnecessary parentheses - --> tests/ui/double_parens.rs:93:14 + --> tests/ui/double_parens.rs:98:14 | LL | dummy_fn((vec![1, 2])); | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` error: unnecessary parentheses - --> tests/ui/double_parens.rs:95:20 + --> tests/ui/double_parens.rs:100:20 | LL | x.dummy_method((vec![1, 2])); | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` -error: aborting due to 10 previous errors +error: unnecessary parentheses + --> tests/ui/double_parens.rs:110:21 + | +LL | let a = (()); + | ^^^^ help: remove them: `()` +... +LL | pub const E: InterruptMask = double_parens!((Self::OE), Self::BE, Self::PE, Self::FE); + | -------------------------------------------------------- in this macro invocation + | + = note: this error originates in the macro `double_parens` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:112:21 + | +LL | let b = ((5)); + | ^^^^^ help: remove them: `(5)` +... +LL | pub const E: InterruptMask = double_parens!((Self::OE), Self::BE, Self::PE, Self::FE); + | -------------------------------------------------------- in this macro invocation + | + = note: this error originates in the macro `double_parens` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:114:44 + | +LL | let c = std::convert::identity((5)); + | ^^^ help: remove them: `5` +... +LL | pub const E: InterruptMask = double_parens!((Self::OE), Self::BE, Self::PE, Self::FE); + | -------------------------------------------------------- in this macro invocation + | + = note: this error originates in the macro `double_parens` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 13 previous errors From 78db66e891a115ad306a42f2e756864357ebd9bf Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 28 Oct 2025 08:16:43 -0500 Subject: [PATCH 076/108] Remove AssignDesugar span --- clippy_lints/src/shadow.rs | 2 +- clippy_lints/src/undocumented_unsafe_blocks.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 7fdea6bec5100..f6083394fea52 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { .tcx .hir_parent_iter(pat.hir_id) .find(|(_, node)| !matches!(node, Node::Pat(_) | Node::PatField(_))) - && let LocalSource::AssignDesugar(_) = let_stmt.source + && let LocalSource::AssignDesugar = let_stmt.source { return; } diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 9afa9d65c2613..9935dc309611f 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -374,7 +374,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>( hir::Stmt { kind: hir::StmtKind::Let(hir::LetStmt { - source: hir::LocalSource::AssignDesugar(_), + source: hir::LocalSource::AssignDesugar, .. }), .. From 97e681fb40aa71d98476b311b38fb6c432ce14e4 Mon Sep 17 00:00:00 2001 From: Aliaksei Semianiuk Date: Sat, 25 Oct 2025 14:37:28 +0500 Subject: [PATCH 077/108] Changelog for Clippy 1.91 --- CHANGELOG.md | 83 ++++++++++++++++++++++++++++++++++ clippy_lints/src/formatting.rs | 2 +- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9279267ebf0a2..f47d5f17a02a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,89 @@ document. [e9b7045...master](https://github.com/rust-lang/rust-clippy/compare/e9b7045...master) +## Rust 1.91 + +Current stable, released 2025-10-30 + +[View all 146 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-07-25T21%3A05%3A11Z..2025-09-04T22%3A34%3A27Z+base%3Amaster) + +### New Lints + +* Added [`possible_missing_else`] to `suspicious` + [#15317](https://github.com/rust-lang/rust-clippy/pull/15317) + +### Moves and Deprecations + +* Moved [`cognitive_complexity`] from `nursery` to `restriction` + [#15415](https://github.com/rust-lang/rust-clippy/pull/15415) +* Moved [`declare_interior_mutable_const`] from `style` to `suspicious` + [#15454](https://github.com/rust-lang/rust-clippy/pull/15454) +* Moved [`crosspointer_transmute`] from `complexity` to `suspicious` + [#15403](https://github.com/rust-lang/rust-clippy/pull/15403) + +### Enhancements + +* [`excessive_precision`] added `const_literal_digits_threshold` option to suppress overly precise constants. + [#15193](https://github.com/rust-lang/rust-clippy/pull/15193) +* [`unwrap_in_result`] rewritten for better accuracy; now lints on `.unwrap()` and `.expect()` + directly and no longer mixes `Result` and `Option`. + [#15445](https://github.com/rust-lang/rust-clippy/pull/15445) +* [`panic`] now works in `const` contexts. + [#15565](https://github.com/rust-lang/rust-clippy/pull/15565) +* [`implicit_clone`] now also lints `to_string` calls (merging [`string_to_string`] behavior). + [#14177](https://github.com/rust-lang/rust-clippy/pull/14177) +* [`collapsible_match`] improved suggestions to handle necessary ref/dereferencing. + [#14221](https://github.com/rust-lang/rust-clippy/pull/14221) +* [`map_identity`] now suggests making variables mutable when required; recognizes tuple struct restructuring. + [#15261](https://github.com/rust-lang/rust-clippy/pull/15261) +* [`option_map_unit_fn`] preserves `unsafe` blocks in suggestions. + [#15570](https://github.com/rust-lang/rust-clippy/pull/15570) +* [`unnecessary_mut_passed`] provides structured, clearer fix suggestions. + [#15438](https://github.com/rust-lang/rust-clippy/pull/15438) +* [`float_equality_without_abs`] now checks `f16` and `f128` types. + [#15054](https://github.com/rust-lang/rust-clippy/pull/15054) +* [`doc_markdown`] expanded whitelist (`InfiniBand`, `RoCE`, `PowerPC`) and improved handling of + identifiers like NixOS. + [#15558](https://github.com/rust-lang/rust-clippy/pull/15558) +* [`clone_on_ref_ptr`] now suggests fully qualified paths to avoid resolution errors. + [#15561](https://github.com/rust-lang/rust-clippy/pull/15561) +* [`manual_assert`] simplifies boolean expressions in suggested fixes. + [#15368](https://github.com/rust-lang/rust-clippy/pull/15368) +* [`four_forward_slashes`] warns about bare CR in comments and avoids invalid autofixes. + [#15175](https://github.com/rust-lang/rust-clippy/pull/15175) + +### False Positive Fixes + +* [`alloc_instead_of_core`] fixed FP when `alloc` is an alias + [#15581](https://github.com/rust-lang/rust-clippy/pull/15581) +* [`needless_range_loop`] fixed FP and FN when meeting multidimensional array + [#15486](https://github.com/rust-lang/rust-clippy/pull/15486) +* [`semicolon_inside_block`] fixed FP when attribute over expr is not enabled + [#15476](https://github.com/rust-lang/rust-clippy/pull/15476) +* [`unnested_or_patterns`] fixed FP on structs with only shorthand field patterns + [#15343](https://github.com/rust-lang/rust-clippy/pull/15343) +* [`match_ref_pats`] fixed FP on match scrutinee of never type + [#15474](https://github.com/rust-lang/rust-clippy/pull/15474) +* [`infinite_loop`] fixed FP in async blocks that are not awaited + [#15157](https://github.com/rust-lang/rust-clippy/pull/15157) +* [`iter_on_single_items`] fixed FP on function pointers and let statements + [#15013](https://github.com/rust-lang/rust-clippy/pull/15013) + +### ICE Fixes + +* [`len_zero`] fix ICE when fn len has a return type without generic type params + [#15660](https://github.com/rust-lang/rust-clippy/pull/15660) + +### Documentation Improvements + +* [`cognitive_complexity`] corrected documentation to state lint is in `restriction`, not `nursery` + [#15563](https://github.com/rust-lang/rust-clippy/pull/15563) + +### Performance Improvements + +* [`doc_broken_link`] optimized by 99.77% (12M → 27k instructions) + [#15385](https://github.com/rust-lang/rust-clippy/pull/15385) + ## Rust 1.90 Current stable, released 2025-09-18 diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 1c751643becb6..3b9c70e23e20d 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -110,7 +110,7 @@ declare_clippy_lint! { /// } if bar { // looks like an `else` is missing here /// } /// ``` - #[clippy::version = "1.90.0"] + #[clippy::version = "1.91.0"] pub POSSIBLE_MISSING_ELSE, suspicious, "possibly missing `else`" From e71b46f14f56901f30125d1ba646f915f4308795 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 26 Oct 2025 11:04:22 +0100 Subject: [PATCH 078/108] refactor(invalid_upcast_comparisons): move to under `operators` and clean up a bit: - put the lint functions in the order they're called - simplify `upcast_comparison_bounds_err` - It first handled `Eq`/`Ne`, then one pair of cases with `Lt`/Le`, then another pair of cases with `Lt`/`Le` -- turn that into a single `match rel`. - It used a pattern of `if invert { a } else { b }`, which I found to be hard because of the need to keep in mind the implicit `invert &&` and `!invert &&` when looking at `a` and `b`. The form `(invert && a) || (!invert && b)` should be a bit more explicit, while compiling down to the same thing - The conditions were already partially written in the order `lb`, `norm_rhs_val`, `ub` -- change the ones that weren't --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 - .../invalid_upcast_comparisons.rs | 148 +++++++----------- clippy_lints/src/operators/mod.rs | 25 +++ 4 files changed, 82 insertions(+), 95 deletions(-) rename clippy_lints/src/{ => operators}/invalid_upcast_comparisons.rs (56%) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index bd2971fa150de..1603114b3103c 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -228,7 +228,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, - crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, crate::item_name_repetitions::ENUM_VARIANT_NAMES_INFO, crate::item_name_repetitions::MODULE_INCEPTION_INFO, crate::item_name_repetitions::MODULE_NAME_REPETITIONS_INFO, @@ -591,6 +590,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::operators::IMPOSSIBLE_COMPARISONS_INFO, crate::operators::INEFFECTIVE_BIT_MASK_INFO, crate::operators::INTEGER_DIVISION_INFO, + crate::operators::INVALID_UPCAST_COMPARISONS_INFO, crate::operators::MANUAL_DIV_CEIL_INFO, crate::operators::MANUAL_IS_MULTIPLE_OF_INFO, crate::operators::MANUAL_MIDPOINT_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6342b80ab134e..5628caeb0b785 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -172,7 +172,6 @@ mod init_numbered_fields; mod inline_fn_without_body; mod int_plus_one; mod integer_division_remainder_used; -mod invalid_upcast_comparisons; mod item_name_repetitions; mod items_after_statements; mod items_after_test_module; @@ -542,7 +541,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(conf))); store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|_| Box::new(empty_enums::EmptyEnums)); - store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/operators/invalid_upcast_comparisons.rs similarity index 56% rename from clippy_lints/src/invalid_upcast_comparisons.rs rename to clippy_lints/src/operators/invalid_upcast_comparisons.rs index 885649074ab63..b32848a323375 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/operators/invalid_upcast_comparisons.rs @@ -1,9 +1,8 @@ use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, IntTy, UintTy}; -use rustc_session::declare_lint_pass; use rustc_span::Span; use clippy_utils::comparisons; @@ -12,29 +11,26 @@ use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet_with_context; -declare_clippy_lint! { - /// ### What it does - /// Checks for comparisons where the relation is always either - /// true or false, but where one side has been upcast so that the comparison is - /// necessary. Only integer types are checked. - /// - /// ### Why is this bad? - /// An expression like `let x : u8 = ...; (x as u32) > 300` - /// will mistakenly imply that it is possible for `x` to be outside the range of - /// `u8`. - /// - /// ### Example - /// ```no_run - /// let x: u8 = 1; - /// (x as u32) > 300; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub INVALID_UPCAST_COMPARISONS, - pedantic, - "a comparison involving an upcast which is always true or false" -} +use super::INVALID_UPCAST_COMPARISONS; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + cmp: BinOpKind, + lhs: &'tcx Expr<'_>, + rhs: &'tcx Expr<'_>, + span: Span, +) { + let normalized = comparisons::normalize_comparison(cmp, lhs, rhs); + let Some((rel, normalized_lhs, normalized_rhs)) = normalized else { + return; + }; -declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]); + let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs); + let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs); + + upcast_comparison_bounds_err(cx, span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false); + upcast_comparison_bounds_err(cx, span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true); +} fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(FullInt, FullInt)> { if let ExprKind::Cast(cast_exp, _) = expr.kind { @@ -68,29 +64,6 @@ fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option< } } -fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { - if let ExprKind::Cast(cast_val, _) = expr.kind { - let mut applicability = Applicability::MachineApplicable; - let (cast_val_snip, _) = snippet_with_context( - cx, - cast_val.span, - expr.span.ctxt(), - "the expression", - &mut applicability, - ); - span_lint( - cx, - INVALID_UPCAST_COMPARISONS, - span, - format!( - "because of the numeric bounds on `{}` prior to casting, this expression is always {}", - cast_val_snip, - if always { "true" } else { "false" }, - ), - ); - } -} - fn upcast_comparison_bounds_err<'tcx>( cx: &LateContext<'tcx>, span: Span, @@ -103,63 +76,54 @@ fn upcast_comparison_bounds_err<'tcx>( if let Some((lb, ub)) = lhs_bounds && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs, span.ctxt()) { - if rel == Rel::Eq || rel == Rel::Ne { - if norm_rhs_val < lb || norm_rhs_val > ub { - err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); - } - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val < lb - } else { - ub < norm_rhs_val + match rel { + Rel::Eq => { + if norm_rhs_val < lb || ub < norm_rhs_val { + err_upcast_comparison(cx, span, lhs, false); } }, - Rel::Le => { - if invert { - norm_rhs_val <= lb - } else { - ub <= norm_rhs_val + Rel::Ne => { + if norm_rhs_val < lb || ub < norm_rhs_val { + err_upcast_comparison(cx, span, lhs, true); } }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, true); - } else if match rel { Rel::Lt => { - if invert { - norm_rhs_val >= ub - } else { - lb >= norm_rhs_val + if (invert && norm_rhs_val < lb) || (!invert && ub < norm_rhs_val) { + err_upcast_comparison(cx, span, lhs, true); + } else if (!invert && norm_rhs_val <= lb) || (invert && ub <= norm_rhs_val) { + err_upcast_comparison(cx, span, lhs, false); } }, Rel::Le => { - if invert { - norm_rhs_val > ub - } else { - lb > norm_rhs_val + if (invert && norm_rhs_val <= lb) || (!invert && ub <= norm_rhs_val) { + err_upcast_comparison(cx, span, lhs, true); + } else if (!invert && norm_rhs_val < lb) || (invert && ub < norm_rhs_val) { + err_upcast_comparison(cx, span, lhs, false); } }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, false); } } } -impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind { - let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs); - let Some((rel, normalized_lhs, normalized_rhs)) = normalized else { - return; - }; - - let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs); - let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs); - - upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false); - upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true); - } +fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { + if let ExprKind::Cast(cast_val, _) = expr.kind { + let mut applicability = Applicability::MachineApplicable; + let (cast_val_snip, _) = snippet_with_context( + cx, + cast_val.span, + expr.span.ctxt(), + "the expression", + &mut applicability, + ); + span_lint( + cx, + INVALID_UPCAST_COMPARISONS, + span, + format!( + "because of the numeric bounds on `{}` prior to casting, this expression is always {}", + cast_val_snip, + if always { "true" } else { "false" }, + ), + ); } } diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 75a6d57efb3c5..b960f4ddffa30 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -11,6 +11,7 @@ mod float_cmp; mod float_equality_without_abs; mod identity_op; mod integer_division; +mod invalid_upcast_comparisons; mod manual_div_ceil; mod manual_is_multiple_of; mod manual_midpoint; @@ -888,6 +889,28 @@ declare_clippy_lint! { "manually reimplementing `div_ceil`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for comparisons where the relation is always either + /// true or false, but where one side has been upcast so that the comparison is + /// necessary. Only integer types are checked. + /// + /// ### Why is this bad? + /// An expression like `let x : u8 = ...; (x as u32) > 300` + /// will mistakenly imply that it is possible for `x` to be outside the range of + /// `u8`. + /// + /// ### Example + /// ```no_run + /// let x: u8 = 1; + /// (x as u32) > 300; + /// ``` + #[clippy::version = "pre 1.29.0"] + pub INVALID_UPCAST_COMPARISONS, + pedantic, + "a comparison involving an upcast which is always true or false" +} + pub struct Operators { arithmetic_context: numeric_arithmetic::Context, verbose_bit_mask_threshold: u64, @@ -935,6 +958,7 @@ impl_lint_pass!(Operators => [ MANUAL_MIDPOINT, MANUAL_IS_MULTIPLE_OF, MANUAL_DIV_CEIL, + INVALID_UPCAST_COMPARISONS, ]); impl<'tcx> LateLintPass<'tcx> for Operators { @@ -950,6 +974,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { } erasing_op::check(cx, e, op.node, lhs, rhs); identity_op::check(cx, e, op.node, lhs, rhs); + invalid_upcast_comparisons::check(cx, op.node, lhs, rhs, e.span); needless_bitwise_bool::check(cx, e, op.node, lhs, rhs); manual_midpoint::check(cx, e, op.node, lhs, rhs, self.msrv); manual_is_multiple_of::check(cx, e, op.node, lhs, rhs, self.msrv); From 90b34cbc19fc6e3c1b03391b7cdf401268783460 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 25 Oct 2025 23:51:17 +0200 Subject: [PATCH 079/108] put the operators in backticks in the diagnostics --- .../src/integer_division_remainder_used.rs | 2 +- .../ui/integer_division_remainder_used.stderr | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/integer_division_remainder_used.rs b/clippy_lints/src/integer_division_remainder_used.rs index a1215491b48c3..494b311943a4a 100644 --- a/clippy_lints/src/integer_division_remainder_used.rs +++ b/clippy_lints/src/integer_division_remainder_used.rs @@ -43,7 +43,7 @@ impl LateLintPass<'_> for IntegerDivisionRemainderUsed { cx, INTEGER_DIVISION_REMAINDER_USED, expr.span.source_callsite(), - format!("use of {} has been disallowed in this context", op.node.as_str()), + format!("use of `{}` has been disallowed in this context", op.node.as_str()), ); } } diff --git a/tests/ui/integer_division_remainder_used.stderr b/tests/ui/integer_division_remainder_used.stderr index ea9f0e716c7d4..32a61a8585f03 100644 --- a/tests/ui/integer_division_remainder_used.stderr +++ b/tests/ui/integer_division_remainder_used.stderr @@ -1,4 +1,4 @@ -error: use of / has been disallowed in this context +error: use of `/` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:10:14 | LL | Self(self.0 / rhs.0) @@ -7,49 +7,49 @@ LL | Self(self.0 / rhs.0) = note: `-D clippy::integer-division-remainder-used` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::integer_division_remainder_used)]` -error: use of % has been disallowed in this context +error: use of `%` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:18:14 | LL | Self(self.0 % rhs.0) | ^^^^^^^^^^^^^^ -error: use of / has been disallowed in this context +error: use of `/` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:27:13 | LL | let c = a / b; | ^^^^^ -error: use of % has been disallowed in this context +error: use of `%` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:29:13 | LL | let d = a % b; | ^^^^^ -error: use of / has been disallowed in this context +error: use of `/` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:31:13 | LL | let e = &a / b; | ^^^^^^ -error: use of % has been disallowed in this context +error: use of `%` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:33:13 | LL | let f = a % &b; | ^^^^^^ -error: use of / has been disallowed in this context +error: use of `/` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:35:13 | LL | let g = &a / &b; | ^^^^^^^ -error: use of % has been disallowed in this context +error: use of `%` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:37:13 | LL | let h = &10 % b; | ^^^^^^^ -error: use of / has been disallowed in this context +error: use of `/` has been disallowed in this context --> tests/ui/integer_division_remainder_used.rs:39:13 | LL | let i = a / &4; From 967d2b1c270847ec44460d779d7689e6b2bed311 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 25 Oct 2025 23:33:14 +0200 Subject: [PATCH 080/108] chore(integer_division_remainder_used): move to under `operators` --- clippy_lints/src/declared_lints.rs | 2 +- .../src/integer_division_remainder_used.rs | 50 ------------------- clippy_lints/src/lib.rs | 2 - .../integer_division_remainder_used.rs | 24 +++++++++ clippy_lints/src/operators/mod.rs | 26 ++++++++++ 5 files changed, 51 insertions(+), 53 deletions(-) delete mode 100644 clippy_lints/src/integer_division_remainder_used.rs create mode 100644 clippy_lints/src/operators/integer_division_remainder_used.rs diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 89163c2887a10..a754eea311651 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -227,7 +227,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, - crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, crate::item_name_repetitions::ENUM_VARIANT_NAMES_INFO, crate::item_name_repetitions::MODULE_INCEPTION_INFO, crate::item_name_repetitions::MODULE_NAME_REPETITIONS_INFO, @@ -590,6 +589,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::operators::IMPOSSIBLE_COMPARISONS_INFO, crate::operators::INEFFECTIVE_BIT_MASK_INFO, crate::operators::INTEGER_DIVISION_INFO, + crate::operators::INTEGER_DIVISION_REMAINDER_USED_INFO, crate::operators::INVALID_UPCAST_COMPARISONS_INFO, crate::operators::MANUAL_DIV_CEIL_INFO, crate::operators::MANUAL_IS_MULTIPLE_OF_INFO, diff --git a/clippy_lints/src/integer_division_remainder_used.rs b/clippy_lints/src/integer_division_remainder_used.rs deleted file mode 100644 index 494b311943a4a..0000000000000 --- a/clippy_lints/src/integer_division_remainder_used.rs +++ /dev/null @@ -1,50 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use rustc_ast::BinOpKind; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self}; -use rustc_session::declare_lint_pass; - -declare_clippy_lint! { - /// ### What it does - /// Checks for the usage of division (`/`) and remainder (`%`) operations - /// when performed on any integer types using the default `Div` and `Rem` trait implementations. - /// - /// ### Why restrict this? - /// In cryptographic contexts, division can result in timing sidechannel vulnerabilities, - /// and needs to be replaced with constant-time code instead (e.g. Barrett reduction). - /// - /// ### Example - /// ```no_run - /// let my_div = 10 / 2; - /// ``` - /// Use instead: - /// ```no_run - /// let my_div = 10 >> 1; - /// ``` - #[clippy::version = "1.79.0"] - pub INTEGER_DIVISION_REMAINDER_USED, - restriction, - "use of disallowed default division and remainder operations" -} - -declare_lint_pass!(IntegerDivisionRemainderUsed => [INTEGER_DIVISION_REMAINDER_USED]); - -impl LateLintPass<'_> for IntegerDivisionRemainderUsed { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Binary(op, lhs, rhs) = &expr.kind - && let BinOpKind::Div | BinOpKind::Rem = op.node - && let lhs_ty = cx.typeck_results().expr_ty(lhs) - && let rhs_ty = cx.typeck_results().expr_ty(rhs) - && let ty::Int(_) | ty::Uint(_) = lhs_ty.peel_refs().kind() - && let ty::Int(_) | ty::Uint(_) = rhs_ty.peel_refs().kind() - { - span_lint( - cx, - INTEGER_DIVISION_REMAINDER_USED, - expr.span.source_callsite(), - format!("use of `{}` has been disallowed in this context", op.node.as_str()), - ); - } - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2483f075a57dc..8e35883ae9d46 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -171,7 +171,6 @@ mod inherent_to_string; mod init_numbered_fields; mod inline_fn_without_body; mod int_plus_one; -mod integer_division_remainder_used; mod item_name_repetitions; mod items_after_statements; mod items_after_test_module; @@ -793,7 +792,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf))); store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); - store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))); store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf))); store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)); diff --git a/clippy_lints/src/operators/integer_division_remainder_used.rs b/clippy_lints/src/operators/integer_division_remainder_used.rs new file mode 100644 index 0000000000000..976b2d8b0c63a --- /dev/null +++ b/clippy_lints/src/operators/integer_division_remainder_used.rs @@ -0,0 +1,24 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::BinOpKind; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::Span; + +use super::INTEGER_DIVISION_REMAINDER_USED; + +pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>, span: Span) { + if let BinOpKind::Div | BinOpKind::Rem = op + && let lhs_ty = cx.typeck_results().expr_ty(lhs) + && let rhs_ty = cx.typeck_results().expr_ty(rhs) + && let ty::Int(_) | ty::Uint(_) = lhs_ty.peel_refs().kind() + && let ty::Int(_) | ty::Uint(_) = rhs_ty.peel_refs().kind() + { + span_lint( + cx, + INTEGER_DIVISION_REMAINDER_USED, + span.source_callsite(), + format!("use of `{}` has been disallowed in this context", op.as_str()), + ); + } +} diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index b960f4ddffa30..8db2cc1d3f578 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -11,6 +11,7 @@ mod float_cmp; mod float_equality_without_abs; mod identity_op; mod integer_division; +mod integer_division_remainder_used; mod invalid_upcast_comparisons; mod manual_div_ceil; mod manual_is_multiple_of; @@ -911,6 +912,29 @@ declare_clippy_lint! { "a comparison involving an upcast which is always true or false" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of division (`/`) and remainder (`%`) operations + /// when performed on any integer types using the default `Div` and `Rem` trait implementations. + /// + /// ### Why restrict this? + /// In cryptographic contexts, division can result in timing sidechannel vulnerabilities, + /// and needs to be replaced with constant-time code instead (e.g. Barrett reduction). + /// + /// ### Example + /// ```no_run + /// let my_div = 10 / 2; + /// ``` + /// Use instead: + /// ```no_run + /// let my_div = 10 >> 1; + /// ``` + #[clippy::version = "1.79.0"] + pub INTEGER_DIVISION_REMAINDER_USED, + restriction, + "use of disallowed default division and remainder operations" +} + pub struct Operators { arithmetic_context: numeric_arithmetic::Context, verbose_bit_mask_threshold: u64, @@ -948,6 +972,7 @@ impl_lint_pass!(Operators => [ FLOAT_EQUALITY_WITHOUT_ABS, IDENTITY_OP, INTEGER_DIVISION, + INTEGER_DIVISION_REMAINDER_USED, CMP_OWNED, FLOAT_CMP, FLOAT_CMP_CONST, @@ -987,6 +1012,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { duration_subsec::check(cx, e, op.node, lhs, rhs); float_equality_without_abs::check(cx, e, op.node, lhs, rhs); integer_division::check(cx, e, op.node, lhs, rhs); + integer_division_remainder_used::check(cx, op.node, lhs, rhs, e.span); cmp_owned::check(cx, op.node, lhs, rhs); float_cmp::check(cx, e, op.node, lhs, rhs); modulo_one::check(cx, e, op.node, rhs); From c95db073b655c69ed686b85fee9359c7640bb675 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 29 Oct 2025 19:13:17 +0100 Subject: [PATCH 081/108] test(while_immutable_condition): call the test file the same as the lint --- ...e_loop.rs => while_immutable_condition.rs} | 2 ++ ...tderr => while_immutable_condition.stderr} | 25 ++++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) rename tests/ui/{infinite_loop.rs => while_immutable_condition.rs} (98%) rename tests/ui/{infinite_loop.stderr => while_immutable_condition.stderr} (76%) diff --git a/tests/ui/infinite_loop.rs b/tests/ui/while_immutable_condition.rs similarity index 98% rename from tests/ui/infinite_loop.rs rename to tests/ui/while_immutable_condition.rs index 8ff7f3b0c18d8..5c18cd41ff792 100644 --- a/tests/ui/infinite_loop.rs +++ b/tests/ui/while_immutable_condition.rs @@ -1,3 +1,5 @@ +#![warn(clippy::while_immutable_condition)] + fn fn_val(i: i32) -> i32 { unimplemented!() } diff --git a/tests/ui/infinite_loop.stderr b/tests/ui/while_immutable_condition.stderr similarity index 76% rename from tests/ui/infinite_loop.stderr rename to tests/ui/while_immutable_condition.stderr index 04da9776c3025..278b473d23cee 100644 --- a/tests/ui/infinite_loop.stderr +++ b/tests/ui/while_immutable_condition.stderr @@ -1,14 +1,15 @@ error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:20:11 + --> tests/ui/while_immutable_condition.rs:22:11 | LL | while y < 10 { | ^^^^^^ | = note: this may lead to an infinite or to a never running loop - = note: `#[deny(clippy::while_immutable_condition)]` on by default + = note: `-D clippy::while-immutable-condition` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::while_immutable_condition)]` error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:27:11 + --> tests/ui/while_immutable_condition.rs:29:11 | LL | while y < 10 && x < 3 { | ^^^^^^^^^^^^^^^ @@ -16,7 +17,7 @@ LL | while y < 10 && x < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:36:11 + --> tests/ui/while_immutable_condition.rs:38:11 | LL | while !cond { | ^^^^^ @@ -24,7 +25,7 @@ LL | while !cond { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:82:11 + --> tests/ui/while_immutable_condition.rs:84:11 | LL | while i < 3 { | ^^^^^ @@ -32,7 +33,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:89:11 + --> tests/ui/while_immutable_condition.rs:91:11 | LL | while i < 3 && j > 0 { | ^^^^^^^^^^^^^^ @@ -40,7 +41,7 @@ LL | while i < 3 && j > 0 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:95:11 + --> tests/ui/while_immutable_condition.rs:97:11 | LL | while i < 3 { | ^^^^^ @@ -48,7 +49,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:112:11 + --> tests/ui/while_immutable_condition.rs:114:11 | LL | while i < 3 { | ^^^^^ @@ -56,7 +57,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:119:11 + --> tests/ui/while_immutable_condition.rs:121:11 | LL | while i < 3 { | ^^^^^ @@ -64,7 +65,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:187:15 + --> tests/ui/while_immutable_condition.rs:189:15 | LL | while self.count < n { | ^^^^^^^^^^^^^^ @@ -72,7 +73,7 @@ LL | while self.count < n { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:197:11 + --> tests/ui/while_immutable_condition.rs:199:11 | LL | while y < 10 { | ^^^^^^ @@ -82,7 +83,7 @@ LL | while y < 10 { = help: rewrite it as `if cond { loop { } }` error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:206:11 + --> tests/ui/while_immutable_condition.rs:208:11 | LL | while y < 10 { | ^^^^^^ From 26f35ae269f99c3067993bf4a08eeac88cc8493c Mon Sep 17 00:00:00 2001 From: Frank King Date: Sun, 13 Apr 2025 22:57:37 +0800 Subject: [PATCH 082/108] Implement pattern matching for `&pin mut|const T` --- compiler/rustc_ast/src/ast.rs | 25 +- compiler/rustc_ast/src/visit.rs | 1 + compiler/rustc_ast_ir/src/lib.rs | 7 + compiler/rustc_ast_pretty/src/pprust/state.rs | 7 +- .../src/diagnostics/mutability_errors.rs | 2 +- compiler/rustc_borrowck/src/lib.rs | 10 + compiler/rustc_hir/src/hir.rs | 2 +- .../rustc_hir_analysis/src/check/region.rs | 2 +- compiler/rustc_hir_pretty/src/lib.rs | 7 +- .../rustc_hir_typeck/src/expr_use_visitor.rs | 30 +- compiler/rustc_hir_typeck/src/pat.rs | 101 ++++- compiler/rustc_lint/src/static_mut_refs.rs | 2 +- compiler/rustc_middle/src/ty/adjustment.rs | 3 + compiler/rustc_middle/src/ty/adt.rs | 11 + compiler/rustc_middle/src/ty/sty.rs | 7 + .../rustc_middle/src/ty/typeck_results.rs | 5 +- .../src/builder/matches/match_pair.rs | 22 +- .../src/builder/matches/mod.rs | 71 ++- .../rustc_mir_build/src/check_unsafety.rs | 2 +- .../src/thir/pattern/check_match.rs | 6 +- .../src/thir/pattern/migration.rs | 2 +- .../rustc_mir_build/src/thir/pattern/mod.rs | 33 +- compiler/rustc_parse/src/parser/mod.rs | 11 +- compiler/rustc_parse/src/parser/pat.rs | 11 +- .../rustc_pattern_analysis/src/constructor.rs | 2 +- .../clippy_lints/src/index_refutable_slice.rs | 2 +- .../clippy_lints/src/matches/match_as_ref.rs | 2 +- .../src/matches/needless_match.rs | 2 +- .../src/matches/redundant_guards.rs | 2 +- .../clippy_lints/src/methods/clone_on_copy.rs | 2 +- .../clippy/clippy_lints/src/question_mark.rs | 8 +- .../clippy_lints/src/toplevel_ref_arg.rs | 4 +- .../clippy/clippy_lints/src/utils/author.rs | 4 + .../clippy/clippy_utils/src/eager_or_lazy.rs | 7 +- src/tools/clippy/clippy_utils/src/lib.rs | 4 +- src/tools/rustfmt/src/patterns.rs | 3 +- ...ct_pattern_match.bar_const.built.after.mir | 33 ++ ...ject_pattern_match.bar_mut.built.after.mir | 33 ++ ...attern_match.baz_baz_const.built.after.mir | 422 ++++++++++++++++++ ..._pattern_match.baz_baz_mut.built.after.mir | 422 ++++++++++++++++++ ...ct_pattern_match.baz_const.built.after.mir | 76 ++++ ...ject_pattern_match.baz_mut.built.after.mir | 76 ++++ ...attern_match.foo_bar_const.built.after.mir | 47 ++ ..._pattern_match.foo_bar_mut.built.after.mir | 47 ++ ...ct_pattern_match.foo_const.built.after.mir | 33 ++ ...ject_pattern_match.foo_mut.built.after.mir | 33 ++ .../pin-ergonomics/project_pattern_match.rs | 95 ++++ .../pattern-matching-deref-pattern.rs | 91 ++++ ...n-matching-mix-deref-pattern.normal.stderr | 66 +++ ...ng-mix-deref-pattern.pin_ergonomics.stderr | 130 ++++++ .../pattern-matching-mix-deref-pattern.rs | 113 +++++ .../pattern-matching.normal.stderr | 313 +++++++++++++ tests/ui/pin-ergonomics/pattern-matching.rs | 186 ++++++++ 53 files changed, 2552 insertions(+), 86 deletions(-) create mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.bar_const.built.after.mir create mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.bar_mut.built.after.mir create mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_const.built.after.mir create mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_mut.built.after.mir create mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.baz_const.built.after.mir create mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.baz_mut.built.after.mir create mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_const.built.after.mir create mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_mut.built.after.mir create mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.foo_const.built.after.mir create mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.foo_mut.built.after.mir create mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.rs create mode 100644 tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs create mode 100644 tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr create mode 100644 tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr create mode 100644 tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs create mode 100644 tests/ui/pin-ergonomics/pattern-matching.normal.stderr create mode 100644 tests/ui/pin-ergonomics/pattern-matching.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e93351bcf7125..9f444670d91a1 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -789,14 +789,14 @@ pub struct PatField { #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Encodable, Decodable, HashStable_Generic, Walkable)] pub enum ByRef { - Yes(Mutability), + Yes(Pinnedness, Mutability), No, } impl ByRef { #[must_use] pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self { - if let ByRef::Yes(old_mutbl) = &mut self { + if let ByRef::Yes(_, old_mutbl) = &mut self { *old_mutbl = cmp::min(*old_mutbl, mutbl); } self @@ -814,20 +814,33 @@ pub struct BindingMode(pub ByRef, pub Mutability); impl BindingMode { pub const NONE: Self = Self(ByRef::No, Mutability::Not); - pub const REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Not); + pub const REF: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Not), Mutability::Not); + pub const REF_PIN: Self = + Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Not), Mutability::Not); pub const MUT: Self = Self(ByRef::No, Mutability::Mut); - pub const REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Not); - pub const MUT_REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Mut); - pub const MUT_REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Mut); + pub const REF_MUT: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Mut), Mutability::Not); + pub const REF_PIN_MUT: Self = + Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Mut), Mutability::Not); + pub const MUT_REF: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Not), Mutability::Mut); + pub const MUT_REF_PIN: Self = + Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Not), Mutability::Mut); + pub const MUT_REF_MUT: Self = + Self(ByRef::Yes(Pinnedness::Not, Mutability::Mut), Mutability::Mut); + pub const MUT_REF_PIN_MUT: Self = + Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Mut), Mutability::Mut); pub fn prefix_str(self) -> &'static str { match self { Self::NONE => "", Self::REF => "ref ", + Self::REF_PIN => "ref pin const ", Self::MUT => "mut ", Self::REF_MUT => "ref mut ", + Self::REF_PIN_MUT => "ref pin mut ", Self::MUT_REF => "mut ref ", + Self::MUT_REF_PIN => "mut ref pin ", Self::MUT_REF_MUT => "mut ref mut ", + Self::MUT_REF_PIN_MUT => "mut ref pin mut ", } } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 03e5a6edeece5..c60407c419c14 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -368,6 +368,7 @@ macro_rules! common_visitor_and_walkers { crate::tokenstream::TokenStream, Movability, Mutability, + Pinnedness, Result<(), rustc_span::ErrorGuaranteed>, rustc_data_structures::fx::FxHashMap, rustc_span::ErrorGuaranteed, diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index 44837b1b49407..0f63fad157431 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -311,3 +311,10 @@ pub enum Pinnedness { Not, Pinned, } + +impl Pinnedness { + /// Return `true` if self is pinned + pub fn is_pinned(self) -> bool { + matches!(self, Self::Pinned) + } +} diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a5e2bcaa3bd06..93f4e47342f14 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1712,10 +1712,15 @@ impl<'a> State<'a> { if mutbl.is_mut() { self.word_nbsp("mut"); } - if let ByRef::Yes(rmutbl) = by_ref { + if let ByRef::Yes(pinnedness, rmutbl) = by_ref { self.word_nbsp("ref"); + if pinnedness.is_pinned() { + self.word_nbsp("pin"); + } if rmutbl.is_mut() { self.word_nbsp("mut"); + } else if pinnedness.is_pinned() { + self.word_nbsp("const"); } } self.print_ident(*ident); diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index b4990ffc7739c..e23c7eebeca1e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1188,7 +1188,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { - binding_mode: BindingMode(ByRef::Yes(_), _), + binding_mode: BindingMode(ByRef::Yes(..), _), .. })) => { let pattern_span: Span = local_decl.source_info.span; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index a57689a45b67b..0b098d1fafde3 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2584,6 +2584,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { _ => bug!("Deref of unexpected type: {:?}", base_ty), } } + // Check as the inner reference type if it is a field projection + // from the `&pin` pattern + ProjectionElem::Field(FieldIdx::ZERO, _) + if let Some(adt) = + place_base.ty(self.body(), self.infcx.tcx).ty.ty_adt_def() + && adt.is_pin() + && self.infcx.tcx.features().pin_ergonomics() => + { + self.is_mutable(place_base, is_local_mutation_allowed) + } // All other projections are owned by their base path, so mutable if // base path is mutable ProjectionElem::Field(..) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 8b55a0d732994..cf950023b4f14 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -13,7 +13,7 @@ use rustc_ast::{ pub use rustc_ast::{ AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, - MetaItemInner, MetaItemLit, Movability, Mutability, UnOp, + MetaItemInner, MetaItemLit, Movability, Mutability, Pinnedness, UnOp, }; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index f99fefcf56ace..bac34454b3b7a 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -575,7 +575,7 @@ fn resolve_local<'tcx>( // & expression, and its lifetime would be extended to the end of the block (due // to a different rule, not the below code). match pat.kind { - PatKind::Binding(hir::BindingMode(hir::ByRef::Yes(_), _), ..) => true, + PatKind::Binding(hir::BindingMode(hir::ByRef::Yes(..), _), ..) => true, PatKind::Struct(_, field_pats, _) => field_pats.iter().any(|fp| is_binding_pat(fp.pat)), diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index ed5f61b3c69ab..935b2e7a1bdb9 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1928,10 +1928,15 @@ impl<'a> State<'a> { if mutbl.is_mut() { self.word_nbsp("mut"); } - if let ByRef::Yes(rmutbl) = by_ref { + if let ByRef::Yes(pinnedness, rmutbl) = by_ref { self.word_nbsp("ref"); + if pinnedness.is_pinned() { + self.word_nbsp("pin"); + } if rmutbl.is_mut() { self.word_nbsp("mut"); + } else if pinnedness.is_pinned() { + self.word_nbsp("const"); } } self.print_ident(ident); diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 2034131882820..b9f1463d8007d 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -986,7 +986,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // of the pattern, as this just looks confusing, instead use the span // of the discriminant. match bm.0 { - hir::ByRef::Yes(m) => { + hir::ByRef::Yes(_, m) => { let bk = ty::BorrowKind::from_mutbl(m); self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); } @@ -1004,7 +1004,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Deref patterns on boxes don't borrow, so we ignore them here. // HACK: this could be a fake pattern corresponding to a deref inserted by match // ergonomics, in which case `pat.hir_id` will be the id of the subpattern. - if let hir::ByRef::Yes(mutability) = + if let hir::ByRef::Yes(_, mutability) = self.cx.typeck_results().deref_pat_borrow_mode(place.place.ty(), subpattern) { let bk = ty::BorrowKind::from_mutbl(mutability); @@ -1256,7 +1256,15 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx .get(pat.hir_id) .expect("missing binding mode"); - if matches!(bm.0, hir::ByRef::Yes(_)) { + if let hir::ByRef::Yes(pinnedness, _) = bm.0 { + let base_ty = if pinnedness.is_pinned() { + base_ty.pinned_ty().ok_or_else(|| { + debug!("By-pin-ref binding of non-`Pin` type: {base_ty:?}"); + self.cx.report_bug(pat.span, "by-pin-ref binding of non-`Pin` type") + })? + } else { + base_ty + }; // a bind-by-ref means that the base_ty will be the type of the ident itself, // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. @@ -1264,7 +1272,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx { Some(ty) => Ok(ty), None => { - debug!("By-ref binding of non-derefable type"); + debug!("By-ref binding of non-derefable type: {base_ty:?}"); Err(self .cx .report_bug(pat.span, "by-ref binding of non-derefable type")) @@ -1706,6 +1714,18 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx }; self.pat_deref_place(pat.hir_id, place_with_id, pat, target_ty)? } + adjustment::PatAdjust::PinDeref => { + debug!("`PinDeref` of non-pinned-reference type: {:?}", adjust.source); + let target_ty = adjust.source.pinned_ty().ok_or_else(|| { + self.cx.report_bug( + self.cx.tcx().hir_span(pat.hir_id), + "`PinDeref` of non-pinned-reference type", + ) + })?; + let kind = ProjectionKind::Field(FieldIdx::ZERO, FIRST_VARIANT); + place_with_id = self.cat_projection(pat.hir_id, place_with_id, target_ty, kind); + self.cat_deref(pat.hir_id, place_with_id)? + } }; } drop(typeck_results); // explicitly release borrow of typeck results, just in case. @@ -1877,7 +1897,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Deref patterns on boxes are lowered using a built-in deref. hir::ByRef::No => self.cat_deref(hir_id, base_place), // For other types, we create a temporary to match on. - hir::ByRef::Yes(mutability) => { + hir::ByRef::Yes(_, mutability) => { let re_erased = self.cx.tcx().lifetimes.re_erased; let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability); // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ... diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index d14463e44a03f..d2b3041ad7e1a 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -18,7 +18,7 @@ use rustc_hir::{ use rustc_hir_analysis::autoderef::report_autoderef_recursion_limit_error; use rustc_infer::infer::RegionVariableOrigin; use rustc_middle::traits::PatternOriginExpr; -use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, Pinnedness, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_session::parse::feature_err; @@ -413,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat, derefed_tys.iter().filter_map(|adjust| match adjust.kind { PatAdjust::OverloadedDeref => Some(adjust.source), - PatAdjust::BuiltinDeref => None, + PatAdjust::BuiltinDeref | PatAdjust::PinDeref => None, }), ); } @@ -471,7 +471,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { #[cfg(debug_assertions)] - if pat_info.binding_mode == ByRef::Yes(Mutability::Mut) + if matches!(pat_info.binding_mode, ByRef::Yes(_, Mutability::Mut)) && pat_info.max_ref_mutbl != MutblCap::Mut && self.downgrade_mut_inside_shared() { @@ -489,12 +489,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let old_pat_info = pat_info; let pat_info = PatInfo { current_depth: old_pat_info.current_depth + 1, ..old_pat_info }; + let adjust_binding_mode = |inner_pinnedness, inner_mutability| { + match pat_info.binding_mode { + // If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const` + // or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or + // `&pin mut`). + ByRef::No => ByRef::Yes(inner_pinnedness, inner_mutability), + // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). + // Pinnedness is preserved. + ByRef::Yes(pinnedness, Mutability::Mut) => ByRef::Yes(pinnedness, inner_mutability), + // Once a `ref`, always a `ref`. + // This is because a `& &mut` cannot mutate the underlying value. + // Pinnedness is preserved. + ByRef::Yes(pinnedness, Mutability::Not) => ByRef::Yes(pinnedness, Mutability::Not), + } + }; + match pat.kind { - // Peel off a `&` or `&mut` from the scrutinee type. See the examples in + // Peel off a `&` or `&mut`from the scrutinee type. See the examples in // `tests/ui/rfcs/rfc-2005-default-binding-mode`. _ if let AdjustMode::Peel { kind: peel_kind } = adjust_mode && pat.default_binding_modes - && let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() + && let &ty::Ref(_, inner_ty, inner_mutability) = expected.kind() && self.should_peel_ref(peel_kind, expected) => { debug!("inspecting {:?}", expected); @@ -508,22 +524,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .or_default() .push(PatAdjustment { kind: PatAdjust::BuiltinDeref, source: expected }); - let mut binding_mode = ByRef::Yes(match pat_info.binding_mode { - // If default binding mode is by value, make it `ref` or `ref mut` - // (depending on whether we observe `&` or `&mut`). - ByRef::No | - // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). - ByRef::Yes(Mutability::Mut) => inner_mutability, - // Once a `ref`, always a `ref`. - // This is because a `& &mut` cannot mutate the underlying value. - ByRef::Yes(Mutability::Not) => Mutability::Not, - }); + let mut binding_mode = adjust_binding_mode(Pinnedness::Not, inner_mutability); let mut max_ref_mutbl = pat_info.max_ref_mutbl; if self.downgrade_mut_inside_shared() { binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl()); } - if binding_mode == ByRef::Yes(Mutability::Not) { + if matches!(binding_mode, ByRef::Yes(_, Mutability::Not)) { max_ref_mutbl = MutblCap::Not; } debug!("default binding mode is now {:?}", binding_mode); @@ -533,6 +540,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Recurse with the new expected type. self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info) } + // If `pin_ergonomics` is enabled, peel the `&pin` from the pinned reference type. See the + // examples in `tests/ui/async-await/pin-ergonomics/`. + _ if self.tcx.features().pin_ergonomics() + && let AdjustMode::Peel { kind: peel_kind } = adjust_mode + && pat.default_binding_modes + && self.should_peel_smart_pointer(peel_kind, expected) + && let Some(pinned_ty) = expected.pinned_ty() + // Currently, only pinned reference is specially handled, leaving other + // pinned types (e.g. `Pin>` to deref patterns) handled as a + // deref pattern. + && let &ty::Ref(_, inner_ty, inner_mutability) = pinned_ty.kind() => + { + debug!("scrutinee ty {expected:?} is a pinned reference, inserting pin deref"); + // Preserve the pinned type. We'll need it later during THIR lowering. + self.typeck_results + .borrow_mut() + .pat_adjustments_mut() + .entry(pat.hir_id) + .or_default() + .push(PatAdjustment { kind: PatAdjust::PinDeref, source: expected }); + + let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability); + // If the pinnedness is `Not`, it means the pattern is unpinned + // and thus requires an `Unpin` bound. + if binding_mode == ByRef::Yes(Pinnedness::Not, Mutability::Mut) { + self.register_bound( + inner_ty, + self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span), + self.misc(pat.span), + ); + } + // Use the old pat info to keep `current_depth` to its old value. + let new_pat_info = PatInfo { binding_mode, ..old_pat_info }; + // Recurse with the new expected type. + // using `break` instead of `return` in case where any shared codes are added + // after the `match pat.kind {}`. + self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info) + } // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the // examples in `tests/ui/pattern/deref_patterns/`. _ if self.tcx.features().deref_patterns() @@ -1061,7 +1106,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Determine the binding mode... let bm = match user_bind_annot { - BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(def_br_mutbl) = def_br => { + BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_, def_br_mutbl) = def_br => { // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and // using other experimental matching features compatible with it. if pat.span.at_least_rust_2024() @@ -1091,8 +1136,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), - BindingMode(ByRef::Yes(user_br_mutbl), _) => { - if let ByRef::Yes(def_br_mutbl) = def_br { + BindingMode(ByRef::Yes(_, user_br_mutbl), _) => { + if let ByRef::Yes(_, def_br_mutbl) = def_br { // `ref`/`ref mut` overrides the binding mode on edition <= 2021 self.add_rust_2024_migration_desugared_pat( pat_info.top_info.hir_id, @@ -1108,7 +1153,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - if bm.0 == ByRef::Yes(Mutability::Mut) + if matches!(bm.0, ByRef::Yes(_, Mutability::Mut)) && let MutblCap::WeaklyNot(and_pat_span) = pat_info.max_ref_mutbl { let mut err = struct_span_code_err!( @@ -1136,7 +1181,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_ty = self.local_ty(pat.span, pat.hir_id); let eq_ty = match bm.0 { - ByRef::Yes(mutbl) => { + ByRef::Yes(Pinnedness::Not, mutbl) => { // If the binding is like `ref x | ref mut x`, // then `x` is assigned a value of type `&M T` where M is the // mutability and T is the expected type. @@ -1146,6 +1191,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // See (note_1) for an explanation. self.new_ref_ty(pat.span, mutbl, expected) } + // Wrapping the type into `Pin` if the binding is like `ref pin const|mut x` + ByRef::Yes(Pinnedness::Pinned, mutbl) => Ty::new_adt( + self.tcx, + self.tcx.adt_def(self.tcx.require_lang_item(hir::LangItem::Pin, pat.span)), + self.tcx.mk_args(&[self.new_ref_ty(pat.span, mutbl, expected).into()]), + ), // Otherwise, the type of x is the expected type `T`. ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). }; @@ -2605,7 +2656,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected = self.try_structurally_resolve_type(pat.span, expected); // Determine whether we're consuming an inherited reference and resetting the default // binding mode, based on edition and enabled experimental features. - if let ByRef::Yes(inh_mut) = pat_info.binding_mode { + if let ByRef::Yes(_, inh_mut) = pat_info.binding_mode { match self.ref_pat_matches_inherited_ref(pat.span.edition()) { InheritedRefMatchRule::EatOuter => { // ref pattern attempts to consume inherited reference @@ -3126,8 +3177,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the user-provided binding modifier doesn't match the default binding mode, we'll // need to suggest reference patterns, which can affect other bindings. // For simplicity, we opt to suggest making the pattern fully explicit. - info.suggest_eliding_modes &= - user_bind_annot == BindingMode(ByRef::Yes(def_br_mutbl), Mutability::Not); + info.suggest_eliding_modes &= matches!( + user_bind_annot, + BindingMode(ByRef::Yes(_, mutbl), Mutability::Not) if mutbl == def_br_mutbl + ); if user_bind_annot == BindingMode(ByRef::No, Mutability::Mut) { info.bad_mut_modifiers = true; "`mut` binding modifier" diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs index 16e1fb0192b32..1c0df1f4234a2 100644 --- a/compiler/rustc_lint/src/static_mut_refs.rs +++ b/compiler/rustc_lint/src/static_mut_refs.rs @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for StaticMutRefs { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) { if let hir::StmtKind::Let(loc) = stmt.kind && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind - && let hir::ByRef::Yes(m) = ba.0 + && let hir::ByRef::Yes(_, m) = ba.0 && let Some(init) = loc.init && let Some(err_span) = path_is_static_mut(init, init.span) { diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 74573455f531a..2920c9cb42ab4 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -232,4 +232,7 @@ pub enum PatAdjust { /// An implicit call to `Deref(Mut)::deref(_mut)` before matching, such as when matching the /// pattern `[..]` against a scrutinee of type `Vec`. OverloadedDeref, + /// An implicit dereference before matching a `&pin` reference (under feature `pin_ergonomics`), + /// which will be lowered as a builtin deref of the private field `__pointer` in `Pin` + PinDeref, } diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index df82c7a826be9..be909a08e8dae 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -55,6 +55,8 @@ bitflags::bitflags! { const IS_UNSAFE_CELL = 1 << 9; /// Indicates whether the type is `UnsafePinned`. const IS_UNSAFE_PINNED = 1 << 10; + /// Indicates whether the type is `Pin`. + const IS_PIN = 1 << 11; } } rustc_data_structures::external_bitflags_debug! { AdtFlags } @@ -313,6 +315,9 @@ impl AdtDefData { if tcx.is_lang_item(did, LangItem::UnsafePinned) { flags |= AdtFlags::IS_UNSAFE_PINNED; } + if tcx.is_lang_item(did, LangItem::Pin) { + flags |= AdtFlags::IS_PIN; + } AdtDefData { did, variants, flags, repr } } @@ -428,6 +433,12 @@ impl<'tcx> AdtDef<'tcx> { self.flags().contains(AdtFlags::IS_MANUALLY_DROP) } + /// Returns `true` if this is `Pin`. + #[inline] + pub fn is_pin(self) -> bool { + self.flags().contains(AdtFlags::IS_PIN) + } + /// Returns `true` if this type has a destructor. pub fn has_dtor(self, tcx: TyCtxt<'tcx>) -> bool { self.destructor(tcx).is_some() diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index ad8b1fd0693b0..d883f63486040 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1349,6 +1349,13 @@ impl<'tcx> Ty<'tcx> { } } + pub fn pinned_ty(self) -> Option> { + match self.kind() { + Adt(def, args) if def.is_pin() => Some(args.type_at(0)), + _ => None, + } + } + /// Panics if called on any type other than `Box`. pub fn expect_boxed_ty(self) -> Ty<'tcx> { self.boxed_ty() diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index d1fb700913d91..6a6ed702373d1 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -11,6 +11,7 @@ use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; use rustc_hir::hir_id::OwnerId; use rustc_hir::{ self as hir, BindingMode, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability, + Pinnedness, }; use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; @@ -479,7 +480,7 @@ impl<'tcx> TypeckResults<'tcx> { let mut has_ref_mut = false; pat.walk(|pat| { if let hir::PatKind::Binding(_, id, _, _) = pat.kind - && let Some(BindingMode(ByRef::Yes(Mutability::Mut), _)) = + && let Some(BindingMode(ByRef::Yes(_, Mutability::Mut), _)) = self.pat_binding_modes().get(id) { has_ref_mut = true; @@ -503,7 +504,7 @@ impl<'tcx> TypeckResults<'tcx> { ByRef::No } else { let mutable = self.pat_has_ref_mut_binding(inner); - ByRef::Yes(if mutable { Mutability::Mut } else { Mutability::Not }) + ByRef::Yes(Pinnedness::Not, if mutable { Mutability::Mut } else { Mutability::Not }) } } diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index c3028b9b5c4ea..af565ef636993 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -1,9 +1,10 @@ use std::sync::Arc; +use rustc_abi::FieldIdx; use rustc_hir::ByRef; use rustc_middle::mir::*; use rustc_middle::thir::*; -use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, Pinnedness, Ty, TypeVisitableExt}; use crate::builder::Builder; use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder}; @@ -299,7 +300,24 @@ impl<'tcx> MatchPairTree<'tcx> { None } - PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(mutability) } => { + PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } => { + let Some(ref_ty) = pattern.ty.pinned_ty() else { + rustc_middle::bug!("RefPin pattern on non-`Pin` type {:?}", pattern.ty); + }; + MatchPairTree::for_pattern( + place_builder.field(FieldIdx::ZERO, ref_ty).deref(), + subpattern, + cx, + &mut subpairs, + extra_data, + ); + None + } + + PatKind::DerefPattern { + ref subpattern, + borrow: ByRef::Yes(Pinnedness::Not, mutability), + } => { // Create a new temporary for each deref pattern. // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls? let temp = cx.temp( diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 82f12a969bb1c..96c58dc14e8cb 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -5,20 +5,21 @@ //! This also includes code for pattern bindings in `let` statements and //! function parameters. +use std::assert_matches::debug_assert_matches; use std::borrow::Borrow; use std::mem; use std::sync::Arc; use itertools::{Itertools, Position}; -use rustc_abi::VariantIdx; +use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node}; -use rustc_middle::bug; +use rustc_hir::{BindingMode, ByRef, LangItem, LetStmt, LocalSource, Node, Pinnedness}; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind}; +use rustc_middle::{bug, span_bug}; use rustc_pattern_analysis::constructor::RangeEnd; use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt}; use rustc_span::{BytePos, Pos, Span, Symbol, sym}; @@ -917,6 +918,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { visit_subpat(self, subpattern, &user_tys.deref(), f); } + PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } => { + visit_subpat(self, subpattern, &user_tys.leaf(FieldIdx::ZERO).deref(), f); + } + PatKind::DerefPattern { ref subpattern, .. } => { visit_subpat(self, subpattern, &ProjectedUserTypesNode::None, f); } @@ -2747,9 +2752,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source); self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); } - ByRef::Yes(mutbl) => { - // The arm binding will be by reference, so eagerly create it now. Drops must - // be scheduled to emit `StorageDead` on the guard's failure/break branches. + ByRef::Yes(pinnedness, mutbl) => { + // The arm binding will be by reference, so eagerly create it now // be scheduled to emit `StorageDead` on the guard's failure/break branches. let value_for_arm = self.storage_live_binding( block, binding.var_id, @@ -2761,6 +2765,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rvalue = Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source); + let rvalue = match pinnedness { + ty::Pinnedness::Not => rvalue, + ty::Pinnedness::Pinned => { + self.pin_borrowed_local(block, value_for_arm.local, rvalue, source_info) + } + }; self.cfg.push_assign(block, source_info, value_for_arm, rvalue); // For the guard binding, take a shared reference to that reference. let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm); @@ -2797,14 +2807,59 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } let rvalue = match binding.binding_mode.0 { ByRef::No => Rvalue::Use(self.consume_by_copy_or_move(binding.source)), - ByRef::Yes(mutbl) => { - Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source) + ByRef::Yes(pinnedness, mutbl) => { + let rvalue = + Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source); + match pinnedness { + ty::Pinnedness::Not => rvalue, + ty::Pinnedness::Pinned => { + self.pin_borrowed_local(block, local.local, rvalue, source_info) + } + } } }; self.cfg.push_assign(block, source_info, local, rvalue); } } + /// Given an rvalue `&[mut]borrow` and a local `local`, generate the pinned borrow for it: + /// ```ignore (illustrative) + /// pinned_temp = &borrow; + /// local = Pin { __pointer: move pinned_temp }; + /// ``` + fn pin_borrowed_local( + &mut self, + block: BasicBlock, + local: Local, + borrow: Rvalue<'tcx>, + source_info: SourceInfo, + ) -> Rvalue<'tcx> { + debug_assert_matches!(borrow, Rvalue::Ref(..)); + + let local_ty = self.local_decls[local].ty; + + let pinned_ty = local_ty.pinned_ty().unwrap_or_else(|| { + span_bug!( + source_info.span, + "expect type `Pin` for a pinned binding, found type {:?}", + local_ty + ) + }); + let pinned_temp = + Place::from(self.local_decls.push(LocalDecl::new(pinned_ty, source_info.span))); + self.cfg.push_assign(block, source_info, pinned_temp, borrow); + Rvalue::Aggregate( + Box::new(AggregateKind::Adt( + self.tcx.require_lang_item(LangItem::Pin, source_info.span), + FIRST_VARIANT, + self.tcx.mk_args(&[pinned_ty.into()]), + None, + None, + )), + std::iter::once(Operand::Move(pinned_temp)).collect(), + ) + } + /// Each binding (`ref mut var`/`ref var`/`mut var`/`var`, where the bound /// `var` has type `T` in the arm body) in a pattern maps to 2 locals. The /// first local is a binding for occurrences of `var` in the guard, which diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 195d45c2c4c49..18e9543407764 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -383,7 +383,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } visit::walk_pat(self, pat); } - PatKind::Binding { mode: BindingMode(ByRef::Yes(rm), _), ty, .. } => { + PatKind::Binding { mode: BindingMode(ByRef::Yes(_, rm), _), ty, .. } => { if self.inside_adt { let ty::Ref(_, ty, _) = ty.kind() else { span_bug!( diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 3929a97eed8f2..2400d297c89cf 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -797,7 +797,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`. let mut conflicts_ref = Vec::new(); sub.each_binding(|_, mode, _, span| { - if matches!(mode, ByRef::Yes(_)) { + if matches!(mode, ByRef::Yes(..)) { conflicts_ref.push(span) } }); @@ -813,7 +813,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: return; } ByRef::No => return, - ByRef::Yes(m) => m, + ByRef::Yes(_, m) => m, }; // We now have `ref $mut_outer binding @ sub` (semantically). @@ -823,7 +823,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: let mut conflicts_mut_ref = Vec::new(); sub.each_binding(|name, mode, ty, span| { match mode { - ByRef::Yes(mut_inner) => match (mut_outer, mut_inner) { + ByRef::Yes(_, mut_inner) => match (mut_outer, mut_inner) { // Both sides are `ref`. (Mutability::Not, Mutability::Not) => {} // 2x `ref mut`. diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs index 8887308530506..095023a471b93 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/migration.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs @@ -212,7 +212,7 @@ impl<'a> PatMigration<'a> { } if !self.info.suggest_eliding_modes && explicit_ba.0 == ByRef::No - && let ByRef::Yes(mutbl) = mode.0 + && let ByRef::Yes(_, mutbl) = mode.0 { // If we can't fix the pattern by eliding modifiers, we'll need to make the pattern // fully explicit. i.e. we'll need to suggest reference patterns for this. diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index dc6b958904497..404f410e22191 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -11,7 +11,7 @@ use rustc_abi::{FieldIdx, Integer}; use rustc_errors::codes::*; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{self as hir, LangItem, RangeEnd}; +use rustc_hir::{self as hir, ByRef, LangItem, Mutability, Pinnedness, RangeEnd}; use rustc_index::Idx; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::LitToConstInput; @@ -114,6 +114,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let borrow = self.typeck_results.deref_pat_borrow_mode(adjust.source, pat); PatKind::DerefPattern { subpattern: thir_pat, borrow } } + PatAdjust::PinDeref => { + let mutable = self.typeck_results.pat_has_ref_mut_binding(pat); + PatKind::DerefPattern { + subpattern: thir_pat, + borrow: ByRef::Yes( + Pinnedness::Pinned, + if mutable { Mutability::Mut } else { Mutability::Not }, + ), + } + } }; Box::new(Pat { span, ty: adjust.source, kind }) }); @@ -354,11 +364,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // A ref x pattern is the same node used for x, and as such it has // x's type, which is &T, where we want T (the type being matched). let var_ty = ty; - if let hir::ByRef::Yes(_) = mode.0 { - if let ty::Ref(_, rty, _) = ty.kind() { - ty = *rty; - } else { - bug!("`ref {}` has wrong type {}", ident, ty); + if let hir::ByRef::Yes(pinnedness, _) = mode.0 { + match pinnedness { + hir::Pinnedness::Pinned + if let Some(pty) = ty.pinned_ty() + && let &ty::Ref(_, rty, _) = pty.kind() => + { + debug_assert!( + self.tcx.features().pin_ergonomics(), + "`pin_ergonomics` must be enabled to have a by-pin-ref binding" + ); + ty = rty; + } + hir::Pinnedness::Not if let &ty::Ref(_, rty, _) = ty.kind() => { + ty = rty; + } + _ => bug!("`ref {}` has wrong type {}", ident, ty), } }; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index ed4069dae933c..30870c810942f 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -36,8 +36,8 @@ use rustc_ast::tokenstream::{ use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, - DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit, - Visibility, VisibilityKind, + DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Pinnedness, Recovered, + Safety, StrLit, Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -1317,7 +1317,12 @@ impl<'a> Parser<'a> { /// Parses reference binding mode (`ref`, `ref mut`, or nothing). fn parse_byref(&mut self) -> ByRef { - if self.eat_keyword(exp!(Ref)) { ByRef::Yes(self.parse_mutability()) } else { ByRef::No } + if self.eat_keyword(exp!(Ref)) { + // FIXME(pin_ergonomics): support `ref pin const|mut` bindings + ByRef::Yes(Pinnedness::Not, self.parse_mutability()) + } else { + ByRef::No + } } /// Possibly parses mutability (`const` or `mut`). diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 4f522d57e4140..f964ecb90326f 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -7,7 +7,8 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{ self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability, - Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind, + Pat, PatField, PatFieldsRest, PatKind, Path, Pinnedness, QSelf, RangeEnd, RangeSyntax, Stmt, + StmtKind, }; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey}; @@ -778,7 +779,11 @@ impl<'a> Parser<'a> { } // Parse ref ident @ pat / ref mut ident @ pat let mutbl = self.parse_mutability(); - self.parse_pat_ident(BindingMode(ByRef::Yes(mutbl), Mutability::Not), syntax_loc)? + self.parse_pat_ident( + // FIXME(pin_ergonomics): support `ref pin const|mut` bindings + BindingMode(ByRef::Yes(Pinnedness::Not, mutbl), Mutability::Not), + syntax_loc, + )? } else if self.eat_keyword(exp!(Box)) { self.parse_pat_box()? } else if self.check_inline_const(0) { @@ -1093,7 +1098,7 @@ impl<'a> Parser<'a> { self.ban_mut_general_pat(mut_span, &pat, changed_any_binding); } - if matches!(pat.kind, PatKind::Ident(BindingMode(ByRef::Yes(_), Mutability::Mut), ..)) { + if matches!(pat.kind, PatKind::Ident(BindingMode(ByRef::Yes(..), Mutability::Mut), ..)) { self.psess.gated_spans.gate(sym::mut_ref, pat.span); } Ok(pat.kind) diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 12f653a13371d..fb5292f48d2b0 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -981,7 +981,7 @@ pub enum ConstructorSet { /// This type has the following list of constructors. If `variants` is empty and /// `non_exhaustive` is false, don't use this; use `NoConstructors` instead. Variants { variants: IndexVec, non_exhaustive: bool }, - /// The type is `&T`. + /// The type is `&T` Ref, /// The type is a union. Union, diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 919702c5714ad..e95816353df61 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -94,7 +94,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap, arm: &Arm<'_>) -> Option { .qpath_res(qpath, arm.pat.hir_id) .ctor_parent(cx) .is_lang_item(cx, LangItem::OptionSome) - && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind + && let PatKind::Binding(BindingMode(ByRef::Yes(_, mutabl), _), .., ident, _) = first_pat.kind && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind && e.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index c9b6821ad98fd..9c6cf66019f01 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -172,7 +172,7 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { }, )), ) => { - return !matches!(annot, BindingMode(ByRef::Yes(_), _)) && pat_ident.name == first_seg.ident.name; + return !matches!(annot, BindingMode(ByRef::Yes(..), _)) && pat_ident.name == first_seg.ident.name; }, // Example: `Custom::TypeA => Custom::TypeB`, or `None => None` ( diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index d39e315cae1f2..7a1dd94567b14 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -176,7 +176,7 @@ fn get_pat_binding<'tcx>( if let PatKind::Binding(bind_annot, hir_id, ident, _) = pat.kind && hir_id == local { - if matches!(bind_annot.0, rustc_ast::ByRef::Yes(_)) { + if matches!(bind_annot.0, rustc_ast::ByRef::Yes(..)) { let _ = byref_ident.insert(ident); } // the second call of `replace()` returns a `Some(span)`, meaning a multi-binding pattern diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs index 2a0ae14a4b088..9cdad980f238b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -56,7 +56,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) _ => false, }, // local binding capturing a reference - Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..)) => { + Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingMode(ByRef::Yes(..), _), ..)) => { return; }, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index e67ea1f5e370d..928a4e02fa975 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -150,7 +150,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren(); // Take care when binding is `ref` let sugg = if let PatKind::Binding( - BindingMode(ByRef::Yes(ref_mutability), binding_mutability), + BindingMode(ByRef::Yes(_,ref_mutability), binding_mutability), _hir_id, ident, subpattern, @@ -169,7 +169,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { // Handle subpattern (@ subpattern) let maybe_subpattern = match subpattern { Some(Pat { - kind: PatKind::Binding(BindingMode(ByRef::Yes(_), _), _, subident, None), + kind: PatKind::Binding(BindingMode(ByRef::Yes(..), _), _, subident, None), .. }) => { // avoid `&ref` @@ -504,8 +504,8 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_)); let method_call_str = match by_ref { - ByRef::Yes(Mutability::Mut) => ".as_mut()", - ByRef::Yes(Mutability::Not) => ".as_ref()", + ByRef::Yes(_, Mutability::Mut) => ".as_mut()", + ByRef::Yes(_, Mutability::Not) => ".as_ref()", ByRef::No => "", }; let sugg = format!( diff --git a/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs b/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs index 074b79263d377..250c277ab5e1f 100644 --- a/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs +++ b/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { ) { if !matches!(k, FnKind::Closure) { for arg in iter_input_pats(decl, body) { - if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind + if let PatKind::Binding(BindingMode(ByRef::Yes(..), _), ..) = arg.pat.kind && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) && !arg.span.in_external_macro(cx.tcx.sess.source_map()) { @@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if let StmtKind::Let(local) = stmt.kind - && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind + && let PatKind::Binding(BindingMode(ByRef::Yes(_, mutabl), _), .., name, None) = local.pat.kind && let Some(init) = local.init // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 68e51dace2db2..92cc11dae8b12 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -745,10 +745,14 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { let ann = match ann { BindingMode::NONE => "NONE", BindingMode::REF => "REF", + BindingMode::REF_PIN => "REF_PIN", BindingMode::MUT => "MUT", BindingMode::REF_MUT => "REF_MUT", + BindingMode::REF_PIN_MUT => "REF_PIN_MUT", BindingMode::MUT_REF => "MUT_REF", + BindingMode::MUT_REF_PIN => "MUT_REF_PIN", BindingMode::MUT_REF_MUT => "MUT_REF_MUT", + BindingMode::MUT_REF_PIN_MUT => "MUT_REF_PIN_MUT", }; kind!("Binding(BindingMode::{ann}, _, {name}, {sub})"); self.ident(name); diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index eb3f442ac754b..6b922a20ca207 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -212,7 +212,12 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Custom `Deref` impl might have side effects ExprKind::Unary(UnOp::Deref, e) - if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() => + if self + .cx + .typeck_results() + .expr_ty(e) + .builtin_deref(true) + .is_none() => { self.eagerness |= NoChange; }, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 6ee991eae137f..7b3de69d90868 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -783,7 +783,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => { capture = CaptureKind::Value; }, - ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => { + ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => { capture = CaptureKind::Ref(Mutability::Mut); }, _ => (), @@ -1831,7 +1831,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< .typeck_results() .pat_binding_modes() .get(pat.hir_id) - .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_))) + .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..))) { // If the parameter is `(x, y)` of type `&(T, T)`, or `[x, y]` of type `&[T; 2]`, then // due to match ergonomics, the inner patterns become references. Don't consider this diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index fa9a3e33914b6..03752c371ae11 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -134,7 +134,8 @@ impl Rewrite for Pat { let mut_prefix = format_mutability(mutability).trim(); let (ref_kw, mut_infix) = match by_ref { - ByRef::Yes(rmutbl) => ("ref", format_mutability(rmutbl).trim()), + // FIXME(pin_ergonomics): format the pinnedness + ByRef::Yes(_, rmutbl) => ("ref", format_mutability(rmutbl).trim()), ByRef::No => ("", ""), }; let id_str = rewrite_ident(context, ident); diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_const.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_const.built.after.mir new file mode 100644 index 0000000000000..15f7446cc5912 --- /dev/null +++ b/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_const.built.after.mir @@ -0,0 +1,33 @@ +// MIR for `bar_const` after built + +fn bar_const(_1: Pin<&Bar>) -> () { + debug bar => _1; + let mut _0: (); + let _2: std::pin::Pin<&T>; + let _3: std::pin::Pin<&U>; + let mut _4: &T; + let mut _5: &U; + scope 1 { + debug x => _2; + debug y => _3; + } + + bb0: { + PlaceMention(_1); + StorageLive(_2); + _4 = &((*(_1.0: &Bar)).0: T); + _2 = Pin::<&T> { pointer: move _4 }; + StorageLive(_3); + _5 = &((*(_1.0: &Bar)).1: U); + _3 = Pin::<&U> { pointer: move _5 }; + _0 = const (); + StorageDead(_3); + StorageDead(_2); + return; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } +} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_mut.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_mut.built.after.mir new file mode 100644 index 0000000000000..46fd15502ffb1 --- /dev/null +++ b/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_mut.built.after.mir @@ -0,0 +1,33 @@ +// MIR for `bar_mut` after built + +fn bar_mut(_1: Pin<&mut Bar>) -> () { + debug bar => _1; + let mut _0: (); + let _2: std::pin::Pin<&mut T>; + let _3: std::pin::Pin<&mut U>; + let mut _4: &mut T; + let mut _5: &mut U; + scope 1 { + debug x => _2; + debug y => _3; + } + + bb0: { + PlaceMention(_1); + StorageLive(_2); + _4 = &mut ((*(_1.0: &mut Bar)).0: T); + _2 = Pin::<&mut T> { pointer: move _4 }; + StorageLive(_3); + _5 = &mut ((*(_1.0: &mut Bar)).1: U); + _3 = Pin::<&mut U> { pointer: move _5 }; + _0 = const (); + StorageDead(_3); + StorageDead(_2); + return; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } +} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_const.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_const.built.after.mir new file mode 100644 index 0000000000000..8cfaf50a5f876 --- /dev/null +++ b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_const.built.after.mir @@ -0,0 +1,422 @@ +// MIR for `baz_baz_const` after built + +fn baz_baz_const(_1: Pin<&Baz, Baz>>) -> () { + debug baz => _1; + let mut _0: (); + let mut _2: isize; + let mut _3: isize; + let mut _4: isize; + let mut _5: isize; + let mut _6: isize; + let mut _7: isize; + let mut _8: isize; + let _9: std::pin::Pin<&T>; + let _10: std::pin::Pin<&U>; + let _11: std::pin::Pin<&T>; + let _12: std::pin::Pin<&U>; + let mut _13: &T; + let mut _14: &U; + let mut _15: &T; + let mut _16: &U; + let _17: std::pin::Pin<&T>; + let _18: std::pin::Pin<&U>; + let _19: std::pin::Pin<&T>; + let _20: std::pin::Pin<&U>; + let mut _21: &T; + let mut _22: &U; + let mut _23: &T; + let mut _24: &U; + let _25: std::pin::Pin<&T>; + let _26: std::pin::Pin<&U>; + let _27: std::pin::Pin<&T>; + let _28: std::pin::Pin<&U>; + let mut _29: &T; + let mut _30: &U; + let mut _31: &T; + let mut _32: &U; + let _33: std::pin::Pin<&T>; + let _34: std::pin::Pin<&U>; + let _35: std::pin::Pin<&T>; + let _36: std::pin::Pin<&U>; + let mut _37: &T; + let mut _38: &U; + let mut _39: &T; + let mut _40: &U; + let _41: std::pin::Pin<&T>; + let _42: std::pin::Pin<&U>; + let _43: std::pin::Pin<&T>; + let _44: std::pin::Pin<&U>; + let mut _45: &T; + let mut _46: &U; + let mut _47: &T; + let mut _48: &U; + let _49: std::pin::Pin<&T>; + let _50: std::pin::Pin<&U>; + let _51: std::pin::Pin<&T>; + let _52: std::pin::Pin<&U>; + let mut _53: &T; + let mut _54: &U; + let mut _55: &T; + let mut _56: &U; + let _57: std::pin::Pin<&T>; + let _58: std::pin::Pin<&U>; + let _59: std::pin::Pin<&T>; + let _60: std::pin::Pin<&U>; + let mut _61: &T; + let mut _62: &U; + let mut _63: &T; + let mut _64: &U; + let _65: std::pin::Pin<&T>; + let _66: std::pin::Pin<&U>; + let _67: std::pin::Pin<&T>; + let _68: std::pin::Pin<&U>; + let mut _69: &T; + let mut _70: &U; + let mut _71: &T; + let mut _72: &U; + scope 1 { + debug x => _9; + debug y => _10; + debug z => _11; + debug w => _12; + } + scope 2 { + debug x => _17; + debug y => _18; + debug z => _19; + debug w => _20; + } + scope 3 { + debug x => _25; + debug y => _26; + debug z => _27; + debug w => _28; + } + scope 4 { + debug x => _33; + debug y => _34; + debug z => _35; + debug w => _36; + } + scope 5 { + debug x => _41; + debug y => _42; + debug z => _43; + debug w => _44; + } + scope 6 { + debug x => _49; + debug y => _50; + debug z => _51; + debug w => _52; + } + scope 7 { + debug x => _57; + debug y => _58; + debug z => _59; + debug w => _60; + } + scope 8 { + debug x => _65; + debug y => _66; + debug z => _67; + debug w => _68; + } + + bb0: { + PlaceMention(_1); + _8 = discriminant((*(_1.0: &Baz, Baz>))); + switchInt(move _8) -> [0: bb2, 1: bb16, otherwise: bb1]; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } + + bb2: { + _4 = discriminant((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz)); + switchInt(move _4) -> [0: bb4, 1: bb10, otherwise: bb3]; + } + + bb3: { + goto -> bb1; + } + + bb4: { + _2 = discriminant((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz)); + switchInt(move _2) -> [0: bb6, 1: bb8, otherwise: bb5]; + } + + bb5: { + goto -> bb3; + } + + bb6: { + falseEdge -> [real: bb36, imaginary: bb8]; + } + + bb7: { + goto -> bb5; + } + + bb8: { + falseEdge -> [real: bb35, imaginary: bb10]; + } + + bb9: { + goto -> bb5; + } + + bb10: { + _3 = discriminant((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz)); + switchInt(move _3) -> [0: bb12, 1: bb14, otherwise: bb11]; + } + + bb11: { + goto -> bb3; + } + + bb12: { + falseEdge -> [real: bb34, imaginary: bb14]; + } + + bb13: { + goto -> bb11; + } + + bb14: { + falseEdge -> [real: bb33, imaginary: bb16]; + } + + bb15: { + goto -> bb11; + } + + bb16: { + _7 = discriminant((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz)); + switchInt(move _7) -> [0: bb18, 1: bb24, otherwise: bb17]; + } + + bb17: { + goto -> bb1; + } + + bb18: { + _5 = discriminant((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz)); + switchInt(move _5) -> [0: bb20, 1: bb22, otherwise: bb19]; + } + + bb19: { + goto -> bb17; + } + + bb20: { + falseEdge -> [real: bb32, imaginary: bb22]; + } + + bb21: { + goto -> bb19; + } + + bb22: { + falseEdge -> [real: bb31, imaginary: bb24]; + } + + bb23: { + goto -> bb19; + } + + bb24: { + _6 = discriminant((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz)); + switchInt(move _6) -> [0: bb26, 1: bb28, otherwise: bb25]; + } + + bb25: { + goto -> bb17; + } + + bb26: { + falseEdge -> [real: bb30, imaginary: bb28]; + } + + bb27: { + goto -> bb25; + } + + bb28: { + StorageLive(_65); + _69 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Bar).0: T); + _65 = Pin::<&T> { pointer: move _69 }; + StorageLive(_66); + _70 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Bar).1: U); + _66 = Pin::<&U> { pointer: move _70 }; + StorageLive(_67); + _71 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Bar).0: T); + _67 = Pin::<&T> { pointer: move _71 }; + StorageLive(_68); + _72 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Bar).1: U); + _68 = Pin::<&U> { pointer: move _72 }; + _0 = const (); + StorageDead(_68); + StorageDead(_67); + StorageDead(_66); + StorageDead(_65); + goto -> bb37; + } + + bb29: { + goto -> bb25; + } + + bb30: { + StorageLive(_57); + _61 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Bar).0: T); + _57 = Pin::<&T> { pointer: move _61 }; + StorageLive(_58); + _62 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Bar).1: U); + _58 = Pin::<&U> { pointer: move _62 }; + StorageLive(_59); + _63 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Foo).0: T); + _59 = Pin::<&T> { pointer: move _63 }; + StorageLive(_60); + _64 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Foo).1: U); + _60 = Pin::<&U> { pointer: move _64 }; + _0 = const (); + StorageDead(_60); + StorageDead(_59); + StorageDead(_58); + StorageDead(_57); + goto -> bb37; + } + + bb31: { + StorageLive(_49); + _53 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Foo).0: T); + _49 = Pin::<&T> { pointer: move _53 }; + StorageLive(_50); + _54 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Foo).1: U); + _50 = Pin::<&U> { pointer: move _54 }; + StorageLive(_51); + _55 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Bar).0: T); + _51 = Pin::<&T> { pointer: move _55 }; + StorageLive(_52); + _56 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Bar).1: U); + _52 = Pin::<&U> { pointer: move _56 }; + _0 = const (); + StorageDead(_52); + StorageDead(_51); + StorageDead(_50); + StorageDead(_49); + goto -> bb37; + } + + bb32: { + StorageLive(_41); + _45 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Foo).0: T); + _41 = Pin::<&T> { pointer: move _45 }; + StorageLive(_42); + _46 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Foo).1: U); + _42 = Pin::<&U> { pointer: move _46 }; + StorageLive(_43); + _47 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Foo).0: T); + _43 = Pin::<&T> { pointer: move _47 }; + StorageLive(_44); + _48 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Foo).1: U); + _44 = Pin::<&U> { pointer: move _48 }; + _0 = const (); + StorageDead(_44); + StorageDead(_43); + StorageDead(_42); + StorageDead(_41); + goto -> bb37; + } + + bb33: { + StorageLive(_33); + _37 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Bar).0: T); + _33 = Pin::<&T> { pointer: move _37 }; + StorageLive(_34); + _38 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Bar).1: U); + _34 = Pin::<&U> { pointer: move _38 }; + StorageLive(_35); + _39 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Bar).0: T); + _35 = Pin::<&T> { pointer: move _39 }; + StorageLive(_36); + _40 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Bar).1: U); + _36 = Pin::<&U> { pointer: move _40 }; + _0 = const (); + StorageDead(_36); + StorageDead(_35); + StorageDead(_34); + StorageDead(_33); + goto -> bb37; + } + + bb34: { + StorageLive(_25); + _29 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Bar).0: T); + _25 = Pin::<&T> { pointer: move _29 }; + StorageLive(_26); + _30 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Bar).1: U); + _26 = Pin::<&U> { pointer: move _30 }; + StorageLive(_27); + _31 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Foo).0: T); + _27 = Pin::<&T> { pointer: move _31 }; + StorageLive(_28); + _32 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Foo).1: U); + _28 = Pin::<&U> { pointer: move _32 }; + _0 = const (); + StorageDead(_28); + StorageDead(_27); + StorageDead(_26); + StorageDead(_25); + goto -> bb37; + } + + bb35: { + StorageLive(_17); + _21 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Foo).0: T); + _17 = Pin::<&T> { pointer: move _21 }; + StorageLive(_18); + _22 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Foo).1: U); + _18 = Pin::<&U> { pointer: move _22 }; + StorageLive(_19); + _23 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Bar).0: T); + _19 = Pin::<&T> { pointer: move _23 }; + StorageLive(_20); + _24 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Bar).1: U); + _20 = Pin::<&U> { pointer: move _24 }; + _0 = const (); + StorageDead(_20); + StorageDead(_19); + StorageDead(_18); + StorageDead(_17); + goto -> bb37; + } + + bb36: { + StorageLive(_9); + _13 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Foo).0: T); + _9 = Pin::<&T> { pointer: move _13 }; + StorageLive(_10); + _14 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Foo).1: U); + _10 = Pin::<&U> { pointer: move _14 }; + StorageLive(_11); + _15 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Foo).0: T); + _11 = Pin::<&T> { pointer: move _15 }; + StorageLive(_12); + _16 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Foo).1: U); + _12 = Pin::<&U> { pointer: move _16 }; + _0 = const (); + StorageDead(_12); + StorageDead(_11); + StorageDead(_10); + StorageDead(_9); + goto -> bb37; + } + + bb37: { + return; + } +} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_mut.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_mut.built.after.mir new file mode 100644 index 0000000000000..43c523cbf5717 --- /dev/null +++ b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_mut.built.after.mir @@ -0,0 +1,422 @@ +// MIR for `baz_baz_mut` after built + +fn baz_baz_mut(_1: Pin<&mut Baz, Baz>>) -> () { + debug baz => _1; + let mut _0: (); + let mut _2: isize; + let mut _3: isize; + let mut _4: isize; + let mut _5: isize; + let mut _6: isize; + let mut _7: isize; + let mut _8: isize; + let _9: std::pin::Pin<&mut T>; + let _10: std::pin::Pin<&mut U>; + let _11: std::pin::Pin<&mut T>; + let _12: std::pin::Pin<&mut U>; + let mut _13: &mut T; + let mut _14: &mut U; + let mut _15: &mut T; + let mut _16: &mut U; + let _17: std::pin::Pin<&mut T>; + let _18: std::pin::Pin<&mut U>; + let _19: std::pin::Pin<&mut T>; + let _20: std::pin::Pin<&mut U>; + let mut _21: &mut T; + let mut _22: &mut U; + let mut _23: &mut T; + let mut _24: &mut U; + let _25: std::pin::Pin<&mut T>; + let _26: std::pin::Pin<&mut U>; + let _27: std::pin::Pin<&mut T>; + let _28: std::pin::Pin<&mut U>; + let mut _29: &mut T; + let mut _30: &mut U; + let mut _31: &mut T; + let mut _32: &mut U; + let _33: std::pin::Pin<&mut T>; + let _34: std::pin::Pin<&mut U>; + let _35: std::pin::Pin<&mut T>; + let _36: std::pin::Pin<&mut U>; + let mut _37: &mut T; + let mut _38: &mut U; + let mut _39: &mut T; + let mut _40: &mut U; + let _41: std::pin::Pin<&mut T>; + let _42: std::pin::Pin<&mut U>; + let _43: std::pin::Pin<&mut T>; + let _44: std::pin::Pin<&mut U>; + let mut _45: &mut T; + let mut _46: &mut U; + let mut _47: &mut T; + let mut _48: &mut U; + let _49: std::pin::Pin<&mut T>; + let _50: std::pin::Pin<&mut U>; + let _51: std::pin::Pin<&mut T>; + let _52: std::pin::Pin<&mut U>; + let mut _53: &mut T; + let mut _54: &mut U; + let mut _55: &mut T; + let mut _56: &mut U; + let _57: std::pin::Pin<&mut T>; + let _58: std::pin::Pin<&mut U>; + let _59: std::pin::Pin<&mut T>; + let _60: std::pin::Pin<&mut U>; + let mut _61: &mut T; + let mut _62: &mut U; + let mut _63: &mut T; + let mut _64: &mut U; + let _65: std::pin::Pin<&mut T>; + let _66: std::pin::Pin<&mut U>; + let _67: std::pin::Pin<&mut T>; + let _68: std::pin::Pin<&mut U>; + let mut _69: &mut T; + let mut _70: &mut U; + let mut _71: &mut T; + let mut _72: &mut U; + scope 1 { + debug x => _9; + debug y => _10; + debug z => _11; + debug w => _12; + } + scope 2 { + debug x => _17; + debug y => _18; + debug z => _19; + debug w => _20; + } + scope 3 { + debug x => _25; + debug y => _26; + debug z => _27; + debug w => _28; + } + scope 4 { + debug x => _33; + debug y => _34; + debug z => _35; + debug w => _36; + } + scope 5 { + debug x => _41; + debug y => _42; + debug z => _43; + debug w => _44; + } + scope 6 { + debug x => _49; + debug y => _50; + debug z => _51; + debug w => _52; + } + scope 7 { + debug x => _57; + debug y => _58; + debug z => _59; + debug w => _60; + } + scope 8 { + debug x => _65; + debug y => _66; + debug z => _67; + debug w => _68; + } + + bb0: { + PlaceMention(_1); + _8 = discriminant((*(_1.0: &mut Baz, Baz>))); + switchInt(move _8) -> [0: bb2, 1: bb16, otherwise: bb1]; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } + + bb2: { + _4 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz)); + switchInt(move _4) -> [0: bb4, 1: bb10, otherwise: bb3]; + } + + bb3: { + goto -> bb1; + } + + bb4: { + _2 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz)); + switchInt(move _2) -> [0: bb6, 1: bb8, otherwise: bb5]; + } + + bb5: { + goto -> bb3; + } + + bb6: { + falseEdge -> [real: bb36, imaginary: bb8]; + } + + bb7: { + goto -> bb5; + } + + bb8: { + falseEdge -> [real: bb35, imaginary: bb10]; + } + + bb9: { + goto -> bb5; + } + + bb10: { + _3 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz)); + switchInt(move _3) -> [0: bb12, 1: bb14, otherwise: bb11]; + } + + bb11: { + goto -> bb3; + } + + bb12: { + falseEdge -> [real: bb34, imaginary: bb14]; + } + + bb13: { + goto -> bb11; + } + + bb14: { + falseEdge -> [real: bb33, imaginary: bb16]; + } + + bb15: { + goto -> bb11; + } + + bb16: { + _7 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz)); + switchInt(move _7) -> [0: bb18, 1: bb24, otherwise: bb17]; + } + + bb17: { + goto -> bb1; + } + + bb18: { + _5 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz)); + switchInt(move _5) -> [0: bb20, 1: bb22, otherwise: bb19]; + } + + bb19: { + goto -> bb17; + } + + bb20: { + falseEdge -> [real: bb32, imaginary: bb22]; + } + + bb21: { + goto -> bb19; + } + + bb22: { + falseEdge -> [real: bb31, imaginary: bb24]; + } + + bb23: { + goto -> bb19; + } + + bb24: { + _6 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz)); + switchInt(move _6) -> [0: bb26, 1: bb28, otherwise: bb25]; + } + + bb25: { + goto -> bb17; + } + + bb26: { + falseEdge -> [real: bb30, imaginary: bb28]; + } + + bb27: { + goto -> bb25; + } + + bb28: { + StorageLive(_65); + _69 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Bar).0: T); + _65 = Pin::<&mut T> { pointer: move _69 }; + StorageLive(_66); + _70 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Bar).1: U); + _66 = Pin::<&mut U> { pointer: move _70 }; + StorageLive(_67); + _71 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Bar).0: T); + _67 = Pin::<&mut T> { pointer: move _71 }; + StorageLive(_68); + _72 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Bar).1: U); + _68 = Pin::<&mut U> { pointer: move _72 }; + _0 = const (); + StorageDead(_68); + StorageDead(_67); + StorageDead(_66); + StorageDead(_65); + goto -> bb37; + } + + bb29: { + goto -> bb25; + } + + bb30: { + StorageLive(_57); + _61 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Bar).0: T); + _57 = Pin::<&mut T> { pointer: move _61 }; + StorageLive(_58); + _62 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Bar).1: U); + _58 = Pin::<&mut U> { pointer: move _62 }; + StorageLive(_59); + _63 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Foo).0: T); + _59 = Pin::<&mut T> { pointer: move _63 }; + StorageLive(_60); + _64 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Foo).1: U); + _60 = Pin::<&mut U> { pointer: move _64 }; + _0 = const (); + StorageDead(_60); + StorageDead(_59); + StorageDead(_58); + StorageDead(_57); + goto -> bb37; + } + + bb31: { + StorageLive(_49); + _53 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Foo).0: T); + _49 = Pin::<&mut T> { pointer: move _53 }; + StorageLive(_50); + _54 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Foo).1: U); + _50 = Pin::<&mut U> { pointer: move _54 }; + StorageLive(_51); + _55 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Bar).0: T); + _51 = Pin::<&mut T> { pointer: move _55 }; + StorageLive(_52); + _56 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Bar).1: U); + _52 = Pin::<&mut U> { pointer: move _56 }; + _0 = const (); + StorageDead(_52); + StorageDead(_51); + StorageDead(_50); + StorageDead(_49); + goto -> bb37; + } + + bb32: { + StorageLive(_41); + _45 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Foo).0: T); + _41 = Pin::<&mut T> { pointer: move _45 }; + StorageLive(_42); + _46 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Foo).1: U); + _42 = Pin::<&mut U> { pointer: move _46 }; + StorageLive(_43); + _47 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Foo).0: T); + _43 = Pin::<&mut T> { pointer: move _47 }; + StorageLive(_44); + _48 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Foo).1: U); + _44 = Pin::<&mut U> { pointer: move _48 }; + _0 = const (); + StorageDead(_44); + StorageDead(_43); + StorageDead(_42); + StorageDead(_41); + goto -> bb37; + } + + bb33: { + StorageLive(_33); + _37 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Bar).0: T); + _33 = Pin::<&mut T> { pointer: move _37 }; + StorageLive(_34); + _38 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Bar).1: U); + _34 = Pin::<&mut U> { pointer: move _38 }; + StorageLive(_35); + _39 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Bar).0: T); + _35 = Pin::<&mut T> { pointer: move _39 }; + StorageLive(_36); + _40 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Bar).1: U); + _36 = Pin::<&mut U> { pointer: move _40 }; + _0 = const (); + StorageDead(_36); + StorageDead(_35); + StorageDead(_34); + StorageDead(_33); + goto -> bb37; + } + + bb34: { + StorageLive(_25); + _29 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Bar).0: T); + _25 = Pin::<&mut T> { pointer: move _29 }; + StorageLive(_26); + _30 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Bar).1: U); + _26 = Pin::<&mut U> { pointer: move _30 }; + StorageLive(_27); + _31 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Foo).0: T); + _27 = Pin::<&mut T> { pointer: move _31 }; + StorageLive(_28); + _32 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Foo).1: U); + _28 = Pin::<&mut U> { pointer: move _32 }; + _0 = const (); + StorageDead(_28); + StorageDead(_27); + StorageDead(_26); + StorageDead(_25); + goto -> bb37; + } + + bb35: { + StorageLive(_17); + _21 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Foo).0: T); + _17 = Pin::<&mut T> { pointer: move _21 }; + StorageLive(_18); + _22 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Foo).1: U); + _18 = Pin::<&mut U> { pointer: move _22 }; + StorageLive(_19); + _23 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Bar).0: T); + _19 = Pin::<&mut T> { pointer: move _23 }; + StorageLive(_20); + _24 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Bar).1: U); + _20 = Pin::<&mut U> { pointer: move _24 }; + _0 = const (); + StorageDead(_20); + StorageDead(_19); + StorageDead(_18); + StorageDead(_17); + goto -> bb37; + } + + bb36: { + StorageLive(_9); + _13 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Foo).0: T); + _9 = Pin::<&mut T> { pointer: move _13 }; + StorageLive(_10); + _14 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Foo).1: U); + _10 = Pin::<&mut U> { pointer: move _14 }; + StorageLive(_11); + _15 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Foo).0: T); + _11 = Pin::<&mut T> { pointer: move _15 }; + StorageLive(_12); + _16 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Foo).1: U); + _12 = Pin::<&mut U> { pointer: move _16 }; + _0 = const (); + StorageDead(_12); + StorageDead(_11); + StorageDead(_10); + StorageDead(_9); + goto -> bb37; + } + + bb37: { + return; + } +} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_const.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_const.built.after.mir new file mode 100644 index 0000000000000..5538449cc43cc --- /dev/null +++ b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_const.built.after.mir @@ -0,0 +1,76 @@ +// MIR for `baz_const` after built + +fn baz_const(_1: Pin<&Baz>) -> () { + debug baz => _1; + let mut _0: (); + let mut _2: isize; + let _3: std::pin::Pin<&T>; + let _4: std::pin::Pin<&U>; + let mut _5: &T; + let mut _6: &U; + let _7: std::pin::Pin<&T>; + let _8: std::pin::Pin<&U>; + let mut _9: &T; + let mut _10: &U; + scope 1 { + debug x => _3; + debug y => _4; + } + scope 2 { + debug x => _7; + debug y => _8; + } + + bb0: { + PlaceMention(_1); + _2 = discriminant((*(_1.0: &Baz))); + switchInt(move _2) -> [0: bb2, 1: bb4, otherwise: bb1]; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } + + bb2: { + falseEdge -> [real: bb6, imaginary: bb4]; + } + + bb3: { + goto -> bb1; + } + + bb4: { + StorageLive(_7); + _9 = &(((*(_1.0: &Baz)) as Bar).0: T); + _7 = Pin::<&T> { pointer: move _9 }; + StorageLive(_8); + _10 = &(((*(_1.0: &Baz)) as Bar).1: U); + _8 = Pin::<&U> { pointer: move _10 }; + _0 = const (); + StorageDead(_8); + StorageDead(_7); + goto -> bb7; + } + + bb5: { + goto -> bb1; + } + + bb6: { + StorageLive(_3); + _5 = &(((*(_1.0: &Baz)) as Foo).0: T); + _3 = Pin::<&T> { pointer: move _5 }; + StorageLive(_4); + _6 = &(((*(_1.0: &Baz)) as Foo).1: U); + _4 = Pin::<&U> { pointer: move _6 }; + _0 = const (); + StorageDead(_4); + StorageDead(_3); + goto -> bb7; + } + + bb7: { + return; + } +} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_mut.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_mut.built.after.mir new file mode 100644 index 0000000000000..4ae472cba3a58 --- /dev/null +++ b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_mut.built.after.mir @@ -0,0 +1,76 @@ +// MIR for `baz_mut` after built + +fn baz_mut(_1: Pin<&mut Baz>) -> () { + debug baz => _1; + let mut _0: (); + let mut _2: isize; + let _3: std::pin::Pin<&mut T>; + let _4: std::pin::Pin<&mut U>; + let mut _5: &mut T; + let mut _6: &mut U; + let _7: std::pin::Pin<&mut T>; + let _8: std::pin::Pin<&mut U>; + let mut _9: &mut T; + let mut _10: &mut U; + scope 1 { + debug x => _3; + debug y => _4; + } + scope 2 { + debug x => _7; + debug y => _8; + } + + bb0: { + PlaceMention(_1); + _2 = discriminant((*(_1.0: &mut Baz))); + switchInt(move _2) -> [0: bb2, 1: bb4, otherwise: bb1]; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } + + bb2: { + falseEdge -> [real: bb6, imaginary: bb4]; + } + + bb3: { + goto -> bb1; + } + + bb4: { + StorageLive(_7); + _9 = &mut (((*(_1.0: &mut Baz)) as Bar).0: T); + _7 = Pin::<&mut T> { pointer: move _9 }; + StorageLive(_8); + _10 = &mut (((*(_1.0: &mut Baz)) as Bar).1: U); + _8 = Pin::<&mut U> { pointer: move _10 }; + _0 = const (); + StorageDead(_8); + StorageDead(_7); + goto -> bb7; + } + + bb5: { + goto -> bb1; + } + + bb6: { + StorageLive(_3); + _5 = &mut (((*(_1.0: &mut Baz)) as Foo).0: T); + _3 = Pin::<&mut T> { pointer: move _5 }; + StorageLive(_4); + _6 = &mut (((*(_1.0: &mut Baz)) as Foo).1: U); + _4 = Pin::<&mut U> { pointer: move _6 }; + _0 = const (); + StorageDead(_4); + StorageDead(_3); + goto -> bb7; + } + + bb7: { + return; + } +} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_const.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_const.built.after.mir new file mode 100644 index 0000000000000..8b397ffd18142 --- /dev/null +++ b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_const.built.after.mir @@ -0,0 +1,47 @@ +// MIR for `foo_bar_const` after built + +fn foo_bar_const(_1: Pin<&Foo, Bar>>) -> () { + debug foo => _1; + let mut _0: (); + let _2: std::pin::Pin<&T>; + let _3: std::pin::Pin<&U>; + let _4: std::pin::Pin<&T>; + let _5: std::pin::Pin<&U>; + let mut _6: &T; + let mut _7: &U; + let mut _8: &T; + let mut _9: &U; + scope 1 { + debug x => _2; + debug y => _3; + debug z => _4; + debug w => _5; + } + + bb0: { + PlaceMention(_1); + StorageLive(_2); + _6 = &(((*(_1.0: &Foo, Bar>)).0: Bar).0: T); + _2 = Pin::<&T> { pointer: move _6 }; + StorageLive(_3); + _7 = &(((*(_1.0: &Foo, Bar>)).0: Bar).1: U); + _3 = Pin::<&U> { pointer: move _7 }; + StorageLive(_4); + _8 = &(((*(_1.0: &Foo, Bar>)).1: Bar).0: T); + _4 = Pin::<&T> { pointer: move _8 }; + StorageLive(_5); + _9 = &(((*(_1.0: &Foo, Bar>)).1: Bar).1: U); + _5 = Pin::<&U> { pointer: move _9 }; + _0 = const (); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } +} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_mut.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_mut.built.after.mir new file mode 100644 index 0000000000000..2e8128bb9d1e4 --- /dev/null +++ b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_mut.built.after.mir @@ -0,0 +1,47 @@ +// MIR for `foo_bar_mut` after built + +fn foo_bar_mut(_1: Pin<&mut Foo, Bar>>) -> () { + debug foo => _1; + let mut _0: (); + let _2: std::pin::Pin<&mut T>; + let _3: std::pin::Pin<&mut U>; + let _4: std::pin::Pin<&mut T>; + let _5: std::pin::Pin<&mut U>; + let mut _6: &mut T; + let mut _7: &mut U; + let mut _8: &mut T; + let mut _9: &mut U; + scope 1 { + debug x => _2; + debug y => _3; + debug z => _4; + debug w => _5; + } + + bb0: { + PlaceMention(_1); + StorageLive(_2); + _6 = &mut (((*(_1.0: &mut Foo, Bar>)).0: Bar).0: T); + _2 = Pin::<&mut T> { pointer: move _6 }; + StorageLive(_3); + _7 = &mut (((*(_1.0: &mut Foo, Bar>)).0: Bar).1: U); + _3 = Pin::<&mut U> { pointer: move _7 }; + StorageLive(_4); + _8 = &mut (((*(_1.0: &mut Foo, Bar>)).1: Bar).0: T); + _4 = Pin::<&mut T> { pointer: move _8 }; + StorageLive(_5); + _9 = &mut (((*(_1.0: &mut Foo, Bar>)).1: Bar).1: U); + _5 = Pin::<&mut U> { pointer: move _9 }; + _0 = const (); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } +} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_const.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_const.built.after.mir new file mode 100644 index 0000000000000..b77b341d4ef76 --- /dev/null +++ b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_const.built.after.mir @@ -0,0 +1,33 @@ +// MIR for `foo_const` after built + +fn foo_const(_1: Pin<&Foo>) -> () { + debug foo => _1; + let mut _0: (); + let _2: std::pin::Pin<&T>; + let _3: std::pin::Pin<&U>; + let mut _4: &T; + let mut _5: &U; + scope 1 { + debug x => _2; + debug y => _3; + } + + bb0: { + PlaceMention(_1); + StorageLive(_2); + _4 = &((*(_1.0: &Foo)).0: T); + _2 = Pin::<&T> { pointer: move _4 }; + StorageLive(_3); + _5 = &((*(_1.0: &Foo)).1: U); + _3 = Pin::<&U> { pointer: move _5 }; + _0 = const (); + StorageDead(_3); + StorageDead(_2); + return; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } +} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_mut.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_mut.built.after.mir new file mode 100644 index 0000000000000..c7b88d239f87a --- /dev/null +++ b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_mut.built.after.mir @@ -0,0 +1,33 @@ +// MIR for `foo_mut` after built + +fn foo_mut(_1: Pin<&mut Foo>) -> () { + debug foo => _1; + let mut _0: (); + let _2: std::pin::Pin<&mut T>; + let _3: std::pin::Pin<&mut U>; + let mut _4: &mut T; + let mut _5: &mut U; + scope 1 { + debug x => _2; + debug y => _3; + } + + bb0: { + PlaceMention(_1); + StorageLive(_2); + _4 = &mut ((*(_1.0: &mut Foo)).0: T); + _2 = Pin::<&mut T> { pointer: move _4 }; + StorageLive(_3); + _5 = &mut ((*(_1.0: &mut Foo)).1: U); + _3 = Pin::<&mut U> { pointer: move _5 }; + _0 = const (); + StorageDead(_3); + StorageDead(_2); + return; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } +} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.rs b/tests/mir-opt/pin-ergonomics/project_pattern_match.rs new file mode 100644 index 0000000000000..8808111f303b3 --- /dev/null +++ b/tests/mir-opt/pin-ergonomics/project_pattern_match.rs @@ -0,0 +1,95 @@ +// skip-filecheck +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +// This test verifies that a `&pin mut Foo` can be projected to a pinned +// reference `&pin mut T` of a `?Unpin` field , and can be projected to +// an unpinned reference `&mut U` of an `Unpin` field . + +struct Foo { + x: T, + y: U, +} + +struct Bar(T, U); + +enum Baz { + Foo(T, U), + Bar { x: T, y: U }, +} + +// EMIT_MIR project_pattern_match.foo_mut.built.after.mir +fn foo_mut(foo: &pin mut Foo) { + let Foo { x, y } = foo; +} + +// EMIT_MIR project_pattern_match.foo_const.built.after.mir +fn foo_const(foo: &pin const Foo) { + let Foo { x, y } = foo; +} + +// EMIT_MIR project_pattern_match.bar_mut.built.after.mir +fn bar_mut(bar: &pin mut Bar) { + let Bar(x, y) = bar; +} + +// EMIT_MIR project_pattern_match.bar_const.built.after.mir +fn bar_const(bar: &pin const Bar) { + let Bar(x, y) = bar; +} + +// EMIT_MIR project_pattern_match.foo_bar_mut.built.after.mir +fn foo_bar_mut(foo: &pin mut Foo, Bar>) { + let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; +} + +// EMIT_MIR project_pattern_match.foo_bar_const.built.after.mir +fn foo_bar_const(foo: &pin const Foo, Bar>) { + let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; +} + +// EMIT_MIR project_pattern_match.baz_mut.built.after.mir +fn baz_mut(baz: &pin mut Baz) { + match baz { + Baz::Foo(x, y) => {} + Baz::Bar { x, y } => {} + } +} + +// EMIT_MIR project_pattern_match.baz_const.built.after.mir +fn baz_const(baz: &pin const Baz) { + match baz { + Baz::Foo(x, y) => {} + Baz::Bar { x, y } => {} + } +} + +// EMIT_MIR project_pattern_match.baz_baz_mut.built.after.mir +fn baz_baz_mut(baz: &pin mut Baz, Baz>) { + match baz { + Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w)) => {} + Baz::Foo(Baz::Foo(x, y), Baz::Bar { x: z, y: w }) => {} + Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w)) => {} + Baz::Foo(Baz::Bar { x, y }, Baz::Bar { x: z, y: w }) => {} + Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) } => {} + Baz::Bar { x: Baz::Foo(x, y), y: Baz::Bar { x: z, y: w } } => {} + Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) } => {} + Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Bar { x: z, y: w } } => {} + } +} + +// EMIT_MIR project_pattern_match.baz_baz_const.built.after.mir +fn baz_baz_const(baz: &pin const Baz, Baz>) { + match baz { + Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w)) => {} + Baz::Foo(Baz::Foo(x, y), Baz::Bar { x: z, y: w }) => {} + Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w)) => {} + Baz::Foo(Baz::Bar { x, y }, Baz::Bar { x: z, y: w }) => {} + Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) } => {} + Baz::Bar { x: Baz::Foo(x, y), y: Baz::Bar { x: z, y: w } } => {} + Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) } => {} + Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Bar { x: z, y: w } } => {} + } +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs new file mode 100644 index 0000000000000..23d29e6f9dae4 --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs @@ -0,0 +1,91 @@ +//@ revisions: pin_ergonomics normal +//@ edition:2024 +//@ check-pass +// //@[normal] check-pass +// //@[pin_ergonomics] rustc-env:RUSTC_LOG=rustc_hir_typeck::expr_use_visitor=DEBUG +#![cfg_attr(pin_ergonomics, feature(pin_ergonomics))] +#![feature(deref_patterns)] +#![allow(incomplete_features)] + +// This test verifies that the `pin_ergonomics` feature works well +// together with the `deref_patterns` feature. + +use std::pin::Pin; + +struct Foo { + x: T, + y: U, +} + +struct Bar(T, U); + +enum Baz { + Foo(T, U), + Bar { x: T, y: U }, +} + +fn foo_mut(foo: Pin<&mut Foo>) { + let Foo { .. } = foo; + let Pin { .. } = foo; + let _ = || { + let Foo { .. } = foo; + let Pin { .. } = foo; + }; + + #[cfg(pin_ergonomics)] + let Foo { x, y } = foo; + #[cfg(pin_ergonomics)] + let _ = || { + let Foo { x, y } = foo; + }; +} + +fn foo_const(foo: Pin<&Foo>) { + let Foo { .. } = foo; + let Pin { .. } = foo; + let _ = || { + let Foo { .. } = foo; + let Pin { .. } = foo; + }; + + #[cfg(pin_ergonomics)] + let Foo { x, y } = foo; + #[cfg(pin_ergonomics)] + let _ = || { + let Foo { x, y } = foo; + }; +} + +fn bar_mut(bar: Pin<&mut Bar>) { + let Bar(..) = bar; + let Pin { .. } = bar; + let _ = || { + let Bar(..) = bar; + let Pin { .. } = bar; + }; + + #[cfg(pin_ergonomics)] + let Bar(x, y) = bar; + #[cfg(pin_ergonomics)] + let _ = || { + let Bar(x, y) = bar; + }; +} + +fn bar_const(bar: Pin<&Bar>) { + let Bar(..) = bar; + let Pin { .. } = bar; + let _ = || { + let Bar(..) = bar; + let Pin { .. } = bar; + }; + + #[cfg(pin_ergonomics)] + let Bar(x, y) = bar; + #[cfg(pin_ergonomics)] + let _ = || { + let Bar(x, y) = bar; + }; +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr new file mode 100644 index 0000000000000..2389fe9ed98cd --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr @@ -0,0 +1,66 @@ +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:27:9 + | +LL | Foo { .. } => {} + | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:31:9 + | +LL | Foo { .. } => {} + | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:49:9 + | +LL | Foo { .. } => {} + | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:53:9 + | +LL | Foo { .. } => {} + | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:71:9 + | +LL | Bar(..) => {} + | ^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:75:9 + | +LL | Bar(..) => {} + | ^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:93:9 + | +LL | Bar(..) => {} + | ^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:97:9 + | +LL | Bar(..) => {} + | ^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: aborting due to 8 previous errors + diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr new file mode 100644 index 0000000000000..f23e5cf72a45e --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr @@ -0,0 +1,130 @@ +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:27:9 + | +LL | Foo { .. } => {} + | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:37:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:31:9 + | +LL | Foo { .. } => {} + | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:42:9 + | +LL | Foo { .. } => {} + | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:49:9 + | +LL | Foo { .. } => {} + | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:59:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:53:9 + | +LL | Foo { .. } => {} + | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:64:9 + | +LL | Foo { .. } => {} + | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:71:9 + | +LL | Bar(..) => {} + | ^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:81:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:75:9 + | +LL | Bar(..) => {} + | ^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:86:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:93:9 + | +LL | Bar(..) => {} + | ^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:103:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:97:9 + | +LL | Bar(..) => {} + | ^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:108:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: aborting due to 16 previous errors + diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs new file mode 100644 index 0000000000000..c5ba9dbeb2e6e --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs @@ -0,0 +1,113 @@ +//@ revisions: pin_ergonomics normal +//@ edition:2024 +#![cfg_attr(pin_ergonomics, feature(pin_ergonomics))] +#![feature(deref_patterns)] +#![allow(incomplete_features)] + +// This test verifies that the `pin_ergonomics` feature works well +// together with the `deref_patterns` feature under the error: +// "mix of deref patterns and normal constructors". + +use std::pin::Pin; + +struct Foo { + x: T, + y: U, +} + +struct Bar(T, U); + +enum Baz { + Foo(T, U), + Bar { x: T, y: U }, +} + +fn foo_mut(foo: Pin<&mut Foo>) { + match foo { + Foo { .. } => {} //~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + let _ = || match foo { + Foo { .. } => {} //~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; + + #[cfg(pin_ergonomics)] + match foo { + Foo { x, y } => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + #[cfg(pin_ergonomics)] + let _ = || match foo { + Foo { .. } => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; +} + +fn foo_const(foo: Pin<&Foo>) { + match foo { + Foo { .. } => {} //~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + let _ = || match foo { + Foo { .. } => {} //~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; + + #[cfg(pin_ergonomics)] + match foo { + Foo { x, y } => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + #[cfg(pin_ergonomics)] + let _ = || match foo { + Foo { .. } => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; +} + +fn bar_mut(bar: Pin<&mut Bar>) { + match bar { + Bar(..) => {} //~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + let _ = || match bar { + Bar(..) => {} //~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; + + #[cfg(pin_ergonomics)] + match bar { + Bar(x, y) => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + #[cfg(pin_ergonomics)] + let _ = || match bar { + Bar(x, y) => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; +} + +fn bar_const(bar: Pin<&Bar>) { + match bar { + Bar(..) => {} //~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + let _ = || match bar { + Bar(..) => {} //~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; + + #[cfg(pin_ergonomics)] + match bar { + Bar(x, y) => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + } + #[cfg(pin_ergonomics)] + let _ = || match bar { + Bar(x, y) => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Pin { .. } => {} + }; +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr new file mode 100644 index 0000000000000..7aaa51e81ed90 --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr @@ -0,0 +1,313 @@ +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:35:9 + | +LL | let Foo { x, y } = foo; + | ^^^^^^^^^^^^ --- this expression has type `Pin<&mut Foo>` + | | + | expected `Pin<&mut Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&mut Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Foo { x, y } = *foo; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:42:9 + | +LL | let Foo { x, y } = foo; + | ^^^^^^^^^^^^ --- this expression has type `Pin<&Foo>` + | | + | expected `Pin<&Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Foo { x, y } = *foo; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:49:9 + | +LL | let Bar(x, y) = bar; + | ^^^^^^^^^ --- this expression has type `Pin<&mut Bar>` + | | + | expected `Pin<&mut Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&mut Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Bar(x, y) = *bar; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:56:9 + | +LL | let Bar(x, y) = bar; + | ^^^^^^^^^ --- this expression has type `Pin<&Bar>` + | | + | expected `Pin<&Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Bar(x, y) = *bar; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:63:9 + | +LL | let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- this expression has type `Pin<&mut Foo, Bar>>` + | | + | expected `Pin<&mut Foo, Bar>>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&mut Foo, Bar>>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Foo { x: Bar(x, y), y: Bar(z, w) } = *foo; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:72:9 + | +LL | let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- this expression has type `Pin<&Foo, Bar>>` + | | + | expected `Pin<&Foo, Bar>>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&Foo, Bar>>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Foo { x: Bar(x, y), y: Bar(z, w) } = *foo; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:82:9 + | +LL | match baz { + | --- this expression has type `Pin<&mut Baz>` +LL | Baz::Foo(x, y) => { + | ^^^^^^^^^^^^^^ expected `Pin<&mut Baz>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&mut Baz>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:87:9 + | +LL | match baz { + | --- this expression has type `Pin<&mut Baz>` +... +LL | Baz::Bar { x, y } => { + | ^^^^^^^^^^^^^^^^^ expected `Pin<&mut Baz>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&mut Baz>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:97:9 + | +LL | match baz { + | --- this expression has type `Pin<&Baz>` +LL | Baz::Foo(x, y) => { + | ^^^^^^^^^^^^^^ expected `Pin<&Baz>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&Baz>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:102:9 + | +LL | match baz { + | --- this expression has type `Pin<&Baz>` +... +LL | Baz::Bar { x, y } => { + | ^^^^^^^^^^^^^^^^^ expected `Pin<&Baz>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&Baz>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:112:9 + | +LL | match baz { + | --- this expression has type `Pin<&mut Baz, Baz>>` +LL | Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut Baz, Baz>>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&mut Baz, Baz>>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:119:9 + | +LL | match baz { + | --- this expression has type `Pin<&mut Baz, Baz>>` +... +LL | Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut Baz, Baz>>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&mut Baz, Baz>>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:126:9 + | +LL | match baz { + | --- this expression has type `Pin<&mut Baz, Baz>>` +... +LL | Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut Baz, Baz>>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&mut Baz, Baz>>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:133:9 + | +LL | match baz { + | --- this expression has type `Pin<&mut Baz, Baz>>` +... +LL | Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut Baz, Baz>>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&mut Baz, Baz>>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:145:9 + | +LL | match baz { + | --- this expression has type `Pin<&Baz, Baz>>` +LL | Baz::Foo(foo, _) if let Baz::Foo(x, y) = foo => { + | ^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&Baz, Baz>>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:150:9 + | +LL | match baz { + | --- this expression has type `Pin<&Baz, Baz>>` +... +LL | Baz::Bar { x: _, y: bar } if let Baz::Bar { x, y } = bar => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&Baz, Baz>>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:155:9 + | +LL | match baz { + | --- this expression has type `Pin<&Baz, Baz>>` +... +LL | Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&Baz, Baz>>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:162:9 + | +LL | match baz { + | --- this expression has type `Pin<&Baz, Baz>>` +... +LL | Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&Baz, Baz>>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:169:9 + | +LL | match baz { + | --- this expression has type `Pin<&Baz, Baz>>` +... +LL | Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&Baz, Baz>>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:176:9 + | +LL | match baz { + | --- this expression has type `Pin<&Baz, Baz>>` +... +LL | Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` + | + = note: expected struct `Pin<&Baz, Baz>>` + found enum `Baz<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *baz { + | + + +error: aborting due to 20 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pattern-matching.rs b/tests/ui/pin-ergonomics/pattern-matching.rs new file mode 100644 index 0000000000000..e480ebfc00626 --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching.rs @@ -0,0 +1,186 @@ +//@ revisions: pin_ergonomics normal +//@ edition:2024 +//@[pin_ergonomics] check-pass +#![cfg_attr(pin_ergonomics, feature(pin_ergonomics))] +#![feature(if_let_guard)] +#![allow(incomplete_features)] + +use std::pin::Pin; + +// This test verifies that a `&pin mut Foo` can be projected to a pinned +// reference `&pin mut T` of a `?Unpin` field , and can be projected to +// an unpinned reference `&mut U` of an `Unpin` field. + +struct Foo { + x: T, + y: U, +} + +struct Bar(T, U); + +enum Baz { + Foo(T, U), + Bar { x: T, y: U }, +} + +trait IsPinMut {} +trait IsPinConst {} +impl IsPinMut for Pin<&mut T> {} +impl IsPinConst for Pin<&T> {} + +fn assert_pin_mut(_: T) {} +fn assert_pin_const(_: T) {} + +fn foo_mut(foo: Pin<&mut Foo>) { + let Foo { x, y } = foo; + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); +} + +fn foo_const(foo: Pin<&Foo>) { + let Foo { x, y } = foo; + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); +} + +fn bar_mut(bar: Pin<&mut Bar>) { + let Bar(x, y) = bar; + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); +} + +fn bar_const(bar: Pin<&Bar>) { + let Bar(x, y) = bar; + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); +} + +fn foo_bar_mut(foo: Pin<&mut Foo, Bar>>) { + let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + assert_pin_mut(z); + assert_pin_mut(w); +} + +fn foo_bar_const(foo: Pin<&Foo, Bar>>) { + let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); + assert_pin_const(z); + assert_pin_const(w); +} + +fn baz_mut(baz: Pin<&mut Baz>) { + match baz { + Baz::Foo(x, y) => { + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + } + Baz::Bar { x, y } => { + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + } + } +} + +fn baz_const(baz: Pin<&Baz>) { + match baz { + Baz::Foo(x, y) => { + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); + } + Baz::Bar { x, y } => { + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); + } + } +} + +fn baz_baz_mut(baz: Pin<&mut Baz, Baz>>) { + match baz { + Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + assert_pin_mut(z); + assert_pin_mut(w); + } + Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + assert_pin_mut(z); + assert_pin_mut(w); + } + Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + assert_pin_mut(z); + assert_pin_mut(w); + } + Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + assert_pin_mut(z); + assert_pin_mut(w); + } + } +} + +fn baz_baz_const(baz: Pin<&Baz, Baz>>) { + match baz { + Baz::Foo(foo, _) if let Baz::Foo(x, y) = foo => { + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); + } + Baz::Bar { x: _, y: bar } if let Baz::Bar { x, y } = bar => { + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); + } + Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); + assert_pin_const(z); + assert_pin_const(w); + } + Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); + assert_pin_const(z); + assert_pin_const(w); + } + Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); + assert_pin_const(z); + assert_pin_const(w); + } + Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); + assert_pin_const(z); + assert_pin_const(w); + } + } +} + +fn main() {} From 9fc1d8aa3b45adaf310601b7ea28f59fdc3573e2 Mon Sep 17 00:00:00 2001 From: Frank King Date: Sat, 19 Jul 2025 21:54:05 +0800 Subject: [PATCH 083/108] require `T: !Unpin` for `&pin mut T` to be projected to `&pin mut T.U` where `U: ?Unpin` --- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 25 +- compiler/rustc_hir_typeck/src/pat.rs | 210 ++++++--- compiler/rustc_middle/src/ty/sty.rs | 10 + .../rustc_middle/src/ty/typeck_results.rs | 26 ++ .../rustc_pattern_analysis/src/constructor.rs | 2 +- .../src/traits/select/candidate_assembly.rs | 76 +++- .../src/traits/select/confirmation.rs | 5 + .../src/traits/select/mod.rs | 52 ++- ...ct_pattern_match.bar_const.built.after.mir | 33 -- ...ject_pattern_match.bar_mut.built.after.mir | 33 -- ...attern_match.baz_baz_const.built.after.mir | 422 ------------------ ..._pattern_match.baz_baz_mut.built.after.mir | 422 ------------------ ...ct_pattern_match.baz_const.built.after.mir | 76 ---- ...ject_pattern_match.baz_mut.built.after.mir | 76 ---- ...attern_match.foo_bar_const.built.after.mir | 47 -- ..._pattern_match.foo_bar_mut.built.after.mir | 47 -- ...ct_pattern_match.foo_const.built.after.mir | 33 -- ...ject_pattern_match.foo_mut.built.after.mir | 33 -- .../pin-ergonomics/project_pattern_match.rs | 95 ---- .../pattern-matching.normal.stderr | 327 +++++--------- tests/ui/pin-ergonomics/pattern-matching.rs | 186 ++++---- .../pin-ergonomics/projection-unpin-checks.rs | 132 ++++++ .../projection-unpin-checks.stderr | 333 ++++++++++++++ 23 files changed, 999 insertions(+), 1702 deletions(-) delete mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.bar_const.built.after.mir delete mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.bar_mut.built.after.mir delete mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_const.built.after.mir delete mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_mut.built.after.mir delete mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.baz_const.built.after.mir delete mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.baz_mut.built.after.mir delete mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_const.built.after.mir delete mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_mut.built.after.mir delete mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.foo_const.built.after.mir delete mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.foo_mut.built.after.mir delete mode 100644 tests/mir-opt/pin-ergonomics/project_pattern_match.rs create mode 100644 tests/ui/pin-ergonomics/projection-unpin-checks.rs create mode 100644 tests/ui/pin-ergonomics/projection-unpin-checks.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index dde6b8ce9b8b2..ec67efe6399e3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -23,7 +23,7 @@ use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, - SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, + SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, Upcast, UserArgs, UserSelfTy, }; use rustc_middle::{bug, span_bug}; @@ -464,6 +464,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + pub(crate) fn register_negative_bound( + &self, + ty: Ty<'tcx>, + def_id: DefId, + cause: traits::ObligationCause<'tcx>, + ) { + if !ty.references_error() { + let trait_ref = ty::TraitRef::new(self.tcx, def_id, [ty]); + let predicate = + ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Negative } + .upcast(self.tcx); + self.fulfillment_cx.borrow_mut().register_predicate_obligation( + self, + traits::Obligation { + cause, + recursion_depth: 0, + param_env: self.param_env, + predicate, + }, + ); + } + } + pub(crate) fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> LoweredTy<'tcx> { let ty = self.lowerer().lower_ty(hir_ty); self.register_wf_obligation(ty.into(), hir_ty.span, ObligationCauseCode::WellFormed(None)); diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index d2b3041ad7e1a..3a6b11653c031 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -27,7 +27,7 @@ use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; +use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use tracing::{debug, instrument, trace}; use ty::VariantDef; use ty::adjustment::{PatAdjust, PatAdjustment}; @@ -403,19 +403,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info); self.write_ty(pat.hir_id, ty); - // If we implicitly inserted overloaded dereferences before matching, check the pattern to - // see if the dereferenced types need `DerefMut` bounds. - if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id) - && derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref) - { - self.register_deref_mut_bounds_if_needed( - pat.span, - pat, - derefed_tys.iter().filter_map(|adjust| match adjust.kind { - PatAdjust::OverloadedDeref => Some(adjust.source), - PatAdjust::BuiltinDeref | PatAdjust::PinDeref => None, - }), - ); + // If we implicitly inserted overloaded dereferences and pinned dereferences before matching, + // check the pattern to see if the dereferenced types need `DerefMut` or `!Unpin` bounds. + if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id) { + let mut has_overloaded_deref = false; + let mut has_pin_deref = false; + derefed_tys.iter().for_each(|adjust| match adjust.kind { + PatAdjust::BuiltinDeref => {} + PatAdjust::OverloadedDeref => has_overloaded_deref = true, + PatAdjust::PinDeref => has_pin_deref = true, + }); + if has_overloaded_deref { + self.register_deref_mut_bounds_if_needed( + pat.span, + pat, + derefed_tys.iter().filter_map(|adjust| match adjust.kind { + PatAdjust::OverloadedDeref => Some(adjust.source), + PatAdjust::BuiltinDeref | PatAdjust::PinDeref => None, + }), + ); + } + if has_pin_deref { + self.register_not_unpin_bounds_if_needed( + pat.span, + pat, + derefed_tys.iter().filter_map(|adjust| match adjust.kind { + PatAdjust::BuiltinDeref | PatAdjust::OverloadedDeref => None, + PatAdjust::PinDeref => { + Some(adjust.source.pinned_ref().expect("expected pinned reference").0) + } + }), + ); + } } // (note_1): In most of the cases where (note_1) is referenced @@ -553,13 +572,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let &ty::Ref(_, inner_ty, inner_mutability) = pinned_ty.kind() => { debug!("scrutinee ty {expected:?} is a pinned reference, inserting pin deref"); - // Preserve the pinned type. We'll need it later during THIR lowering. - self.typeck_results - .borrow_mut() - .pat_adjustments_mut() - .entry(pat.hir_id) - .or_default() - .push(PatAdjustment { kind: PatAdjust::PinDeref, source: expected }); let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability); // If the pinnedness is `Not`, it means the pattern is unpinned @@ -569,14 +581,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { inner_ty, self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span), self.misc(pat.span), - ); + ) } + // Once we've checked `pat`, we'll add a `!Unpin` bound if it contains any + // `ref pin` bindings. See `Self::register_not_unpin_bounds_if_needed`. + + debug!("default binding mode is now {:?}", binding_mode); + // Use the old pat info to keep `current_depth` to its old value. let new_pat_info = PatInfo { binding_mode, ..old_pat_info }; - // Recurse with the new expected type. - // using `break` instead of `return` in case where any shared codes are added - // after the `match pat.kind {}`. - self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info) + + self.check_deref_pattern( + pat, + opt_path_res, + adjust_mode, + expected, + inner_ty, + PatAdjust::PinDeref, + new_pat_info, + ) } // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the // examples in `tests/ui/pattern/deref_patterns/`. @@ -585,35 +608,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && pat.default_binding_modes && self.should_peel_smart_pointer(peel_kind, expected) => { - debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref"); + debug!("scrutinee ty {expected:?} is a smart pointer, inserting pin deref"); + // The scrutinee is a smart pointer; implicitly dereference it. This adds a // requirement that `expected: DerefPure`. - let mut inner_ty = self.deref_pat_target(pat.span, expected); + let inner_ty = self.deref_pat_target(pat.span, expected); // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any // `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`. - let mut typeck_results = self.typeck_results.borrow_mut(); - let mut pat_adjustments_table = typeck_results.pat_adjustments_mut(); - let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default(); - // We may reach the recursion limit if a user matches on a type `T` satisfying - // `T: Deref`; error gracefully in this case. - // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move - // this check out of this branch. Alternatively, this loop could be implemented with - // autoderef and this check removed. For now though, don't break code compiling on - // stable with lots of `&`s and a low recursion limit, if anyone's done that. - if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) { - // Preserve the smart pointer type for THIR lowering and closure upvar analysis. - pat_adjustments - .push(PatAdjustment { kind: PatAdjust::OverloadedDeref, source: expected }); - } else { - let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected); - inner_ty = Ty::new_error(self.tcx, guar); - } - drop(typeck_results); - - // Recurse, using the old pat info to keep `current_depth` to its old value. - // Peeling smart pointers does not update the default binding mode. - self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, old_pat_info) + self.check_deref_pattern( + pat, + opt_path_res, + adjust_mode, + expected, + inner_ty, + PatAdjust::OverloadedDeref, + old_pat_info, + ) } PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. @@ -692,6 +703,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn check_deref_pattern( + &self, + pat: &'tcx Pat<'tcx>, + opt_path_res: Option, ErrorGuaranteed>>, + adjust_mode: AdjustMode, + expected: Ty<'tcx>, + mut inner_ty: Ty<'tcx>, + pat_adjust_kind: PatAdjust, + pat_info: PatInfo<'tcx>, + ) -> Ty<'tcx> { + debug_assert!( + !matches!(pat_adjust_kind, PatAdjust::BuiltinDeref), + "unexpected deref pattern for builtin reference type {expected:?}", + ); + + let mut typeck_results = self.typeck_results.borrow_mut(); + let mut pat_adjustments_table = typeck_results.pat_adjustments_mut(); + let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default(); + // We may reach the recursion limit if a user matches on a type `T` satisfying + // `T: Deref`; error gracefully in this case. + // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move + // this check out of this branch. Alternatively, this loop could be implemented with + // autoderef and this check removed. For now though, don't break code compiling on + // stable with lots of `&`s and a low recursion limit, if anyone's done that. + if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) { + // Preserve the smart pointer type for THIR lowering and closure upvar analysis. + pat_adjustments.push(PatAdjustment { kind: pat_adjust_kind, source: expected }); + } else { + let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected); + inner_ty = Ty::new_error(self.tcx, guar); + } + drop(typeck_results); + + // Recurse, using the old pat info to keep `current_depth` to its old value. + // Peeling smart pointers does not update the default binding mode. + self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, pat_info) + } + /// How should the binding mode and expected type be adjusted? /// /// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`. @@ -2630,6 +2679,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Check if the interior of a pin pattern (either explicit or implicit) has any `ref pin` + /// bindings of non-`Unpin` types, which would require `!Unpin` to be emitted. + fn register_not_unpin_bounds_if_needed( + &self, + span: Span, + inner: &'tcx Pat<'tcx>, + derefed_tys: impl IntoIterator>, + ) { + // Check if there are subpatterns with `ref pin` binding modes of non-`Unpin` types. + let unpin = self.tcx.require_lang_item(hir::LangItem::Unpin, span); + let cause = self.misc(span); + let unpin_obligations = self.probe(|_| { + let ocx = ObligationCtxt::new(&self); + self.typeck_results.borrow().pat_walk_ref_pin_binding_of_non_unpin_type(inner, |ty| { + let ty = ocx + .normalize(&cause, self.param_env, ty) + .pinned_ref() + .expect("expect pinned reference") + .0; + debug!("check if `Unpin` is implemented for `{ty:?}`"); + ocx.register_bound(cause.clone(), self.param_env, ty, unpin); + }); + ocx.select_all_or_error() + }); + + // If any, the current pattern type should implement `!Unpin`. + if !unpin_obligations.is_empty() { + for pinned_derefed_ty in derefed_tys { + debug!("register `!Unpin` for `{pinned_derefed_ty:?}`"); + self.register_negative_bound( + pinned_derefed_ty, + self.tcx.require_lang_item(hir::LangItem::Unpin, span), + self.misc(span), + ); + } + } + } + // Precondition: Pat is Ref(inner) fn check_pat_ref( &self, @@ -2656,7 +2743,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected = self.try_structurally_resolve_type(pat.span, expected); // Determine whether we're consuming an inherited reference and resetting the default // binding mode, based on edition and enabled experimental features. - if let ByRef::Yes(_, inh_mut) = pat_info.binding_mode { + // FIXME(pin_ergonomics): since `&pin` pattern is supported, the condition here + // should be adjusted to `pat_pin == inh_pin` + if let ByRef::Yes(Pinnedness::Not, inh_mut) = pat_info.binding_mode { match self.ref_pat_matches_inherited_ref(pat.span.edition()) { InheritedRefMatchRule::EatOuter => { // ref pattern attempts to consume inherited reference @@ -2675,9 +2764,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return expected; } InheritedRefMatchRule::EatInner => { - if let ty::Ref(_, _, r_mutbl) = *expected.kind() - && pat_mutbl <= r_mutbl - { + let expected_ref_or_pinned_ref = || { + if self.tcx.features().pin_ergonomics() + && let Some(ty::Ref(_, _, r_mutbl)) = + expected.pinned_ty().map(|ty| *ty.kind()) + && pat_mutbl <= r_mutbl + { + return Some((Pinnedness::Pinned, r_mutbl)); + } + if let ty::Ref(_, _, r_mutbl) = *expected.kind() + && pat_mutbl <= r_mutbl + { + return Some((Pinnedness::Not, r_mutbl)); + } + None + }; + if let Some((_, r_mutbl)) = expected_ref_or_pinned_ref() { // Match against the reference type; don't consume the inherited ref. // NB: The check for compatible pattern and ref type mutability assumes that // `&` patterns can match against mutable references (RFC 3627, Rule 5). If diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index d883f63486040..953c806658aef 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1356,6 +1356,16 @@ impl<'tcx> Ty<'tcx> { } } + pub fn pinned_ref(self) -> Option<(Ty<'tcx>, ty::Mutability)> { + if let Adt(def, args) = self.kind() + && def.is_pin() + && let &ty::Ref(_, ty, mutbl) = args.type_at(0).kind() + { + return Some((ty, mutbl)); + } + None + } + /// Panics if called on any type other than `Box`. pub fn expect_boxed_ty(self) -> Ty<'tcx> { self.boxed_ty() diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 6a6ed702373d1..06e9e28d0fdc8 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -493,6 +493,32 @@ impl<'tcx> TypeckResults<'tcx> { has_ref_mut } + /// Visits the pattern recursively whether it contains a `ref pin` binding + /// of non-`Unpin` type in it. + /// + /// This is used to determined whether a `&pin` pattern should emit a `!Unpin` + /// call for its pattern scrutinee. + /// + /// This is computed from the typeck results since we want to make + /// sure to apply any match-ergonomics adjustments, which we cannot + /// determine from the HIR alone. + pub fn pat_walk_ref_pin_binding_of_non_unpin_type<'a>( + &self, + pat: &hir::Pat<'_>, + mut ty_visitor: impl FnMut(Ty<'tcx>) + 'a, + ) { + pat.walk(|pat| { + if let hir::PatKind::Binding(_, id, _, _) = pat.kind + && let Some(BindingMode(ByRef::Yes(Pinnedness::Pinned, _), _)) = + self.pat_binding_modes().get(id) + { + let ty = self.pat_ty(pat); + ty_visitor(ty); + } + true + }); + } + /// How should a deref pattern find the place for its inner pattern to match on? /// /// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index fb5292f48d2b0..12f653a13371d 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -981,7 +981,7 @@ pub enum ConstructorSet { /// This type has the following list of constructors. If `variants` is empty and /// `non_exhaustive` is false, don't use this; use `NoConstructors` instead. Variants { variants: IndexVec, non_exhaustive: bool }, - /// The type is `&T` + /// The type is `&T`. Ref, /// The type is a union. Union, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index cecb43e537a8e..12c8aa424d6d2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -52,20 +52,35 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; + let def_id = obligation.predicate.def_id(); + let tcx = self.tcx(); + + let lang_item = tcx.as_lang_item(def_id); + // Negative trait predicates have different rules than positive trait predicates. if obligation.polarity() == ty::PredicatePolarity::Negative { self.assemble_candidates_for_trait_alias(obligation, &mut candidates); self.assemble_candidates_from_impls(obligation, &mut candidates); self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; + + match lang_item { + Some(LangItem::Unpin) if tcx.features().pin_ergonomics() => { + debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); + + // impl `!Unpin` automatically for tuples, slices, and arrays + // to support projections for pinned patterns. + self.assemble_builtin_neg_unpin_candidate( + obligation.predicate.self_ty().skip_binder(), + &mut candidates, + ); + } + _ => {} + } } else { self.assemble_candidates_for_trait_alias(obligation, &mut candidates); // Other bounds. Consider both in-scope bounds from fn decl // and applicable impls. There is a certain set of precedence rules here. - let def_id = obligation.predicate.def_id(); - let tcx = self.tcx(); - - let lang_item = tcx.as_lang_item(def_id); match lang_item { Some(LangItem::Copy | LangItem::Clone) => { debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); @@ -1223,6 +1238,59 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + /// Assembles `!Unpin` candidates for built-in types with no libcore-defined + /// `!Unpin` impls. + #[instrument(level = "debug", skip(self, candidates))] + fn assemble_builtin_neg_unpin_candidate( + &mut self, + self_ty: Ty<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + match *self_ty.kind() { + // `Unpin` types + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(..) + | ty::Dynamic(..) + | ty::Str + | ty::Foreign(..) + | ty::UnsafeBinder(_) + | ty::Pat(..) => {} + + ty::Array(..) | ty::Slice(_) | ty::Tuple(_) => { + candidates.vec.push(BuiltinCandidate); + } + + // FIXME(pin_ergonomics): should we impl `!Unpin` for coroutines or closures? + ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) => {} + + ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {} + + ty::Infer(ty::TyVar(_)) => { + candidates.ambiguous = true; + } + + // We can make this an ICE if/once we actually instantiate the trait obligation eagerly. + ty::Bound(..) => {} + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } + } + } + /// Assembles the `Sized` and `MetaSized` traits which are built-in to the language itself. #[instrument(level = "debug", skip(self, candidates))] fn assemble_builtin_sized_candidate( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 88f512708ff04..d6b7fbdec78e1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -250,6 +250,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { bug!("`PointeeSized` is removing during lowering"); } Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(self_ty), + Some(LangItem::Unpin) if obligation.polarity() == ty::PredicatePolarity::Negative => { + self.neg_unpin_conditions(self_ty) + } Some(LangItem::FusedIterator) => { if self.coroutine_is_gen(self_ty) { ty::Binder::dummy(vec![]) @@ -275,6 +278,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause, obligation.recursion_depth + 1, trait_def, + obligation.polarity(), types, ) } @@ -401,6 +405,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause.clone(), obligation.recursion_depth + 1, obligation.predicate.def_id(), + obligation.polarity(), constituents.types, ); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index ea903bac9d617..e4207b3271d7a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2272,6 +2272,52 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } + fn neg_unpin_conditions(&mut self, self_ty: Ty<'tcx>) -> ty::Binder<'tcx, Vec>> { + match *self_ty.kind() { + ty::Array(ty, _) | ty::Slice(ty) => { + // (*) binder moved here + ty::Binder::dummy(vec![ty]) + } + ty::Tuple(tys) => { + // (*) binder moved here + ty::Binder::dummy(tys.iter().collect()) + } + + // `Unpin` types + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(..) + | ty::Dynamic(..) + | ty::Str + | ty::Foreign(..) + | ty::UnsafeBinder(_) + | ty::Pat(..) => bug!("`Unpin` type cannot have `!Unpin` bound"), + + ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Infer(ty::TyVar(_)) + | ty::Bound(..) + | ty::Adt(..) + | ty::Alias(..) + | ty::Param(..) + | ty::Placeholder(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty) + } + } + } + fn coroutine_is_gen(&mut self, self_ty: Ty<'tcx>) -> bool { matches!(*self_ty.kind(), ty::Coroutine(did, ..) if self.tcx().coroutine_is_gen(did)) @@ -2422,6 +2468,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { cause: ObligationCause<'tcx>, recursion_depth: usize, trait_def_id: DefId, + polarity: ty::PredicatePolarity, types: Vec>, ) -> PredicateObligations<'tcx> { // Because the types were potentially derived from @@ -2466,7 +2513,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ty::TraitRef::new_from_args(tcx, trait_def_id, err_args) }; - let obligation = Obligation::new(self.tcx(), cause.clone(), param_env, trait_ref); + let trait_predicate = ty::TraitPredicate { trait_ref, polarity }; + + let obligation = + Obligation::new(self.tcx(), cause.clone(), param_env, trait_predicate); obligations.push(obligation); obligations }) diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_const.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_const.built.after.mir deleted file mode 100644 index 15f7446cc5912..0000000000000 --- a/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_const.built.after.mir +++ /dev/null @@ -1,33 +0,0 @@ -// MIR for `bar_const` after built - -fn bar_const(_1: Pin<&Bar>) -> () { - debug bar => _1; - let mut _0: (); - let _2: std::pin::Pin<&T>; - let _3: std::pin::Pin<&U>; - let mut _4: &T; - let mut _5: &U; - scope 1 { - debug x => _2; - debug y => _3; - } - - bb0: { - PlaceMention(_1); - StorageLive(_2); - _4 = &((*(_1.0: &Bar)).0: T); - _2 = Pin::<&T> { pointer: move _4 }; - StorageLive(_3); - _5 = &((*(_1.0: &Bar)).1: U); - _3 = Pin::<&U> { pointer: move _5 }; - _0 = const (); - StorageDead(_3); - StorageDead(_2); - return; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } -} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_mut.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_mut.built.after.mir deleted file mode 100644 index 46fd15502ffb1..0000000000000 --- a/tests/mir-opt/pin-ergonomics/project_pattern_match.bar_mut.built.after.mir +++ /dev/null @@ -1,33 +0,0 @@ -// MIR for `bar_mut` after built - -fn bar_mut(_1: Pin<&mut Bar>) -> () { - debug bar => _1; - let mut _0: (); - let _2: std::pin::Pin<&mut T>; - let _3: std::pin::Pin<&mut U>; - let mut _4: &mut T; - let mut _5: &mut U; - scope 1 { - debug x => _2; - debug y => _3; - } - - bb0: { - PlaceMention(_1); - StorageLive(_2); - _4 = &mut ((*(_1.0: &mut Bar)).0: T); - _2 = Pin::<&mut T> { pointer: move _4 }; - StorageLive(_3); - _5 = &mut ((*(_1.0: &mut Bar)).1: U); - _3 = Pin::<&mut U> { pointer: move _5 }; - _0 = const (); - StorageDead(_3); - StorageDead(_2); - return; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } -} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_const.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_const.built.after.mir deleted file mode 100644 index 8cfaf50a5f876..0000000000000 --- a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_const.built.after.mir +++ /dev/null @@ -1,422 +0,0 @@ -// MIR for `baz_baz_const` after built - -fn baz_baz_const(_1: Pin<&Baz, Baz>>) -> () { - debug baz => _1; - let mut _0: (); - let mut _2: isize; - let mut _3: isize; - let mut _4: isize; - let mut _5: isize; - let mut _6: isize; - let mut _7: isize; - let mut _8: isize; - let _9: std::pin::Pin<&T>; - let _10: std::pin::Pin<&U>; - let _11: std::pin::Pin<&T>; - let _12: std::pin::Pin<&U>; - let mut _13: &T; - let mut _14: &U; - let mut _15: &T; - let mut _16: &U; - let _17: std::pin::Pin<&T>; - let _18: std::pin::Pin<&U>; - let _19: std::pin::Pin<&T>; - let _20: std::pin::Pin<&U>; - let mut _21: &T; - let mut _22: &U; - let mut _23: &T; - let mut _24: &U; - let _25: std::pin::Pin<&T>; - let _26: std::pin::Pin<&U>; - let _27: std::pin::Pin<&T>; - let _28: std::pin::Pin<&U>; - let mut _29: &T; - let mut _30: &U; - let mut _31: &T; - let mut _32: &U; - let _33: std::pin::Pin<&T>; - let _34: std::pin::Pin<&U>; - let _35: std::pin::Pin<&T>; - let _36: std::pin::Pin<&U>; - let mut _37: &T; - let mut _38: &U; - let mut _39: &T; - let mut _40: &U; - let _41: std::pin::Pin<&T>; - let _42: std::pin::Pin<&U>; - let _43: std::pin::Pin<&T>; - let _44: std::pin::Pin<&U>; - let mut _45: &T; - let mut _46: &U; - let mut _47: &T; - let mut _48: &U; - let _49: std::pin::Pin<&T>; - let _50: std::pin::Pin<&U>; - let _51: std::pin::Pin<&T>; - let _52: std::pin::Pin<&U>; - let mut _53: &T; - let mut _54: &U; - let mut _55: &T; - let mut _56: &U; - let _57: std::pin::Pin<&T>; - let _58: std::pin::Pin<&U>; - let _59: std::pin::Pin<&T>; - let _60: std::pin::Pin<&U>; - let mut _61: &T; - let mut _62: &U; - let mut _63: &T; - let mut _64: &U; - let _65: std::pin::Pin<&T>; - let _66: std::pin::Pin<&U>; - let _67: std::pin::Pin<&T>; - let _68: std::pin::Pin<&U>; - let mut _69: &T; - let mut _70: &U; - let mut _71: &T; - let mut _72: &U; - scope 1 { - debug x => _9; - debug y => _10; - debug z => _11; - debug w => _12; - } - scope 2 { - debug x => _17; - debug y => _18; - debug z => _19; - debug w => _20; - } - scope 3 { - debug x => _25; - debug y => _26; - debug z => _27; - debug w => _28; - } - scope 4 { - debug x => _33; - debug y => _34; - debug z => _35; - debug w => _36; - } - scope 5 { - debug x => _41; - debug y => _42; - debug z => _43; - debug w => _44; - } - scope 6 { - debug x => _49; - debug y => _50; - debug z => _51; - debug w => _52; - } - scope 7 { - debug x => _57; - debug y => _58; - debug z => _59; - debug w => _60; - } - scope 8 { - debug x => _65; - debug y => _66; - debug z => _67; - debug w => _68; - } - - bb0: { - PlaceMention(_1); - _8 = discriminant((*(_1.0: &Baz, Baz>))); - switchInt(move _8) -> [0: bb2, 1: bb16, otherwise: bb1]; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } - - bb2: { - _4 = discriminant((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz)); - switchInt(move _4) -> [0: bb4, 1: bb10, otherwise: bb3]; - } - - bb3: { - goto -> bb1; - } - - bb4: { - _2 = discriminant((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz)); - switchInt(move _2) -> [0: bb6, 1: bb8, otherwise: bb5]; - } - - bb5: { - goto -> bb3; - } - - bb6: { - falseEdge -> [real: bb36, imaginary: bb8]; - } - - bb7: { - goto -> bb5; - } - - bb8: { - falseEdge -> [real: bb35, imaginary: bb10]; - } - - bb9: { - goto -> bb5; - } - - bb10: { - _3 = discriminant((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz)); - switchInt(move _3) -> [0: bb12, 1: bb14, otherwise: bb11]; - } - - bb11: { - goto -> bb3; - } - - bb12: { - falseEdge -> [real: bb34, imaginary: bb14]; - } - - bb13: { - goto -> bb11; - } - - bb14: { - falseEdge -> [real: bb33, imaginary: bb16]; - } - - bb15: { - goto -> bb11; - } - - bb16: { - _7 = discriminant((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz)); - switchInt(move _7) -> [0: bb18, 1: bb24, otherwise: bb17]; - } - - bb17: { - goto -> bb1; - } - - bb18: { - _5 = discriminant((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz)); - switchInt(move _5) -> [0: bb20, 1: bb22, otherwise: bb19]; - } - - bb19: { - goto -> bb17; - } - - bb20: { - falseEdge -> [real: bb32, imaginary: bb22]; - } - - bb21: { - goto -> bb19; - } - - bb22: { - falseEdge -> [real: bb31, imaginary: bb24]; - } - - bb23: { - goto -> bb19; - } - - bb24: { - _6 = discriminant((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz)); - switchInt(move _6) -> [0: bb26, 1: bb28, otherwise: bb25]; - } - - bb25: { - goto -> bb17; - } - - bb26: { - falseEdge -> [real: bb30, imaginary: bb28]; - } - - bb27: { - goto -> bb25; - } - - bb28: { - StorageLive(_65); - _69 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Bar).0: T); - _65 = Pin::<&T> { pointer: move _69 }; - StorageLive(_66); - _70 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Bar).1: U); - _66 = Pin::<&U> { pointer: move _70 }; - StorageLive(_67); - _71 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Bar).0: T); - _67 = Pin::<&T> { pointer: move _71 }; - StorageLive(_68); - _72 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Bar).1: U); - _68 = Pin::<&U> { pointer: move _72 }; - _0 = const (); - StorageDead(_68); - StorageDead(_67); - StorageDead(_66); - StorageDead(_65); - goto -> bb37; - } - - bb29: { - goto -> bb25; - } - - bb30: { - StorageLive(_57); - _61 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Bar).0: T); - _57 = Pin::<&T> { pointer: move _61 }; - StorageLive(_58); - _62 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Bar).1: U); - _58 = Pin::<&U> { pointer: move _62 }; - StorageLive(_59); - _63 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Foo).0: T); - _59 = Pin::<&T> { pointer: move _63 }; - StorageLive(_60); - _64 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Foo).1: U); - _60 = Pin::<&U> { pointer: move _64 }; - _0 = const (); - StorageDead(_60); - StorageDead(_59); - StorageDead(_58); - StorageDead(_57); - goto -> bb37; - } - - bb31: { - StorageLive(_49); - _53 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Foo).0: T); - _49 = Pin::<&T> { pointer: move _53 }; - StorageLive(_50); - _54 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Foo).1: U); - _50 = Pin::<&U> { pointer: move _54 }; - StorageLive(_51); - _55 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Bar).0: T); - _51 = Pin::<&T> { pointer: move _55 }; - StorageLive(_52); - _56 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Bar).1: U); - _52 = Pin::<&U> { pointer: move _56 }; - _0 = const (); - StorageDead(_52); - StorageDead(_51); - StorageDead(_50); - StorageDead(_49); - goto -> bb37; - } - - bb32: { - StorageLive(_41); - _45 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Foo).0: T); - _41 = Pin::<&T> { pointer: move _45 }; - StorageLive(_42); - _46 = &(((((*(_1.0: &Baz, Baz>)) as Bar).0: Baz) as Foo).1: U); - _42 = Pin::<&U> { pointer: move _46 }; - StorageLive(_43); - _47 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Foo).0: T); - _43 = Pin::<&T> { pointer: move _47 }; - StorageLive(_44); - _48 = &(((((*(_1.0: &Baz, Baz>)) as Bar).1: Baz) as Foo).1: U); - _44 = Pin::<&U> { pointer: move _48 }; - _0 = const (); - StorageDead(_44); - StorageDead(_43); - StorageDead(_42); - StorageDead(_41); - goto -> bb37; - } - - bb33: { - StorageLive(_33); - _37 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Bar).0: T); - _33 = Pin::<&T> { pointer: move _37 }; - StorageLive(_34); - _38 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Bar).1: U); - _34 = Pin::<&U> { pointer: move _38 }; - StorageLive(_35); - _39 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Bar).0: T); - _35 = Pin::<&T> { pointer: move _39 }; - StorageLive(_36); - _40 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Bar).1: U); - _36 = Pin::<&U> { pointer: move _40 }; - _0 = const (); - StorageDead(_36); - StorageDead(_35); - StorageDead(_34); - StorageDead(_33); - goto -> bb37; - } - - bb34: { - StorageLive(_25); - _29 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Bar).0: T); - _25 = Pin::<&T> { pointer: move _29 }; - StorageLive(_26); - _30 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Bar).1: U); - _26 = Pin::<&U> { pointer: move _30 }; - StorageLive(_27); - _31 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Foo).0: T); - _27 = Pin::<&T> { pointer: move _31 }; - StorageLive(_28); - _32 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Foo).1: U); - _28 = Pin::<&U> { pointer: move _32 }; - _0 = const (); - StorageDead(_28); - StorageDead(_27); - StorageDead(_26); - StorageDead(_25); - goto -> bb37; - } - - bb35: { - StorageLive(_17); - _21 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Foo).0: T); - _17 = Pin::<&T> { pointer: move _21 }; - StorageLive(_18); - _22 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Foo).1: U); - _18 = Pin::<&U> { pointer: move _22 }; - StorageLive(_19); - _23 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Bar).0: T); - _19 = Pin::<&T> { pointer: move _23 }; - StorageLive(_20); - _24 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Bar).1: U); - _20 = Pin::<&U> { pointer: move _24 }; - _0 = const (); - StorageDead(_20); - StorageDead(_19); - StorageDead(_18); - StorageDead(_17); - goto -> bb37; - } - - bb36: { - StorageLive(_9); - _13 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Foo).0: T); - _9 = Pin::<&T> { pointer: move _13 }; - StorageLive(_10); - _14 = &(((((*(_1.0: &Baz, Baz>)) as Foo).0: Baz) as Foo).1: U); - _10 = Pin::<&U> { pointer: move _14 }; - StorageLive(_11); - _15 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Foo).0: T); - _11 = Pin::<&T> { pointer: move _15 }; - StorageLive(_12); - _16 = &(((((*(_1.0: &Baz, Baz>)) as Foo).1: Baz) as Foo).1: U); - _12 = Pin::<&U> { pointer: move _16 }; - _0 = const (); - StorageDead(_12); - StorageDead(_11); - StorageDead(_10); - StorageDead(_9); - goto -> bb37; - } - - bb37: { - return; - } -} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_mut.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_mut.built.after.mir deleted file mode 100644 index 43c523cbf5717..0000000000000 --- a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_baz_mut.built.after.mir +++ /dev/null @@ -1,422 +0,0 @@ -// MIR for `baz_baz_mut` after built - -fn baz_baz_mut(_1: Pin<&mut Baz, Baz>>) -> () { - debug baz => _1; - let mut _0: (); - let mut _2: isize; - let mut _3: isize; - let mut _4: isize; - let mut _5: isize; - let mut _6: isize; - let mut _7: isize; - let mut _8: isize; - let _9: std::pin::Pin<&mut T>; - let _10: std::pin::Pin<&mut U>; - let _11: std::pin::Pin<&mut T>; - let _12: std::pin::Pin<&mut U>; - let mut _13: &mut T; - let mut _14: &mut U; - let mut _15: &mut T; - let mut _16: &mut U; - let _17: std::pin::Pin<&mut T>; - let _18: std::pin::Pin<&mut U>; - let _19: std::pin::Pin<&mut T>; - let _20: std::pin::Pin<&mut U>; - let mut _21: &mut T; - let mut _22: &mut U; - let mut _23: &mut T; - let mut _24: &mut U; - let _25: std::pin::Pin<&mut T>; - let _26: std::pin::Pin<&mut U>; - let _27: std::pin::Pin<&mut T>; - let _28: std::pin::Pin<&mut U>; - let mut _29: &mut T; - let mut _30: &mut U; - let mut _31: &mut T; - let mut _32: &mut U; - let _33: std::pin::Pin<&mut T>; - let _34: std::pin::Pin<&mut U>; - let _35: std::pin::Pin<&mut T>; - let _36: std::pin::Pin<&mut U>; - let mut _37: &mut T; - let mut _38: &mut U; - let mut _39: &mut T; - let mut _40: &mut U; - let _41: std::pin::Pin<&mut T>; - let _42: std::pin::Pin<&mut U>; - let _43: std::pin::Pin<&mut T>; - let _44: std::pin::Pin<&mut U>; - let mut _45: &mut T; - let mut _46: &mut U; - let mut _47: &mut T; - let mut _48: &mut U; - let _49: std::pin::Pin<&mut T>; - let _50: std::pin::Pin<&mut U>; - let _51: std::pin::Pin<&mut T>; - let _52: std::pin::Pin<&mut U>; - let mut _53: &mut T; - let mut _54: &mut U; - let mut _55: &mut T; - let mut _56: &mut U; - let _57: std::pin::Pin<&mut T>; - let _58: std::pin::Pin<&mut U>; - let _59: std::pin::Pin<&mut T>; - let _60: std::pin::Pin<&mut U>; - let mut _61: &mut T; - let mut _62: &mut U; - let mut _63: &mut T; - let mut _64: &mut U; - let _65: std::pin::Pin<&mut T>; - let _66: std::pin::Pin<&mut U>; - let _67: std::pin::Pin<&mut T>; - let _68: std::pin::Pin<&mut U>; - let mut _69: &mut T; - let mut _70: &mut U; - let mut _71: &mut T; - let mut _72: &mut U; - scope 1 { - debug x => _9; - debug y => _10; - debug z => _11; - debug w => _12; - } - scope 2 { - debug x => _17; - debug y => _18; - debug z => _19; - debug w => _20; - } - scope 3 { - debug x => _25; - debug y => _26; - debug z => _27; - debug w => _28; - } - scope 4 { - debug x => _33; - debug y => _34; - debug z => _35; - debug w => _36; - } - scope 5 { - debug x => _41; - debug y => _42; - debug z => _43; - debug w => _44; - } - scope 6 { - debug x => _49; - debug y => _50; - debug z => _51; - debug w => _52; - } - scope 7 { - debug x => _57; - debug y => _58; - debug z => _59; - debug w => _60; - } - scope 8 { - debug x => _65; - debug y => _66; - debug z => _67; - debug w => _68; - } - - bb0: { - PlaceMention(_1); - _8 = discriminant((*(_1.0: &mut Baz, Baz>))); - switchInt(move _8) -> [0: bb2, 1: bb16, otherwise: bb1]; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } - - bb2: { - _4 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz)); - switchInt(move _4) -> [0: bb4, 1: bb10, otherwise: bb3]; - } - - bb3: { - goto -> bb1; - } - - bb4: { - _2 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz)); - switchInt(move _2) -> [0: bb6, 1: bb8, otherwise: bb5]; - } - - bb5: { - goto -> bb3; - } - - bb6: { - falseEdge -> [real: bb36, imaginary: bb8]; - } - - bb7: { - goto -> bb5; - } - - bb8: { - falseEdge -> [real: bb35, imaginary: bb10]; - } - - bb9: { - goto -> bb5; - } - - bb10: { - _3 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz)); - switchInt(move _3) -> [0: bb12, 1: bb14, otherwise: bb11]; - } - - bb11: { - goto -> bb3; - } - - bb12: { - falseEdge -> [real: bb34, imaginary: bb14]; - } - - bb13: { - goto -> bb11; - } - - bb14: { - falseEdge -> [real: bb33, imaginary: bb16]; - } - - bb15: { - goto -> bb11; - } - - bb16: { - _7 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz)); - switchInt(move _7) -> [0: bb18, 1: bb24, otherwise: bb17]; - } - - bb17: { - goto -> bb1; - } - - bb18: { - _5 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz)); - switchInt(move _5) -> [0: bb20, 1: bb22, otherwise: bb19]; - } - - bb19: { - goto -> bb17; - } - - bb20: { - falseEdge -> [real: bb32, imaginary: bb22]; - } - - bb21: { - goto -> bb19; - } - - bb22: { - falseEdge -> [real: bb31, imaginary: bb24]; - } - - bb23: { - goto -> bb19; - } - - bb24: { - _6 = discriminant((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz)); - switchInt(move _6) -> [0: bb26, 1: bb28, otherwise: bb25]; - } - - bb25: { - goto -> bb17; - } - - bb26: { - falseEdge -> [real: bb30, imaginary: bb28]; - } - - bb27: { - goto -> bb25; - } - - bb28: { - StorageLive(_65); - _69 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Bar).0: T); - _65 = Pin::<&mut T> { pointer: move _69 }; - StorageLive(_66); - _70 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Bar).1: U); - _66 = Pin::<&mut U> { pointer: move _70 }; - StorageLive(_67); - _71 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Bar).0: T); - _67 = Pin::<&mut T> { pointer: move _71 }; - StorageLive(_68); - _72 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Bar).1: U); - _68 = Pin::<&mut U> { pointer: move _72 }; - _0 = const (); - StorageDead(_68); - StorageDead(_67); - StorageDead(_66); - StorageDead(_65); - goto -> bb37; - } - - bb29: { - goto -> bb25; - } - - bb30: { - StorageLive(_57); - _61 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Bar).0: T); - _57 = Pin::<&mut T> { pointer: move _61 }; - StorageLive(_58); - _62 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Bar).1: U); - _58 = Pin::<&mut U> { pointer: move _62 }; - StorageLive(_59); - _63 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Foo).0: T); - _59 = Pin::<&mut T> { pointer: move _63 }; - StorageLive(_60); - _64 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Foo).1: U); - _60 = Pin::<&mut U> { pointer: move _64 }; - _0 = const (); - StorageDead(_60); - StorageDead(_59); - StorageDead(_58); - StorageDead(_57); - goto -> bb37; - } - - bb31: { - StorageLive(_49); - _53 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Foo).0: T); - _49 = Pin::<&mut T> { pointer: move _53 }; - StorageLive(_50); - _54 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Foo).1: U); - _50 = Pin::<&mut U> { pointer: move _54 }; - StorageLive(_51); - _55 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Bar).0: T); - _51 = Pin::<&mut T> { pointer: move _55 }; - StorageLive(_52); - _56 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Bar).1: U); - _52 = Pin::<&mut U> { pointer: move _56 }; - _0 = const (); - StorageDead(_52); - StorageDead(_51); - StorageDead(_50); - StorageDead(_49); - goto -> bb37; - } - - bb32: { - StorageLive(_41); - _45 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Foo).0: T); - _41 = Pin::<&mut T> { pointer: move _45 }; - StorageLive(_42); - _46 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).0: Baz) as Foo).1: U); - _42 = Pin::<&mut U> { pointer: move _46 }; - StorageLive(_43); - _47 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Foo).0: T); - _43 = Pin::<&mut T> { pointer: move _47 }; - StorageLive(_44); - _48 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Bar).1: Baz) as Foo).1: U); - _44 = Pin::<&mut U> { pointer: move _48 }; - _0 = const (); - StorageDead(_44); - StorageDead(_43); - StorageDead(_42); - StorageDead(_41); - goto -> bb37; - } - - bb33: { - StorageLive(_33); - _37 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Bar).0: T); - _33 = Pin::<&mut T> { pointer: move _37 }; - StorageLive(_34); - _38 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Bar).1: U); - _34 = Pin::<&mut U> { pointer: move _38 }; - StorageLive(_35); - _39 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Bar).0: T); - _35 = Pin::<&mut T> { pointer: move _39 }; - StorageLive(_36); - _40 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Bar).1: U); - _36 = Pin::<&mut U> { pointer: move _40 }; - _0 = const (); - StorageDead(_36); - StorageDead(_35); - StorageDead(_34); - StorageDead(_33); - goto -> bb37; - } - - bb34: { - StorageLive(_25); - _29 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Bar).0: T); - _25 = Pin::<&mut T> { pointer: move _29 }; - StorageLive(_26); - _30 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Bar).1: U); - _26 = Pin::<&mut U> { pointer: move _30 }; - StorageLive(_27); - _31 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Foo).0: T); - _27 = Pin::<&mut T> { pointer: move _31 }; - StorageLive(_28); - _32 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Foo).1: U); - _28 = Pin::<&mut U> { pointer: move _32 }; - _0 = const (); - StorageDead(_28); - StorageDead(_27); - StorageDead(_26); - StorageDead(_25); - goto -> bb37; - } - - bb35: { - StorageLive(_17); - _21 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Foo).0: T); - _17 = Pin::<&mut T> { pointer: move _21 }; - StorageLive(_18); - _22 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Foo).1: U); - _18 = Pin::<&mut U> { pointer: move _22 }; - StorageLive(_19); - _23 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Bar).0: T); - _19 = Pin::<&mut T> { pointer: move _23 }; - StorageLive(_20); - _24 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Bar).1: U); - _20 = Pin::<&mut U> { pointer: move _24 }; - _0 = const (); - StorageDead(_20); - StorageDead(_19); - StorageDead(_18); - StorageDead(_17); - goto -> bb37; - } - - bb36: { - StorageLive(_9); - _13 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Foo).0: T); - _9 = Pin::<&mut T> { pointer: move _13 }; - StorageLive(_10); - _14 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).0: Baz) as Foo).1: U); - _10 = Pin::<&mut U> { pointer: move _14 }; - StorageLive(_11); - _15 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Foo).0: T); - _11 = Pin::<&mut T> { pointer: move _15 }; - StorageLive(_12); - _16 = &mut (((((*(_1.0: &mut Baz, Baz>)) as Foo).1: Baz) as Foo).1: U); - _12 = Pin::<&mut U> { pointer: move _16 }; - _0 = const (); - StorageDead(_12); - StorageDead(_11); - StorageDead(_10); - StorageDead(_9); - goto -> bb37; - } - - bb37: { - return; - } -} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_const.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_const.built.after.mir deleted file mode 100644 index 5538449cc43cc..0000000000000 --- a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_const.built.after.mir +++ /dev/null @@ -1,76 +0,0 @@ -// MIR for `baz_const` after built - -fn baz_const(_1: Pin<&Baz>) -> () { - debug baz => _1; - let mut _0: (); - let mut _2: isize; - let _3: std::pin::Pin<&T>; - let _4: std::pin::Pin<&U>; - let mut _5: &T; - let mut _6: &U; - let _7: std::pin::Pin<&T>; - let _8: std::pin::Pin<&U>; - let mut _9: &T; - let mut _10: &U; - scope 1 { - debug x => _3; - debug y => _4; - } - scope 2 { - debug x => _7; - debug y => _8; - } - - bb0: { - PlaceMention(_1); - _2 = discriminant((*(_1.0: &Baz))); - switchInt(move _2) -> [0: bb2, 1: bb4, otherwise: bb1]; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } - - bb2: { - falseEdge -> [real: bb6, imaginary: bb4]; - } - - bb3: { - goto -> bb1; - } - - bb4: { - StorageLive(_7); - _9 = &(((*(_1.0: &Baz)) as Bar).0: T); - _7 = Pin::<&T> { pointer: move _9 }; - StorageLive(_8); - _10 = &(((*(_1.0: &Baz)) as Bar).1: U); - _8 = Pin::<&U> { pointer: move _10 }; - _0 = const (); - StorageDead(_8); - StorageDead(_7); - goto -> bb7; - } - - bb5: { - goto -> bb1; - } - - bb6: { - StorageLive(_3); - _5 = &(((*(_1.0: &Baz)) as Foo).0: T); - _3 = Pin::<&T> { pointer: move _5 }; - StorageLive(_4); - _6 = &(((*(_1.0: &Baz)) as Foo).1: U); - _4 = Pin::<&U> { pointer: move _6 }; - _0 = const (); - StorageDead(_4); - StorageDead(_3); - goto -> bb7; - } - - bb7: { - return; - } -} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_mut.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_mut.built.after.mir deleted file mode 100644 index 4ae472cba3a58..0000000000000 --- a/tests/mir-opt/pin-ergonomics/project_pattern_match.baz_mut.built.after.mir +++ /dev/null @@ -1,76 +0,0 @@ -// MIR for `baz_mut` after built - -fn baz_mut(_1: Pin<&mut Baz>) -> () { - debug baz => _1; - let mut _0: (); - let mut _2: isize; - let _3: std::pin::Pin<&mut T>; - let _4: std::pin::Pin<&mut U>; - let mut _5: &mut T; - let mut _6: &mut U; - let _7: std::pin::Pin<&mut T>; - let _8: std::pin::Pin<&mut U>; - let mut _9: &mut T; - let mut _10: &mut U; - scope 1 { - debug x => _3; - debug y => _4; - } - scope 2 { - debug x => _7; - debug y => _8; - } - - bb0: { - PlaceMention(_1); - _2 = discriminant((*(_1.0: &mut Baz))); - switchInt(move _2) -> [0: bb2, 1: bb4, otherwise: bb1]; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } - - bb2: { - falseEdge -> [real: bb6, imaginary: bb4]; - } - - bb3: { - goto -> bb1; - } - - bb4: { - StorageLive(_7); - _9 = &mut (((*(_1.0: &mut Baz)) as Bar).0: T); - _7 = Pin::<&mut T> { pointer: move _9 }; - StorageLive(_8); - _10 = &mut (((*(_1.0: &mut Baz)) as Bar).1: U); - _8 = Pin::<&mut U> { pointer: move _10 }; - _0 = const (); - StorageDead(_8); - StorageDead(_7); - goto -> bb7; - } - - bb5: { - goto -> bb1; - } - - bb6: { - StorageLive(_3); - _5 = &mut (((*(_1.0: &mut Baz)) as Foo).0: T); - _3 = Pin::<&mut T> { pointer: move _5 }; - StorageLive(_4); - _6 = &mut (((*(_1.0: &mut Baz)) as Foo).1: U); - _4 = Pin::<&mut U> { pointer: move _6 }; - _0 = const (); - StorageDead(_4); - StorageDead(_3); - goto -> bb7; - } - - bb7: { - return; - } -} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_const.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_const.built.after.mir deleted file mode 100644 index 8b397ffd18142..0000000000000 --- a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_const.built.after.mir +++ /dev/null @@ -1,47 +0,0 @@ -// MIR for `foo_bar_const` after built - -fn foo_bar_const(_1: Pin<&Foo, Bar>>) -> () { - debug foo => _1; - let mut _0: (); - let _2: std::pin::Pin<&T>; - let _3: std::pin::Pin<&U>; - let _4: std::pin::Pin<&T>; - let _5: std::pin::Pin<&U>; - let mut _6: &T; - let mut _7: &U; - let mut _8: &T; - let mut _9: &U; - scope 1 { - debug x => _2; - debug y => _3; - debug z => _4; - debug w => _5; - } - - bb0: { - PlaceMention(_1); - StorageLive(_2); - _6 = &(((*(_1.0: &Foo, Bar>)).0: Bar).0: T); - _2 = Pin::<&T> { pointer: move _6 }; - StorageLive(_3); - _7 = &(((*(_1.0: &Foo, Bar>)).0: Bar).1: U); - _3 = Pin::<&U> { pointer: move _7 }; - StorageLive(_4); - _8 = &(((*(_1.0: &Foo, Bar>)).1: Bar).0: T); - _4 = Pin::<&T> { pointer: move _8 }; - StorageLive(_5); - _9 = &(((*(_1.0: &Foo, Bar>)).1: Bar).1: U); - _5 = Pin::<&U> { pointer: move _9 }; - _0 = const (); - StorageDead(_5); - StorageDead(_4); - StorageDead(_3); - StorageDead(_2); - return; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } -} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_mut.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_mut.built.after.mir deleted file mode 100644 index 2e8128bb9d1e4..0000000000000 --- a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_bar_mut.built.after.mir +++ /dev/null @@ -1,47 +0,0 @@ -// MIR for `foo_bar_mut` after built - -fn foo_bar_mut(_1: Pin<&mut Foo, Bar>>) -> () { - debug foo => _1; - let mut _0: (); - let _2: std::pin::Pin<&mut T>; - let _3: std::pin::Pin<&mut U>; - let _4: std::pin::Pin<&mut T>; - let _5: std::pin::Pin<&mut U>; - let mut _6: &mut T; - let mut _7: &mut U; - let mut _8: &mut T; - let mut _9: &mut U; - scope 1 { - debug x => _2; - debug y => _3; - debug z => _4; - debug w => _5; - } - - bb0: { - PlaceMention(_1); - StorageLive(_2); - _6 = &mut (((*(_1.0: &mut Foo, Bar>)).0: Bar).0: T); - _2 = Pin::<&mut T> { pointer: move _6 }; - StorageLive(_3); - _7 = &mut (((*(_1.0: &mut Foo, Bar>)).0: Bar).1: U); - _3 = Pin::<&mut U> { pointer: move _7 }; - StorageLive(_4); - _8 = &mut (((*(_1.0: &mut Foo, Bar>)).1: Bar).0: T); - _4 = Pin::<&mut T> { pointer: move _8 }; - StorageLive(_5); - _9 = &mut (((*(_1.0: &mut Foo, Bar>)).1: Bar).1: U); - _5 = Pin::<&mut U> { pointer: move _9 }; - _0 = const (); - StorageDead(_5); - StorageDead(_4); - StorageDead(_3); - StorageDead(_2); - return; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } -} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_const.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_const.built.after.mir deleted file mode 100644 index b77b341d4ef76..0000000000000 --- a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_const.built.after.mir +++ /dev/null @@ -1,33 +0,0 @@ -// MIR for `foo_const` after built - -fn foo_const(_1: Pin<&Foo>) -> () { - debug foo => _1; - let mut _0: (); - let _2: std::pin::Pin<&T>; - let _3: std::pin::Pin<&U>; - let mut _4: &T; - let mut _5: &U; - scope 1 { - debug x => _2; - debug y => _3; - } - - bb0: { - PlaceMention(_1); - StorageLive(_2); - _4 = &((*(_1.0: &Foo)).0: T); - _2 = Pin::<&T> { pointer: move _4 }; - StorageLive(_3); - _5 = &((*(_1.0: &Foo)).1: U); - _3 = Pin::<&U> { pointer: move _5 }; - _0 = const (); - StorageDead(_3); - StorageDead(_2); - return; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } -} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_mut.built.after.mir b/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_mut.built.after.mir deleted file mode 100644 index c7b88d239f87a..0000000000000 --- a/tests/mir-opt/pin-ergonomics/project_pattern_match.foo_mut.built.after.mir +++ /dev/null @@ -1,33 +0,0 @@ -// MIR for `foo_mut` after built - -fn foo_mut(_1: Pin<&mut Foo>) -> () { - debug foo => _1; - let mut _0: (); - let _2: std::pin::Pin<&mut T>; - let _3: std::pin::Pin<&mut U>; - let mut _4: &mut T; - let mut _5: &mut U; - scope 1 { - debug x => _2; - debug y => _3; - } - - bb0: { - PlaceMention(_1); - StorageLive(_2); - _4 = &mut ((*(_1.0: &mut Foo)).0: T); - _2 = Pin::<&mut T> { pointer: move _4 }; - StorageLive(_3); - _5 = &mut ((*(_1.0: &mut Foo)).1: U); - _3 = Pin::<&mut U> { pointer: move _5 }; - _0 = const (); - StorageDead(_3); - StorageDead(_2); - return; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } -} diff --git a/tests/mir-opt/pin-ergonomics/project_pattern_match.rs b/tests/mir-opt/pin-ergonomics/project_pattern_match.rs deleted file mode 100644 index 8808111f303b3..0000000000000 --- a/tests/mir-opt/pin-ergonomics/project_pattern_match.rs +++ /dev/null @@ -1,95 +0,0 @@ -// skip-filecheck -#![feature(pin_ergonomics)] -#![allow(incomplete_features)] - -// This test verifies that a `&pin mut Foo` can be projected to a pinned -// reference `&pin mut T` of a `?Unpin` field , and can be projected to -// an unpinned reference `&mut U` of an `Unpin` field . - -struct Foo { - x: T, - y: U, -} - -struct Bar(T, U); - -enum Baz { - Foo(T, U), - Bar { x: T, y: U }, -} - -// EMIT_MIR project_pattern_match.foo_mut.built.after.mir -fn foo_mut(foo: &pin mut Foo) { - let Foo { x, y } = foo; -} - -// EMIT_MIR project_pattern_match.foo_const.built.after.mir -fn foo_const(foo: &pin const Foo) { - let Foo { x, y } = foo; -} - -// EMIT_MIR project_pattern_match.bar_mut.built.after.mir -fn bar_mut(bar: &pin mut Bar) { - let Bar(x, y) = bar; -} - -// EMIT_MIR project_pattern_match.bar_const.built.after.mir -fn bar_const(bar: &pin const Bar) { - let Bar(x, y) = bar; -} - -// EMIT_MIR project_pattern_match.foo_bar_mut.built.after.mir -fn foo_bar_mut(foo: &pin mut Foo, Bar>) { - let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; -} - -// EMIT_MIR project_pattern_match.foo_bar_const.built.after.mir -fn foo_bar_const(foo: &pin const Foo, Bar>) { - let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; -} - -// EMIT_MIR project_pattern_match.baz_mut.built.after.mir -fn baz_mut(baz: &pin mut Baz) { - match baz { - Baz::Foo(x, y) => {} - Baz::Bar { x, y } => {} - } -} - -// EMIT_MIR project_pattern_match.baz_const.built.after.mir -fn baz_const(baz: &pin const Baz) { - match baz { - Baz::Foo(x, y) => {} - Baz::Bar { x, y } => {} - } -} - -// EMIT_MIR project_pattern_match.baz_baz_mut.built.after.mir -fn baz_baz_mut(baz: &pin mut Baz, Baz>) { - match baz { - Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w)) => {} - Baz::Foo(Baz::Foo(x, y), Baz::Bar { x: z, y: w }) => {} - Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w)) => {} - Baz::Foo(Baz::Bar { x, y }, Baz::Bar { x: z, y: w }) => {} - Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) } => {} - Baz::Bar { x: Baz::Foo(x, y), y: Baz::Bar { x: z, y: w } } => {} - Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) } => {} - Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Bar { x: z, y: w } } => {} - } -} - -// EMIT_MIR project_pattern_match.baz_baz_const.built.after.mir -fn baz_baz_const(baz: &pin const Baz, Baz>) { - match baz { - Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w)) => {} - Baz::Foo(Baz::Foo(x, y), Baz::Bar { x: z, y: w }) => {} - Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w)) => {} - Baz::Foo(Baz::Bar { x, y }, Baz::Bar { x: z, y: w }) => {} - Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) } => {} - Baz::Bar { x: Baz::Foo(x, y), y: Baz::Bar { x: z, y: w } } => {} - Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) } => {} - Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Bar { x: z, y: w } } => {} - } -} - -fn main() {} diff --git a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr index 7aaa51e81ed90..5e91fae18cca5 100644 --- a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr @@ -1,8 +1,8 @@ error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:35:9 + --> $DIR/pattern-matching.rs:41:9 | -LL | let Foo { x, y } = foo; - | ^^^^^^^^^^^^ --- this expression has type `Pin<&mut Foo>` +LL | let Foo { x, y } = foo_mut; + | ^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Foo>` | | | expected `Pin<&mut Foo>`, found `Foo<_, _>` | @@ -10,14 +10,14 @@ LL | let Foo { x, y } = foo; found struct `Foo<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | let Foo { x, y } = *foo; +LL | let Foo { x, y } = *foo_mut; | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:42:9 + --> $DIR/pattern-matching.rs:46:9 | -LL | let Foo { x, y } = foo; - | ^^^^^^^^^^^^ --- this expression has type `Pin<&Foo>` +LL | let Foo { x, y } = foo_const; + | ^^^^^^^^^^^^ --------- this expression has type `Pin<&Foo>` | | | expected `Pin<&Foo>`, found `Foo<_, _>` | @@ -25,289 +25,184 @@ LL | let Foo { x, y } = foo; found struct `Foo<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | let Foo { x, y } = *foo; +LL | let Foo { x, y } = *foo_const; | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:49:9 + --> $DIR/pattern-matching.rs:54:9 | -LL | let Bar(x, y) = bar; - | ^^^^^^^^^ --- this expression has type `Pin<&mut Bar>` +LL | let Foo { x, .. } = foo_mut; + | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Foo>` | | - | expected `Pin<&mut Bar>`, found `Bar<_, _>` - | - = note: expected struct `Pin<&mut Bar>` - found struct `Bar<_, _>` -help: consider dereferencing to access the inner value using the Deref trait - | -LL | let Bar(x, y) = *bar; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:56:9 - | -LL | let Bar(x, y) = bar; - | ^^^^^^^^^ --- this expression has type `Pin<&Bar>` - | | - | expected `Pin<&Bar>`, found `Bar<_, _>` - | - = note: expected struct `Pin<&Bar>` - found struct `Bar<_, _>` -help: consider dereferencing to access the inner value using the Deref trait - | -LL | let Bar(x, y) = *bar; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:63:9 - | -LL | let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- this expression has type `Pin<&mut Foo, Bar>>` - | | - | expected `Pin<&mut Foo, Bar>>`, found `Foo<_, _>` + | expected `Pin<&mut Foo>`, found `Foo<_, _>` | - = note: expected struct `Pin<&mut Foo, Bar>>` + = note: expected struct `Pin<&mut Foo>` found struct `Foo<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | let Foo { x: Bar(x, y), y: Bar(z, w) } = *foo; - | + +LL | let Foo { x, .. } = *foo_mut; + | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:72:9 + --> $DIR/pattern-matching.rs:58:9 | -LL | let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- this expression has type `Pin<&Foo, Bar>>` +LL | let Foo { x, .. } = foo_const; + | ^^^^^^^^^^^^^ --------- this expression has type `Pin<&Foo>` | | - | expected `Pin<&Foo, Bar>>`, found `Foo<_, _>` + | expected `Pin<&Foo>`, found `Foo<_, _>` | - = note: expected struct `Pin<&Foo, Bar>>` + = note: expected struct `Pin<&Foo>` found struct `Foo<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | let Foo { x: Bar(x, y), y: Bar(z, w) } = *foo; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:82:9 - | -LL | match baz { - | --- this expression has type `Pin<&mut Baz>` -LL | Baz::Foo(x, y) => { - | ^^^^^^^^^^^^^^ expected `Pin<&mut Baz>`, found `Baz<_, _>` - | - = note: expected struct `Pin<&mut Baz>` - found enum `Baz<_, _>` -help: consider dereferencing to access the inner value using the Deref trait - | -LL | match *baz { - | + +LL | let Foo { x, .. } = *foo_const; + | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:87:9 - | -LL | match baz { - | --- this expression has type `Pin<&mut Baz>` -... -LL | Baz::Bar { x, y } => { - | ^^^^^^^^^^^^^^^^^ expected `Pin<&mut Baz>`, found `Baz<_, _>` - | - = note: expected struct `Pin<&mut Baz>` - found enum `Baz<_, _>` -help: consider dereferencing to access the inner value using the Deref trait + --> $DIR/pattern-matching.rs:68:9 | -LL | match *baz { - | + - -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:97:9 - | -LL | match baz { - | --- this expression has type `Pin<&Baz>` -LL | Baz::Foo(x, y) => { - | ^^^^^^^^^^^^^^ expected `Pin<&Baz>`, found `Baz<_, _>` +LL | let NegUnpinFoo { x, y } = foo_mut; + | ^^^^^^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NegUnpinFoo>` + | | + | expected `Pin<&mut NegUnpinFoo>`, found `NegUnpinFoo<_, _>` | - = note: expected struct `Pin<&Baz>` - found enum `Baz<_, _>` + = note: expected struct `Pin<&mut NegUnpinFoo>` + found struct `NegUnpinFoo<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | match *baz { - | + +LL | let NegUnpinFoo { x, y } = *foo_mut; + | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:102:9 + --> $DIR/pattern-matching.rs:73:9 | -LL | match baz { - | --- this expression has type `Pin<&Baz>` -... -LL | Baz::Bar { x, y } => { - | ^^^^^^^^^^^^^^^^^ expected `Pin<&Baz>`, found `Baz<_, _>` +LL | let NegUnpinFoo { x, y } = foo_const; + | ^^^^^^^^^^^^^^^^^^^^ --------- this expression has type `Pin<&NegUnpinFoo>` + | | + | expected `Pin<&NegUnpinFoo>`, found `NegUnpinFoo<_, _>` | - = note: expected struct `Pin<&Baz>` - found enum `Baz<_, _>` + = note: expected struct `Pin<&NegUnpinFoo>` + found struct `NegUnpinFoo<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | match *baz { - | + +LL | let NegUnpinFoo { x, y } = *foo_const; + | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:112:9 + --> $DIR/pattern-matching.rs:85:9 | -LL | match baz { - | --- this expression has type `Pin<&mut Baz, Baz>>` -LL | Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut Baz, Baz>>`, found `Baz<_, _>` +LL | match bar_mut { + | ------- this expression has type `Pin<&mut NegUnpinBar>` +LL | NegUnpinBar::Foo(x, y) => { + | ^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut NegUnpinBar>`, found `NegUnpinBar<_, _>` | - = note: expected struct `Pin<&mut Baz, Baz>>` - found enum `Baz<_, _>` + = note: expected struct `Pin<&mut NegUnpinBar>` + found enum `NegUnpinBar<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | match *baz { +LL | match *bar_mut { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:119:9 + --> $DIR/pattern-matching.rs:90:18 | -LL | match baz { - | --- this expression has type `Pin<&mut Baz, Baz>>` -... -LL | Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut Baz, Baz>>`, found `Baz<_, _>` +LL | _ if let NegUnpinBar::Bar { x, y } = bar_mut => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NegUnpinBar>` + | | + | expected `Pin<&mut NegUnpinBar>`, found `NegUnpinBar<_, _>` | - = note: expected struct `Pin<&mut Baz, Baz>>` - found enum `Baz<_, _>` + = note: expected struct `Pin<&mut NegUnpinBar>` + found enum `NegUnpinBar<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | match *baz { - | + +LL | _ if let NegUnpinBar::Bar { x, y } = *bar_mut => { + | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:126:9 + --> $DIR/pattern-matching.rs:98:9 | -LL | match baz { - | --- this expression has type `Pin<&mut Baz, Baz>>` -... -LL | Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut Baz, Baz>>`, found `Baz<_, _>` +LL | match bar_const { + | --------- this expression has type `Pin<&NegUnpinBar>` +LL | NegUnpinBar::Bar { x, y } => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&NegUnpinBar>`, found `NegUnpinBar<_, _>` | - = note: expected struct `Pin<&mut Baz, Baz>>` - found enum `Baz<_, _>` + = note: expected struct `Pin<&NegUnpinBar>` + found enum `NegUnpinBar<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | match *baz { +LL | match *bar_const { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:133:9 + --> $DIR/pattern-matching.rs:103:18 | -LL | match baz { - | --- this expression has type `Pin<&mut Baz, Baz>>` -... -LL | Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut Baz, Baz>>`, found `Baz<_, _>` +LL | _ if let NegUnpinBar::Foo(x, y) = bar_const => { + | ^^^^^^^^^^^^^^^^^^^^^^ --------- this expression has type `Pin<&NegUnpinBar>` + | | + | expected `Pin<&NegUnpinBar>`, found `NegUnpinBar<_, _>` | - = note: expected struct `Pin<&mut Baz, Baz>>` - found enum `Baz<_, _>` + = note: expected struct `Pin<&NegUnpinBar>` + found enum `NegUnpinBar<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | match *baz { - | + +LL | _ if let NegUnpinBar::Foo(x, y) = *bar_const => { + | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:145:9 + --> $DIR/pattern-matching.rs:116:9 | -LL | match baz { - | --- this expression has type `Pin<&Baz, Baz>>` -LL | Baz::Foo(foo, _) if let Baz::Foo(x, y) = foo => { - | ^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` +LL | let (NegUnpinFoo { x, y },) = foo_mut; + | ^^^^^^^^^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut (NegUnpinFoo,)>` + | | + | expected `Pin<&mut (NegUnpinFoo,)>`, found `(_,)` | - = note: expected struct `Pin<&Baz, Baz>>` - found enum `Baz<_, _>` + = note: expected struct `Pin<&mut (NegUnpinFoo,)>` + found tuple `(_,)` help: consider dereferencing to access the inner value using the Deref trait | -LL | match *baz { - | + +LL | let (NegUnpinFoo { x, y },) = *foo_mut; + | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:150:9 + --> $DIR/pattern-matching.rs:120:9 | -LL | match baz { - | --- this expression has type `Pin<&Baz, Baz>>` -... -LL | Baz::Bar { x: _, y: bar } if let Baz::Bar { x, y } = bar => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` +LL | let (NegUnpinFoo { x, y },) = foo_const; + | ^^^^^^^^^^^^^^^^^^^^^^^ --------- this expression has type `Pin<&(NegUnpinFoo,)>` + | | + | expected `Pin<&(NegUnpinFoo,)>`, found `(_,)` | - = note: expected struct `Pin<&Baz, Baz>>` - found enum `Baz<_, _>` + = note: expected struct `Pin<&(NegUnpinFoo,)>` + found tuple `(_,)` help: consider dereferencing to access the inner value using the Deref trait | -LL | match *baz { - | + +LL | let (NegUnpinFoo { x, y },) = *foo_const; + | + -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:155:9 - | -LL | match baz { - | --- this expression has type `Pin<&Baz, Baz>>` -... -LL | Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` - | - = note: expected struct `Pin<&Baz, Baz>>` - found enum `Baz<_, _>` -help: consider dereferencing to access the inner value using the Deref trait +error[E0529]: expected an array or slice, found `Pin<&mut [NegUnpinFoo; 1]>` + --> $DIR/pattern-matching.rs:130:9 | -LL | match *baz { - | + +LL | let [NegUnpinFoo { x, y }] = foo_mut; + | ^^^^^^^^^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [NegUnpinFoo; 1]>` -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:162:9 - | -LL | match baz { - | --- this expression has type `Pin<&Baz, Baz>>` -... -LL | Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` - | - = note: expected struct `Pin<&Baz, Baz>>` - found enum `Baz<_, _>` -help: consider dereferencing to access the inner value using the Deref trait +error[E0529]: expected an array or slice, found `Pin<&[NegUnpinFoo; 1]>` + --> $DIR/pattern-matching.rs:134:9 | -LL | match *baz { - | + +LL | let [NegUnpinFoo { x, y }] = foo_const; + | ^^^^^^^^^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[NegUnpinFoo; 1]>` -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:169:9 - | -LL | match baz { - | --- this expression has type `Pin<&Baz, Baz>>` -... -LL | Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` +error[E0529]: expected an array or slice, found `Pin<&mut [NegUnpinFoo]>` + --> $DIR/pattern-matching.rs:144:12 | - = note: expected struct `Pin<&Baz, Baz>>` - found enum `Baz<_, _>` -help: consider dereferencing to access the inner value using the Deref trait - | -LL | match *baz { - | + +LL | if let [NegUnpinFoo { x, y }] = foo_mut { + | ^^^^^^^^^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [NegUnpinFoo]>` -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:176:9 +error[E0529]: expected an array or slice, found `Pin<&[NegUnpinFoo]>` + --> $DIR/pattern-matching.rs:149:12 | -LL | match baz { - | --- this expression has type `Pin<&Baz, Baz>>` -... -LL | Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&Baz, Baz>>`, found `Baz<_, _>` - | - = note: expected struct `Pin<&Baz, Baz>>` - found enum `Baz<_, _>` -help: consider dereferencing to access the inner value using the Deref trait - | -LL | match *baz { - | + +LL | if let [NegUnpinFoo { x, y }] = foo_const { + | ^^^^^^^^^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[NegUnpinFoo]>` -error: aborting due to 20 previous errors +error: aborting due to 16 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0308, E0529. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pattern-matching.rs b/tests/ui/pin-ergonomics/pattern-matching.rs index e480ebfc00626..42cb86d7fdc6f 100644 --- a/tests/ui/pin-ergonomics/pattern-matching.rs +++ b/tests/ui/pin-ergonomics/pattern-matching.rs @@ -2,27 +2,32 @@ //@ edition:2024 //@[pin_ergonomics] check-pass #![cfg_attr(pin_ergonomics, feature(pin_ergonomics))] -#![feature(if_let_guard)] +#![feature(if_let_guard, negative_impls)] #![allow(incomplete_features)] use std::pin::Pin; -// This test verifies that a `&pin mut Foo` can be projected to a pinned -// reference `&pin mut T` of a `?Unpin` field , and can be projected to -// an unpinned reference `&mut U` of an `Unpin` field. +// This test verifies that a `&pin mut T` can be projected to a pinned +// reference field `&pin mut T.U` when `T: !Unpin` or when `U: Unpin`. struct Foo { x: T, y: U, } -struct Bar(T, U); +struct NegUnpinFoo { + x: T, + y: U, +} -enum Baz { +enum NegUnpinBar { Foo(T, U), Bar { x: T, y: U }, } +impl !Unpin for NegUnpinFoo {} +impl !Unpin for NegUnpinBar {} + trait IsPinMut {} trait IsPinConst {} impl IsPinMut for Pin<&mut T> {} @@ -31,155 +36,120 @@ impl IsPinConst for Pin<&T> {} fn assert_pin_mut(_: T) {} fn assert_pin_const(_: T) {} -fn foo_mut(foo: Pin<&mut Foo>) { - let Foo { x, y } = foo; +// Pinned references can be projected to pinned references of `Unpin` fields +fn unpin2unpin(foo_mut: Pin<&mut Foo>, foo_const: Pin<&Foo>) { + let Foo { x, y } = foo_mut; //[normal]~^ ERROR mismatched types assert_pin_mut(x); assert_pin_mut(y); -} -fn foo_const(foo: Pin<&Foo>) { - let Foo { x, y } = foo; + let Foo { x, y } = foo_const; //[normal]~^ ERROR mismatched types assert_pin_const(x); assert_pin_const(y); } -fn bar_mut(bar: Pin<&mut Bar>) { - let Bar(x, y) = bar; +// Pinned references can be only projected to pinned references of `Unpin` fields +fn unpin2partial_unpin(foo_mut: Pin<&mut Foo>, foo_const: Pin<&Foo>) { + let Foo { x, .. } = foo_mut; //[normal]~^ ERROR mismatched types assert_pin_mut(x); - assert_pin_mut(y); -} -fn bar_const(bar: Pin<&Bar>) { - let Bar(x, y) = bar; + let Foo { x, .. } = foo_const; //[normal]~^ ERROR mismatched types assert_pin_const(x); - assert_pin_const(y); } -fn foo_bar_mut(foo: Pin<&mut Foo, Bar>>) { - let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; +// Pinned references of `!Unpin` types can be projected to pinned references +fn neg_unpin2not_unpin( + foo_mut: Pin<&mut NegUnpinFoo>, + foo_const: Pin<&NegUnpinFoo>, +) { + let NegUnpinFoo { x, y } = foo_mut; //[normal]~^ ERROR mismatched types assert_pin_mut(x); assert_pin_mut(y); - assert_pin_mut(z); - assert_pin_mut(w); -} -fn foo_bar_const(foo: Pin<&Foo, Bar>>) { - let Foo { x: Bar(x, y), y: Bar(z, w) } = foo; + let NegUnpinFoo { x, y } = foo_const; //[normal]~^ ERROR mismatched types assert_pin_const(x); assert_pin_const(y); - assert_pin_const(z); - assert_pin_const(w); } -fn baz_mut(baz: Pin<&mut Baz>) { - match baz { - Baz::Foo(x, y) => { +// Pinned references of `!Unpin` types can be projected to pinned references of `Unpin` fields +fn neg_unpin2unpin( + bar_mut: Pin<&mut NegUnpinBar>, + bar_const: Pin<&NegUnpinBar>, +) { + match bar_mut { + NegUnpinBar::Foo(x, y) => { //[normal]~^ ERROR mismatched types assert_pin_mut(x); assert_pin_mut(y); } - Baz::Bar { x, y } => { + _ if let NegUnpinBar::Bar { x, y } = bar_mut => { //[normal]~^ ERROR mismatched types assert_pin_mut(x); assert_pin_mut(y); } + _ => {} } -} - -fn baz_const(baz: Pin<&Baz>) { - match baz { - Baz::Foo(x, y) => { + match bar_const { + NegUnpinBar::Bar { x, y } => { //[normal]~^ ERROR mismatched types assert_pin_const(x); assert_pin_const(y); } - Baz::Bar { x, y } => { + _ if let NegUnpinBar::Foo(x, y) = bar_const => { //[normal]~^ ERROR mismatched types assert_pin_const(x); assert_pin_const(y); } + _ => {} } } -fn baz_baz_mut(baz: Pin<&mut Baz, Baz>>) { - match baz { - Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { - //[normal]~^ ERROR mismatched types - assert_pin_mut(x); - assert_pin_mut(y); - assert_pin_mut(z); - assert_pin_mut(w); - } - Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { - //[normal]~^ ERROR mismatched types - assert_pin_mut(x); - assert_pin_mut(y); - assert_pin_mut(z); - assert_pin_mut(w); - } - Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { - //[normal]~^ ERROR mismatched types - assert_pin_mut(x); - assert_pin_mut(y); - assert_pin_mut(z); - assert_pin_mut(w); - } - Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { - //[normal]~^ ERROR mismatched types - assert_pin_mut(x); - assert_pin_mut(y); - assert_pin_mut(z); - assert_pin_mut(w); - } - } +fn neg_unpin_tuple( + foo_mut: Pin<&mut (NegUnpinFoo,)>, + foo_const: Pin<&(NegUnpinFoo,)>, +) { + let (NegUnpinFoo { x, y },) = foo_mut; + //[normal]~^ ERROR mismatched types + assert_pin_mut(x); + assert_pin_mut(y); + let (NegUnpinFoo { x, y },) = foo_const; + //[normal]~^ ERROR mismatched types + assert_pin_const(x); + assert_pin_const(y); } -fn baz_baz_const(baz: Pin<&Baz, Baz>>) { - match baz { - Baz::Foo(foo, _) if let Baz::Foo(x, y) = foo => { - //[normal]~^ ERROR mismatched types - assert_pin_const(x); - assert_pin_const(y); - } - Baz::Bar { x: _, y: bar } if let Baz::Bar { x, y } = bar => { - //[normal]~^ ERROR mismatched types - assert_pin_const(x); - assert_pin_const(y); - } - Baz::Foo(Baz::Foo(x, y), Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { - //[normal]~^ ERROR mismatched types - assert_pin_const(x); - assert_pin_const(y); - assert_pin_const(z); - assert_pin_const(w); - } - Baz::Foo(Baz::Bar { x, y }, Baz::Foo(z, w) | Baz::Bar { x: z, y: w }) => { - //[normal]~^ ERROR mismatched types - assert_pin_const(x); - assert_pin_const(y); - assert_pin_const(z); - assert_pin_const(w); - } - Baz::Bar { x: Baz::Foo(x, y), y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { - //[normal]~^ ERROR mismatched types - assert_pin_const(x); - assert_pin_const(y); - assert_pin_const(z); - assert_pin_const(w); - } - Baz::Bar { x: Baz::Bar { x, y }, y: Baz::Foo(z, w) | Baz::Bar { x: z, y: w } } => { - //[normal]~^ ERROR mismatched types - assert_pin_const(x); - assert_pin_const(y); - assert_pin_const(z); - assert_pin_const(w); - } +fn neg_unpin_array( + foo_mut: Pin<&mut [NegUnpinFoo; 1]>, + foo_const: Pin<&[NegUnpinFoo; 1]>, +) { + let [NegUnpinFoo { x, y }] = foo_mut; + //[normal]~^ ERROR expected an array or slice, found `Pin<&mut [NegUnpinFoo; 1]>` + assert_pin_mut(x); + assert_pin_mut(y); + let [NegUnpinFoo { x, y }] = foo_const; + //[normal]~^ ERROR expected an array or slice, found `Pin<&[NegUnpinFoo; 1]>` + assert_pin_const(x); + assert_pin_const(y); +} + +fn neg_unpin_slice( + foo_mut: Pin<&mut [NegUnpinFoo]>, + foo_const: Pin<&[NegUnpinFoo]>, +) { + if let [NegUnpinFoo { x, y }] = foo_mut { + //[normal]~^ ERROR expected an array or slice, found `Pin<&mut [NegUnpinFoo]>` + assert_pin_mut(x); + assert_pin_mut(y); + } + if let [NegUnpinFoo { x, y }] = foo_const { + //[normal]~^ ERROR expected an array or slice, found `Pin<&[NegUnpinFoo]>` + assert_pin_const(x); + assert_pin_const(y); } } diff --git a/tests/ui/pin-ergonomics/projection-unpin-checks.rs b/tests/ui/pin-ergonomics/projection-unpin-checks.rs new file mode 100644 index 0000000000000..3a7c4f04c6903 --- /dev/null +++ b/tests/ui/pin-ergonomics/projection-unpin-checks.rs @@ -0,0 +1,132 @@ +//@ edition:2024 +#![feature(pin_ergonomics, negative_impls)] +#![allow(incomplete_features)] + +// This test verifies that a `&pin mut T` cannot be projected to a pinned +// reference field `&pin mut T.U` if neither `T: !Unpin` nor `U: Unpin`. + +struct Foo(T); +struct NotUnpinFoo(T, std::marker::PhantomPinned); +struct NegUnpinFoo(T); + +impl !Unpin for NegUnpinFoo {} + +fn unpin2not_unpin(foo_mut: &pin mut Foo, foo_const: &pin const Foo) { + let Foo(_x) = foo_mut; //~ ERROR the trait bound `Foo: !Unpin` is not satisfied [E0277] + let Foo(_x) = foo_const; //~ ERROR the trait bound `Foo: !Unpin` is not satisfied [E0277] + let Foo(_x) = (|| foo_mut)(); //~ ERROR the trait bound `Foo: !Unpin` is not satisfied [E0277] + let Foo(_x) = (|| foo_const)(); //~ ERROR the trait bound `Foo: !Unpin` is not satisfied [E0277] + let &mut _foo_mut = foo_mut; //~ ERROR mismatched types + let &_foo_const = foo_const; //~ ERROR mismatched types + // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready + // let &pin mut _foo_mut = foo_mut; // error + // let &pin mut _foo_const = foo_const; // error +} + +fn unpin2unpin(foo_mut: &pin mut Foo, foo_const: &pin const Foo) { + let Foo(_x) = foo_mut; // ok + let Foo(_x) = foo_const; // ok + let Foo(_x) = (|| foo_mut)(); // ok + let Foo(_x) = (|| foo_const)(); // ok + let &mut _foo_mut = foo_mut; //~ ERROR mismatched types + let &_foo_const = foo_const; //~ ERROR mismatched types + // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready + // let &pin mut _foo_mut = foo_mut; // ok + // let &pin mut _foo_const = foo_const; // ok +} + +fn not_unpin2not_unpin(foo_mut: &pin mut NotUnpinFoo, foo_const: &pin const NotUnpinFoo) { + let NotUnpinFoo(_x, _) = foo_mut; //~ ERROR the trait bound `NotUnpinFoo: !Unpin` is not satisfied [E0277] + let NotUnpinFoo(_x, _) = foo_const; //~ ERROR the trait bound `NotUnpinFoo: !Unpin` is not satisfied [E0277] + let NotUnpinFoo(_x, _) = (|| foo_mut)(); //~ ERROR the trait bound `NotUnpinFoo: !Unpin` is not satisfied [E0277] + let NotUnpinFoo(_x, _) = (|| foo_const)(); //~ ERROR the trait bound `NotUnpinFoo: !Unpin` is not satisfied [E0277] + let &mut _foo_mut = foo_mut; //~ ERROR mismatched types + let &_foo_const = foo_const; //~ ERROR mismatched types + // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready + // let &pin mut _foo_mut = foo_mut; // error + // let &pin mut _foo_const = foo_const; // error +} + +fn not_unpin2unpin( + foo_mut: &pin mut NotUnpinFoo, + foo_const: &pin const NotUnpinFoo, +) { + let NotUnpinFoo(_x, _) = foo_mut; // ok + let NotUnpinFoo(_x, _) = foo_const; // ok + let NotUnpinFoo(_x, _) = (|| foo_mut)(); // ok + let NotUnpinFoo(_x, _) = (|| foo_const)(); // ok + let &mut _foo_mut = foo_mut; //~ ERROR mismatched types + let &_foo_const = foo_const; //~ ERROR mismatched types + // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready + // let &pin mut _foo_mut = foo_mut; // ok + // let &pin mut _foo_const = foo_const; // ok +} + +fn neg_unpin2not_unpin(foo_mut: &pin mut NegUnpinFoo, foo_const: &pin const NegUnpinFoo) { + let NegUnpinFoo(_x) = foo_mut; // ok + let NegUnpinFoo(_x) = foo_const; // ok + let NegUnpinFoo(_x) = (|| foo_mut)(); // ok + let NegUnpinFoo(_x) = (|| foo_const)(); // ok + let &mut _foo_mut = foo_mut; //~ ERROR mismatched types + let &_foo_const = foo_const; //~ ERROR mismatched types + // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready + // let &pin mut _foo_mut = foo_mut; // ok + // let &pin mut _foo_const = foo_const; // ok +} + +fn neg_unpin2unpin( + foo_mut: &pin mut NegUnpinFoo, + foo_const: &pin const NegUnpinFoo, +) { + let NegUnpinFoo(_x) = foo_mut; // ok + let NegUnpinFoo(_x) = foo_const; // ok + let NegUnpinFoo(_x) = (|| foo_mut)(); // ok + let NegUnpinFoo(_x) = (|| foo_const)(); // ok + let &mut _foo_mut = foo_mut; //~ ERROR mismatched types + let &_foo_const = foo_const; //~ ERROR mismatched types + // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready + // let &pin mut _foo_mut = foo_mut; // ok + // let &pin mut _foo_const = foo_const; // ok +} + +fn tuple_ref_mut_pat_and_pin_mut_of_tuple_mut_ty<'a, T>( + (&mut x,): &'a pin mut (&'a mut NegUnpinFoo,), + //~^ ERROR the trait bound `&'a mut NegUnpinFoo: !Unpin` is not satisfied in `(&'a mut NegUnpinFoo,)` [E0277] +) -> &'a pin mut NegUnpinFoo { + x +} + +fn tuple_ref_mut_pat_and_pin_mut_of_mut_tuple_ty<'a, T>( + (&mut x,): &'a pin mut &'a mut (NegUnpinFoo,), + //~^ ERROR mismatched type + //~| ERROR the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied [E0277] +) -> &'a pin mut NegUnpinFoo { + x +} + +fn ref_mut_tuple_pat_and_pin_mut_of_tuple_mut_ty<'a, T>( + &mut (x,): &'a pin mut (&'a mut NegUnpinFoo,), //~ ERROR mismatched type +) -> &'a pin mut NegUnpinFoo { + x +} + +fn ref_mut_tuple_pat_and_pin_mut_of_mut_tuple_ty<'a, T>( + &mut (x,): &'a pin mut &'a mut (NegUnpinFoo,), //~ ERROR mismatched type +) -> &'a pin mut NegUnpinFoo { + x +} + +fn tuple_pat_and_pin_mut_of_tuple_mut_ty<'a, T>( + (x,): &'a pin mut (&'a mut NegUnpinFoo,), +) -> &'a pin mut &'a mut NegUnpinFoo { + x // ok +} + +fn tuple_pat_and_pin_mut_of_mut_tuple_ty<'a, T>( + (x,): &'a pin mut &'a mut (NegUnpinFoo,), + //~^ ERROR the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied [E0277] +) -> &'a pin mut NegUnpinFoo { + x +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/projection-unpin-checks.stderr b/tests/ui/pin-ergonomics/projection-unpin-checks.stderr new file mode 100644 index 0000000000000..c79e1e11b0771 --- /dev/null +++ b/tests/ui/pin-ergonomics/projection-unpin-checks.stderr @@ -0,0 +1,333 @@ +error[E0277]: the trait bound `Foo: !Unpin` is not satisfied + --> $DIR/projection-unpin-checks.rs:15:9 + | +LL | let Foo(_x) = foo_mut; + | ^^^^^^^ the trait bound `Foo: !Unpin` is not satisfied + +error[E0277]: the trait bound `Foo: !Unpin` is not satisfied + --> $DIR/projection-unpin-checks.rs:16:9 + | +LL | let Foo(_x) = foo_const; + | ^^^^^^^ the trait bound `Foo: !Unpin` is not satisfied + +error[E0277]: the trait bound `Foo: !Unpin` is not satisfied + --> $DIR/projection-unpin-checks.rs:17:9 + | +LL | let Foo(_x) = (|| foo_mut)(); + | ^^^^^^^ the trait bound `Foo: !Unpin` is not satisfied + +error[E0277]: the trait bound `Foo: !Unpin` is not satisfied + --> $DIR/projection-unpin-checks.rs:18:9 + | +LL | let Foo(_x) = (|| foo_const)(); + | ^^^^^^^ the trait bound `Foo: !Unpin` is not satisfied + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:19:9 + | +LL | let &mut _foo_mut = foo_mut; + | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Foo>` + | | + | expected `Pin<&mut Foo>`, found `&mut _` + | + = note: expected struct `Pin<&mut Foo>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&mut Foo` + | +LL | let &mut _foo_mut = foo_mut.pointer; + | ++++++++ +help: to declare a mutable variable use + | +LL - let &mut _foo_mut = foo_mut; +LL + let mut _foo_mut = foo_mut; + | + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:20:9 + | +LL | let &_foo_const = foo_const; + | ^^^^^^^^^^^ --------- this expression has type `Pin<&Foo>` + | | + | expected `Pin<&Foo>`, found `&_` + | + = note: expected struct `Pin<&Foo>` + found reference `&_` +help: you might have meant to use field `pointer` whose type is `&Foo` + | +LL | let &_foo_const = foo_const.pointer; + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:31:9 + | +LL | let &mut _foo_mut = foo_mut; + | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Foo>` + | | + | expected `Pin<&mut Foo>`, found `&mut _` + | + = note: expected struct `Pin<&mut Foo>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&mut Foo` + | +LL | let &mut _foo_mut = foo_mut.pointer; + | ++++++++ +help: to declare a mutable variable use + | +LL - let &mut _foo_mut = foo_mut; +LL + let mut _foo_mut = foo_mut; + | + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:32:9 + | +LL | let &_foo_const = foo_const; + | ^^^^^^^^^^^ --------- this expression has type `Pin<&Foo>` + | | + | expected `Pin<&Foo>`, found `&_` + | + = note: expected struct `Pin<&Foo>` + found reference `&_` +help: you might have meant to use field `pointer` whose type is `&Foo` + | +LL | let &_foo_const = foo_const.pointer; + | ++++++++ + +error[E0277]: the trait bound `NotUnpinFoo: !Unpin` is not satisfied + --> $DIR/projection-unpin-checks.rs:39:9 + | +LL | let NotUnpinFoo(_x, _) = foo_mut; + | ^^^^^^^^^^^^^^^^^^ the trait bound `NotUnpinFoo: !Unpin` is not satisfied + +error[E0277]: the trait bound `NotUnpinFoo: !Unpin` is not satisfied + --> $DIR/projection-unpin-checks.rs:40:9 + | +LL | let NotUnpinFoo(_x, _) = foo_const; + | ^^^^^^^^^^^^^^^^^^ the trait bound `NotUnpinFoo: !Unpin` is not satisfied + +error[E0277]: the trait bound `NotUnpinFoo: !Unpin` is not satisfied + --> $DIR/projection-unpin-checks.rs:41:9 + | +LL | let NotUnpinFoo(_x, _) = (|| foo_mut)(); + | ^^^^^^^^^^^^^^^^^^ the trait bound `NotUnpinFoo: !Unpin` is not satisfied + +error[E0277]: the trait bound `NotUnpinFoo: !Unpin` is not satisfied + --> $DIR/projection-unpin-checks.rs:42:9 + | +LL | let NotUnpinFoo(_x, _) = (|| foo_const)(); + | ^^^^^^^^^^^^^^^^^^ the trait bound `NotUnpinFoo: !Unpin` is not satisfied + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:43:9 + | +LL | let &mut _foo_mut = foo_mut; + | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NotUnpinFoo>` + | | + | expected `Pin<&mut NotUnpinFoo>`, found `&mut _` + | + = note: expected struct `Pin<&mut NotUnpinFoo>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&mut NotUnpinFoo` + | +LL | let &mut _foo_mut = foo_mut.pointer; + | ++++++++ +help: to declare a mutable variable use + | +LL - let &mut _foo_mut = foo_mut; +LL + let mut _foo_mut = foo_mut; + | + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:44:9 + | +LL | let &_foo_const = foo_const; + | ^^^^^^^^^^^ --------- this expression has type `Pin<&NotUnpinFoo>` + | | + | expected `Pin<&NotUnpinFoo>`, found `&_` + | + = note: expected struct `Pin<&NotUnpinFoo>` + found reference `&_` +help: you might have meant to use field `pointer` whose type is `&NotUnpinFoo` + | +LL | let &_foo_const = foo_const.pointer; + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:58:9 + | +LL | let &mut _foo_mut = foo_mut; + | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NotUnpinFoo>` + | | + | expected `Pin<&mut NotUnpinFoo>`, found `&mut _` + | + = note: expected struct `Pin<&mut NotUnpinFoo>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&mut NotUnpinFoo` + | +LL | let &mut _foo_mut = foo_mut.pointer; + | ++++++++ +help: to declare a mutable variable use + | +LL - let &mut _foo_mut = foo_mut; +LL + let mut _foo_mut = foo_mut; + | + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:59:9 + | +LL | let &_foo_const = foo_const; + | ^^^^^^^^^^^ --------- this expression has type `Pin<&NotUnpinFoo>` + | | + | expected `Pin<&NotUnpinFoo>`, found `&_` + | + = note: expected struct `Pin<&NotUnpinFoo>` + found reference `&_` +help: you might have meant to use field `pointer` whose type is `&NotUnpinFoo` + | +LL | let &_foo_const = foo_const.pointer; + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:70:9 + | +LL | let &mut _foo_mut = foo_mut; + | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NegUnpinFoo>` + | | + | expected `Pin<&mut NegUnpinFoo>`, found `&mut _` + | + = note: expected struct `Pin<&mut NegUnpinFoo>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&mut NegUnpinFoo` + | +LL | let &mut _foo_mut = foo_mut.pointer; + | ++++++++ +help: to declare a mutable variable use + | +LL - let &mut _foo_mut = foo_mut; +LL + let mut _foo_mut = foo_mut; + | + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:71:9 + | +LL | let &_foo_const = foo_const; + | ^^^^^^^^^^^ --------- this expression has type `Pin<&NegUnpinFoo>` + | | + | expected `Pin<&NegUnpinFoo>`, found `&_` + | + = note: expected struct `Pin<&NegUnpinFoo>` + found reference `&_` +help: you might have meant to use field `pointer` whose type is `&NegUnpinFoo` + | +LL | let &_foo_const = foo_const.pointer; + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:85:9 + | +LL | let &mut _foo_mut = foo_mut; + | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NegUnpinFoo>` + | | + | expected `Pin<&mut NegUnpinFoo>`, found `&mut _` + | + = note: expected struct `Pin<&mut NegUnpinFoo>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&mut NegUnpinFoo` + | +LL | let &mut _foo_mut = foo_mut.pointer; + | ++++++++ +help: to declare a mutable variable use + | +LL - let &mut _foo_mut = foo_mut; +LL + let mut _foo_mut = foo_mut; + | + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:86:9 + | +LL | let &_foo_const = foo_const; + | ^^^^^^^^^^^ --------- this expression has type `Pin<&NegUnpinFoo>` + | | + | expected `Pin<&NegUnpinFoo>`, found `&_` + | + = note: expected struct `Pin<&NegUnpinFoo>` + found reference `&_` +help: you might have meant to use field `pointer` whose type is `&NegUnpinFoo` + | +LL | let &_foo_const = foo_const.pointer; + | ++++++++ + +error[E0277]: the trait bound `&'a mut NegUnpinFoo: !Unpin` is not satisfied in `(&'a mut NegUnpinFoo,)` + --> $DIR/projection-unpin-checks.rs:93:5 + | +LL | (&mut x,): &'a pin mut (&'a mut NegUnpinFoo,), + | ^^^^^^^^^ within `(&'a mut NegUnpinFoo,)`, the trait bound `&'a mut NegUnpinFoo: !Unpin` is not satisfied + | + = note: required because it appears within the type `(&'a mut NegUnpinFoo,)` + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:100:6 + | +LL | (&mut x,): &'a pin mut &'a mut (NegUnpinFoo,), + | ^^^^^^ ------------------------------------- expected due to this + | | + | expected `NegUnpinFoo`, found `&mut _` + | + = note: expected struct `NegUnpinFoo` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/projection-unpin-checks.rs:100:6 + | +LL | (&mut x,): &'a pin mut &'a mut (NegUnpinFoo,), + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - (&mut x,): &'a pin mut &'a mut (NegUnpinFoo,), +LL + (x,): &'a pin mut &'a mut (NegUnpinFoo,), + | + +error[E0277]: the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied + --> $DIR/projection-unpin-checks.rs:100:5 + | +LL | (&mut x,): &'a pin mut &'a mut (NegUnpinFoo,), + | ^^^^^^^^^ the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:108:5 + | +LL | &mut (x,): &'a pin mut (&'a mut NegUnpinFoo,), + | ^^^^^^^^^ ------------------------------------- expected due to this + | | + | expected `Pin<&mut (&mut NegUnpinFoo,)>`, found `&mut _` + | + = note: expected struct `Pin<&'a mut (&'a mut NegUnpinFoo,)>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&'a mut (&'a mut NegUnpinFoo,)` + | +LL | &mut (x,): &'a pin mut (&'a mut NegUnpinFoo,).pointer, + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/projection-unpin-checks.rs:114:5 + | +LL | &mut (x,): &'a pin mut &'a mut (NegUnpinFoo,), + | ^^^^^^^^^ ------------------------------------- expected due to this + | | + | expected `Pin<&mut &mut (NegUnpinFoo,)>`, found `&mut _` + | + = note: expected struct `Pin<&'a mut &'a mut (NegUnpinFoo,)>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&'a mut &'a mut (NegUnpinFoo,)` + | +LL | &mut (x,): &'a pin mut &'a mut (NegUnpinFoo,).pointer, + | ++++++++ + +error[E0277]: the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied + --> $DIR/projection-unpin-checks.rs:126:5 + | +LL | (x,): &'a pin mut &'a mut (NegUnpinFoo,), + | ^^^^ the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied + +error: aborting due to 26 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. From 7070709cca9af5f2216ca229464a806c74fc29db Mon Sep 17 00:00:00 2001 From: Frank King Date: Sun, 3 Aug 2025 11:10:59 +0800 Subject: [PATCH 084/108] Remove `!Unpin` related bounds --- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 25 +- compiler/rustc_hir_typeck/src/pat.rs | 88 +---- .../src/traits/select/candidate_assembly.rs | 76 +--- .../src/traits/select/confirmation.rs | 5 - .../src/traits/select/mod.rs | 51 +-- .../pattern-matching.normal.stderr | 182 ++++------ tests/ui/pin-ergonomics/pattern-matching.rs | 94 ++--- .../pin-ergonomics/projection-unpin-checks.rs | 132 ------- .../projection-unpin-checks.stderr | 333 ------------------ 9 files changed, 105 insertions(+), 881 deletions(-) delete mode 100644 tests/ui/pin-ergonomics/projection-unpin-checks.rs delete mode 100644 tests/ui/pin-ergonomics/projection-unpin-checks.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index ec67efe6399e3..dde6b8ce9b8b2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -23,7 +23,7 @@ use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, - SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, Upcast, UserArgs, + SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, UserSelfTy, }; use rustc_middle::{bug, span_bug}; @@ -464,29 +464,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(crate) fn register_negative_bound( - &self, - ty: Ty<'tcx>, - def_id: DefId, - cause: traits::ObligationCause<'tcx>, - ) { - if !ty.references_error() { - let trait_ref = ty::TraitRef::new(self.tcx, def_id, [ty]); - let predicate = - ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Negative } - .upcast(self.tcx); - self.fulfillment_cx.borrow_mut().register_predicate_obligation( - self, - traits::Obligation { - cause, - recursion_depth: 0, - param_env: self.param_env, - predicate, - }, - ); - } - } - pub(crate) fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> LoweredTy<'tcx> { let ty = self.lowerer().lower_ty(hir_ty); self.register_wf_obligation(ty.into(), hir_ty.span, ObligationCauseCode::WellFormed(None)); diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 3a6b11653c031..615e80f1ba0e5 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -27,7 +27,7 @@ use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; +use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use tracing::{debug, instrument, trace}; use ty::VariantDef; use ty::adjustment::{PatAdjust, PatAdjustment}; @@ -403,38 +403,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info); self.write_ty(pat.hir_id, ty); - // If we implicitly inserted overloaded dereferences and pinned dereferences before matching, - // check the pattern to see if the dereferenced types need `DerefMut` or `!Unpin` bounds. - if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id) { - let mut has_overloaded_deref = false; - let mut has_pin_deref = false; - derefed_tys.iter().for_each(|adjust| match adjust.kind { - PatAdjust::BuiltinDeref => {} - PatAdjust::OverloadedDeref => has_overloaded_deref = true, - PatAdjust::PinDeref => has_pin_deref = true, - }); - if has_overloaded_deref { - self.register_deref_mut_bounds_if_needed( - pat.span, - pat, - derefed_tys.iter().filter_map(|adjust| match adjust.kind { - PatAdjust::OverloadedDeref => Some(adjust.source), - PatAdjust::BuiltinDeref | PatAdjust::PinDeref => None, - }), - ); - } - if has_pin_deref { - self.register_not_unpin_bounds_if_needed( - pat.span, - pat, - derefed_tys.iter().filter_map(|adjust| match adjust.kind { - PatAdjust::BuiltinDeref | PatAdjust::OverloadedDeref => None, - PatAdjust::PinDeref => { - Some(adjust.source.pinned_ref().expect("expected pinned reference").0) - } - }), - ); - } + // If we implicitly inserted overloaded dereferences before matching check the pattern to + // see if the dereferenced types need `DerefMut` bounds. + if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id) + && derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref) + { + self.register_deref_mut_bounds_if_needed( + pat.span, + pat, + derefed_tys.iter().filter_map(|adjust| match adjust.kind { + PatAdjust::OverloadedDeref => Some(adjust.source), + PatAdjust::BuiltinDeref | PatAdjust::PinDeref => None, + }), + ); } // (note_1): In most of the cases where (note_1) is referenced @@ -583,9 +564,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.misc(pat.span), ) } - // Once we've checked `pat`, we'll add a `!Unpin` bound if it contains any - // `ref pin` bindings. See `Self::register_not_unpin_bounds_if_needed`. - debug!("default binding mode is now {:?}", binding_mode); // Use the old pat info to keep `current_depth` to its old value. @@ -2679,44 +2657,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - /// Check if the interior of a pin pattern (either explicit or implicit) has any `ref pin` - /// bindings of non-`Unpin` types, which would require `!Unpin` to be emitted. - fn register_not_unpin_bounds_if_needed( - &self, - span: Span, - inner: &'tcx Pat<'tcx>, - derefed_tys: impl IntoIterator>, - ) { - // Check if there are subpatterns with `ref pin` binding modes of non-`Unpin` types. - let unpin = self.tcx.require_lang_item(hir::LangItem::Unpin, span); - let cause = self.misc(span); - let unpin_obligations = self.probe(|_| { - let ocx = ObligationCtxt::new(&self); - self.typeck_results.borrow().pat_walk_ref_pin_binding_of_non_unpin_type(inner, |ty| { - let ty = ocx - .normalize(&cause, self.param_env, ty) - .pinned_ref() - .expect("expect pinned reference") - .0; - debug!("check if `Unpin` is implemented for `{ty:?}`"); - ocx.register_bound(cause.clone(), self.param_env, ty, unpin); - }); - ocx.select_all_or_error() - }); - - // If any, the current pattern type should implement `!Unpin`. - if !unpin_obligations.is_empty() { - for pinned_derefed_ty in derefed_tys { - debug!("register `!Unpin` for `{pinned_derefed_ty:?}`"); - self.register_negative_bound( - pinned_derefed_ty, - self.tcx.require_lang_item(hir::LangItem::Unpin, span), - self.misc(span), - ); - } - } - } - // Precondition: Pat is Ref(inner) fn check_pat_ref( &self, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 12c8aa424d6d2..cecb43e537a8e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -52,35 +52,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - let def_id = obligation.predicate.def_id(); - let tcx = self.tcx(); - - let lang_item = tcx.as_lang_item(def_id); - // Negative trait predicates have different rules than positive trait predicates. if obligation.polarity() == ty::PredicatePolarity::Negative { self.assemble_candidates_for_trait_alias(obligation, &mut candidates); self.assemble_candidates_from_impls(obligation, &mut candidates); self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; - - match lang_item { - Some(LangItem::Unpin) if tcx.features().pin_ergonomics() => { - debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); - - // impl `!Unpin` automatically for tuples, slices, and arrays - // to support projections for pinned patterns. - self.assemble_builtin_neg_unpin_candidate( - obligation.predicate.self_ty().skip_binder(), - &mut candidates, - ); - } - _ => {} - } } else { self.assemble_candidates_for_trait_alias(obligation, &mut candidates); // Other bounds. Consider both in-scope bounds from fn decl // and applicable impls. There is a certain set of precedence rules here. + let def_id = obligation.predicate.def_id(); + let tcx = self.tcx(); + + let lang_item = tcx.as_lang_item(def_id); match lang_item { Some(LangItem::Copy | LangItem::Clone) => { debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); @@ -1238,59 +1223,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - /// Assembles `!Unpin` candidates for built-in types with no libcore-defined - /// `!Unpin` impls. - #[instrument(level = "debug", skip(self, candidates))] - fn assemble_builtin_neg_unpin_candidate( - &mut self, - self_ty: Ty<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) { - match *self_ty.kind() { - // `Unpin` types - ty::FnDef(..) - | ty::FnPtr(..) - | ty::Error(_) - | ty::Uint(_) - | ty::Int(_) - | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Bool - | ty::Float(_) - | ty::Char - | ty::RawPtr(..) - | ty::Never - | ty::Ref(..) - | ty::Dynamic(..) - | ty::Str - | ty::Foreign(..) - | ty::UnsafeBinder(_) - | ty::Pat(..) => {} - - ty::Array(..) | ty::Slice(_) | ty::Tuple(_) => { - candidates.vec.push(BuiltinCandidate); - } - - // FIXME(pin_ergonomics): should we impl `!Unpin` for coroutines or closures? - ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) => {} - - ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {} - - ty::Infer(ty::TyVar(_)) => { - candidates.ambiguous = true; - } - - // We can make this an ICE if/once we actually instantiate the trait obligation eagerly. - ty::Bound(..) => {} - - ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); - } - } - } - /// Assembles the `Sized` and `MetaSized` traits which are built-in to the language itself. #[instrument(level = "debug", skip(self, candidates))] fn assemble_builtin_sized_candidate( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index d6b7fbdec78e1..88f512708ff04 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -250,9 +250,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { bug!("`PointeeSized` is removing during lowering"); } Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(self_ty), - Some(LangItem::Unpin) if obligation.polarity() == ty::PredicatePolarity::Negative => { - self.neg_unpin_conditions(self_ty) - } Some(LangItem::FusedIterator) => { if self.coroutine_is_gen(self_ty) { ty::Binder::dummy(vec![]) @@ -278,7 +275,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause, obligation.recursion_depth + 1, trait_def, - obligation.polarity(), types, ) } @@ -405,7 +401,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause.clone(), obligation.recursion_depth + 1, obligation.predicate.def_id(), - obligation.polarity(), constituents.types, ); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e4207b3271d7a..ed63949ee022e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2272,52 +2272,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } - fn neg_unpin_conditions(&mut self, self_ty: Ty<'tcx>) -> ty::Binder<'tcx, Vec>> { - match *self_ty.kind() { - ty::Array(ty, _) | ty::Slice(ty) => { - // (*) binder moved here - ty::Binder::dummy(vec![ty]) - } - ty::Tuple(tys) => { - // (*) binder moved here - ty::Binder::dummy(tys.iter().collect()) - } - - // `Unpin` types - ty::FnDef(..) - | ty::FnPtr(..) - | ty::Error(_) - | ty::Uint(_) - | ty::Int(_) - | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Bool - | ty::Float(_) - | ty::Char - | ty::RawPtr(..) - | ty::Never - | ty::Ref(..) - | ty::Dynamic(..) - | ty::Str - | ty::Foreign(..) - | ty::UnsafeBinder(_) - | ty::Pat(..) => bug!("`Unpin` type cannot have `!Unpin` bound"), - - ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Infer(ty::TyVar(_)) - | ty::Bound(..) - | ty::Adt(..) - | ty::Alias(..) - | ty::Param(..) - | ty::Placeholder(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty) - } - } - } - fn coroutine_is_gen(&mut self, self_ty: Ty<'tcx>) -> bool { matches!(*self_ty.kind(), ty::Coroutine(did, ..) if self.tcx().coroutine_is_gen(did)) @@ -2468,7 +2422,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { cause: ObligationCause<'tcx>, recursion_depth: usize, trait_def_id: DefId, - polarity: ty::PredicatePolarity, types: Vec>, ) -> PredicateObligations<'tcx> { // Because the types were potentially derived from @@ -2513,10 +2466,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ty::TraitRef::new_from_args(tcx, trait_def_id, err_args) }; - let trait_predicate = ty::TraitPredicate { trait_ref, polarity }; - let obligation = - Obligation::new(self.tcx(), cause.clone(), param_env, trait_predicate); + Obligation::new(self.tcx(), cause.clone(), param_env, trait_ref); obligations.push(obligation); obligations }) diff --git a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr index 5e91fae18cca5..1b4908abdc176 100644 --- a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:41:9 + --> $DIR/pattern-matching.rs:35:9 | LL | let Foo { x, y } = foo_mut; | ^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Foo>` @@ -14,7 +14,7 @@ LL | let Foo { x, y } = *foo_mut; | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:46:9 + --> $DIR/pattern-matching.rs:40:9 | LL | let Foo { x, y } = foo_const; | ^^^^^^^^^^^^ --------- this expression has type `Pin<&Foo>` @@ -29,180 +29,120 @@ LL | let Foo { x, y } = *foo_const; | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:54:9 - | -LL | let Foo { x, .. } = foo_mut; - | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Foo>` - | | - | expected `Pin<&mut Foo>`, found `Foo<_, _>` - | - = note: expected struct `Pin<&mut Foo>` - found struct `Foo<_, _>` -help: consider dereferencing to access the inner value using the Deref trait - | -LL | let Foo { x, .. } = *foo_mut; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:58:9 - | -LL | let Foo { x, .. } = foo_const; - | ^^^^^^^^^^^^^ --------- this expression has type `Pin<&Foo>` - | | - | expected `Pin<&Foo>`, found `Foo<_, _>` - | - = note: expected struct `Pin<&Foo>` - found struct `Foo<_, _>` -help: consider dereferencing to access the inner value using the Deref trait - | -LL | let Foo { x, .. } = *foo_const; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:68:9 - | -LL | let NegUnpinFoo { x, y } = foo_mut; - | ^^^^^^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NegUnpinFoo>` - | | - | expected `Pin<&mut NegUnpinFoo>`, found `NegUnpinFoo<_, _>` - | - = note: expected struct `Pin<&mut NegUnpinFoo>` - found struct `NegUnpinFoo<_, _>` -help: consider dereferencing to access the inner value using the Deref trait - | -LL | let NegUnpinFoo { x, y } = *foo_mut; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:73:9 - | -LL | let NegUnpinFoo { x, y } = foo_const; - | ^^^^^^^^^^^^^^^^^^^^ --------- this expression has type `Pin<&NegUnpinFoo>` - | | - | expected `Pin<&NegUnpinFoo>`, found `NegUnpinFoo<_, _>` - | - = note: expected struct `Pin<&NegUnpinFoo>` - found struct `NegUnpinFoo<_, _>` -help: consider dereferencing to access the inner value using the Deref trait - | -LL | let NegUnpinFoo { x, y } = *foo_const; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:85:9 + --> $DIR/pattern-matching.rs:48:9 | LL | match bar_mut { - | ------- this expression has type `Pin<&mut NegUnpinBar>` -LL | NegUnpinBar::Foo(x, y) => { - | ^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut NegUnpinBar>`, found `NegUnpinBar<_, _>` + | ------- this expression has type `Pin<&mut Bar>` +LL | Bar::Foo(x, y) => { + | ^^^^^^^^^^^^^^ expected `Pin<&mut Bar>`, found `Bar<_, _>` | - = note: expected struct `Pin<&mut NegUnpinBar>` - found enum `NegUnpinBar<_, _>` + = note: expected struct `Pin<&mut Bar>` + found enum `Bar<_, _>` help: consider dereferencing to access the inner value using the Deref trait | LL | match *bar_mut { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:90:18 + --> $DIR/pattern-matching.rs:53:18 | -LL | _ if let NegUnpinBar::Bar { x, y } = bar_mut => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NegUnpinBar>` +LL | _ if let Bar::Bar { x, y } = bar_mut => { + | ^^^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Bar>` | | - | expected `Pin<&mut NegUnpinBar>`, found `NegUnpinBar<_, _>` + | expected `Pin<&mut Bar>`, found `Bar<_, _>` | - = note: expected struct `Pin<&mut NegUnpinBar>` - found enum `NegUnpinBar<_, _>` + = note: expected struct `Pin<&mut Bar>` + found enum `Bar<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | _ if let NegUnpinBar::Bar { x, y } = *bar_mut => { - | + +LL | _ if let Bar::Bar { x, y } = *bar_mut => { + | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:98:9 + --> $DIR/pattern-matching.rs:61:9 | LL | match bar_const { - | --------- this expression has type `Pin<&NegUnpinBar>` -LL | NegUnpinBar::Bar { x, y } => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<&NegUnpinBar>`, found `NegUnpinBar<_, _>` + | --------- this expression has type `Pin<&Bar>` +LL | Bar::Bar { x, y } => { + | ^^^^^^^^^^^^^^^^^ expected `Pin<&Bar>`, found `Bar<_, _>` | - = note: expected struct `Pin<&NegUnpinBar>` - found enum `NegUnpinBar<_, _>` + = note: expected struct `Pin<&Bar>` + found enum `Bar<_, _>` help: consider dereferencing to access the inner value using the Deref trait | LL | match *bar_const { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:103:18 + --> $DIR/pattern-matching.rs:66:18 | -LL | _ if let NegUnpinBar::Foo(x, y) = bar_const => { - | ^^^^^^^^^^^^^^^^^^^^^^ --------- this expression has type `Pin<&NegUnpinBar>` +LL | _ if let Bar::Foo(x, y) = bar_const => { + | ^^^^^^^^^^^^^^ --------- this expression has type `Pin<&Bar>` | | - | expected `Pin<&NegUnpinBar>`, found `NegUnpinBar<_, _>` + | expected `Pin<&Bar>`, found `Bar<_, _>` | - = note: expected struct `Pin<&NegUnpinBar>` - found enum `NegUnpinBar<_, _>` + = note: expected struct `Pin<&Bar>` + found enum `Bar<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | _ if let NegUnpinBar::Foo(x, y) = *bar_const => { - | + +LL | _ if let Bar::Foo(x, y) = *bar_const => { + | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:116:9 + --> $DIR/pattern-matching.rs:76:9 | -LL | let (NegUnpinFoo { x, y },) = foo_mut; - | ^^^^^^^^^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut (NegUnpinFoo,)>` +LL | let (Foo { x, y },) = foo_mut; + | ^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut (Foo,)>` | | - | expected `Pin<&mut (NegUnpinFoo,)>`, found `(_,)` + | expected `Pin<&mut (Foo,)>`, found `(_,)` | - = note: expected struct `Pin<&mut (NegUnpinFoo,)>` + = note: expected struct `Pin<&mut (Foo,)>` found tuple `(_,)` help: consider dereferencing to access the inner value using the Deref trait | -LL | let (NegUnpinFoo { x, y },) = *foo_mut; - | + +LL | let (Foo { x, y },) = *foo_mut; + | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:120:9 + --> $DIR/pattern-matching.rs:80:9 | -LL | let (NegUnpinFoo { x, y },) = foo_const; - | ^^^^^^^^^^^^^^^^^^^^^^^ --------- this expression has type `Pin<&(NegUnpinFoo,)>` +LL | let (Foo { x, y },) = foo_const; + | ^^^^^^^^^^^^^^^ --------- this expression has type `Pin<&(Foo,)>` | | - | expected `Pin<&(NegUnpinFoo,)>`, found `(_,)` + | expected `Pin<&(Foo,)>`, found `(_,)` | - = note: expected struct `Pin<&(NegUnpinFoo,)>` + = note: expected struct `Pin<&(Foo,)>` found tuple `(_,)` help: consider dereferencing to access the inner value using the Deref trait | -LL | let (NegUnpinFoo { x, y },) = *foo_const; - | + +LL | let (Foo { x, y },) = *foo_const; + | + -error[E0529]: expected an array or slice, found `Pin<&mut [NegUnpinFoo; 1]>` - --> $DIR/pattern-matching.rs:130:9 +error[E0529]: expected an array or slice, found `Pin<&mut [Foo; 1]>` + --> $DIR/pattern-matching.rs:87:9 | -LL | let [NegUnpinFoo { x, y }] = foo_mut; - | ^^^^^^^^^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [NegUnpinFoo; 1]>` +LL | let [Foo { x, y }] = foo_mut; + | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [Foo; 1]>` -error[E0529]: expected an array or slice, found `Pin<&[NegUnpinFoo; 1]>` - --> $DIR/pattern-matching.rs:134:9 +error[E0529]: expected an array or slice, found `Pin<&[Foo; 1]>` + --> $DIR/pattern-matching.rs:91:9 | -LL | let [NegUnpinFoo { x, y }] = foo_const; - | ^^^^^^^^^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[NegUnpinFoo; 1]>` +LL | let [Foo { x, y }] = foo_const; + | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[Foo; 1]>` -error[E0529]: expected an array or slice, found `Pin<&mut [NegUnpinFoo]>` - --> $DIR/pattern-matching.rs:144:12 +error[E0529]: expected an array or slice, found `Pin<&mut [Foo]>` + --> $DIR/pattern-matching.rs:98:12 | -LL | if let [NegUnpinFoo { x, y }] = foo_mut { - | ^^^^^^^^^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [NegUnpinFoo]>` +LL | if let [Foo { x, y }] = foo_mut { + | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [Foo]>` -error[E0529]: expected an array or slice, found `Pin<&[NegUnpinFoo]>` - --> $DIR/pattern-matching.rs:149:12 +error[E0529]: expected an array or slice, found `Pin<&[Foo]>` + --> $DIR/pattern-matching.rs:103:12 | -LL | if let [NegUnpinFoo { x, y }] = foo_const { - | ^^^^^^^^^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[NegUnpinFoo]>` +LL | if let [Foo { x, y }] = foo_const { + | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[Foo]>` -error: aborting due to 16 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0308, E0529. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pattern-matching.rs b/tests/ui/pin-ergonomics/pattern-matching.rs index 42cb86d7fdc6f..8aff264aaa8e3 100644 --- a/tests/ui/pin-ergonomics/pattern-matching.rs +++ b/tests/ui/pin-ergonomics/pattern-matching.rs @@ -8,26 +8,21 @@ use std::pin::Pin; // This test verifies that a `&pin mut T` can be projected to a pinned -// reference field `&pin mut T.U` when `T: !Unpin` or when `U: Unpin`. +// reference field `&pin mut T.U`. +// FIXME(pin_ergonomics): it is unsound to project `&pin mut T` to +// `&pin mut T.U` when `U: !Unpin` but `T: Unpin`, or when there exists +// `impl Drop for T` that takes a `&mut self` receiver. struct Foo { x: T, y: U, } -struct NegUnpinFoo { - x: T, - y: U, -} - -enum NegUnpinBar { +enum Bar { Foo(T, U), Bar { x: T, y: U }, } -impl !Unpin for NegUnpinFoo {} -impl !Unpin for NegUnpinBar {} - trait IsPinMut {} trait IsPinConst {} impl IsPinMut for Pin<&mut T> {} @@ -36,8 +31,7 @@ impl IsPinConst for Pin<&T> {} fn assert_pin_mut(_: T) {} fn assert_pin_const(_: T) {} -// Pinned references can be projected to pinned references of `Unpin` fields -fn unpin2unpin(foo_mut: Pin<&mut Foo>, foo_const: Pin<&Foo>) { +fn foo(foo_mut: Pin<&mut Foo>, foo_const: Pin<&Foo>) { let Foo { x, y } = foo_mut; //[normal]~^ ERROR mismatched types assert_pin_mut(x); @@ -49,45 +43,14 @@ fn unpin2unpin(foo_mut: Pin<&mut Foo>, foo_const: Pin< assert_pin_const(y); } -// Pinned references can be only projected to pinned references of `Unpin` fields -fn unpin2partial_unpin(foo_mut: Pin<&mut Foo>, foo_const: Pin<&Foo>) { - let Foo { x, .. } = foo_mut; - //[normal]~^ ERROR mismatched types - assert_pin_mut(x); - - let Foo { x, .. } = foo_const; - //[normal]~^ ERROR mismatched types - assert_pin_const(x); -} - -// Pinned references of `!Unpin` types can be projected to pinned references -fn neg_unpin2not_unpin( - foo_mut: Pin<&mut NegUnpinFoo>, - foo_const: Pin<&NegUnpinFoo>, -) { - let NegUnpinFoo { x, y } = foo_mut; - //[normal]~^ ERROR mismatched types - assert_pin_mut(x); - assert_pin_mut(y); - - let NegUnpinFoo { x, y } = foo_const; - //[normal]~^ ERROR mismatched types - assert_pin_const(x); - assert_pin_const(y); -} - -// Pinned references of `!Unpin` types can be projected to pinned references of `Unpin` fields -fn neg_unpin2unpin( - bar_mut: Pin<&mut NegUnpinBar>, - bar_const: Pin<&NegUnpinBar>, -) { +fn bar(bar_mut: Pin<&mut Bar>, bar_const: Pin<&Bar>) { match bar_mut { - NegUnpinBar::Foo(x, y) => { + Bar::Foo(x, y) => { //[normal]~^ ERROR mismatched types assert_pin_mut(x); assert_pin_mut(y); } - _ if let NegUnpinBar::Bar { x, y } = bar_mut => { + _ if let Bar::Bar { x, y } = bar_mut => { //[normal]~^ ERROR mismatched types assert_pin_mut(x); assert_pin_mut(y); @@ -95,12 +58,12 @@ fn neg_unpin2unpin( _ => {} } match bar_const { - NegUnpinBar::Bar { x, y } => { + Bar::Bar { x, y } => { //[normal]~^ ERROR mismatched types assert_pin_const(x); assert_pin_const(y); } - _ if let NegUnpinBar::Foo(x, y) = bar_const => { + _ if let Bar::Foo(x, y) = bar_const => { //[normal]~^ ERROR mismatched types assert_pin_const(x); assert_pin_const(y); @@ -109,45 +72,36 @@ fn neg_unpin2unpin( } } -fn neg_unpin_tuple( - foo_mut: Pin<&mut (NegUnpinFoo,)>, - foo_const: Pin<&(NegUnpinFoo,)>, -) { - let (NegUnpinFoo { x, y },) = foo_mut; +fn pin_mut_tuple(foo_mut: Pin<&mut (Foo,)>, foo_const: Pin<&(Foo,)>) { + let (Foo { x, y },) = foo_mut; //[normal]~^ ERROR mismatched types assert_pin_mut(x); assert_pin_mut(y); - let (NegUnpinFoo { x, y },) = foo_const; + let (Foo { x, y },) = foo_const; //[normal]~^ ERROR mismatched types assert_pin_const(x); assert_pin_const(y); } -fn neg_unpin_array( - foo_mut: Pin<&mut [NegUnpinFoo; 1]>, - foo_const: Pin<&[NegUnpinFoo; 1]>, -) { - let [NegUnpinFoo { x, y }] = foo_mut; - //[normal]~^ ERROR expected an array or slice, found `Pin<&mut [NegUnpinFoo; 1]>` +fn pin_mut_array(foo_mut: Pin<&mut [Foo; 1]>, foo_const: Pin<&[Foo; 1]>) { + let [Foo { x, y }] = foo_mut; + //[normal]~^ ERROR expected an array or slice, found `Pin<&mut [Foo; 1]>` assert_pin_mut(x); assert_pin_mut(y); - let [NegUnpinFoo { x, y }] = foo_const; - //[normal]~^ ERROR expected an array or slice, found `Pin<&[NegUnpinFoo; 1]>` + let [Foo { x, y }] = foo_const; + //[normal]~^ ERROR expected an array or slice, found `Pin<&[Foo; 1]>` assert_pin_const(x); assert_pin_const(y); } -fn neg_unpin_slice( - foo_mut: Pin<&mut [NegUnpinFoo]>, - foo_const: Pin<&[NegUnpinFoo]>, -) { - if let [NegUnpinFoo { x, y }] = foo_mut { - //[normal]~^ ERROR expected an array or slice, found `Pin<&mut [NegUnpinFoo]>` +fn pin_mut_slice(foo_mut: Pin<&mut [Foo]>, foo_const: Pin<&[Foo]>) { + if let [Foo { x, y }] = foo_mut { + //[normal]~^ ERROR expected an array or slice, found `Pin<&mut [Foo]>` assert_pin_mut(x); assert_pin_mut(y); } - if let [NegUnpinFoo { x, y }] = foo_const { - //[normal]~^ ERROR expected an array or slice, found `Pin<&[NegUnpinFoo]>` + if let [Foo { x, y }] = foo_const { + //[normal]~^ ERROR expected an array or slice, found `Pin<&[Foo]>` assert_pin_const(x); assert_pin_const(y); } diff --git a/tests/ui/pin-ergonomics/projection-unpin-checks.rs b/tests/ui/pin-ergonomics/projection-unpin-checks.rs deleted file mode 100644 index 3a7c4f04c6903..0000000000000 --- a/tests/ui/pin-ergonomics/projection-unpin-checks.rs +++ /dev/null @@ -1,132 +0,0 @@ -//@ edition:2024 -#![feature(pin_ergonomics, negative_impls)] -#![allow(incomplete_features)] - -// This test verifies that a `&pin mut T` cannot be projected to a pinned -// reference field `&pin mut T.U` if neither `T: !Unpin` nor `U: Unpin`. - -struct Foo(T); -struct NotUnpinFoo(T, std::marker::PhantomPinned); -struct NegUnpinFoo(T); - -impl !Unpin for NegUnpinFoo {} - -fn unpin2not_unpin(foo_mut: &pin mut Foo, foo_const: &pin const Foo) { - let Foo(_x) = foo_mut; //~ ERROR the trait bound `Foo: !Unpin` is not satisfied [E0277] - let Foo(_x) = foo_const; //~ ERROR the trait bound `Foo: !Unpin` is not satisfied [E0277] - let Foo(_x) = (|| foo_mut)(); //~ ERROR the trait bound `Foo: !Unpin` is not satisfied [E0277] - let Foo(_x) = (|| foo_const)(); //~ ERROR the trait bound `Foo: !Unpin` is not satisfied [E0277] - let &mut _foo_mut = foo_mut; //~ ERROR mismatched types - let &_foo_const = foo_const; //~ ERROR mismatched types - // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready - // let &pin mut _foo_mut = foo_mut; // error - // let &pin mut _foo_const = foo_const; // error -} - -fn unpin2unpin(foo_mut: &pin mut Foo, foo_const: &pin const Foo) { - let Foo(_x) = foo_mut; // ok - let Foo(_x) = foo_const; // ok - let Foo(_x) = (|| foo_mut)(); // ok - let Foo(_x) = (|| foo_const)(); // ok - let &mut _foo_mut = foo_mut; //~ ERROR mismatched types - let &_foo_const = foo_const; //~ ERROR mismatched types - // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready - // let &pin mut _foo_mut = foo_mut; // ok - // let &pin mut _foo_const = foo_const; // ok -} - -fn not_unpin2not_unpin(foo_mut: &pin mut NotUnpinFoo, foo_const: &pin const NotUnpinFoo) { - let NotUnpinFoo(_x, _) = foo_mut; //~ ERROR the trait bound `NotUnpinFoo: !Unpin` is not satisfied [E0277] - let NotUnpinFoo(_x, _) = foo_const; //~ ERROR the trait bound `NotUnpinFoo: !Unpin` is not satisfied [E0277] - let NotUnpinFoo(_x, _) = (|| foo_mut)(); //~ ERROR the trait bound `NotUnpinFoo: !Unpin` is not satisfied [E0277] - let NotUnpinFoo(_x, _) = (|| foo_const)(); //~ ERROR the trait bound `NotUnpinFoo: !Unpin` is not satisfied [E0277] - let &mut _foo_mut = foo_mut; //~ ERROR mismatched types - let &_foo_const = foo_const; //~ ERROR mismatched types - // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready - // let &pin mut _foo_mut = foo_mut; // error - // let &pin mut _foo_const = foo_const; // error -} - -fn not_unpin2unpin( - foo_mut: &pin mut NotUnpinFoo, - foo_const: &pin const NotUnpinFoo, -) { - let NotUnpinFoo(_x, _) = foo_mut; // ok - let NotUnpinFoo(_x, _) = foo_const; // ok - let NotUnpinFoo(_x, _) = (|| foo_mut)(); // ok - let NotUnpinFoo(_x, _) = (|| foo_const)(); // ok - let &mut _foo_mut = foo_mut; //~ ERROR mismatched types - let &_foo_const = foo_const; //~ ERROR mismatched types - // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready - // let &pin mut _foo_mut = foo_mut; // ok - // let &pin mut _foo_const = foo_const; // ok -} - -fn neg_unpin2not_unpin(foo_mut: &pin mut NegUnpinFoo, foo_const: &pin const NegUnpinFoo) { - let NegUnpinFoo(_x) = foo_mut; // ok - let NegUnpinFoo(_x) = foo_const; // ok - let NegUnpinFoo(_x) = (|| foo_mut)(); // ok - let NegUnpinFoo(_x) = (|| foo_const)(); // ok - let &mut _foo_mut = foo_mut; //~ ERROR mismatched types - let &_foo_const = foo_const; //~ ERROR mismatched types - // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready - // let &pin mut _foo_mut = foo_mut; // ok - // let &pin mut _foo_const = foo_const; // ok -} - -fn neg_unpin2unpin( - foo_mut: &pin mut NegUnpinFoo, - foo_const: &pin const NegUnpinFoo, -) { - let NegUnpinFoo(_x) = foo_mut; // ok - let NegUnpinFoo(_x) = foo_const; // ok - let NegUnpinFoo(_x) = (|| foo_mut)(); // ok - let NegUnpinFoo(_x) = (|| foo_const)(); // ok - let &mut _foo_mut = foo_mut; //~ ERROR mismatched types - let &_foo_const = foo_const; //~ ERROR mismatched types - // FIXME(pin_ergonomics): add these tests since `&pin` pattern is ready - // let &pin mut _foo_mut = foo_mut; // ok - // let &pin mut _foo_const = foo_const; // ok -} - -fn tuple_ref_mut_pat_and_pin_mut_of_tuple_mut_ty<'a, T>( - (&mut x,): &'a pin mut (&'a mut NegUnpinFoo,), - //~^ ERROR the trait bound `&'a mut NegUnpinFoo: !Unpin` is not satisfied in `(&'a mut NegUnpinFoo,)` [E0277] -) -> &'a pin mut NegUnpinFoo { - x -} - -fn tuple_ref_mut_pat_and_pin_mut_of_mut_tuple_ty<'a, T>( - (&mut x,): &'a pin mut &'a mut (NegUnpinFoo,), - //~^ ERROR mismatched type - //~| ERROR the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied [E0277] -) -> &'a pin mut NegUnpinFoo { - x -} - -fn ref_mut_tuple_pat_and_pin_mut_of_tuple_mut_ty<'a, T>( - &mut (x,): &'a pin mut (&'a mut NegUnpinFoo,), //~ ERROR mismatched type -) -> &'a pin mut NegUnpinFoo { - x -} - -fn ref_mut_tuple_pat_and_pin_mut_of_mut_tuple_ty<'a, T>( - &mut (x,): &'a pin mut &'a mut (NegUnpinFoo,), //~ ERROR mismatched type -) -> &'a pin mut NegUnpinFoo { - x -} - -fn tuple_pat_and_pin_mut_of_tuple_mut_ty<'a, T>( - (x,): &'a pin mut (&'a mut NegUnpinFoo,), -) -> &'a pin mut &'a mut NegUnpinFoo { - x // ok -} - -fn tuple_pat_and_pin_mut_of_mut_tuple_ty<'a, T>( - (x,): &'a pin mut &'a mut (NegUnpinFoo,), - //~^ ERROR the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied [E0277] -) -> &'a pin mut NegUnpinFoo { - x -} - -fn main() {} diff --git a/tests/ui/pin-ergonomics/projection-unpin-checks.stderr b/tests/ui/pin-ergonomics/projection-unpin-checks.stderr deleted file mode 100644 index c79e1e11b0771..0000000000000 --- a/tests/ui/pin-ergonomics/projection-unpin-checks.stderr +++ /dev/null @@ -1,333 +0,0 @@ -error[E0277]: the trait bound `Foo: !Unpin` is not satisfied - --> $DIR/projection-unpin-checks.rs:15:9 - | -LL | let Foo(_x) = foo_mut; - | ^^^^^^^ the trait bound `Foo: !Unpin` is not satisfied - -error[E0277]: the trait bound `Foo: !Unpin` is not satisfied - --> $DIR/projection-unpin-checks.rs:16:9 - | -LL | let Foo(_x) = foo_const; - | ^^^^^^^ the trait bound `Foo: !Unpin` is not satisfied - -error[E0277]: the trait bound `Foo: !Unpin` is not satisfied - --> $DIR/projection-unpin-checks.rs:17:9 - | -LL | let Foo(_x) = (|| foo_mut)(); - | ^^^^^^^ the trait bound `Foo: !Unpin` is not satisfied - -error[E0277]: the trait bound `Foo: !Unpin` is not satisfied - --> $DIR/projection-unpin-checks.rs:18:9 - | -LL | let Foo(_x) = (|| foo_const)(); - | ^^^^^^^ the trait bound `Foo: !Unpin` is not satisfied - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:19:9 - | -LL | let &mut _foo_mut = foo_mut; - | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Foo>` - | | - | expected `Pin<&mut Foo>`, found `&mut _` - | - = note: expected struct `Pin<&mut Foo>` - found mutable reference `&mut _` -help: you might have meant to use field `pointer` whose type is `&mut Foo` - | -LL | let &mut _foo_mut = foo_mut.pointer; - | ++++++++ -help: to declare a mutable variable use - | -LL - let &mut _foo_mut = foo_mut; -LL + let mut _foo_mut = foo_mut; - | - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:20:9 - | -LL | let &_foo_const = foo_const; - | ^^^^^^^^^^^ --------- this expression has type `Pin<&Foo>` - | | - | expected `Pin<&Foo>`, found `&_` - | - = note: expected struct `Pin<&Foo>` - found reference `&_` -help: you might have meant to use field `pointer` whose type is `&Foo` - | -LL | let &_foo_const = foo_const.pointer; - | ++++++++ - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:31:9 - | -LL | let &mut _foo_mut = foo_mut; - | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Foo>` - | | - | expected `Pin<&mut Foo>`, found `&mut _` - | - = note: expected struct `Pin<&mut Foo>` - found mutable reference `&mut _` -help: you might have meant to use field `pointer` whose type is `&mut Foo` - | -LL | let &mut _foo_mut = foo_mut.pointer; - | ++++++++ -help: to declare a mutable variable use - | -LL - let &mut _foo_mut = foo_mut; -LL + let mut _foo_mut = foo_mut; - | - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:32:9 - | -LL | let &_foo_const = foo_const; - | ^^^^^^^^^^^ --------- this expression has type `Pin<&Foo>` - | | - | expected `Pin<&Foo>`, found `&_` - | - = note: expected struct `Pin<&Foo>` - found reference `&_` -help: you might have meant to use field `pointer` whose type is `&Foo` - | -LL | let &_foo_const = foo_const.pointer; - | ++++++++ - -error[E0277]: the trait bound `NotUnpinFoo: !Unpin` is not satisfied - --> $DIR/projection-unpin-checks.rs:39:9 - | -LL | let NotUnpinFoo(_x, _) = foo_mut; - | ^^^^^^^^^^^^^^^^^^ the trait bound `NotUnpinFoo: !Unpin` is not satisfied - -error[E0277]: the trait bound `NotUnpinFoo: !Unpin` is not satisfied - --> $DIR/projection-unpin-checks.rs:40:9 - | -LL | let NotUnpinFoo(_x, _) = foo_const; - | ^^^^^^^^^^^^^^^^^^ the trait bound `NotUnpinFoo: !Unpin` is not satisfied - -error[E0277]: the trait bound `NotUnpinFoo: !Unpin` is not satisfied - --> $DIR/projection-unpin-checks.rs:41:9 - | -LL | let NotUnpinFoo(_x, _) = (|| foo_mut)(); - | ^^^^^^^^^^^^^^^^^^ the trait bound `NotUnpinFoo: !Unpin` is not satisfied - -error[E0277]: the trait bound `NotUnpinFoo: !Unpin` is not satisfied - --> $DIR/projection-unpin-checks.rs:42:9 - | -LL | let NotUnpinFoo(_x, _) = (|| foo_const)(); - | ^^^^^^^^^^^^^^^^^^ the trait bound `NotUnpinFoo: !Unpin` is not satisfied - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:43:9 - | -LL | let &mut _foo_mut = foo_mut; - | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NotUnpinFoo>` - | | - | expected `Pin<&mut NotUnpinFoo>`, found `&mut _` - | - = note: expected struct `Pin<&mut NotUnpinFoo>` - found mutable reference `&mut _` -help: you might have meant to use field `pointer` whose type is `&mut NotUnpinFoo` - | -LL | let &mut _foo_mut = foo_mut.pointer; - | ++++++++ -help: to declare a mutable variable use - | -LL - let &mut _foo_mut = foo_mut; -LL + let mut _foo_mut = foo_mut; - | - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:44:9 - | -LL | let &_foo_const = foo_const; - | ^^^^^^^^^^^ --------- this expression has type `Pin<&NotUnpinFoo>` - | | - | expected `Pin<&NotUnpinFoo>`, found `&_` - | - = note: expected struct `Pin<&NotUnpinFoo>` - found reference `&_` -help: you might have meant to use field `pointer` whose type is `&NotUnpinFoo` - | -LL | let &_foo_const = foo_const.pointer; - | ++++++++ - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:58:9 - | -LL | let &mut _foo_mut = foo_mut; - | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NotUnpinFoo>` - | | - | expected `Pin<&mut NotUnpinFoo>`, found `&mut _` - | - = note: expected struct `Pin<&mut NotUnpinFoo>` - found mutable reference `&mut _` -help: you might have meant to use field `pointer` whose type is `&mut NotUnpinFoo` - | -LL | let &mut _foo_mut = foo_mut.pointer; - | ++++++++ -help: to declare a mutable variable use - | -LL - let &mut _foo_mut = foo_mut; -LL + let mut _foo_mut = foo_mut; - | - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:59:9 - | -LL | let &_foo_const = foo_const; - | ^^^^^^^^^^^ --------- this expression has type `Pin<&NotUnpinFoo>` - | | - | expected `Pin<&NotUnpinFoo>`, found `&_` - | - = note: expected struct `Pin<&NotUnpinFoo>` - found reference `&_` -help: you might have meant to use field `pointer` whose type is `&NotUnpinFoo` - | -LL | let &_foo_const = foo_const.pointer; - | ++++++++ - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:70:9 - | -LL | let &mut _foo_mut = foo_mut; - | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NegUnpinFoo>` - | | - | expected `Pin<&mut NegUnpinFoo>`, found `&mut _` - | - = note: expected struct `Pin<&mut NegUnpinFoo>` - found mutable reference `&mut _` -help: you might have meant to use field `pointer` whose type is `&mut NegUnpinFoo` - | -LL | let &mut _foo_mut = foo_mut.pointer; - | ++++++++ -help: to declare a mutable variable use - | -LL - let &mut _foo_mut = foo_mut; -LL + let mut _foo_mut = foo_mut; - | - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:71:9 - | -LL | let &_foo_const = foo_const; - | ^^^^^^^^^^^ --------- this expression has type `Pin<&NegUnpinFoo>` - | | - | expected `Pin<&NegUnpinFoo>`, found `&_` - | - = note: expected struct `Pin<&NegUnpinFoo>` - found reference `&_` -help: you might have meant to use field `pointer` whose type is `&NegUnpinFoo` - | -LL | let &_foo_const = foo_const.pointer; - | ++++++++ - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:85:9 - | -LL | let &mut _foo_mut = foo_mut; - | ^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut NegUnpinFoo>` - | | - | expected `Pin<&mut NegUnpinFoo>`, found `&mut _` - | - = note: expected struct `Pin<&mut NegUnpinFoo>` - found mutable reference `&mut _` -help: you might have meant to use field `pointer` whose type is `&mut NegUnpinFoo` - | -LL | let &mut _foo_mut = foo_mut.pointer; - | ++++++++ -help: to declare a mutable variable use - | -LL - let &mut _foo_mut = foo_mut; -LL + let mut _foo_mut = foo_mut; - | - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:86:9 - | -LL | let &_foo_const = foo_const; - | ^^^^^^^^^^^ --------- this expression has type `Pin<&NegUnpinFoo>` - | | - | expected `Pin<&NegUnpinFoo>`, found `&_` - | - = note: expected struct `Pin<&NegUnpinFoo>` - found reference `&_` -help: you might have meant to use field `pointer` whose type is `&NegUnpinFoo` - | -LL | let &_foo_const = foo_const.pointer; - | ++++++++ - -error[E0277]: the trait bound `&'a mut NegUnpinFoo: !Unpin` is not satisfied in `(&'a mut NegUnpinFoo,)` - --> $DIR/projection-unpin-checks.rs:93:5 - | -LL | (&mut x,): &'a pin mut (&'a mut NegUnpinFoo,), - | ^^^^^^^^^ within `(&'a mut NegUnpinFoo,)`, the trait bound `&'a mut NegUnpinFoo: !Unpin` is not satisfied - | - = note: required because it appears within the type `(&'a mut NegUnpinFoo,)` - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:100:6 - | -LL | (&mut x,): &'a pin mut &'a mut (NegUnpinFoo,), - | ^^^^^^ ------------------------------------- expected due to this - | | - | expected `NegUnpinFoo`, found `&mut _` - | - = note: expected struct `NegUnpinFoo` - found mutable reference `&mut _` -note: to declare a mutable binding use: `mut x` - --> $DIR/projection-unpin-checks.rs:100:6 - | -LL | (&mut x,): &'a pin mut &'a mut (NegUnpinFoo,), - | ^^^^^^ -help: consider removing `&mut` from the pattern - | -LL - (&mut x,): &'a pin mut &'a mut (NegUnpinFoo,), -LL + (x,): &'a pin mut &'a mut (NegUnpinFoo,), - | - -error[E0277]: the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied - --> $DIR/projection-unpin-checks.rs:100:5 - | -LL | (&mut x,): &'a pin mut &'a mut (NegUnpinFoo,), - | ^^^^^^^^^ the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:108:5 - | -LL | &mut (x,): &'a pin mut (&'a mut NegUnpinFoo,), - | ^^^^^^^^^ ------------------------------------- expected due to this - | | - | expected `Pin<&mut (&mut NegUnpinFoo,)>`, found `&mut _` - | - = note: expected struct `Pin<&'a mut (&'a mut NegUnpinFoo,)>` - found mutable reference `&mut _` -help: you might have meant to use field `pointer` whose type is `&'a mut (&'a mut NegUnpinFoo,)` - | -LL | &mut (x,): &'a pin mut (&'a mut NegUnpinFoo,).pointer, - | ++++++++ - -error[E0308]: mismatched types - --> $DIR/projection-unpin-checks.rs:114:5 - | -LL | &mut (x,): &'a pin mut &'a mut (NegUnpinFoo,), - | ^^^^^^^^^ ------------------------------------- expected due to this - | | - | expected `Pin<&mut &mut (NegUnpinFoo,)>`, found `&mut _` - | - = note: expected struct `Pin<&'a mut &'a mut (NegUnpinFoo,)>` - found mutable reference `&mut _` -help: you might have meant to use field `pointer` whose type is `&'a mut &'a mut (NegUnpinFoo,)` - | -LL | &mut (x,): &'a pin mut &'a mut (NegUnpinFoo,).pointer, - | ++++++++ - -error[E0277]: the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied - --> $DIR/projection-unpin-checks.rs:126:5 - | -LL | (x,): &'a pin mut &'a mut (NegUnpinFoo,), - | ^^^^ the trait bound `&'a mut (NegUnpinFoo,): !Unpin` is not satisfied - -error: aborting due to 26 previous errors - -Some errors have detailed explanations: E0277, E0308. -For more information about an error, try `rustc --explain E0277`. From cb369ab26f1ec2ec10f1feff484b9d4aae189c1d Mon Sep 17 00:00:00 2001 From: Frank King Date: Sun, 3 Aug 2025 21:26:38 +0800 Subject: [PATCH 085/108] Fix `pin_ergonomics` tests --- compiler/rustc_hir_typeck/src/pat.rs | 29 +--- ...ttern-matching-deref-pattern.normal.stderr | 155 ++++++++++++++++++ .../pattern-matching-deref-pattern.rs | 110 ++++++++----- ...ern-matching-mix-deref-pattern.both.stderr | 74 +++++++++ ...ng-mix-deref-pattern.deref_patterns.stderr | 74 +++++++++ ...n-matching-mix-deref-pattern.normal.stderr | 151 +++++++++++------ ...ng-mix-deref-pattern.pin_ergonomics.stderr | 96 +++-------- .../pattern-matching-mix-deref-pattern.rs | 98 +++++------ .../pattern-matching.normal.stderr | 100 +++++++++-- .../pattern-matching.pin_ergonomics.stderr | 54 ++++++ tests/ui/pin-ergonomics/pattern-matching.rs | 37 ++++- 11 files changed, 726 insertions(+), 252 deletions(-) create mode 100644 tests/ui/pin-ergonomics/pattern-matching-deref-pattern.normal.stderr create mode 100644 tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr create mode 100644 tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.deref_patterns.stderr create mode 100644 tests/ui/pin-ergonomics/pattern-matching.pin_ergonomics.stderr diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 615e80f1ba0e5..12db66155f8a2 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -557,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability); // If the pinnedness is `Not`, it means the pattern is unpinned // and thus requires an `Unpin` bound. - if binding_mode == ByRef::Yes(Pinnedness::Not, Mutability::Mut) { + if matches!(binding_mode, ByRef::Yes(Pinnedness::Not, _)) { self.register_bound( inner_ty, self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span), @@ -2683,9 +2683,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected = self.try_structurally_resolve_type(pat.span, expected); // Determine whether we're consuming an inherited reference and resetting the default // binding mode, based on edition and enabled experimental features. - // FIXME(pin_ergonomics): since `&pin` pattern is supported, the condition here - // should be adjusted to `pat_pin == inh_pin` - if let ByRef::Yes(Pinnedness::Not, inh_mut) = pat_info.binding_mode { + if let ByRef::Yes(inh_pin, inh_mut) = pat_info.binding_mode + // FIXME(pin_ergonomics): since `&pin` pattern is supported, the condition here + // should be adjusted to `pat_pin == inh_pin` + && (!self.tcx.features().pin_ergonomics() || inh_pin == Pinnedness::Not) + { match self.ref_pat_matches_inherited_ref(pat.span.edition()) { InheritedRefMatchRule::EatOuter => { // ref pattern attempts to consume inherited reference @@ -2704,22 +2706,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return expected; } InheritedRefMatchRule::EatInner => { - let expected_ref_or_pinned_ref = || { - if self.tcx.features().pin_ergonomics() - && let Some(ty::Ref(_, _, r_mutbl)) = - expected.pinned_ty().map(|ty| *ty.kind()) - && pat_mutbl <= r_mutbl - { - return Some((Pinnedness::Pinned, r_mutbl)); - } - if let ty::Ref(_, _, r_mutbl) = *expected.kind() - && pat_mutbl <= r_mutbl - { - return Some((Pinnedness::Not, r_mutbl)); - } - None - }; - if let Some((_, r_mutbl)) = expected_ref_or_pinned_ref() { + if let ty::Ref(_, _, r_mutbl) = *expected.kind() + && pat_mutbl <= r_mutbl + { // Match against the reference type; don't consume the inherited ref. // NB: The check for compatible pattern and ref type mutability assumes that // `&` patterns can match against mutable references (RFC 3627, Rule 5). If diff --git a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.normal.stderr new file mode 100644 index 0000000000000..559465bc13aba --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.normal.stderr @@ -0,0 +1,155 @@ +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:45:28 + | +LL | let Foo { x, y } = foo.as_mut(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Foo { ref x, y } = foo.as_mut(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Foo { x, ref y } = foo.as_mut(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:35:24 + | +LL | let Foo { x, y } = foo.as_mut(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Foo { ref x, y } = foo.as_mut(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Foo { x, ref y } = foo.as_mut(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:67:28 + | +LL | let Foo { x, y } = foo.as_ref(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Foo { ref x, y } = foo.as_ref(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Foo { x, ref y } = foo.as_ref(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:57:24 + | +LL | let Foo { x, y } = foo.as_ref(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Foo { ref x, y } = foo.as_ref(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Foo { x, ref y } = foo.as_ref(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:89:25 + | +LL | let Bar(x, y) = bar.as_mut(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Bar(ref x, y) = bar.as_mut(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Bar(x, ref y) = bar.as_mut(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:79:21 + | +LL | let Bar(x, y) = bar.as_mut(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Bar(ref x, y) = bar.as_mut(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Bar(x, ref y) = bar.as_mut(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:111:25 + | +LL | let Bar(x, y) = bar.as_ref(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Bar(ref x, y) = bar.as_ref(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Bar(x, ref y) = bar.as_ref(); + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/pattern-matching-deref-pattern.rs:101:21 + | +LL | let Bar(x, y) = bar.as_ref(); + | - - ^^^^^^^^^^^^ + | | | + | | ...and here + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider borrowing the pattern binding + | +LL | let Bar(ref x, y) = bar.as_ref(); + | +++ +help: consider borrowing the pattern binding + | +LL | let Bar(x, ref y) = bar.as_ref(); + | +++ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs index 23d29e6f9dae4..6ecb22e535b13 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs +++ b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs @@ -1,8 +1,6 @@ //@ revisions: pin_ergonomics normal //@ edition:2024 -//@ check-pass -// //@[normal] check-pass -// //@[pin_ergonomics] rustc-env:RUSTC_LOG=rustc_hir_typeck::expr_use_visitor=DEBUG +//@[pin_ergonomics] check-pass #![cfg_attr(pin_ergonomics, feature(pin_ergonomics))] #![feature(deref_patterns)] #![allow(incomplete_features)] @@ -24,67 +22,99 @@ enum Baz { Bar { x: T, y: U }, } -fn foo_mut(foo: Pin<&mut Foo>) { - let Foo { .. } = foo; - let Pin { .. } = foo; - let _ = || { - let Foo { .. } = foo; - let Pin { .. } = foo; - }; +trait IsPinMut {} +trait IsPinConst {} +impl IsPinMut for Pin<&mut T> {} +impl IsPinConst for Pin<&T> {} +fn assert_pin_mut(_: T) {} +fn assert_pin_const(_: T) {} + +fn foo_mut(mut foo: Pin<&mut Foo>) { + let Foo { .. } = foo.as_mut(); + let Foo { x, y } = foo.as_mut(); + //[normal]~^ ERROR cannot move out of a shared reference #[cfg(pin_ergonomics)] - let Foo { x, y } = foo; + assert_pin_mut(x); #[cfg(pin_ergonomics)] + assert_pin_mut(y); + let Pin { .. } = foo.as_mut(); + let _ = || { - let Foo { x, y } = foo; + let Foo { .. } = foo.as_mut(); + let Foo { x, y } = foo.as_mut(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_mut(x); + #[cfg(pin_ergonomics)] + assert_pin_mut(y); + let Pin { .. } = foo.as_mut(); }; } fn foo_const(foo: Pin<&Foo>) { - let Foo { .. } = foo; - let Pin { .. } = foo; - let _ = || { - let Foo { .. } = foo; - let Pin { .. } = foo; - }; - + let Foo { .. } = foo.as_ref(); + let Foo { x, y } = foo.as_ref(); + //[normal]~^ ERROR cannot move out of a shared reference #[cfg(pin_ergonomics)] - let Foo { x, y } = foo; + assert_pin_const(x); #[cfg(pin_ergonomics)] - let _ = || { - let Foo { x, y } = foo; - }; -} + assert_pin_const(y); + let Pin { .. } = foo.as_ref(); -fn bar_mut(bar: Pin<&mut Bar>) { - let Bar(..) = bar; - let Pin { .. } = bar; let _ = || { - let Bar(..) = bar; - let Pin { .. } = bar; + let Foo { .. } = foo.as_ref(); + let Foo { x, y } = foo.as_ref(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_const(x); + #[cfg(pin_ergonomics)] + assert_pin_const(y); + let Pin { .. } = foo.as_ref(); }; +} +fn bar_mut(mut bar: Pin<&mut Bar>) { + let Bar(..) = bar.as_mut(); + let Bar(x, y) = bar.as_mut(); + //[normal]~^ ERROR cannot move out of a shared reference #[cfg(pin_ergonomics)] - let Bar(x, y) = bar; + assert_pin_mut(x); #[cfg(pin_ergonomics)] + assert_pin_mut(y); + let Pin { .. } = bar.as_mut(); + let _ = || { - let Bar(x, y) = bar; + let Bar(..) = bar.as_mut(); + let Bar(x, y) = bar.as_mut(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_mut(x); + #[cfg(pin_ergonomics)] + assert_pin_mut(y); + let Pin { .. } = bar.as_mut(); }; } fn bar_const(bar: Pin<&Bar>) { - let Bar(..) = bar; - let Pin { .. } = bar; - let _ = || { - let Bar(..) = bar; - let Pin { .. } = bar; - }; - + let Bar(..) = bar.as_ref(); + let Bar(x, y) = bar.as_ref(); + //[normal]~^ ERROR cannot move out of a shared reference #[cfg(pin_ergonomics)] - let Bar(x, y) = bar; + assert_pin_const(x); #[cfg(pin_ergonomics)] + assert_pin_const(y); + let Pin { .. } = bar.as_ref(); + let _ = || { - let Bar(x, y) = bar; + let Bar(..) = bar.as_ref(); + let Bar(x, y) = bar.as_ref(); + //[normal]~^ ERROR cannot move out of a shared reference + #[cfg(pin_ergonomics)] + assert_pin_const(x); + #[cfg(pin_ergonomics)] + assert_pin_const(y); + let Pin { .. } = bar.as_ref(); }; } diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr new file mode 100644 index 0000000000000..3e883fddb7a6b --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr @@ -0,0 +1,74 @@ +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:27:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:35:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:46:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:54:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:65:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:73:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:84:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:92:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: aborting due to 8 previous errors + diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.deref_patterns.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.deref_patterns.stderr new file mode 100644 index 0000000000000..3e883fddb7a6b --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.deref_patterns.stderr @@ -0,0 +1,74 @@ +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:27:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:35:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:46:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:54:9 + | +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Foo>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:65:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:73:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:84:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:92:9 + | +LL | Bar(x, y) => {} + | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&Bar>` + +error: aborting due to 8 previous errors + diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr index 2389fe9ed98cd..a39d7d79916c5 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr @@ -1,66 +1,123 @@ -error: mix of deref patterns and normal constructors +error[E0308]: mismatched types --> $DIR/pattern-matching-mix-deref-pattern.rs:27:9 | -LL | Foo { .. } => {} - | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` +LL | match foo { + | --- this expression has type `Pin<&mut Foo>` +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ expected `Pin<&mut Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&mut Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *foo { + | + -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:31:9 +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:35:9 + | +LL | let _ = || match foo { + | --- this expression has type `Pin<&mut Foo>` +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ expected `Pin<&mut Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&mut Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait | -LL | Foo { .. } => {} - | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` +LL | let _ = || match *foo { + | + -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:49:9 +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:46:9 | -LL | Foo { .. } => {} - | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&Foo>` +LL | match foo { + | --- this expression has type `Pin<&Foo>` +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ expected `Pin<&Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *foo { + | + -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:53:9 +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:54:9 + | +LL | let _ = || match foo { + | --- this expression has type `Pin<&Foo>` +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ expected `Pin<&Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait | -LL | Foo { .. } => {} - | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&Foo>` +LL | let _ = || match *foo { + | + -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:71:9 +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:65:9 | -LL | Bar(..) => {} - | ^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` +LL | match bar { + | --- this expression has type `Pin<&mut Bar>` +LL | Bar(x, y) => {} + | ^^^^^^^^^ expected `Pin<&mut Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&mut Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *bar { + | + -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:75:9 +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:73:9 + | +LL | let _ = || match bar { + | --- this expression has type `Pin<&mut Bar>` +LL | Bar(x, y) => {} + | ^^^^^^^^^ expected `Pin<&mut Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&mut Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait | -LL | Bar(..) => {} - | ^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` +LL | let _ = || match *bar { + | + -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:93:9 +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:84:9 | -LL | Bar(..) => {} - | ^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&Bar>` +LL | match bar { + | --- this expression has type `Pin<&Bar>` +LL | Bar(x, y) => {} + | ^^^^^^^^^ expected `Pin<&Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *bar { + | + -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:97:9 +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:92:9 + | +LL | let _ = || match bar { + | --- this expression has type `Pin<&Bar>` +LL | Bar(x, y) => {} + | ^^^^^^^^^ expected `Pin<&Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait | -LL | Bar(..) => {} - | ^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&Bar>` +LL | let _ = || match *bar { + | + error: aborting due to 8 previous errors +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr index f23e5cf72a45e..3e883fddb7a6b 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr @@ -1,130 +1,74 @@ error: mix of deref patterns and normal constructors --> $DIR/pattern-matching-mix-deref-pattern.rs:27:9 | -LL | Foo { .. } => {} - | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` - -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:37:9 - | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:31:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:35:9 | -LL | Foo { .. } => {} - | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` - -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:42:9 - | -LL | Foo { .. } => {} - | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` +... LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:49:9 - | -LL | Foo { .. } => {} - | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&Foo>` - -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:59:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:46:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:53:9 - | -LL | Foo { .. } => {} - | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&Foo>` - -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:64:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:54:9 | -LL | Foo { .. } => {} - | ^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +LL | Foo { x, y } => {} + | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` +... LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:71:9 - | -LL | Bar(..) => {} - | ^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` - -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:81:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:65:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:75:9 - | -LL | Bar(..) => {} - | ^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` - -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:86:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:73:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` +... LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:93:9 - | -LL | Bar(..) => {} - | ^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&Bar>` - -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:103:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:84:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:97:9 - | -LL | Bar(..) => {} - | ^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` -LL | Pin { .. } => {} - | ^^^^^^^^^^ matches directly on `Pin<&Bar>` - -error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:108:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:92:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` +... LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Bar>` -error: aborting due to 16 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs index c5ba9dbeb2e6e..8945205bea176 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs @@ -1,7 +1,7 @@ -//@ revisions: pin_ergonomics normal +//@ revisions: normal pin_ergonomics deref_patterns both //@ edition:2024 -#![cfg_attr(pin_ergonomics, feature(pin_ergonomics))] -#![feature(deref_patterns)] +#![cfg_attr(any(pin_ergonomics, both), feature(pin_ergonomics))] +#![cfg_attr(any(deref_patterns, both), feature(deref_patterns))] #![allow(incomplete_features)] // This test verifies that the `pin_ergonomics` feature works well @@ -24,88 +24,76 @@ enum Baz { fn foo_mut(foo: Pin<&mut Foo>) { match foo { - Foo { .. } => {} //~ ERROR mix of deref patterns and normal constructors + Foo { x, y } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors Pin { .. } => {} } let _ = || match foo { - Foo { .. } => {} //~ ERROR mix of deref patterns and normal constructors - Pin { .. } => {} - }; - - #[cfg(pin_ergonomics)] - match foo { - Foo { x, y } => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors - Pin { .. } => {} - } - #[cfg(pin_ergonomics)] - let _ = || match foo { - Foo { .. } => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Foo { x, y } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors Pin { .. } => {} }; } fn foo_const(foo: Pin<&Foo>) { match foo { - Foo { .. } => {} //~ ERROR mix of deref patterns and normal constructors - Pin { .. } => {} - } - let _ = || match foo { - Foo { .. } => {} //~ ERROR mix of deref patterns and normal constructors - Pin { .. } => {} - }; - - #[cfg(pin_ergonomics)] - match foo { - Foo { x, y } => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Foo { x, y } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors Pin { .. } => {} } - #[cfg(pin_ergonomics)] let _ = || match foo { - Foo { .. } => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Foo { x, y } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors Pin { .. } => {} }; } fn bar_mut(bar: Pin<&mut Bar>) { match bar { - Bar(..) => {} //~ ERROR mix of deref patterns and normal constructors + Bar(x, y) => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors Pin { .. } => {} } let _ = || match bar { - Bar(..) => {} //~ ERROR mix of deref patterns and normal constructors - Pin { .. } => {} - }; - - #[cfg(pin_ergonomics)] - match bar { - Bar(x, y) => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors - Pin { .. } => {} - } - #[cfg(pin_ergonomics)] - let _ = || match bar { - Bar(x, y) => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Bar(x, y) => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors Pin { .. } => {} }; } fn bar_const(bar: Pin<&Bar>) { match bar { - Bar(..) => {} //~ ERROR mix of deref patterns and normal constructors - Pin { .. } => {} - } - let _ = || match bar { - Bar(..) => {} //~ ERROR mix of deref patterns and normal constructors - Pin { .. } => {} - }; - - #[cfg(pin_ergonomics)] - match bar { - Bar(x, y) => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Bar(x, y) => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors Pin { .. } => {} } - #[cfg(pin_ergonomics)] let _ = || match bar { - Bar(x, y) => {} //[pin_ergonomics]~ ERROR mix of deref patterns and normal constructors + Bar(x, y) => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR mix of deref patterns and normal constructors + //[both]~^^^^ ERROR mix of deref patterns and normal constructors Pin { .. } => {} }; } diff --git a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr index 1b4908abdc176..26f057195a15e 100644 --- a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:35:9 + --> $DIR/pattern-matching.rs:34:9 | LL | let Foo { x, y } = foo_mut; | ^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Foo>` @@ -14,7 +14,7 @@ LL | let Foo { x, y } = *foo_mut; | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:40:9 + --> $DIR/pattern-matching.rs:39:9 | LL | let Foo { x, y } = foo_const; | ^^^^^^^^^^^^ --------- this expression has type `Pin<&Foo>` @@ -29,7 +29,7 @@ LL | let Foo { x, y } = *foo_const; | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:48:9 + --> $DIR/pattern-matching.rs:47:9 | LL | match bar_mut { | ------- this expression has type `Pin<&mut Bar>` @@ -44,7 +44,7 @@ LL | match *bar_mut { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:53:18 + --> $DIR/pattern-matching.rs:52:18 | LL | _ if let Bar::Bar { x, y } = bar_mut => { | ^^^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Bar>` @@ -59,7 +59,7 @@ LL | _ if let Bar::Bar { x, y } = *bar_mut => { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:61:9 + --> $DIR/pattern-matching.rs:60:9 | LL | match bar_const { | --------- this expression has type `Pin<&Bar>` @@ -74,7 +74,7 @@ LL | match *bar_const { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:66:18 + --> $DIR/pattern-matching.rs:65:18 | LL | _ if let Bar::Foo(x, y) = bar_const => { | ^^^^^^^^^^^^^^ --------- this expression has type `Pin<&Bar>` @@ -89,7 +89,7 @@ LL | _ if let Bar::Foo(x, y) = *bar_const => { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:76:9 + --> $DIR/pattern-matching.rs:75:9 | LL | let (Foo { x, y },) = foo_mut; | ^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut (Foo,)>` @@ -104,7 +104,7 @@ LL | let (Foo { x, y },) = *foo_mut; | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:80:9 + --> $DIR/pattern-matching.rs:79:9 | LL | let (Foo { x, y },) = foo_const; | ^^^^^^^^^^^^^^^ --------- this expression has type `Pin<&(Foo,)>` @@ -119,30 +119,104 @@ LL | let (Foo { x, y },) = *foo_const; | + error[E0529]: expected an array or slice, found `Pin<&mut [Foo; 1]>` - --> $DIR/pattern-matching.rs:87:9 + --> $DIR/pattern-matching.rs:86:9 | LL | let [Foo { x, y }] = foo_mut; | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [Foo; 1]>` error[E0529]: expected an array or slice, found `Pin<&[Foo; 1]>` - --> $DIR/pattern-matching.rs:91:9 + --> $DIR/pattern-matching.rs:90:9 | LL | let [Foo { x, y }] = foo_const; | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[Foo; 1]>` error[E0529]: expected an array or slice, found `Pin<&mut [Foo]>` - --> $DIR/pattern-matching.rs:98:12 + --> $DIR/pattern-matching.rs:97:12 | LL | if let [Foo { x, y }] = foo_mut { | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [Foo]>` error[E0529]: expected an array or slice, found `Pin<&[Foo]>` - --> $DIR/pattern-matching.rs:103:12 + --> $DIR/pattern-matching.rs:102:12 | LL | if let [Foo { x, y }] = foo_const { | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[Foo]>` -error: aborting due to 12 previous errors +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:110:5 + | +LL | (&mut x,): Pin<&'a mut (&'a mut Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut (&mut Foo,)>`, found `(_,)` + | + = note: expected struct `Pin<&'a mut (&'a mut Foo,)>` + found tuple `(_,)` + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:116:5 + | +LL | (&mut x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut &mut (Foo,)>`, found `(_,)` + | + = note: expected struct `Pin<&'a mut &'a mut (Foo,)>` + found tuple `(_,)` + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:122:5 + | +LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut (&mut Foo,)>`, found `&mut _` + | + = note: expected struct `Pin<&'a mut (&'a mut Foo,)>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&'a mut (&'a mut Foo,)` + | +LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>.pointer, + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:128:5 + | +LL | &mut (x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut &mut (Foo,)>`, found `&mut _` + | + = note: expected struct `Pin<&'a mut &'a mut (Foo,)>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&'a mut &'a mut (Foo,)` + | +LL | &mut (x,): Pin<&'a mut &'a mut (Foo,)>.pointer, + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:134:5 + | +LL | (x,): Pin<&'a mut (&'a mut Foo,)>, + | ^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut (&mut Foo,)>`, found `(_,)` + | + = note: expected struct `Pin<&'a mut (&'a mut Foo,)>` + found tuple `(_,)` + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:140:5 + | +LL | (x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut &mut (Foo,)>`, found `(_,)` + | + = note: expected struct `Pin<&'a mut &'a mut (Foo,)>` + found tuple `(_,)` + +error: aborting due to 18 previous errors Some errors have detailed explanations: E0308, E0529. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pattern-matching.pin_ergonomics.stderr b/tests/ui/pin-ergonomics/pattern-matching.pin_ergonomics.stderr new file mode 100644 index 0000000000000..fd442b8263c05 --- /dev/null +++ b/tests/ui/pin-ergonomics/pattern-matching.pin_ergonomics.stderr @@ -0,0 +1,54 @@ +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:116:6 + | +LL | (&mut x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^^^ --------------------------------- expected due to this + | | + | expected `Foo`, found `&mut _` + | + = note: expected struct `Foo` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-matching.rs:116:6 + | +LL | (&mut x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - (&mut x,): Pin<&'a mut &'a mut (Foo,)>, +LL + (x,): Pin<&'a mut &'a mut (Foo,)>, + | + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:122:5 + | +LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut (&mut Foo,)>`, found `&mut _` + | + = note: expected struct `Pin<&'a mut (&'a mut Foo,)>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&'a mut (&'a mut Foo,)` + | +LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>.pointer, + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/pattern-matching.rs:128:5 + | +LL | &mut (x,): Pin<&'a mut &'a mut (Foo,)>, + | ^^^^^^^^^ --------------------------------- expected due to this + | | + | expected `Pin<&mut &mut (Foo,)>`, found `&mut _` + | + = note: expected struct `Pin<&'a mut &'a mut (Foo,)>` + found mutable reference `&mut _` +help: you might have meant to use field `pointer` whose type is `&'a mut &'a mut (Foo,)` + | +LL | &mut (x,): Pin<&'a mut &'a mut (Foo,)>.pointer, + | ++++++++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pattern-matching.rs b/tests/ui/pin-ergonomics/pattern-matching.rs index 8aff264aaa8e3..4f2a23afbb8ff 100644 --- a/tests/ui/pin-ergonomics/pattern-matching.rs +++ b/tests/ui/pin-ergonomics/pattern-matching.rs @@ -1,6 +1,5 @@ //@ revisions: pin_ergonomics normal //@ edition:2024 -//@[pin_ergonomics] check-pass #![cfg_attr(pin_ergonomics, feature(pin_ergonomics))] #![feature(if_let_guard, negative_impls)] #![allow(incomplete_features)] @@ -107,4 +106,40 @@ fn pin_mut_slice(foo_mut: Pin<&mut [Foo]>, foo_const: Pin<&[Foo( + (&mut x,): Pin<&'a mut (&'a mut Foo,)>, //[normal]~ ERROR mismatched type +) -> Pin<&'a mut Foo> { + x +} + +fn tuple_ref_mut_pat_and_pin_mut_of_mut_tuple_ty<'a, T, U>( + (&mut x,): Pin<&'a mut &'a mut (Foo,)>, //~ ERROR mismatched type +) -> Pin<&'a mut Foo> { + x +} + +fn ref_mut_tuple_pat_and_pin_mut_of_tuple_mut_ty<'a, T, U>( + &mut (x,): Pin<&'a mut (&'a mut Foo,)>, //~ ERROR mismatched type +) -> Pin<&'a mut Foo> { + x +} + +fn ref_mut_tuple_pat_and_pin_mut_of_mut_tuple_ty<'a, T, U>( + &mut (x,): Pin<&'a mut &'a mut (Foo,)>, //~ ERROR mismatched type +) -> Pin<&'a mut Foo> { + x +} + +fn tuple_pat_and_pin_mut_of_tuple_mut_ty<'a, T, U>( + (x,): Pin<&'a mut (&'a mut Foo,)>, //[normal]~ ERROR mismatched type +) -> Pin<&'a mut &'a mut Foo> { + x // ok +} + +fn tuple_pat_and_pin_mut_of_mut_tuple_ty<'a, T, U>( + (x,): Pin<&'a mut &'a mut (Foo,)>, //[normal]~ ERROR mismatched type +) -> Pin<&'a mut Foo> { + x +} + fn main() {} From b36f15e840be94a3aab523c92a35083053bc96fe Mon Sep 17 00:00:00 2001 From: Frank King Date: Sat, 27 Sep 2025 12:14:43 +0800 Subject: [PATCH 086/108] Add `#[pin_project]` attribute for structurally pinning --- .../rustc_attr_parsing/src/attributes/mod.rs | 1 + .../src/attributes/pin_project.rs | 21 ++ compiler/rustc_attr_parsing/src/context.rs | 2 + compiler/rustc_feature/src/builtin_attrs.rs | 9 + .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir_typeck/messages.ftl | 4 + compiler/rustc_hir_typeck/src/errors.rs | 11 + compiler/rustc_hir_typeck/src/pat.rs | 15 + compiler/rustc_middle/src/ty/adt.rs | 13 + .../rustc_middle/src/ty/typeck_results.rs | 26 -- compiler/rustc_passes/src/check_attr.rs | 3 +- compiler/rustc_span/src/symbol.rs | 1 + .../src/traits/select/mod.rs | 3 +- .../feature-gate-pin_ergonomics.rs | 8 +- .../feature-gate-pin_ergonomics.stderr | 16 +- ...ttern-matching-deref-pattern.normal.stderr | 16 +- .../pattern-matching-deref-pattern.rs | 3 + ...ern-matching-mix-deref-pattern.both.stderr | 86 ++++- ...ng-mix-deref-pattern.deref_patterns.stderr | 36 +- ...n-matching-mix-deref-pattern.normal.stderr | 162 +++++++-- ...ng-mix-deref-pattern.pin_ergonomics.stderr | 86 ++++- .../pattern-matching-mix-deref-pattern.rs | 57 +++- .../pattern-matching.normal.stderr | 60 ++-- .../pattern-matching.pin_ergonomics.stderr | 8 +- tests/ui/pin-ergonomics/pattern-matching.rs | 7 +- tests/ui/pin-ergonomics/pin_project-attr.rs | 144 ++++++++ .../ui/pin-ergonomics/pin_project-attr.stderr | 318 ++++++++++++++++++ 28 files changed, 995 insertions(+), 125 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/pin_project.rs create mode 100644 tests/ui/pin-ergonomics/pin_project-attr.rs create mode 100644 tests/ui/pin-ergonomics/pin_project-attr.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 639c75d7c5e47..207fdb7ccabd7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -48,6 +48,7 @@ pub(crate) mod must_use; pub(crate) mod no_implicit_prelude; pub(crate) mod non_exhaustive; pub(crate) mod path; +pub(crate) mod pin_project; pub(crate) mod proc_macro_attrs; pub(crate) mod prototype; pub(crate) mod repr; diff --git a/compiler/rustc_attr_parsing/src/attributes/pin_project.rs b/compiler/rustc_attr_parsing/src/attributes/pin_project.rs new file mode 100644 index 0000000000000..9a81f82c9f54d --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/pin_project.rs @@ -0,0 +1,21 @@ +use rustc_hir::Target; +use rustc_hir::attrs::AttributeKind; +use rustc_span::{Span, Symbol, sym}; + +use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; +use crate::context::Stage; +use crate::target_checking::AllowedTargets; +use crate::target_checking::Policy::Allow; + +pub(crate) struct PinProjectParser; + +impl NoArgsAttributeParser for PinProjectParser { + const PATH: &[Symbol] = &[sym::pin_project]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Enum), + Allow(Target::Struct), + Allow(Target::Union), + ]); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::PinProject; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6694dac8bb25a..b2bd5ffad8d43 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -47,6 +47,7 @@ use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; use crate::attributes::non_exhaustive::NonExhaustiveParser; use crate::attributes::path::PathParser as PathAttributeParser; +use crate::attributes::pin_project::PinProjectParser; use crate::attributes::proc_macro_attrs::{ ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, }; @@ -233,6 +234,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index a2acac8b3045d..c97c457086a40 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -893,6 +893,15 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::No, loop_match, experimental!(loop_match) ), + // The `#[pin_project]` attribute is part of the `pin_ergonomics` experiment + // that allows structurally pinning, tracked in: + // + // - https://github.com/rust-lang/rust/issues/130494 + gated!( + pin_project, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::Yes, pin_ergonomics, experimental!(pin_project), + ), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 828c15cc391dd..29b4d2d96dd20 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -637,6 +637,9 @@ pub enum AttributeKind { /// Represents `#[pattern_complexity_limit]` PatternComplexityLimit { attr_span: Span, limit_span: Span, limit: Limit }, + /// Represents `#[pin_project]` + PinProject(Span), + /// Represents `#[pointee]` Pointee(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 362ab407aab35..e9a183437693f 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -77,6 +77,7 @@ impl AttributeKind { PassByValue(..) => Yes, Path(..) => No, PatternComplexityLimit { .. } => No, + PinProject(..) => Yes, Pointee(..) => No, ProcMacro(..) => No, ProcMacroAttribute(..) => No, diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 1ed0756fdd6a7..f14441a6363e7 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -227,6 +227,10 @@ hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function .suggestion = cast the value to `{$cast_ty}` .teach_help = certain types, like `{$ty}`, must be cast before passing them to a variadic function to match the implicit cast that a C compiler would perform as part of C's numeric promotion rules +hir_typeck_project_on_non_pin_project_type = cannot project on type that is not `#[pin_project]` + .note = type defined here + .suggestion = add `#[pin_project]` here + hir_typeck_ptr_cast_add_auto_to_object = cannot add {$traits_len -> [1] auto trait {$traits} *[other] auto traits {$traits} diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index d15d092b7d3da..34e0c9f0f68a6 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1156,3 +1156,14 @@ pub(crate) struct ConstContinueBadLabel { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(hir_typeck_project_on_non_pin_project_type)] +pub(crate) struct ProjectOnNonPinProjectType { + #[primary_span] + pub span: Span, + #[note] + pub def_span: Option, + #[suggestion(code = "#[pin_project]\n", applicability = "machine-applicable")] + pub sugg_span: Option, +} diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 12db66155f8a2..d3867bda6fa0a 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -554,6 +554,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { debug!("scrutinee ty {expected:?} is a pinned reference, inserting pin deref"); + // if the inner_ty is an ADT, make sure that it can be structurally pinned + // (i.e., it is `#[pin_project]`). + if let Some(adt) = inner_ty.ty_adt_def() + && !adt.is_pin_project() + && !adt.is_pin() + { + let def_span: Option = self.tcx.hir_span_if_local(adt.did()); + let sugg_span = def_span.map(|span| span.shrink_to_lo()); + self.dcx().emit_err(crate::errors::ProjectOnNonPinProjectType { + span: pat.span, + def_span, + sugg_span, + }); + } + let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability); // If the pinnedness is `Not`, it means the pattern is unpinned // and thus requires an `Unpin` bound. diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index be909a08e8dae..8386504245cde 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -57,6 +57,8 @@ bitflags::bitflags! { const IS_UNSAFE_PINNED = 1 << 10; /// Indicates whether the type is `Pin`. const IS_PIN = 1 << 11; + /// Indicates whether the type is `#[pin_project]`. + const IS_PIN_PROJECT = 1 << 12; } } rustc_data_structures::external_bitflags_debug! { AdtFlags } @@ -286,6 +288,10 @@ impl AdtDefData { debug!("found non-exhaustive variant list for {:?}", did); flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; } + if find_attr!(tcx.get_all_attrs(did), AttributeKind::PinProject(..)) { + debug!("found pin-project type {:?}", did); + flags |= AdtFlags::IS_PIN_PROJECT; + } flags |= match kind { AdtKind::Enum => AdtFlags::IS_ENUM, @@ -439,6 +445,13 @@ impl<'tcx> AdtDef<'tcx> { self.flags().contains(AdtFlags::IS_PIN) } + /// Returns `true` is this is `#[pin_project]` for the purposes + /// of structural pinning. + #[inline] + pub fn is_pin_project(self) -> bool { + self.flags().contains(AdtFlags::IS_PIN_PROJECT) + } + /// Returns `true` if this type has a destructor. pub fn has_dtor(self, tcx: TyCtxt<'tcx>) -> bool { self.destructor(tcx).is_some() diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 06e9e28d0fdc8..6a6ed702373d1 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -493,32 +493,6 @@ impl<'tcx> TypeckResults<'tcx> { has_ref_mut } - /// Visits the pattern recursively whether it contains a `ref pin` binding - /// of non-`Unpin` type in it. - /// - /// This is used to determined whether a `&pin` pattern should emit a `!Unpin` - /// call for its pattern scrutinee. - /// - /// This is computed from the typeck results since we want to make - /// sure to apply any match-ergonomics adjustments, which we cannot - /// determine from the HIR alone. - pub fn pat_walk_ref_pin_binding_of_non_unpin_type<'a>( - &self, - pat: &hir::Pat<'_>, - mut ty_visitor: impl FnMut(Ty<'tcx>) + 'a, - ) { - pat.walk(|pat| { - if let hir::PatKind::Binding(_, id, _, _) = pat.kind - && let Some(BindingMode(ByRef::Yes(Pinnedness::Pinned, _), _)) = - self.pat_binding_modes().get(id) - { - let ty = self.pat_ty(pat); - ty_visitor(ty); - } - true - }); - } - /// How should a deref pattern find the place for its inner pattern to match on? /// /// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ef42c42f68b37..3cd1d5ba61b48 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -283,7 +283,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::ObjcSelector { .. } | AttributeKind::RustcCoherenceIsCore(..) | AttributeKind::DebuggerVisualizer(..) - | AttributeKind::RustcMain, + | AttributeKind::RustcMain + | AttributeKind::PinProject(..), ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 223d818a2949b..c378927ded895 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1667,6 +1667,7 @@ symbols! { pin, pin_ergonomics, pin_macro, + pin_project, platform_intrinsics, plugin, plugin_registrar, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index ed63949ee022e..ea903bac9d617 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2466,8 +2466,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ty::TraitRef::new_from_args(tcx, trait_def_id, err_args) }; - let obligation = - Obligation::new(self.tcx(), cause.clone(), param_env, trait_ref); + let obligation = Obligation::new(self.tcx(), cause.clone(), param_env, trait_ref); obligations.push(obligation); obligations }) diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs index 7746654555dd8..47c4b36a07703 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs @@ -2,11 +2,11 @@ use std::pin::Pin; +#[pin_project] //~ ERROR the `#[pin_project]` attribute is an experimental feature struct Foo; impl Foo { - fn foo(self: Pin<&mut Self>) { - } + fn foo(self: Pin<&mut Self>) {} fn foo_sugar(&pin mut self) {} //~ ERROR pinned reference syntax is experimental fn foo_sugar_const(&pin const self) {} //~ ERROR pinned reference syntax is experimental } @@ -51,11 +51,11 @@ fn borrows() { mod not_compiled { use std::pin::Pin; + #[pin_project] struct Foo; impl Foo { - fn foo(self: Pin<&mut Self>) { - } + fn foo(self: Pin<&mut Self>) {} fn foo_sugar(&pin mut self) {} //~ ERROR pinned reference syntax is experimental fn foo_sugar_const(&pin const self) {} //~ ERROR pinned reference syntax is experimental } diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr index a8890254facea..3952ba78d3c17 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr @@ -148,6 +148,16 @@ LL | let x: Pin<&_> = &pin const Foo; = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: the `#[pin_project]` attribute is an experimental feature + --> $DIR/feature-gate-pin_ergonomics.rs:5:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` 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[E0382]: use of moved value: `x` --> $DIR/feature-gate-pin_ergonomics.rs:28:9 | @@ -177,16 +187,16 @@ LL | x.foo(); | ^ value used here after move | note: `Foo::foo` takes ownership of the receiver `self`, which moves `x` - --> $DIR/feature-gate-pin_ergonomics.rs:8:12 + --> $DIR/feature-gate-pin_ergonomics.rs:9:12 | -LL | fn foo(self: Pin<&mut Self>) { +LL | fn foo(self: Pin<&mut Self>) {} | ^^^^ help: consider reborrowing the `Pin` instead of moving it | LL | x.as_mut().foo(); | +++++++++ -error: aborting due to 17 previous errors +error: aborting due to 18 previous errors Some errors have detailed explanations: E0382, E0658. For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.normal.stderr index 559465bc13aba..96bac039df723 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.normal.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.normal.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of a shared reference - --> $DIR/pattern-matching-deref-pattern.rs:45:28 + --> $DIR/pattern-matching-deref-pattern.rs:48:28 | LL | let Foo { x, y } = foo.as_mut(); | - - ^^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | let Foo { x, ref y } = foo.as_mut(); | +++ error[E0507]: cannot move out of a shared reference - --> $DIR/pattern-matching-deref-pattern.rs:35:24 + --> $DIR/pattern-matching-deref-pattern.rs:38:24 | LL | let Foo { x, y } = foo.as_mut(); | - - ^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | let Foo { x, ref y } = foo.as_mut(); | +++ error[E0507]: cannot move out of a shared reference - --> $DIR/pattern-matching-deref-pattern.rs:67:28 + --> $DIR/pattern-matching-deref-pattern.rs:70:28 | LL | let Foo { x, y } = foo.as_ref(); | - - ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let Foo { x, ref y } = foo.as_ref(); | +++ error[E0507]: cannot move out of a shared reference - --> $DIR/pattern-matching-deref-pattern.rs:57:24 + --> $DIR/pattern-matching-deref-pattern.rs:60:24 | LL | let Foo { x, y } = foo.as_ref(); | - - ^^^^^^^^^^^^ @@ -75,7 +75,7 @@ LL | let Foo { x, ref y } = foo.as_ref(); | +++ error[E0507]: cannot move out of a shared reference - --> $DIR/pattern-matching-deref-pattern.rs:89:25 + --> $DIR/pattern-matching-deref-pattern.rs:92:25 | LL | let Bar(x, y) = bar.as_mut(); | - - ^^^^^^^^^^^^ @@ -94,7 +94,7 @@ LL | let Bar(x, ref y) = bar.as_mut(); | +++ error[E0507]: cannot move out of a shared reference - --> $DIR/pattern-matching-deref-pattern.rs:79:21 + --> $DIR/pattern-matching-deref-pattern.rs:82:21 | LL | let Bar(x, y) = bar.as_mut(); | - - ^^^^^^^^^^^^ @@ -113,7 +113,7 @@ LL | let Bar(x, ref y) = bar.as_mut(); | +++ error[E0507]: cannot move out of a shared reference - --> $DIR/pattern-matching-deref-pattern.rs:111:25 + --> $DIR/pattern-matching-deref-pattern.rs:114:25 | LL | let Bar(x, y) = bar.as_ref(); | - - ^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | let Bar(x, ref y) = bar.as_ref(); | +++ error[E0507]: cannot move out of a shared reference - --> $DIR/pattern-matching-deref-pattern.rs:101:21 + --> $DIR/pattern-matching-deref-pattern.rs:104:21 | LL | let Bar(x, y) = bar.as_ref(); | - - ^^^^^^^^^^^^ diff --git a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs index 6ecb22e535b13..26f52a0c79cf6 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs +++ b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs @@ -10,13 +10,16 @@ use std::pin::Pin; +#[cfg_attr(pin_ergonomics, pin_project)] struct Foo { x: T, y: U, } +#[cfg_attr(pin_ergonomics, pin_project)] struct Bar(T, U); +#[cfg_attr(pin_ergonomics, pin_project)] enum Baz { Foo(T, U), Bar { x: T, y: U }, diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr index 3e883fddb7a6b..cc3be291580bb 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr @@ -1,5 +1,73 @@ +error: cannot project on type that is not `#[pin_project]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:121:9 + | +LL | let NonPinProject { x } = foo; + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_project]` here + | +LL + #[pin_project] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_project]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:125:9 + | +LL | let NonPinProject { x } = bar; + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_project]` here + | +LL + #[pin_project] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_project]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:131:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_project]` here + | +LL + #[pin_project] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_project]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:139:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_project]` here + | +LL + #[pin_project] +LL | struct NonPinProject { + | + error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:27:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:37:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` @@ -8,7 +76,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:35:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:45:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` @@ -17,7 +85,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:46:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:59:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` @@ -26,7 +94,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:54:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:67:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` @@ -35,7 +103,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:65:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:81:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` @@ -44,7 +112,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:73:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:89:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` @@ -53,7 +121,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:84:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:103:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` @@ -62,7 +130,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:92:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:111:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` @@ -70,5 +138,5 @@ LL | Bar(x, y) => {} LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Bar>` -error: aborting due to 8 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.deref_patterns.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.deref_patterns.stderr index 3e883fddb7a6b..fa2e418591c1f 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.deref_patterns.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.deref_patterns.stderr @@ -1,5 +1,5 @@ error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:27:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:37:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` @@ -8,7 +8,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:35:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:45:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` @@ -17,7 +17,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:46:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:59:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` @@ -26,7 +26,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:54:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:67:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` @@ -35,7 +35,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:65:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:81:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` @@ -44,7 +44,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:73:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:89:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` @@ -53,7 +53,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:84:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:103:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` @@ -62,7 +62,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:92:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:111:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` @@ -70,5 +70,23 @@ LL | Bar(x, y) => {} LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Bar>` -error: aborting due to 8 previous errors +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:131:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut NonPinProject>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&mut NonPinProject>` + +error: mix of deref patterns and normal constructors + --> $DIR/pattern-matching-mix-deref-pattern.rs:139:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&NonPinProject>` +... +LL | Pin { .. } => {} + | ^^^^^^^^^^ matches directly on `Pin<&NonPinProject>` + +error: aborting due to 10 previous errors diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr index a39d7d79916c5..0e55a51702842 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.normal.stderr @@ -1,8 +1,23 @@ error[E0308]: mismatched types - --> $DIR/pattern-matching-mix-deref-pattern.rs:27:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:33:9 | -LL | match foo { - | --- this expression has type `Pin<&mut Foo>` +LL | let Foo { x, y } = foo.as_mut(); + | ^^^^^^^^^^^^ ------------ this expression has type `Pin<&mut Foo>` + | | + | expected `Pin<&mut Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&mut Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Foo { x, y } = *foo.as_mut(); + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:37:9 + | +LL | match foo.as_mut() { + | ------------ this expression has type `Pin<&mut Foo>` LL | Foo { x, y } => {} | ^^^^^^^^^^^^ expected `Pin<&mut Foo>`, found `Foo<_, _>` | @@ -10,14 +25,14 @@ LL | Foo { x, y } => {} found struct `Foo<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | match *foo { +LL | match *foo.as_mut() { | + error[E0308]: mismatched types - --> $DIR/pattern-matching-mix-deref-pattern.rs:35:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:45:9 | -LL | let _ = || match foo { - | --- this expression has type `Pin<&mut Foo>` +LL | let _ = || match foo.as_mut() { + | ------------ this expression has type `Pin<&mut Foo>` LL | Foo { x, y } => {} | ^^^^^^^^^^^^ expected `Pin<&mut Foo>`, found `Foo<_, _>` | @@ -25,11 +40,26 @@ LL | Foo { x, y } => {} found struct `Foo<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | let _ = || match *foo { +LL | let _ = || match *foo.as_mut() { | + error[E0308]: mismatched types - --> $DIR/pattern-matching-mix-deref-pattern.rs:46:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:55:9 + | +LL | let Foo { x, y } = foo; + | ^^^^^^^^^^^^ --- this expression has type `Pin<&Foo>` + | | + | expected `Pin<&Foo>`, found `Foo<_, _>` + | + = note: expected struct `Pin<&Foo>` + found struct `Foo<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Foo { x, y } = *foo; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:59:9 | LL | match foo { | --- this expression has type `Pin<&Foo>` @@ -44,7 +74,7 @@ LL | match *foo { | + error[E0308]: mismatched types - --> $DIR/pattern-matching-mix-deref-pattern.rs:54:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:67:9 | LL | let _ = || match foo { | --- this expression has type `Pin<&Foo>` @@ -59,10 +89,25 @@ LL | let _ = || match *foo { | + error[E0308]: mismatched types - --> $DIR/pattern-matching-mix-deref-pattern.rs:65:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:77:9 | -LL | match bar { - | --- this expression has type `Pin<&mut Bar>` +LL | let Bar(x, y) = bar.as_mut(); + | ^^^^^^^^^ ------------ this expression has type `Pin<&mut Bar>` + | | + | expected `Pin<&mut Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&mut Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Bar(x, y) = *bar.as_mut(); + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:81:9 + | +LL | match bar.as_mut() { + | ------------ this expression has type `Pin<&mut Bar>` LL | Bar(x, y) => {} | ^^^^^^^^^ expected `Pin<&mut Bar>`, found `Bar<_, _>` | @@ -70,14 +115,14 @@ LL | Bar(x, y) => {} found struct `Bar<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | match *bar { +LL | match *bar.as_mut() { | + error[E0308]: mismatched types - --> $DIR/pattern-matching-mix-deref-pattern.rs:73:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:89:9 | -LL | let _ = || match bar { - | --- this expression has type `Pin<&mut Bar>` +LL | let _ = || match bar.as_mut() { + | ------------ this expression has type `Pin<&mut Bar>` LL | Bar(x, y) => {} | ^^^^^^^^^ expected `Pin<&mut Bar>`, found `Bar<_, _>` | @@ -85,11 +130,26 @@ LL | Bar(x, y) => {} found struct `Bar<_, _>` help: consider dereferencing to access the inner value using the Deref trait | -LL | let _ = || match *bar { +LL | let _ = || match *bar.as_mut() { | + error[E0308]: mismatched types - --> $DIR/pattern-matching-mix-deref-pattern.rs:84:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:99:9 + | +LL | let Bar(x, y) = bar; + | ^^^^^^^^^ --- this expression has type `Pin<&Bar>` + | | + | expected `Pin<&Bar>`, found `Bar<_, _>` + | + = note: expected struct `Pin<&Bar>` + found struct `Bar<_, _>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let Bar(x, y) = *bar; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:103:9 | LL | match bar { | --- this expression has type `Pin<&Bar>` @@ -104,7 +164,7 @@ LL | match *bar { | + error[E0308]: mismatched types - --> $DIR/pattern-matching-mix-deref-pattern.rs:92:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:111:9 | LL | let _ = || match bar { | --- this expression has type `Pin<&Bar>` @@ -118,6 +178,66 @@ help: consider dereferencing to access the inner value using the Deref trait LL | let _ = || match *bar { | + -error: aborting due to 8 previous errors +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:121:9 + | +LL | let NonPinProject { x } = foo; + | ^^^^^^^^^^^^^^^^^^^ --- this expression has type `Pin<&mut NonPinProject>` + | | + | expected `Pin<&mut NonPinProject>`, found `NonPinProject<_>` + | + = note: expected struct `Pin<&mut NonPinProject>` + found struct `NonPinProject<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let NonPinProject { x } = *foo; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:125:9 + | +LL | let NonPinProject { x } = bar; + | ^^^^^^^^^^^^^^^^^^^ --- this expression has type `Pin<&NonPinProject>` + | | + | expected `Pin<&NonPinProject>`, found `NonPinProject<_>` + | + = note: expected struct `Pin<&NonPinProject>` + found struct `NonPinProject<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | let NonPinProject { x } = *bar; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:131:9 + | +LL | match foo { + | --- this expression has type `Pin<&mut NonPinProject>` +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ expected `Pin<&mut NonPinProject>`, found `NonPinProject<_>` + | + = note: expected struct `Pin<&mut NonPinProject>` + found struct `NonPinProject<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *foo { + | + + +error[E0308]: mismatched types + --> $DIR/pattern-matching-mix-deref-pattern.rs:139:9 + | +LL | match bar { + | --- this expression has type `Pin<&NonPinProject>` +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ expected `Pin<&NonPinProject>`, found `NonPinProject<_>` + | + = note: expected struct `Pin<&NonPinProject>` + found struct `NonPinProject<_>` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *bar { + | + + +error: aborting due to 16 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr index 3e883fddb7a6b..cc3be291580bb 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr @@ -1,5 +1,73 @@ +error: cannot project on type that is not `#[pin_project]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:121:9 + | +LL | let NonPinProject { x } = foo; + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_project]` here + | +LL + #[pin_project] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_project]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:125:9 + | +LL | let NonPinProject { x } = bar; + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_project]` here + | +LL + #[pin_project] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_project]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:131:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_project]` here + | +LL + #[pin_project] +LL | struct NonPinProject { + | + +error: cannot project on type that is not `#[pin_project]` + --> $DIR/pattern-matching-mix-deref-pattern.rs:139:9 + | +LL | NonPinProject { x } => {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/pattern-matching-mix-deref-pattern.rs:28:1 + | +LL | struct NonPinProject { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_project]` here + | +LL + #[pin_project] +LL | struct NonPinProject { + | + error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:27:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:37:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` @@ -8,7 +76,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:35:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:45:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Foo>` @@ -17,7 +85,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:46:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:59:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` @@ -26,7 +94,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:54:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:67:9 | LL | Foo { x, y } => {} | ^^^^^^^^^^^^ matches on the result of dereferencing `Pin<&Foo>` @@ -35,7 +103,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Foo>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:65:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:81:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` @@ -44,7 +112,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:73:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:89:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&mut Bar>` @@ -53,7 +121,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&mut Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:84:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:103:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` @@ -62,7 +130,7 @@ LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Bar>` error: mix of deref patterns and normal constructors - --> $DIR/pattern-matching-mix-deref-pattern.rs:92:9 + --> $DIR/pattern-matching-mix-deref-pattern.rs:111:9 | LL | Bar(x, y) => {} | ^^^^^^^^^ matches on the result of dereferencing `Pin<&Bar>` @@ -70,5 +138,5 @@ LL | Bar(x, y) => {} LL | Pin { .. } => {} | ^^^^^^^^^^ matches directly on `Pin<&Bar>` -error: aborting due to 8 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs index 8945205bea176..691d5d51cafcd 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs @@ -10,20 +10,30 @@ use std::pin::Pin; +#[cfg_attr(any(pin_ergonomics, both), pin_project)] struct Foo { x: T, y: U, } +#[cfg_attr(any(pin_ergonomics, both), pin_project)] struct Bar(T, U); +#[cfg_attr(any(pin_ergonomics, both), pin_project)] enum Baz { Foo(T, U), Bar { x: T, y: U }, } -fn foo_mut(foo: Pin<&mut Foo>) { - match foo { +struct NonPinProject { + x: T, +} + +fn foo_mut(mut foo: Pin<&mut Foo>) { + let Foo { x, y } = foo.as_mut(); + //[normal]~^ ERROR mismatched types + + match foo.as_mut() { Foo { x, y } => {} //[normal]~^ ERROR mismatched types //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors @@ -31,7 +41,7 @@ fn foo_mut(foo: Pin<&mut Foo>) { //[both]~^^^^ ERROR mix of deref patterns and normal constructors Pin { .. } => {} } - let _ = || match foo { + let _ = || match foo.as_mut() { Foo { x, y } => {} //[normal]~^ ERROR mismatched types //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors @@ -42,6 +52,9 @@ fn foo_mut(foo: Pin<&mut Foo>) { } fn foo_const(foo: Pin<&Foo>) { + let Foo { x, y } = foo; + //[normal]~^ ERROR mismatched types + match foo { Foo { x, y } => {} //[normal]~^ ERROR mismatched types @@ -61,7 +74,10 @@ fn foo_const(foo: Pin<&Foo>) { } fn bar_mut(bar: Pin<&mut Bar>) { - match bar { + let Bar(x, y) = bar.as_mut(); + //[normal]~^ ERROR mismatched types + + match bar.as_mut() { Bar(x, y) => {} //[normal]~^ ERROR mismatched types //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors @@ -69,7 +85,7 @@ fn bar_mut(bar: Pin<&mut Bar>) { //[both]~^^^^ ERROR mix of deref patterns and normal constructors Pin { .. } => {} } - let _ = || match bar { + let _ = || match bar.as_mut() { Bar(x, y) => {} //[normal]~^ ERROR mismatched types //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors @@ -80,6 +96,9 @@ fn bar_mut(bar: Pin<&mut Bar>) { } fn bar_const(bar: Pin<&Bar>) { + let Bar(x, y) = bar; + //[normal]~^ ERROR mismatched types + match bar { Bar(x, y) => {} //[normal]~^ ERROR mismatched types @@ -98,4 +117,32 @@ fn bar_const(bar: Pin<&Bar>) { }; } +fn non_pin_project(foo: Pin<&mut NonPinProject>, bar: Pin<&NonPinProject>) { + let NonPinProject { x } = foo; + //[normal]~^ ERROR mismatched types + //[pin_ergonomics]~^^ ERROR cannot project on type that is not `#[pin_project]` + //[both]~^^^ ERROR cannot project on type that is not `#[pin_project]` + let NonPinProject { x } = bar; + //[normal]~^ ERROR mismatched types + //[pin_ergonomics]~^^ ERROR cannot project on type that is not `#[pin_project]` + //[both]~^^^ ERROR cannot project on type that is not `#[pin_project]` + + match foo { + NonPinProject { x } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR cannot project on type that is not `#[pin_project]` + //[both]~^^^^ ERROR cannot project on type that is not `#[pin_project]` + Pin { .. } => {} + } + match bar { + NonPinProject { x } => {} + //[normal]~^ ERROR mismatched types + //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors + //[pin_ergonomics]~^^^ ERROR cannot project on type that is not `#[pin_project]` + //[both]~^^^^ ERROR cannot project on type that is not `#[pin_project]` + Pin { .. } => {} + } +} + fn main() {} diff --git a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr index 26f057195a15e..5d2d133761c43 100644 --- a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr @@ -1,5 +1,25 @@ +error[E0658]: the `#[pin_project]` attribute is an experimental feature + --> $DIR/pattern-matching.rs:12:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `#[pin_project]` attribute is an experimental feature + --> $DIR/pattern-matching.rs:18:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` 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[E0308]: mismatched types - --> $DIR/pattern-matching.rs:34:9 + --> $DIR/pattern-matching.rs:33:9 | LL | let Foo { x, y } = foo_mut; | ^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Foo>` @@ -14,7 +34,7 @@ LL | let Foo { x, y } = *foo_mut; | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:39:9 + --> $DIR/pattern-matching.rs:38:9 | LL | let Foo { x, y } = foo_const; | ^^^^^^^^^^^^ --------- this expression has type `Pin<&Foo>` @@ -29,7 +49,7 @@ LL | let Foo { x, y } = *foo_const; | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:47:9 + --> $DIR/pattern-matching.rs:46:9 | LL | match bar_mut { | ------- this expression has type `Pin<&mut Bar>` @@ -44,7 +64,7 @@ LL | match *bar_mut { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:52:18 + --> $DIR/pattern-matching.rs:51:18 | LL | _ if let Bar::Bar { x, y } = bar_mut => { | ^^^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut Bar>` @@ -59,7 +79,7 @@ LL | _ if let Bar::Bar { x, y } = *bar_mut => { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:60:9 + --> $DIR/pattern-matching.rs:59:9 | LL | match bar_const { | --------- this expression has type `Pin<&Bar>` @@ -74,7 +94,7 @@ LL | match *bar_const { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:65:18 + --> $DIR/pattern-matching.rs:64:18 | LL | _ if let Bar::Foo(x, y) = bar_const => { | ^^^^^^^^^^^^^^ --------- this expression has type `Pin<&Bar>` @@ -89,7 +109,7 @@ LL | _ if let Bar::Foo(x, y) = *bar_const => { | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:75:9 + --> $DIR/pattern-matching.rs:74:9 | LL | let (Foo { x, y },) = foo_mut; | ^^^^^^^^^^^^^^^ ------- this expression has type `Pin<&mut (Foo,)>` @@ -104,7 +124,7 @@ LL | let (Foo { x, y },) = *foo_mut; | + error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:79:9 + --> $DIR/pattern-matching.rs:78:9 | LL | let (Foo { x, y },) = foo_const; | ^^^^^^^^^^^^^^^ --------- this expression has type `Pin<&(Foo,)>` @@ -119,31 +139,31 @@ LL | let (Foo { x, y },) = *foo_const; | + error[E0529]: expected an array or slice, found `Pin<&mut [Foo; 1]>` - --> $DIR/pattern-matching.rs:86:9 + --> $DIR/pattern-matching.rs:85:9 | LL | let [Foo { x, y }] = foo_mut; | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [Foo; 1]>` error[E0529]: expected an array or slice, found `Pin<&[Foo; 1]>` - --> $DIR/pattern-matching.rs:90:9 + --> $DIR/pattern-matching.rs:89:9 | LL | let [Foo { x, y }] = foo_const; | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[Foo; 1]>` error[E0529]: expected an array or slice, found `Pin<&mut [Foo]>` - --> $DIR/pattern-matching.rs:97:12 + --> $DIR/pattern-matching.rs:96:12 | LL | if let [Foo { x, y }] = foo_mut { | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&mut [Foo]>` error[E0529]: expected an array or slice, found `Pin<&[Foo]>` - --> $DIR/pattern-matching.rs:102:12 + --> $DIR/pattern-matching.rs:101:12 | LL | if let [Foo { x, y }] = foo_const { | ^^^^^^^^^^^^^^ pattern cannot match with input type `Pin<&[Foo]>` error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:110:5 + --> $DIR/pattern-matching.rs:109:5 | LL | (&mut x,): Pin<&'a mut (&'a mut Foo,)>, | ^^^^^^^^^ --------------------------------- expected due to this @@ -154,7 +174,7 @@ LL | (&mut x,): Pin<&'a mut (&'a mut Foo,)>, found tuple `(_,)` error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:116:5 + --> $DIR/pattern-matching.rs:115:5 | LL | (&mut x,): Pin<&'a mut &'a mut (Foo,)>, | ^^^^^^^^^ --------------------------------- expected due to this @@ -165,7 +185,7 @@ LL | (&mut x,): Pin<&'a mut &'a mut (Foo,)>, found tuple `(_,)` error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:122:5 + --> $DIR/pattern-matching.rs:121:5 | LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>, | ^^^^^^^^^ --------------------------------- expected due to this @@ -180,7 +200,7 @@ LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>.pointer, | ++++++++ error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:128:5 + --> $DIR/pattern-matching.rs:127:5 | LL | &mut (x,): Pin<&'a mut &'a mut (Foo,)>, | ^^^^^^^^^ --------------------------------- expected due to this @@ -195,7 +215,7 @@ LL | &mut (x,): Pin<&'a mut &'a mut (Foo,)>.pointer, | ++++++++ error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:134:5 + --> $DIR/pattern-matching.rs:133:5 | LL | (x,): Pin<&'a mut (&'a mut Foo,)>, | ^^^^ --------------------------------- expected due to this @@ -206,7 +226,7 @@ LL | (x,): Pin<&'a mut (&'a mut Foo,)>, found tuple `(_,)` error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:140:5 + --> $DIR/pattern-matching.rs:139:5 | LL | (x,): Pin<&'a mut &'a mut (Foo,)>, | ^^^^ --------------------------------- expected due to this @@ -216,7 +236,7 @@ LL | (x,): Pin<&'a mut &'a mut (Foo,)>, = note: expected struct `Pin<&'a mut &'a mut (Foo,)>` found tuple `(_,)` -error: aborting due to 18 previous errors +error: aborting due to 20 previous errors -Some errors have detailed explanations: E0308, E0529. +Some errors have detailed explanations: E0308, E0529, E0658. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pattern-matching.pin_ergonomics.stderr b/tests/ui/pin-ergonomics/pattern-matching.pin_ergonomics.stderr index fd442b8263c05..8cb032b73d859 100644 --- a/tests/ui/pin-ergonomics/pattern-matching.pin_ergonomics.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching.pin_ergonomics.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:116:6 + --> $DIR/pattern-matching.rs:115:6 | LL | (&mut x,): Pin<&'a mut &'a mut (Foo,)>, | ^^^^^^ --------------------------------- expected due to this @@ -9,7 +9,7 @@ LL | (&mut x,): Pin<&'a mut &'a mut (Foo,)>, = note: expected struct `Foo` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-matching.rs:116:6 + --> $DIR/pattern-matching.rs:115:6 | LL | (&mut x,): Pin<&'a mut &'a mut (Foo,)>, | ^^^^^^ @@ -20,7 +20,7 @@ LL + (x,): Pin<&'a mut &'a mut (Foo,)>, | error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:122:5 + --> $DIR/pattern-matching.rs:121:5 | LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>, | ^^^^^^^^^ --------------------------------- expected due to this @@ -35,7 +35,7 @@ LL | &mut (x,): Pin<&'a mut (&'a mut Foo,)>.pointer, | ++++++++ error[E0308]: mismatched types - --> $DIR/pattern-matching.rs:128:5 + --> $DIR/pattern-matching.rs:127:5 | LL | &mut (x,): Pin<&'a mut &'a mut (Foo,)>, | ^^^^^^^^^ --------------------------------- expected due to this diff --git a/tests/ui/pin-ergonomics/pattern-matching.rs b/tests/ui/pin-ergonomics/pattern-matching.rs index 4f2a23afbb8ff..8798e2ed7402e 100644 --- a/tests/ui/pin-ergonomics/pattern-matching.rs +++ b/tests/ui/pin-ergonomics/pattern-matching.rs @@ -7,16 +7,15 @@ use std::pin::Pin; // This test verifies that a `&pin mut T` can be projected to a pinned -// reference field `&pin mut T.U`. -// FIXME(pin_ergonomics): it is unsound to project `&pin mut T` to -// `&pin mut T.U` when `U: !Unpin` but `T: Unpin`, or when there exists -// `impl Drop for T` that takes a `&mut self` receiver. +// reference field `&pin mut T.U` when `T` is marked with `#[pin_project]`. +#[pin_project] //[normal]~ ERROR the `#[pin_project]` attribute is an experimental feature struct Foo { x: T, y: U, } +#[pin_project] //[normal]~ ERROR the `#[pin_project]` attribute is an experimental feature enum Bar { Foo(T, U), Bar { x: T, y: U }, diff --git a/tests/ui/pin-ergonomics/pin_project-attr.rs b/tests/ui/pin-ergonomics/pin_project-attr.rs new file mode 100644 index 0000000000000..be9bcc34d4947 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin_project-attr.rs @@ -0,0 +1,144 @@ +#![feature( + pin_ergonomics, + where_clause_attrs, + trait_alias, + extern_types, + associated_type_defaults, + fn_delegation, +)] +#![allow(incomplete_features)] +#![pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on crates + +// allowed + +#[pin_project] +struct Struct {} + +#[pin_project] +enum Enum {} + +#[pin_project] +union Union { + field: (), +} + +// disallowed + +enum Foo<#[pin_project] T, #[pin_project] U = ()> { + //~^ ERROR `#[pin_project]` attribute cannot be used on function params + //~| ERROR `#[pin_project]` attribute cannot be used on function params + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on enum variants + UnitVariant, + TupleVariant(#[pin_project] T), //~ ERROR `#[pin_project]` attribute cannot be used on struct fields + StructVariant { + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on struct fields + field: U, + }, +} + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on traits +trait Trait { + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on associated consts + const ASSOC_CONST: () = (); + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on associated types + type AssocType = (); + + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on required trait methods + fn method(); + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on provided trait methods + fn method_with_body() {} +} + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on trait aliases +trait TraitAlias = Trait; + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on inherent impl blocks +impl Struct { + // FIXME: delegation macros are not tested yet (how to?) + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on delegations + reuse ::type_id; + + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on inherent methods + fn method() {} +} + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on trait impl blocks +impl Trait for Enum { + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on trait methods in impl blocks + fn method() {} +} + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on extern crates +extern crate alloc; + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on use statements +use std::pin::Pin; + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on statics +static STATIC: () = (); + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on constants +const CONST: () = (); + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on functions +fn f(#[pin_project] param: Foo) +//~^ ERROR `#[pin_project]` attribute cannot be used on function params +//~| ERROR allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters +where + #[pin_project] + //~^ ERROR `#[pin_project]` attribute cannot be used on where predicates + T:, +{ + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on closures + || (); + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on expressions + [(), (), ()]; + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on statements + let _: Foo<(), ()> = Foo::StructVariant { + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on struct fields + field: (), + }; + match param { + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on match arms + Foo::UnitVariant => {} + Foo::TupleVariant(..) => {} + Foo::StructVariant { + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on pattern fields + field, + } => {} + } +} + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on modules +mod m {} + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on foreign modules +extern "C" { + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on foreign types + type ForeignTy; + + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on foreign statics + static EXTERN_STATIC: (); + + #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on foreign functions + fn extern_fn(); +} + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on type alias +type Type = (); + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on macro defs +macro_rules! macro_def { + () => {}; +} + +#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on macro calls +macro_def!(); + +std::arch::global_asm! { + "{}", + #[pin_project] //~ ERROR this attribute is not supported on assembly + const 0 +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pin_project-attr.stderr b/tests/ui/pin-ergonomics/pin_project-attr.stderr new file mode 100644 index 0000000000000..aa1c376401ba7 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin_project-attr.stderr @@ -0,0 +1,318 @@ +error: `#[pin_project]` attribute cannot be used on macro calls + --> $DIR/pin_project-attr.rs:135:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: this attribute is not supported on assembly + --> $DIR/pin_project-attr.rs:140:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + +error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters + --> $DIR/pin_project-attr.rs:84:12 + | +LL | fn f(#[pin_project] param: Foo) + | ^^^^^^^^^^^^^^ + +error: `#[pin_project]` attribute cannot be used on crates + --> $DIR/pin_project-attr.rs:10:1 + | +LL | #![pin_project] + | ^^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on function params + --> $DIR/pin_project-attr.rs:27:10 + | +LL | enum Foo<#[pin_project] T, #[pin_project] U = ()> { + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on function params + --> $DIR/pin_project-attr.rs:27:28 + | +LL | enum Foo<#[pin_project] T, #[pin_project] U = ()> { + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on enum variants + --> $DIR/pin_project-attr.rs:30:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on struct fields + --> $DIR/pin_project-attr.rs:32:18 + | +LL | TupleVariant(#[pin_project] T), + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on struct fields + --> $DIR/pin_project-attr.rs:34:9 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on traits + --> $DIR/pin_project-attr.rs:39:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on associated consts + --> $DIR/pin_project-attr.rs:41:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on associated types + --> $DIR/pin_project-attr.rs:43:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on required trait methods + --> $DIR/pin_project-attr.rs:46:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on provided trait methods + --> $DIR/pin_project-attr.rs:48:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on trait aliases + --> $DIR/pin_project-attr.rs:52:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on inherent impl blocks + --> $DIR/pin_project-attr.rs:55:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on delegations + --> $DIR/pin_project-attr.rs:58:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on inherent methods + --> $DIR/pin_project-attr.rs:61:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on trait impl blocks + --> $DIR/pin_project-attr.rs:65:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on trait methods in impl blocks + --> $DIR/pin_project-attr.rs:67:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on extern crates + --> $DIR/pin_project-attr.rs:71:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on use statements + --> $DIR/pin_project-attr.rs:74:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on statics + --> $DIR/pin_project-attr.rs:77:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on constants + --> $DIR/pin_project-attr.rs:80:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on functions + --> $DIR/pin_project-attr.rs:83:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on function params + --> $DIR/pin_project-attr.rs:84:12 + | +LL | fn f(#[pin_project] param: Foo) + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on closures + --> $DIR/pin_project-attr.rs:92:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on expressions + --> $DIR/pin_project-attr.rs:94:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on struct fields + --> $DIR/pin_project-attr.rs:98:9 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on statements + --> $DIR/pin_project-attr.rs:96:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on match arms + --> $DIR/pin_project-attr.rs:102:9 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on pattern fields + --> $DIR/pin_project-attr.rs:106:13 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on where predicates + --> $DIR/pin_project-attr.rs:88:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on modules + --> $DIR/pin_project-attr.rs:112:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on foreign modules + --> $DIR/pin_project-attr.rs:115:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on foreign types + --> $DIR/pin_project-attr.rs:117:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on foreign statics + --> $DIR/pin_project-attr.rs:120:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on foreign functions + --> $DIR/pin_project-attr.rs:123:5 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on type aliases + --> $DIR/pin_project-attr.rs:127:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: `#[pin_project]` attribute cannot be used on macro defs + --> $DIR/pin_project-attr.rs:130:1 + | +LL | #[pin_project] + | ^^^^^^^^^^^^^^ + | + = help: `#[pin_project]` can be applied to data types and unions + +error: aborting due to 40 previous errors + From ace83458a4d2a36681f2344677dc5b04dc78dd39 Mon Sep 17 00:00:00 2001 From: Frank King Date: Fri, 24 Oct 2025 20:59:33 +0800 Subject: [PATCH 087/108] Rename `#[pin_project]` to `#[pin_v2]` to avoid naming conflicts --- .../rustc_attr_parsing/src/attributes/mod.rs | 2 +- .../attributes/{pin_project.rs => pin_v2.rs} | 8 +- compiler/rustc_attr_parsing/src/context.rs | 4 +- compiler/rustc_feature/src/builtin_attrs.rs | 6 +- .../rustc_hir/src/attrs/data_structures.rs | 4 +- .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 +- compiler/rustc_hir_typeck/messages.ftl | 4 +- compiler/rustc_hir_typeck/src/errors.rs | 2 +- compiler/rustc_hir_typeck/src/pat.rs | 2 +- compiler/rustc_middle/src/ty/adt.rs | 4 +- compiler/rustc_passes/src/check_attr.rs | 2 +- compiler/rustc_span/src/symbol.rs | 2 +- .../tests/ui/explicit_write_in_test.stderr | 0 .../feature-gate-pin_ergonomics.rs | 4 +- .../feature-gate-pin_ergonomics.stderr | 6 +- .../pattern-matching-deref-pattern.rs | 6 +- ...ern-matching-mix-deref-pattern.both.stderr | 24 +- ...ng-mix-deref-pattern.pin_ergonomics.stderr | 24 +- .../pattern-matching-mix-deref-pattern.rs | 22 +- .../pattern-matching.normal.stderr | 12 +- tests/ui/pin-ergonomics/pattern-matching.rs | 6 +- tests/ui/pin-ergonomics/pin_project-attr.rs | 144 -------- .../ui/pin-ergonomics/pin_project-attr.stderr | 318 ------------------ tests/ui/pin-ergonomics/pin_v2-attr.rs | 144 ++++++++ tests/ui/pin-ergonomics/pin_v2-attr.stderr | 318 ++++++++++++++++++ 25 files changed, 535 insertions(+), 535 deletions(-) rename compiler/rustc_attr_parsing/src/attributes/{pin_project.rs => pin_v2.rs} (78%) delete mode 100644 src/tools/clippy/tests/ui/explicit_write_in_test.stderr delete mode 100644 tests/ui/pin-ergonomics/pin_project-attr.rs delete mode 100644 tests/ui/pin-ergonomics/pin_project-attr.stderr create mode 100644 tests/ui/pin-ergonomics/pin_v2-attr.rs create mode 100644 tests/ui/pin-ergonomics/pin_v2-attr.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 207fdb7ccabd7..093969586596d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -48,7 +48,7 @@ pub(crate) mod must_use; pub(crate) mod no_implicit_prelude; pub(crate) mod non_exhaustive; pub(crate) mod path; -pub(crate) mod pin_project; +pub(crate) mod pin_v2; pub(crate) mod proc_macro_attrs; pub(crate) mod prototype; pub(crate) mod repr; diff --git a/compiler/rustc_attr_parsing/src/attributes/pin_project.rs b/compiler/rustc_attr_parsing/src/attributes/pin_v2.rs similarity index 78% rename from compiler/rustc_attr_parsing/src/attributes/pin_project.rs rename to compiler/rustc_attr_parsing/src/attributes/pin_v2.rs index 9a81f82c9f54d..597a9515b0048 100644 --- a/compiler/rustc_attr_parsing/src/attributes/pin_project.rs +++ b/compiler/rustc_attr_parsing/src/attributes/pin_v2.rs @@ -7,15 +7,15 @@ use crate::context::Stage; use crate::target_checking::AllowedTargets; use crate::target_checking::Policy::Allow; -pub(crate) struct PinProjectParser; +pub(crate) struct PinV2Parser; -impl NoArgsAttributeParser for PinProjectParser { - const PATH: &[Symbol] = &[sym::pin_project]; +impl NoArgsAttributeParser for PinV2Parser { + const PATH: &[Symbol] = &[sym::pin_v2]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Enum), Allow(Target::Struct), Allow(Target::Union), ]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::PinProject; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::PinV2; } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b2bd5ffad8d43..15904fd7d3348 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -47,7 +47,7 @@ use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; use crate::attributes::non_exhaustive::NonExhaustiveParser; use crate::attributes::path::PathParser as PathAttributeParser; -use crate::attributes::pin_project::PinProjectParser; +use crate::attributes::pin_v2::PinV2Parser; use crate::attributes::proc_macro_attrs::{ ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, }; @@ -234,7 +234,7 @@ attribute_parsers!( Single>, Single>, Single>, - Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index c97c457086a40..2c472801aa044 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -893,13 +893,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::No, loop_match, experimental!(loop_match) ), - // The `#[pin_project]` attribute is part of the `pin_ergonomics` experiment + // The `#[pin_v2]` attribute is part of the `pin_ergonomics` experiment // that allows structurally pinning, tracked in: // // - https://github.com/rust-lang/rust/issues/130494 gated!( - pin_project, Normal, template!(Word), ErrorFollowing, - EncodeCrossCrate::Yes, pin_ergonomics, experimental!(pin_project), + pin_v2, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::Yes, pin_ergonomics, experimental!(pin_v2), ), // ========================================================================== diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 29b4d2d96dd20..1bb87673d52d2 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -637,8 +637,8 @@ pub enum AttributeKind { /// Represents `#[pattern_complexity_limit]` PatternComplexityLimit { attr_span: Span, limit_span: Span, limit: Limit }, - /// Represents `#[pin_project]` - PinProject(Span), + /// Represents `#[pin_v2]` + PinV2(Span), /// Represents `#[pointee]` Pointee(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index e9a183437693f..11c54b0ac1da0 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -77,7 +77,7 @@ impl AttributeKind { PassByValue(..) => Yes, Path(..) => No, PatternComplexityLimit { .. } => No, - PinProject(..) => Yes, + PinV2(..) => Yes, Pointee(..) => No, ProcMacro(..) => No, ProcMacroAttribute(..) => No, diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index f14441a6363e7..60303d5ee81e4 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -227,9 +227,9 @@ hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function .suggestion = cast the value to `{$cast_ty}` .teach_help = certain types, like `{$ty}`, must be cast before passing them to a variadic function to match the implicit cast that a C compiler would perform as part of C's numeric promotion rules -hir_typeck_project_on_non_pin_project_type = cannot project on type that is not `#[pin_project]` +hir_typeck_project_on_non_pin_project_type = cannot project on type that is not `#[pin_v2]` .note = type defined here - .suggestion = add `#[pin_project]` here + .suggestion = add `#[pin_v2]` here hir_typeck_ptr_cast_add_auto_to_object = cannot add {$traits_len -> [1] auto trait {$traits} diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 34e0c9f0f68a6..615a8c95056d1 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1164,6 +1164,6 @@ pub(crate) struct ProjectOnNonPinProjectType { pub span: Span, #[note] pub def_span: Option, - #[suggestion(code = "#[pin_project]\n", applicability = "machine-applicable")] + #[suggestion(code = "#[pin_v2]\n", applicability = "machine-applicable")] pub sugg_span: Option, } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index d3867bda6fa0a..f27085d59c0ed 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -555,7 +555,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("scrutinee ty {expected:?} is a pinned reference, inserting pin deref"); // if the inner_ty is an ADT, make sure that it can be structurally pinned - // (i.e., it is `#[pin_project]`). + // (i.e., it is `#[pin_v2]`). if let Some(adt) = inner_ty.ty_adt_def() && !adt.is_pin_project() && !adt.is_pin() diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 8386504245cde..510c546f82a4e 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -288,7 +288,7 @@ impl AdtDefData { debug!("found non-exhaustive variant list for {:?}", did); flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; } - if find_attr!(tcx.get_all_attrs(did), AttributeKind::PinProject(..)) { + if find_attr!(tcx.get_all_attrs(did), AttributeKind::PinV2(..)) { debug!("found pin-project type {:?}", did); flags |= AdtFlags::IS_PIN_PROJECT; } @@ -445,7 +445,7 @@ impl<'tcx> AdtDef<'tcx> { self.flags().contains(AdtFlags::IS_PIN) } - /// Returns `true` is this is `#[pin_project]` for the purposes + /// Returns `true` is this is `#[pin_v2]` for the purposes /// of structural pinning. #[inline] pub fn is_pin_project(self) -> bool { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3cd1d5ba61b48..c214104d60670 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -284,7 +284,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcCoherenceIsCore(..) | AttributeKind::DebuggerVisualizer(..) | AttributeKind::RustcMain - | AttributeKind::PinProject(..), + | AttributeKind::PinV2(..), ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c378927ded895..811bd1a6136e9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1667,7 +1667,7 @@ symbols! { pin, pin_ergonomics, pin_macro, - pin_project, + pin_v2, platform_intrinsics, plugin, plugin_registrar, diff --git a/src/tools/clippy/tests/ui/explicit_write_in_test.stderr b/src/tools/clippy/tests/ui/explicit_write_in_test.stderr deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs index 47c4b36a07703..07aba0d1d2815 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs @@ -2,7 +2,7 @@ use std::pin::Pin; -#[pin_project] //~ ERROR the `#[pin_project]` attribute is an experimental feature +#[pin_v2] //~ ERROR the `#[pin_v2]` attribute is an experimental feature struct Foo; impl Foo { @@ -51,7 +51,7 @@ fn borrows() { mod not_compiled { use std::pin::Pin; - #[pin_project] + #[pin_v2] struct Foo; impl Foo { diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr index 3952ba78d3c17..cff564c01fc6b 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr @@ -148,11 +148,11 @@ LL | let x: Pin<&_> = &pin const Foo; = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the `#[pin_project]` attribute is an experimental feature +error[E0658]: the `#[pin_v2]` attribute is an experimental feature --> $DIR/feature-gate-pin_ergonomics.rs:5:1 | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ +LL | #[pin_v2] + | ^^^^^^^^^ | = note: see issue #130494 for more information = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable diff --git a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs index 26f52a0c79cf6..3ec9b8fe476f1 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs +++ b/tests/ui/pin-ergonomics/pattern-matching-deref-pattern.rs @@ -10,16 +10,16 @@ use std::pin::Pin; -#[cfg_attr(pin_ergonomics, pin_project)] +#[cfg_attr(pin_ergonomics, pin_v2)] struct Foo { x: T, y: U, } -#[cfg_attr(pin_ergonomics, pin_project)] +#[cfg_attr(pin_ergonomics, pin_v2)] struct Bar(T, U); -#[cfg_attr(pin_ergonomics, pin_project)] +#[cfg_attr(pin_ergonomics, pin_v2)] enum Baz { Foo(T, U), Bar { x: T, y: U }, diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr index cc3be291580bb..9bcb41299b7da 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.both.stderr @@ -1,4 +1,4 @@ -error: cannot project on type that is not `#[pin_project]` +error: cannot project on type that is not `#[pin_v2]` --> $DIR/pattern-matching-mix-deref-pattern.rs:121:9 | LL | let NonPinProject { x } = foo; @@ -9,13 +9,13 @@ note: type defined here | LL | struct NonPinProject { | ^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[pin_project]` here +help: add `#[pin_v2]` here | -LL + #[pin_project] +LL + #[pin_v2] LL | struct NonPinProject { | -error: cannot project on type that is not `#[pin_project]` +error: cannot project on type that is not `#[pin_v2]` --> $DIR/pattern-matching-mix-deref-pattern.rs:125:9 | LL | let NonPinProject { x } = bar; @@ -26,13 +26,13 @@ note: type defined here | LL | struct NonPinProject { | ^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[pin_project]` here +help: add `#[pin_v2]` here | -LL + #[pin_project] +LL + #[pin_v2] LL | struct NonPinProject { | -error: cannot project on type that is not `#[pin_project]` +error: cannot project on type that is not `#[pin_v2]` --> $DIR/pattern-matching-mix-deref-pattern.rs:131:9 | LL | NonPinProject { x } => {} @@ -43,13 +43,13 @@ note: type defined here | LL | struct NonPinProject { | ^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[pin_project]` here +help: add `#[pin_v2]` here | -LL + #[pin_project] +LL + #[pin_v2] LL | struct NonPinProject { | -error: cannot project on type that is not `#[pin_project]` +error: cannot project on type that is not `#[pin_v2]` --> $DIR/pattern-matching-mix-deref-pattern.rs:139:9 | LL | NonPinProject { x } => {} @@ -60,9 +60,9 @@ note: type defined here | LL | struct NonPinProject { | ^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[pin_project]` here +help: add `#[pin_v2]` here | -LL + #[pin_project] +LL + #[pin_v2] LL | struct NonPinProject { | diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr index cc3be291580bb..9bcb41299b7da 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.pin_ergonomics.stderr @@ -1,4 +1,4 @@ -error: cannot project on type that is not `#[pin_project]` +error: cannot project on type that is not `#[pin_v2]` --> $DIR/pattern-matching-mix-deref-pattern.rs:121:9 | LL | let NonPinProject { x } = foo; @@ -9,13 +9,13 @@ note: type defined here | LL | struct NonPinProject { | ^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[pin_project]` here +help: add `#[pin_v2]` here | -LL + #[pin_project] +LL + #[pin_v2] LL | struct NonPinProject { | -error: cannot project on type that is not `#[pin_project]` +error: cannot project on type that is not `#[pin_v2]` --> $DIR/pattern-matching-mix-deref-pattern.rs:125:9 | LL | let NonPinProject { x } = bar; @@ -26,13 +26,13 @@ note: type defined here | LL | struct NonPinProject { | ^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[pin_project]` here +help: add `#[pin_v2]` here | -LL + #[pin_project] +LL + #[pin_v2] LL | struct NonPinProject { | -error: cannot project on type that is not `#[pin_project]` +error: cannot project on type that is not `#[pin_v2]` --> $DIR/pattern-matching-mix-deref-pattern.rs:131:9 | LL | NonPinProject { x } => {} @@ -43,13 +43,13 @@ note: type defined here | LL | struct NonPinProject { | ^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[pin_project]` here +help: add `#[pin_v2]` here | -LL + #[pin_project] +LL + #[pin_v2] LL | struct NonPinProject { | -error: cannot project on type that is not `#[pin_project]` +error: cannot project on type that is not `#[pin_v2]` --> $DIR/pattern-matching-mix-deref-pattern.rs:139:9 | LL | NonPinProject { x } => {} @@ -60,9 +60,9 @@ note: type defined here | LL | struct NonPinProject { | ^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[pin_project]` here +help: add `#[pin_v2]` here | -LL + #[pin_project] +LL + #[pin_v2] LL | struct NonPinProject { | diff --git a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs index 691d5d51cafcd..2b2a4e61abfd1 100644 --- a/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs +++ b/tests/ui/pin-ergonomics/pattern-matching-mix-deref-pattern.rs @@ -10,16 +10,16 @@ use std::pin::Pin; -#[cfg_attr(any(pin_ergonomics, both), pin_project)] +#[cfg_attr(any(pin_ergonomics, both), pin_v2)] struct Foo { x: T, y: U, } -#[cfg_attr(any(pin_ergonomics, both), pin_project)] +#[cfg_attr(any(pin_ergonomics, both), pin_v2)] struct Bar(T, U); -#[cfg_attr(any(pin_ergonomics, both), pin_project)] +#[cfg_attr(any(pin_ergonomics, both), pin_v2)] enum Baz { Foo(T, U), Bar { x: T, y: U }, @@ -120,27 +120,27 @@ fn bar_const(bar: Pin<&Bar>) { fn non_pin_project(foo: Pin<&mut NonPinProject>, bar: Pin<&NonPinProject>) { let NonPinProject { x } = foo; //[normal]~^ ERROR mismatched types - //[pin_ergonomics]~^^ ERROR cannot project on type that is not `#[pin_project]` - //[both]~^^^ ERROR cannot project on type that is not `#[pin_project]` + //[pin_ergonomics]~^^ ERROR cannot project on type that is not `#[pin_v2]` + //[both]~^^^ ERROR cannot project on type that is not `#[pin_v2]` let NonPinProject { x } = bar; //[normal]~^ ERROR mismatched types - //[pin_ergonomics]~^^ ERROR cannot project on type that is not `#[pin_project]` - //[both]~^^^ ERROR cannot project on type that is not `#[pin_project]` + //[pin_ergonomics]~^^ ERROR cannot project on type that is not `#[pin_v2]` + //[both]~^^^ ERROR cannot project on type that is not `#[pin_v2]` match foo { NonPinProject { x } => {} //[normal]~^ ERROR mismatched types //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors - //[pin_ergonomics]~^^^ ERROR cannot project on type that is not `#[pin_project]` - //[both]~^^^^ ERROR cannot project on type that is not `#[pin_project]` + //[pin_ergonomics]~^^^ ERROR cannot project on type that is not `#[pin_v2]` + //[both]~^^^^ ERROR cannot project on type that is not `#[pin_v2]` Pin { .. } => {} } match bar { NonPinProject { x } => {} //[normal]~^ ERROR mismatched types //[deref_patterns]~^^ ERROR mix of deref patterns and normal constructors - //[pin_ergonomics]~^^^ ERROR cannot project on type that is not `#[pin_project]` - //[both]~^^^^ ERROR cannot project on type that is not `#[pin_project]` + //[pin_ergonomics]~^^^ ERROR cannot project on type that is not `#[pin_v2]` + //[both]~^^^^ ERROR cannot project on type that is not `#[pin_v2]` Pin { .. } => {} } } diff --git a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr index 5d2d133761c43..8ec481fba9e0d 100644 --- a/tests/ui/pin-ergonomics/pattern-matching.normal.stderr +++ b/tests/ui/pin-ergonomics/pattern-matching.normal.stderr @@ -1,18 +1,18 @@ -error[E0658]: the `#[pin_project]` attribute is an experimental feature +error[E0658]: the `#[pin_v2]` attribute is an experimental feature --> $DIR/pattern-matching.rs:12:1 | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ +LL | #[pin_v2] + | ^^^^^^^^^ | = note: see issue #130494 for more information = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the `#[pin_project]` attribute is an experimental feature +error[E0658]: the `#[pin_v2]` attribute is an experimental feature --> $DIR/pattern-matching.rs:18:1 | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ +LL | #[pin_v2] + | ^^^^^^^^^ | = note: see issue #130494 for more information = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable diff --git a/tests/ui/pin-ergonomics/pattern-matching.rs b/tests/ui/pin-ergonomics/pattern-matching.rs index 8798e2ed7402e..96a4705bf9b3b 100644 --- a/tests/ui/pin-ergonomics/pattern-matching.rs +++ b/tests/ui/pin-ergonomics/pattern-matching.rs @@ -7,15 +7,15 @@ use std::pin::Pin; // This test verifies that a `&pin mut T` can be projected to a pinned -// reference field `&pin mut T.U` when `T` is marked with `#[pin_project]`. +// reference field `&pin mut T.U` when `T` is marked with `#[pin_v2]`. -#[pin_project] //[normal]~ ERROR the `#[pin_project]` attribute is an experimental feature +#[pin_v2] //[normal]~ ERROR the `#[pin_v2]` attribute is an experimental feature struct Foo { x: T, y: U, } -#[pin_project] //[normal]~ ERROR the `#[pin_project]` attribute is an experimental feature +#[pin_v2] //[normal]~ ERROR the `#[pin_v2]` attribute is an experimental feature enum Bar { Foo(T, U), Bar { x: T, y: U }, diff --git a/tests/ui/pin-ergonomics/pin_project-attr.rs b/tests/ui/pin-ergonomics/pin_project-attr.rs deleted file mode 100644 index be9bcc34d4947..0000000000000 --- a/tests/ui/pin-ergonomics/pin_project-attr.rs +++ /dev/null @@ -1,144 +0,0 @@ -#![feature( - pin_ergonomics, - where_clause_attrs, - trait_alias, - extern_types, - associated_type_defaults, - fn_delegation, -)] -#![allow(incomplete_features)] -#![pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on crates - -// allowed - -#[pin_project] -struct Struct {} - -#[pin_project] -enum Enum {} - -#[pin_project] -union Union { - field: (), -} - -// disallowed - -enum Foo<#[pin_project] T, #[pin_project] U = ()> { - //~^ ERROR `#[pin_project]` attribute cannot be used on function params - //~| ERROR `#[pin_project]` attribute cannot be used on function params - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on enum variants - UnitVariant, - TupleVariant(#[pin_project] T), //~ ERROR `#[pin_project]` attribute cannot be used on struct fields - StructVariant { - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on struct fields - field: U, - }, -} - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on traits -trait Trait { - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on associated consts - const ASSOC_CONST: () = (); - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on associated types - type AssocType = (); - - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on required trait methods - fn method(); - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on provided trait methods - fn method_with_body() {} -} - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on trait aliases -trait TraitAlias = Trait; - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on inherent impl blocks -impl Struct { - // FIXME: delegation macros are not tested yet (how to?) - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on delegations - reuse ::type_id; - - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on inherent methods - fn method() {} -} - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on trait impl blocks -impl Trait for Enum { - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on trait methods in impl blocks - fn method() {} -} - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on extern crates -extern crate alloc; - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on use statements -use std::pin::Pin; - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on statics -static STATIC: () = (); - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on constants -const CONST: () = (); - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on functions -fn f(#[pin_project] param: Foo) -//~^ ERROR `#[pin_project]` attribute cannot be used on function params -//~| ERROR allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters -where - #[pin_project] - //~^ ERROR `#[pin_project]` attribute cannot be used on where predicates - T:, -{ - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on closures - || (); - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on expressions - [(), (), ()]; - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on statements - let _: Foo<(), ()> = Foo::StructVariant { - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on struct fields - field: (), - }; - match param { - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on match arms - Foo::UnitVariant => {} - Foo::TupleVariant(..) => {} - Foo::StructVariant { - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on pattern fields - field, - } => {} - } -} - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on modules -mod m {} - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on foreign modules -extern "C" { - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on foreign types - type ForeignTy; - - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on foreign statics - static EXTERN_STATIC: (); - - #[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on foreign functions - fn extern_fn(); -} - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on type alias -type Type = (); - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on macro defs -macro_rules! macro_def { - () => {}; -} - -#[pin_project] //~ ERROR `#[pin_project]` attribute cannot be used on macro calls -macro_def!(); - -std::arch::global_asm! { - "{}", - #[pin_project] //~ ERROR this attribute is not supported on assembly - const 0 -} - -fn main() {} diff --git a/tests/ui/pin-ergonomics/pin_project-attr.stderr b/tests/ui/pin-ergonomics/pin_project-attr.stderr deleted file mode 100644 index aa1c376401ba7..0000000000000 --- a/tests/ui/pin-ergonomics/pin_project-attr.stderr +++ /dev/null @@ -1,318 +0,0 @@ -error: `#[pin_project]` attribute cannot be used on macro calls - --> $DIR/pin_project-attr.rs:135:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: this attribute is not supported on assembly - --> $DIR/pin_project-attr.rs:140:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - -error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters - --> $DIR/pin_project-attr.rs:84:12 - | -LL | fn f(#[pin_project] param: Foo) - | ^^^^^^^^^^^^^^ - -error: `#[pin_project]` attribute cannot be used on crates - --> $DIR/pin_project-attr.rs:10:1 - | -LL | #![pin_project] - | ^^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on function params - --> $DIR/pin_project-attr.rs:27:10 - | -LL | enum Foo<#[pin_project] T, #[pin_project] U = ()> { - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on function params - --> $DIR/pin_project-attr.rs:27:28 - | -LL | enum Foo<#[pin_project] T, #[pin_project] U = ()> { - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on enum variants - --> $DIR/pin_project-attr.rs:30:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on struct fields - --> $DIR/pin_project-attr.rs:32:18 - | -LL | TupleVariant(#[pin_project] T), - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on struct fields - --> $DIR/pin_project-attr.rs:34:9 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on traits - --> $DIR/pin_project-attr.rs:39:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on associated consts - --> $DIR/pin_project-attr.rs:41:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on associated types - --> $DIR/pin_project-attr.rs:43:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on required trait methods - --> $DIR/pin_project-attr.rs:46:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on provided trait methods - --> $DIR/pin_project-attr.rs:48:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on trait aliases - --> $DIR/pin_project-attr.rs:52:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on inherent impl blocks - --> $DIR/pin_project-attr.rs:55:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on delegations - --> $DIR/pin_project-attr.rs:58:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on inherent methods - --> $DIR/pin_project-attr.rs:61:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on trait impl blocks - --> $DIR/pin_project-attr.rs:65:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on trait methods in impl blocks - --> $DIR/pin_project-attr.rs:67:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on extern crates - --> $DIR/pin_project-attr.rs:71:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on use statements - --> $DIR/pin_project-attr.rs:74:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on statics - --> $DIR/pin_project-attr.rs:77:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on constants - --> $DIR/pin_project-attr.rs:80:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on functions - --> $DIR/pin_project-attr.rs:83:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on function params - --> $DIR/pin_project-attr.rs:84:12 - | -LL | fn f(#[pin_project] param: Foo) - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on closures - --> $DIR/pin_project-attr.rs:92:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on expressions - --> $DIR/pin_project-attr.rs:94:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on struct fields - --> $DIR/pin_project-attr.rs:98:9 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on statements - --> $DIR/pin_project-attr.rs:96:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on match arms - --> $DIR/pin_project-attr.rs:102:9 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on pattern fields - --> $DIR/pin_project-attr.rs:106:13 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on where predicates - --> $DIR/pin_project-attr.rs:88:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on modules - --> $DIR/pin_project-attr.rs:112:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on foreign modules - --> $DIR/pin_project-attr.rs:115:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on foreign types - --> $DIR/pin_project-attr.rs:117:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on foreign statics - --> $DIR/pin_project-attr.rs:120:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on foreign functions - --> $DIR/pin_project-attr.rs:123:5 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on type aliases - --> $DIR/pin_project-attr.rs:127:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: `#[pin_project]` attribute cannot be used on macro defs - --> $DIR/pin_project-attr.rs:130:1 - | -LL | #[pin_project] - | ^^^^^^^^^^^^^^ - | - = help: `#[pin_project]` can be applied to data types and unions - -error: aborting due to 40 previous errors - diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.rs b/tests/ui/pin-ergonomics/pin_v2-attr.rs new file mode 100644 index 0000000000000..af958952d0840 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin_v2-attr.rs @@ -0,0 +1,144 @@ +#![feature( + pin_ergonomics, + where_clause_attrs, + trait_alias, + extern_types, + associated_type_defaults, + fn_delegation, +)] +#![allow(incomplete_features)] +#![pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on crates + +// allowed + +#[pin_v2] +struct Struct {} + +#[pin_v2] +enum Enum {} + +#[pin_v2] +union Union { + field: (), +} + +// disallowed + +enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { + //~^ ERROR `#[pin_v2]` attribute cannot be used on function params + //~| ERROR `#[pin_v2]` attribute cannot be used on function params + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on enum variants + UnitVariant, + TupleVariant(#[pin_v2] T), //~ ERROR `#[pin_v2]` attribute cannot be used on struct fields + StructVariant { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on struct fields + field: U, + }, +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on traits +trait Trait { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on associated consts + const ASSOC_CONST: () = (); + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on associated types + type AssocType = (); + + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on required trait methods + fn method(); + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on provided trait methods + fn method_with_body() {} +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on trait aliases +trait TraitAlias = Trait; + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on inherent impl blocks +impl Struct { + // FIXME: delegation macros are not tested yet (how to?) + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on delegations + reuse ::type_id; + + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on inherent methods + fn method() {} +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on trait impl blocks +impl Trait for Enum { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on trait methods in impl blocks + fn method() {} +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on extern crates +extern crate alloc; + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on use statements +use std::pin::Pin; + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on statics +static STATIC: () = (); + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on constants +const CONST: () = (); + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on functions +fn f(#[pin_v2] param: Foo) +//~^ ERROR `#[pin_v2]` attribute cannot be used on function params +//~| ERROR allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters +where + #[pin_v2] + //~^ ERROR `#[pin_v2]` attribute cannot be used on where predicates + T:, +{ + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on closures + || (); + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on expressions + [(), (), ()]; + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on statements + let _: Foo<(), ()> = Foo::StructVariant { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on struct fields + field: (), + }; + match param { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on match arms + Foo::UnitVariant => {} + Foo::TupleVariant(..) => {} + Foo::StructVariant { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on pattern fields + field, + } => {} + } +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on modules +mod m {} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on foreign modules +extern "C" { + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on foreign types + type ForeignTy; + + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on foreign statics + static EXTERN_STATIC: (); + + #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on foreign functions + fn extern_fn(); +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on type alias +type Type = (); + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on macro defs +macro_rules! macro_def { + () => {}; +} + +#[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on macro calls +macro_def!(); + +std::arch::global_asm! { + "{}", + #[pin_v2] //~ ERROR this attribute is not supported on assembly + const 0 +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.stderr b/tests/ui/pin-ergonomics/pin_v2-attr.stderr new file mode 100644 index 0000000000000..fb3e50a23fe74 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin_v2-attr.stderr @@ -0,0 +1,318 @@ +error: `#[pin_v2]` attribute cannot be used on macro calls + --> $DIR/pin_v2-attr.rs:135:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: this attribute is not supported on assembly + --> $DIR/pin_v2-attr.rs:140:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + +error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters + --> $DIR/pin_v2-attr.rs:84:12 + | +LL | fn f(#[pin_v2] param: Foo) + | ^^^^^^^^^ + +error: `#[pin_v2]` attribute cannot be used on crates + --> $DIR/pin_v2-attr.rs:10:1 + | +LL | #![pin_v2] + | ^^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on function params + --> $DIR/pin_v2-attr.rs:27:10 + | +LL | enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on function params + --> $DIR/pin_v2-attr.rs:27:23 + | +LL | enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on enum variants + --> $DIR/pin_v2-attr.rs:30:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on struct fields + --> $DIR/pin_v2-attr.rs:32:18 + | +LL | TupleVariant(#[pin_v2] T), + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on struct fields + --> $DIR/pin_v2-attr.rs:34:9 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on traits + --> $DIR/pin_v2-attr.rs:39:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on associated consts + --> $DIR/pin_v2-attr.rs:41:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on associated types + --> $DIR/pin_v2-attr.rs:43:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on required trait methods + --> $DIR/pin_v2-attr.rs:46:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on provided trait methods + --> $DIR/pin_v2-attr.rs:48:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on trait aliases + --> $DIR/pin_v2-attr.rs:52:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on inherent impl blocks + --> $DIR/pin_v2-attr.rs:55:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on delegations + --> $DIR/pin_v2-attr.rs:58:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on inherent methods + --> $DIR/pin_v2-attr.rs:61:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on trait impl blocks + --> $DIR/pin_v2-attr.rs:65:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on trait methods in impl blocks + --> $DIR/pin_v2-attr.rs:67:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on extern crates + --> $DIR/pin_v2-attr.rs:71:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on use statements + --> $DIR/pin_v2-attr.rs:74:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on statics + --> $DIR/pin_v2-attr.rs:77:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on constants + --> $DIR/pin_v2-attr.rs:80:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on functions + --> $DIR/pin_v2-attr.rs:83:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on function params + --> $DIR/pin_v2-attr.rs:84:12 + | +LL | fn f(#[pin_v2] param: Foo) + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on closures + --> $DIR/pin_v2-attr.rs:92:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on expressions + --> $DIR/pin_v2-attr.rs:94:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on struct fields + --> $DIR/pin_v2-attr.rs:98:9 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on statements + --> $DIR/pin_v2-attr.rs:96:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on match arms + --> $DIR/pin_v2-attr.rs:102:9 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on pattern fields + --> $DIR/pin_v2-attr.rs:106:13 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on where predicates + --> $DIR/pin_v2-attr.rs:88:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on modules + --> $DIR/pin_v2-attr.rs:112:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on foreign modules + --> $DIR/pin_v2-attr.rs:115:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on foreign types + --> $DIR/pin_v2-attr.rs:117:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on foreign statics + --> $DIR/pin_v2-attr.rs:120:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on foreign functions + --> $DIR/pin_v2-attr.rs:123:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on type aliases + --> $DIR/pin_v2-attr.rs:127:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: `#[pin_v2]` attribute cannot be used on macro defs + --> $DIR/pin_v2-attr.rs:130:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can be applied to data types and unions + +error: aborting due to 40 previous errors + From 731981178d8a9b4a30aae70fb2bd91e0e016bb33 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 30 Oct 2025 16:01:58 +0100 Subject: [PATCH 088/108] [rustdoc search] Include extern crates when filtering on `import` --- src/librustdoc/html/static/js/search.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 0a73d32dac2de..e281139ca38ff 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -3906,6 +3906,8 @@ class DocSearch { return name === "traitalias"; case "macro": return name === "attr" || name === "derive"; + case "import": + return name === "externcrate"; } // No match From e614ed4456c944dc1afd0a0b26a216aa0f76093f Mon Sep 17 00:00:00 2001 From: Frank King Date: Fri, 31 Oct 2025 08:31:55 +0800 Subject: [PATCH 089/108] Restrict `#[pin_v2]` test on `global_asm!` to stablized archs only --- tests/ui/pin-ergonomics/pin_v2-attr.rs | 14 ++++++++++++++ tests/ui/pin-ergonomics/pin_v2-attr.stderr | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.rs b/tests/ui/pin-ergonomics/pin_v2-attr.rs index af958952d0840..eccb9b6a87bfa 100644 --- a/tests/ui/pin-ergonomics/pin_v2-attr.rs +++ b/tests/ui/pin-ergonomics/pin_v2-attr.rs @@ -135,6 +135,20 @@ macro_rules! macro_def { #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on macro calls macro_def!(); +// Restricted on architectures where inline assembly is stable +// accroding to `compiler/rustc_ast_lowering/src/asm.rs` +#[cfg(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "arm64ec", + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "loongarch32", + target_arch = "loongarch64", + target_arch = "s390x", +))] std::arch::global_asm! { "{}", #[pin_v2] //~ ERROR this attribute is not supported on assembly diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.stderr b/tests/ui/pin-ergonomics/pin_v2-attr.stderr index fb3e50a23fe74..607dccd4019b7 100644 --- a/tests/ui/pin-ergonomics/pin_v2-attr.stderr +++ b/tests/ui/pin-ergonomics/pin_v2-attr.stderr @@ -7,7 +7,7 @@ LL | #[pin_v2] = help: `#[pin_v2]` can be applied to data types and unions error: this attribute is not supported on assembly - --> $DIR/pin_v2-attr.rs:140:5 + --> $DIR/pin_v2-attr.rs:154:5 | LL | #[pin_v2] | ^^^^^^^^^ From d8ace32252c8ca49d9e41be46e5758fbcf666770 Mon Sep 17 00:00:00 2001 From: Frank King Date: Fri, 31 Oct 2025 08:48:01 +0800 Subject: [PATCH 090/108] Remove the `#[pin_v2]` test since it produces different errors on different archs --- tests/ui/pin-ergonomics/pin_v2-attr.rs | 20 -------------------- tests/ui/pin-ergonomics/pin_v2-attr.stderr | 8 +------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.rs b/tests/ui/pin-ergonomics/pin_v2-attr.rs index eccb9b6a87bfa..cfafe4b0b8999 100644 --- a/tests/ui/pin-ergonomics/pin_v2-attr.rs +++ b/tests/ui/pin-ergonomics/pin_v2-attr.rs @@ -135,24 +135,4 @@ macro_rules! macro_def { #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on macro calls macro_def!(); -// Restricted on architectures where inline assembly is stable -// accroding to `compiler/rustc_ast_lowering/src/asm.rs` -#[cfg(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64", - target_arch = "arm64ec", - target_arch = "riscv32", - target_arch = "riscv64", - target_arch = "loongarch32", - target_arch = "loongarch64", - target_arch = "s390x", -))] -std::arch::global_asm! { - "{}", - #[pin_v2] //~ ERROR this attribute is not supported on assembly - const 0 -} - fn main() {} diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.stderr b/tests/ui/pin-ergonomics/pin_v2-attr.stderr index 607dccd4019b7..81e086f5a7ef8 100644 --- a/tests/ui/pin-ergonomics/pin_v2-attr.stderr +++ b/tests/ui/pin-ergonomics/pin_v2-attr.stderr @@ -6,12 +6,6 @@ LL | #[pin_v2] | = help: `#[pin_v2]` can be applied to data types and unions -error: this attribute is not supported on assembly - --> $DIR/pin_v2-attr.rs:154:5 - | -LL | #[pin_v2] - | ^^^^^^^^^ - error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters --> $DIR/pin_v2-attr.rs:84:12 | @@ -314,5 +308,5 @@ LL | #[pin_v2] | = help: `#[pin_v2]` can be applied to data types and unions -error: aborting due to 40 previous errors +error: aborting due to 39 previous errors From 88554d64bba8af60277310d5c257a3e187bf96e4 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 29 Oct 2025 19:14:42 +0000 Subject: [PATCH 091/108] Remove/fix setTimeout logic in lints page --- util/gh-pages/script.js | 50 ++++++++++++----------------------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index 2b6ee67c37dc7..b468b52aea5bb 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -1,23 +1,12 @@ "use strict"; window.searchState = { - timeout: null, inputElem: document.getElementById("search-input"), lastSearch: '', clearInput: () => { searchState.inputElem.value = ""; searchState.filterLints(); }, - clearInputTimeout: () => { - if (searchState.timeout !== null) { - clearTimeout(searchState.timeout); - searchState.timeout = null - } - }, - resetInputTimeout: () => { - searchState.clearInputTimeout(); - setTimeout(searchState.filterLints, 50); - }, filterLints: () => { function matchesSearch(lint, terms, searchStr) { // Search by id @@ -42,8 +31,6 @@ window.searchState = { return true; } - searchState.clearInputTimeout(); - let searchStr = searchState.inputElem.value.trim().toLowerCase(); if (searchStr.startsWith("clippy::")) { searchStr = searchStr.slice(8); @@ -79,7 +66,7 @@ function handleInputChanged(event) { if (event.target !== document.activeElement) { return; } - searchState.resetInputTimeout(); + searchState.filterLints(); } function handleShortcut(ev) { @@ -149,27 +136,25 @@ function lintAnchor(event) { expandLint(id); } +const clipboardTimeouts = new Map(); function copyToClipboard(event) { event.preventDefault(); event.stopPropagation(); const clipboard = event.target; - let resetClipboardTimeout = null; - const resetClipboardIcon = clipboard.innerHTML; - - function resetClipboard() { - resetClipboardTimeout = null; - clipboard.innerHTML = resetClipboardIcon; - } - navigator.clipboard.writeText("clippy::" + clipboard.parentElement.id.slice(5)); - clipboard.innerHTML = "✓"; - if (resetClipboardTimeout !== null) { - clearTimeout(resetClipboardTimeout); - } - resetClipboardTimeout = setTimeout(resetClipboard, 1000); + clipboard.textContent = "✓"; + + clearTimeout(clipboardTimeouts.get(clipboard)); + clipboardTimeouts.set( + clipboard, + setTimeout(() => { + clipboard.textContent = "📋"; + clipboardTimeouts.delete(clipboard); + }, 1000) + ); } function handleBlur(event, elementId) { @@ -487,14 +472,6 @@ function generateSettings() { setupDropdown("version-filter"); } -function generateSearch() { - searchState.inputElem.addEventListener("change", handleInputChanged); - searchState.inputElem.addEventListener("input", handleInputChanged); - searchState.inputElem.addEventListener("keydown", handleInputChanged); - searchState.inputElem.addEventListener("keyup", handleInputChanged); - searchState.inputElem.addEventListener("paste", handleInputChanged); -} - function scrollToLint(lintId) { const target = document.getElementById(lintId); if (!target) { @@ -540,6 +517,8 @@ function parseURLFilters() { } function addListeners() { + searchState.inputElem.addEventListener("input", handleInputChanged); + disableShortcutsButton.addEventListener("change", () => { disableShortcuts = disableShortcutsButton.checked; storeValue("disable-shortcuts", disableShortcuts); @@ -594,7 +573,6 @@ disableShortcutsButton.checked = disableShortcuts; addListeners(); highlightLazily(); generateSettings(); -generateSearch(); parseURLFilters(); scrollToLintByURL(); filters.filterLints(); From 23edbb67bd832d187b6052fba515bd23b45479d7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 31 Oct 2025 15:29:32 +0000 Subject: [PATCH 092/108] Don't require dlltool with the dummy backend on MinGW The dummy backend should be able to cross-compile to any target without requiring any external tool or library other than the rust standard library. --- compiler/rustc_interface/src/util.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index c9fdb61e9c2cb..5aacf1f8d960e 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -7,7 +7,7 @@ use std::{env, thread}; use rustc_ast as ast; use rustc_attr_parsing::{ShouldEmit, validate_attr}; -use rustc_codegen_ssa::back::archive::ArArchiveBuilderBuilder; +use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder}; use rustc_codegen_ssa::back::link::link_binary; use rustc_codegen_ssa::target_features::{self, cfg_target_feature}; use rustc_codegen_ssa::traits::CodegenBackend; @@ -440,7 +440,7 @@ impl CodegenBackend for DummyCodegenBackend { link_binary( sess, - &ArArchiveBuilderBuilder, + &DummyArchiveBuilderBuilder, codegen_results, metadata, outputs, @@ -449,6 +449,28 @@ impl CodegenBackend for DummyCodegenBackend { } } +struct DummyArchiveBuilderBuilder; + +impl ArchiveBuilderBuilder for DummyArchiveBuilderBuilder { + fn new_archive_builder<'a>( + &self, + sess: &'a Session, + ) -> Box { + ArArchiveBuilderBuilder.new_archive_builder(sess) + } + + fn create_dll_import_lib( + &self, + sess: &Session, + _lib_name: &str, + _items: Vec, + output_path: &Path, + ) { + // Build an empty static library to avoid calling an external dlltool on mingw + ArArchiveBuilderBuilder.new_archive_builder(sess).build(output_path); + } +} + // This is used for rustdoc, but it uses similar machinery to codegen backend // loading, so we leave the code here. It is potentially useful for other tools // that want to invoke the rustc binary while linking to rustc as well. From 04a10f76106a5f80cfebb596bf2c762d6c6c8626 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 27 Oct 2025 14:34:57 +0100 Subject: [PATCH 093/108] Simplify code to generate line numbers in highlight --- src/librustdoc/html/highlight.rs | 56 +++++++++++++++++++------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index c37736f137df9..1a59456a5322e 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -343,7 +343,7 @@ struct TokenHandler<'a, 'tcx, F: Write> { /// We need to keep the `Class` for each element because it could contain a `Span` which is /// used to generate links. href_context: Option>, - write_line_number: fn(u32) -> String, + write_line_number: LineNumberKind, line: u32, max_lines: u32, } @@ -355,10 +355,10 @@ impl std::fmt::Debug for TokenHandler<'_, '_, F> { } impl<'a, F: Write> TokenHandler<'a, '_, F> { - fn handle_backline(&mut self) -> Option { + fn handle_backline(&mut self) -> Option> { self.line += 1; if self.line < self.max_lines { - return Some((self.write_line_number)(self.line)); + return Some(self.write_line_number.render(self.line)); } None } @@ -376,8 +376,7 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> { if text == "\n" && let Some(backline) = self.handle_backline() { - self.out.write_str(&text).unwrap(); - self.out.write_str(&backline).unwrap(); + write!(self.out, "{text}{backline}").unwrap(); } else { self.push_token_without_backline_check(class, text, true); } @@ -437,20 +436,29 @@ impl Drop for TokenHandler<'_, '_, F> { } } -fn scraped_line_number(line: u32) -> String { - // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr - // Do not show "1 2 3 4 5 ..." in web search results. - format!("{line}") -} - -fn line_number(line: u32) -> String { - // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr - // Do not show "1 2 3 4 5 ..." in web search results. - format!("{line}") +/// Represents line numbers to be generated as HTML. +#[derive(Clone, Copy)] +enum LineNumberKind { + /// Used for scraped code examples. + Scraped, + /// Used for source code pages. + Normal, + /// Code examples in documentation don't have line number generated by rustdoc. + Empty, } -fn empty_line_number(_: u32) -> String { - String::new() +impl LineNumberKind { + fn render(self, line: u32) -> impl Display { + fmt::from_fn(move |f| { + match self { + // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr + // Do not show "1 2 3 4 5 ..." in web search results. + Self::Scraped => write!(f, "{line}"), + Self::Normal => write!(f, "{line}"), + Self::Empty => Ok(()), + } + }) + } } fn get_next_expansion( @@ -537,12 +545,12 @@ pub(super) fn write_code( write_line_number: match line_info { Some(line_info) => { if line_info.is_scraped_example { - scraped_line_number + LineNumberKind::Scraped } else { - line_number + LineNumberKind::Normal } } - None => empty_line_number, + None => LineNumberKind::Empty, }, line: 0, max_lines: u32::MAX, @@ -552,8 +560,12 @@ pub(super) fn write_code( if let Some(line_info) = line_info { token_handler.line = line_info.start_line - 1; token_handler.max_lines = line_info.max_lines; - if let Some(text) = token_handler.handle_backline() { - token_handler.push_token_without_backline_check(None, Cow::Owned(text), false); + if let Some(backline) = token_handler.handle_backline() { + token_handler.push_token_without_backline_check( + None, + Cow::Owned(backline.to_string()), + false, + ); } } From 5fabd2da1c0665a80eb6c5cc339208b200ffa35e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 31 Oct 2025 17:32:42 +0100 Subject: [PATCH 094/108] Improve code --- src/librustdoc/html/highlight.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 1a59456a5322e..0363418f91647 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -343,7 +343,7 @@ struct TokenHandler<'a, 'tcx, F: Write> { /// We need to keep the `Class` for each element because it could contain a `Span` which is /// used to generate links. href_context: Option>, - write_line_number: LineNumberKind, + line_number_kind: LineNumberKind, line: u32, max_lines: u32, } @@ -358,7 +358,7 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> { fn handle_backline(&mut self) -> Option> { self.line += 1; if self.line < self.max_lines { - return Some(self.write_line_number.render(self.line)); + return Some(self.line_number_kind.render(self.line)); } None } @@ -436,7 +436,7 @@ impl Drop for TokenHandler<'_, '_, F> { } } -/// Represents line numbers to be generated as HTML. +/// Represents the type of line number to be generated as HTML. #[derive(Clone, Copy)] enum LineNumberKind { /// Used for scraped code examples. @@ -542,7 +542,7 @@ pub(super) fn write_code( let mut token_handler = TokenHandler { out, href_context, - write_line_number: match line_info { + line_number_kind: match line_info { Some(line_info) => { if line_info.is_scraped_example { LineNumberKind::Scraped From efb9a41f0ecdf432b9e163806d6714f53f090bd8 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Fri, 31 Oct 2025 13:47:57 -0400 Subject: [PATCH 095/108] cleanup: upstream dropped amx-transpose functionality See also LLVM change 5322fb626820. Looks like this was just removed entirely. --- compiler/rustc_target/src/target_features.rs | 1 - library/std_detect/src/detect/arch/x86.rs | 3 --- library/std_detect/src/detect/os/x86.rs | 1 - library/std_detect/tests/x86-specific.rs | 1 - tests/ui/check-cfg/target_feature.stderr | 1 - 5 files changed, 7 deletions(-) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index dc70089c385fe..288ee6357a510 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -386,7 +386,6 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("amx-movrs", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-tf32", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), - ("amx-transpose", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("apxf", Unstable(sym::apx_target_feature), &[]), ("avx", Stable, &["sse4.2"]), ("avx2", Stable, &["avx"]), diff --git a/library/std_detect/src/detect/arch/x86.rs b/library/std_detect/src/detect/arch/x86.rs index bd749b88f566d..e4318bedb2a6f 100644 --- a/library/std_detect/src/detect/arch/x86.rs +++ b/library/std_detect/src/detect/arch/x86.rs @@ -93,7 +93,6 @@ features! { /// * `"amx-fp8"` /// * `"amx-movrs"` /// * `"amx-tf32"` - /// * `"amx-transpose"` /// * `"f16c"` /// * `"fma"` /// * `"bmi1"` @@ -231,8 +230,6 @@ features! { /// AMX-MOVRS (Matrix MOVERS operations) @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_tf32: "amx-tf32"; /// AMX-TF32 (TensorFloat32 Operations) - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_transpose: "amx-transpose"; - /// AMX-TRANSPOSE (Matrix Transpose Operations) @FEATURE: #[unstable(feature = "apx_target_feature", issue = "139284")] apxf: "apxf"; /// APX-F (Advanced Performance Extensions - Foundation) @FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_1: "avx10.1"; diff --git a/library/std_detect/src/detect/os/x86.rs b/library/std_detect/src/detect/os/x86.rs index cf11d8333127f..18925da2b2755 100644 --- a/library/std_detect/src/detect/os/x86.rs +++ b/library/std_detect/src/detect/os/x86.rs @@ -285,7 +285,6 @@ pub(crate) fn detect_features() -> cache::Initializer { unsafe { __cpuid_count(0x1e_u32, 1) }; enable(amx_feature_flags_eax, 4, Feature::amx_fp8); - enable(amx_feature_flags_eax, 5, Feature::amx_transpose); enable(amx_feature_flags_eax, 6, Feature::amx_tf32); enable(amx_feature_flags_eax, 7, Feature::amx_avx512); enable(amx_feature_flags_eax, 8, Feature::amx_movrs); diff --git a/library/std_detect/tests/x86-specific.rs b/library/std_detect/tests/x86-specific.rs index 2ed2bb2a99ecd..90ca32208e78d 100644 --- a/library/std_detect/tests/x86-specific.rs +++ b/library/std_detect/tests/x86-specific.rs @@ -76,7 +76,6 @@ fn dump() { println!("widekl: {:?}", is_x86_feature_detected!("widekl")); println!("movrs: {:?}", is_x86_feature_detected!("movrs")); println!("amx-fp8: {:?}", is_x86_feature_detected!("amx-fp8")); - println!("amx-transpose: {:?}", is_x86_feature_detected!("amx-transpose")); println!("amx-tf32: {:?}", is_x86_feature_detected!("amx-tf32")); println!("amx-avx512: {:?}", is_x86_feature_detected!("amx-avx512")); println!("amx-movrs: {:?}", is_x86_feature_detected!("amx-movrs")); diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 258f21324661b..d25e7f094964e 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -27,7 +27,6 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `amx-movrs` `amx-tf32` `amx-tile` -`amx-transpose` `apxf` `atomics` `avx` From 2b9c8f19817043bc0241f74eadbd4fed7570c606 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 31 Oct 2025 18:58:33 +0100 Subject: [PATCH 096/108] Bump nightly version -> 2025-10-31 --- 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 9d12b46b9546f..45463b4fa1db8 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-10-16 +nightly-2025-10-31 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d5d96448a97d3..d23fd74d9accd 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-10-16" +channel = "nightly-2025-10-31" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From a53104042e6bbca27a6ff96b6adab9fda858ba2b Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 31 Oct 2025 18:58:42 +0100 Subject: [PATCH 097/108] Bump Clippy version -> 0.1.93 --- Cargo.toml | 2 +- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bedcc300f8567..fee885d8fa7e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.92" +version = "0.1.93" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index f8c748290e418..3f6b26d3334ed 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.92" +version = "0.1.93" edition = "2024" publish = false diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 42486e182ee34..bc97746a1cbae 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.92" +version = "0.1.93" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index d58b47bf6deb3..a1e1b763dccb1 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.92" +version = "0.1.93" edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 4de7b5fb5924d..b73a7c7bb4d98 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.92" +version = "0.1.93" edition = "2024" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0" From be5093efcb5020d7a6691b41afeb3d4ff51ddf3e Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 31 Oct 2025 19:15:50 +0100 Subject: [PATCH 098/108] Update Cargo.lock --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2320e33bc4bf1..b7083221ec3bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -580,7 +580,7 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clippy" -version = "0.1.92" +version = "0.1.93" dependencies = [ "anstream", "askama", @@ -607,7 +607,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.92" +version = "0.1.93" dependencies = [ "clippy_utils", "itertools", @@ -630,7 +630,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.92" +version = "0.1.93" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -662,7 +662,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.92" +version = "0.1.93" dependencies = [ "arrayvec", "itertools", @@ -1066,7 +1066,7 @@ dependencies = [ [[package]] name = "declare_clippy_lint" -version = "0.1.92" +version = "0.1.93" [[package]] name = "derive-where" From c169ea03aba02d1f907423d9c96cf8565bea293d Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 29 Oct 2025 17:23:07 +0000 Subject: [PATCH 099/108] Unpin `libc` in `compiler` and `rustbook` --- Cargo.lock | 18 +++++++++--------- compiler/rustc_session/Cargo.toml | 4 +--- src/tools/rustbook/Cargo.lock | 16 ++++++++-------- src/tools/rustbook/Cargo.toml | 4 +--- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e9ba9b432793..b93c474a5be6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1289,7 +1289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -2102,9 +2102,9 @@ checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libdbus-sys" @@ -2155,7 +2155,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.3", + "windows-targets 0.52.6", ] [[package]] @@ -2217,9 +2217,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -4898,15 +4898,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 60d56b1b808df..0516982aeabb9 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -27,10 +27,8 @@ tracing = "0.1" # tidy-alphabetical-end [target.'cfg(unix)'.dependencies] -# FIXME: Remove this pin once this rustix issue is resolved -# https://github.com/bytecodealliance/rustix/issues/1496 # tidy-alphabetical-start -libc = "=0.2.174" +libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index cd7ee6fb4feab..5788fef82d70e 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -464,7 +464,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -807,9 +807,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "linereader" @@ -822,9 +822,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -1394,15 +1394,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags 2.9.2", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml index b34c39c225dc8..1a5b2c29d20be 100644 --- a/src/tools/rustbook/Cargo.toml +++ b/src/tools/rustbook/Cargo.toml @@ -10,9 +10,7 @@ edition = "2021" [dependencies] clap = "4.0.32" env_logger = "0.11" -# FIXME: Remove this pin once this rustix issue is resolved -# https://github.com/bytecodealliance/rustix/issues/1496 -libc = "=0.2.174" +libc = "0.2" mdbook-trpl = { path = "../../doc/book/packages/mdbook-trpl" } mdbook-i18n-helpers = "0.3.3" mdbook-spec = { path = "../../doc/reference/mdbook-spec" } From 7069400c477c71609928024dc9a3416eb1336041 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Fri, 31 Oct 2025 14:49:45 -0400 Subject: [PATCH 100/108] revert combined nonpoison/poison tests for condvar Setup for writing different tests for the `nonpoison::Condvar` since it will have a different API. Signed-off-by: Connor Tsui --- library/std/tests/sync/condvar.rs | 472 ++++++++++++++---------------- 1 file changed, 226 insertions(+), 246 deletions(-) diff --git a/library/std/tests/sync/condvar.rs b/library/std/tests/sync/condvar.rs index 42b880e283afe..74328793caeae 100644 --- a/library/std/tests/sync/condvar.rs +++ b/library/std/tests/sync/condvar.rs @@ -17,256 +17,237 @@ nonpoison_and_poison_unwrap_test!( } ); +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: notify_one, - test_body: { - use locks::{Condvar, Mutex}; +fn notify_one() { + use std::sync::{Condvar, Mutex}; - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); - let g = maybe_unwrap(m.lock()); - let _t = thread::spawn(move || { - let _g = maybe_unwrap(m2.lock()); - c2.notify_one(); - }); - let g = maybe_unwrap(c.wait(g)); - drop(g); - } -); + let g = m.lock().unwrap(); + let _t = thread::spawn(move || { + let _g = m2.lock().unwrap(); + c2.notify_one(); + }); -#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: notify_all, - test_body: { - use locks::{Condvar, Mutex}; - - const N: usize = 10; - - let data = Arc::new((Mutex::new(0), Condvar::new())); - let (tx, rx) = channel(); - for _ in 0..N { - let data = data.clone(); - let tx = tx.clone(); - thread::spawn(move || { - let &(ref lock, ref cond) = &*data; - let mut cnt = maybe_unwrap(lock.lock()); - *cnt += 1; - if *cnt == N { - tx.send(()).unwrap(); - } - while *cnt != 0 { - cnt = maybe_unwrap(cond.wait(cnt)); - } - tx.send(()).unwrap(); - }); - } - drop(tx); - - let &(ref lock, ref cond) = &*data; - rx.recv().unwrap(); - let mut cnt = maybe_unwrap(lock.lock()); - *cnt = 0; - cond.notify_all(); - drop(cnt); - - for _ in 0..N { - rx.recv().unwrap(); - } - } -); + let g = c.wait(g).unwrap(); + drop(g); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: test_mutex_arc_condvar, - test_body: { - use locks::{Condvar, Mutex}; - - struct Packet(Arc<(Mutex, Condvar)>); +fn notify_all() { + use std::sync::{Condvar, Mutex}; - let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); + const N: usize = 10; - let (tx, rx) = channel(); - - let _t = thread::spawn(move || { - // Wait until our parent has taken the lock. - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; - - // Set the data to `true` and wake up our parent. - let mut guard = maybe_unwrap(lock.lock()); - *guard = true; - cvar.notify_one(); + let data = Arc::new((Mutex::new(0), Condvar::new())); + let (tx, rx) = channel(); + for _ in 0..N { + let data = data.clone(); + let tx = tx.clone(); + thread::spawn(move || { + let &(ref lock, ref cond) = &*data; + let mut cnt = lock.lock().unwrap(); + *cnt += 1; + if *cnt == N { + tx.send(()).unwrap(); + } + while *cnt != 0 { + cnt = cond.wait(cnt).unwrap(); + } + tx.send(()).unwrap(); }); + } + drop(tx); - let &(ref lock, ref cvar) = &*packet.0; - let mut guard = maybe_unwrap(lock.lock()); - // Wake up our child. - tx.send(()).unwrap(); + let &(ref lock, ref cond) = &*data; + rx.recv().unwrap(); + let mut cnt = lock.lock().unwrap(); + *cnt = 0; + cond.notify_all(); + drop(cnt); - // Wait until our child has set the data to `true`. - assert!(!*guard); - while !*guard { - guard = maybe_unwrap(cvar.wait(guard)); - } + for _ in 0..N { + rx.recv().unwrap(); } -); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_while, - test_body: { - use locks::{Condvar, Mutex}; +fn test_mutex_arc_condvar() { + use std::sync::{Condvar, Mutex}; - let pair = Arc::new((Mutex::new(false), Condvar::new())); - let pair2 = pair.clone(); + struct Packet(Arc<(Mutex, Condvar)>); - // Inside of our lock, spawn a new thread, and then wait for it to start. - thread::spawn(move || { - let &(ref lock, ref cvar) = &*pair2; - let mut started = maybe_unwrap(lock.lock()); - *started = true; - // We notify the condvar that the value has changed. - cvar.notify_one(); - }); + let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); - // Wait for the thread to start up. - let &(ref lock, ref cvar) = &*pair; - let guard = cvar.wait_while(maybe_unwrap(lock.lock()), |started| !*started); - assert!(*maybe_unwrap(guard)); + let (tx, rx) = channel(); + + let _t = thread::spawn(move || { + // Wait until our parent has taken the lock. + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + + // Set the data to `true` and wake up our parent. + let mut guard = lock.lock().unwrap(); + *guard = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut guard = lock.lock().unwrap(); + // Wake up our child. + tx.send(()).unwrap(); + + // Wait until our child has set the data to `true`. + assert!(!*guard); + while !*guard { + guard = cvar.wait(guard).unwrap(); } -); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_timeout_wait, - test_body: { - use locks::{Condvar, Mutex}; - - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - loop { - let g = maybe_unwrap(m.lock()); - let (_g, no_timeout) = maybe_unwrap(c.wait_timeout(g, Duration::from_millis(1))); - // spurious wakeups mean this isn't necessarily true - // so execute test again, if not timeout - if !no_timeout.timed_out() { - continue; - } - - break; +fn wait_while() { + use std::sync::{Condvar, Mutex}; + + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); + + // Inside of our lock, spawn a new thread, and then wait for it to start. + thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair2; + let mut started = lock.lock().unwrap(); + *started = true; + // We notify the condvar that the value has changed. + cvar.notify_one(); + }); + + // Wait for the thread to start up. + let &(ref lock, ref cvar) = &*pair; + let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started).unwrap(); + assert!(*guard); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn wait_timeout_wait() { + use std::sync::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + loop { + let g = m.lock().unwrap(); + let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap(); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not timeout + if !no_timeout.timed_out() { + continue; } + + break; } -); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_timeout_while_wait, - test_body: { - use locks::{Condvar, Mutex}; +fn wait_timeout_while_wait() { + use std::sync::{Condvar, Mutex}; - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); - let g = maybe_unwrap(m.lock()); - let (_g, wait) = maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(1), |_| true)); - // no spurious wakeups. ensure it timed-out - assert!(wait.timed_out()); - } -); + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap(); + // no spurious wakeups. ensure it timed-out + assert!(wait.timed_out()); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_timeout_while_instant_satisfy, - test_body: { - use locks::{Condvar, Mutex}; +fn wait_timeout_while_instant_satisfy() { + use std::sync::{Condvar, Mutex}; - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); - let g = maybe_unwrap(m.lock()); - let (_g, wait) = - maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(0), |_| false)); - // ensure it didn't time-out even if we were not given any time. - assert!(!wait.timed_out()); - } -); + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); +} +#[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_timeout_while_wake, - test_body: { - use locks::{Condvar, Mutex}; +fn wait_timeout_while_wake() { + use std::sync::{Condvar, Mutex}; + + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair_copy = pair.clone(); + + let &(ref m, ref c) = &*pair; + let g = m.lock().unwrap(); + let _t = thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair_copy; + let mut started = lock.lock().unwrap(); + thread::sleep(Duration::from_millis(1)); + *started = true; + cvar.notify_one(); + }); + + let (g2, wait) = c + .wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified) + .unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + assert!(*g2); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn wait_timeout_wake() { + use std::sync::{Condvar, Mutex}; - let pair = Arc::new((Mutex::new(false), Condvar::new())); - let pair_copy = pair.clone(); + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); - let &(ref m, ref c) = &*pair; - let g = maybe_unwrap(m.lock()); - let _t = thread::spawn(move || { - let &(ref lock, ref cvar) = &*pair_copy; - let mut started = maybe_unwrap(lock.lock()); - thread::sleep(Duration::from_millis(1)); - *started = true; - cvar.notify_one(); - }); - let (g2, wait) = maybe_unwrap(c.wait_timeout_while( - g, - Duration::from_millis(u64::MAX), - |&mut notified| !notified - )); - // ensure it didn't time-out even if we were not given any time. - assert!(!wait.timed_out()); - assert!(*g2); - } -); + loop { + let g = m.lock().unwrap(); -#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -nonpoison_and_poison_unwrap_test!( - name: wait_timeout_wake, - test_body: { - use locks::{Condvar, Mutex}; + let c2 = c.clone(); + let m2 = m.clone(); - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); + let notified = Arc::new(AtomicBool::new(false)); + let notified_copy = notified.clone(); - loop { - let g = maybe_unwrap(m.lock()); - - let c2 = c.clone(); - let m2 = m.clone(); - - let notified = Arc::new(AtomicBool::new(false)); - let notified_copy = notified.clone(); - - let t = thread::spawn(move || { - let _g = maybe_unwrap(m2.lock()); - thread::sleep(Duration::from_millis(1)); - notified_copy.store(true, Ordering::Relaxed); - c2.notify_one(); - }); - let (g, timeout_res) = - maybe_unwrap(c.wait_timeout(g, Duration::from_millis(u64::MAX))); - assert!(!timeout_res.timed_out()); - // spurious wakeups mean this isn't necessarily true - // so execute test again, if not notified - if !notified.load(Ordering::Relaxed) { - t.join().unwrap(); - continue; - } - drop(g); + let t = thread::spawn(move || { + let _g = m2.lock().unwrap(); + thread::sleep(Duration::from_millis(1)); + notified_copy.store(true, Ordering::Relaxed); + c2.notify_one(); + }); + let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap(); + assert!(!timeout_res.timed_out()); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not notified + if !notified.load(Ordering::Relaxed) { t.join().unwrap(); - - break; + continue; } + drop(g); + + t.join().unwrap(); + + break; } -); +} // Some platforms internally cast the timeout duration into nanoseconds. // If they fail to consider overflow during the conversion (I'm looking @@ -274,42 +255,41 @@ nonpoison_and_poison_unwrap_test!( // timeout for durations that are slightly longer than u64::MAX nanoseconds. // `std` should guard against this by clamping the timeout. // See #37440 for context. -nonpoison_and_poison_unwrap_test!( - name: timeout_nanoseconds, - test_body: { - use locks::Mutex; - use locks::Condvar; +#[test] +fn timeout_nanoseconds() { + use std::sync::{Condvar, Mutex}; + + let sent = Mutex::new(false); + let cond = Condvar::new(); + + thread::scope(|s| { + s.spawn(|| { + // Sleep so that the other thread has a chance to encounter the + // timeout. + thread::sleep(Duration::from_secs(2)); + *sent.lock().unwrap() = true; + cond.notify_all(); + }); - let sent = Mutex::new(false); - let cond = Condvar::new(); - - thread::scope(|s| { - s.spawn(|| { - // Sleep so that the other thread has a chance to encounter the - // timeout. - thread::sleep(Duration::from_secs(2)); - maybe_unwrap(sent.set(true)); - cond.notify_all(); - }); - - let mut guard = maybe_unwrap(sent.lock()); - // Loop until `sent` is set by the thread to guard against spurious - // wakeups. If the `wait_timeout` happens just before the signal by - // the other thread, such a spurious wakeup might prevent the - // miscalculated timeout from occurring, but this is basically just - // a smoke test anyway. - loop { - if *guard { - break; - } - - // If there is internal overflow, this call will return almost - // immediately, before the other thread has reached the `notify_all`, - // and indicate a timeout. - let (g, res) = maybe_unwrap(cond.wait_timeout(guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000)))); - assert!(!res.timed_out()); - guard = g; + let mut guard = sent.lock().unwrap(); + // Loop until `sent` is set by the thread to guard against spurious + // wakeups. If the `wait_timeout` happens just before the signal by + // the other thread, such a spurious wakeup might prevent the + // miscalculated timeout from occurring, but this is basically just + // a smoke test anyway. + loop { + if *guard { + break; } - }) - } -); + + // If there is internal overflow, this call will return almost + // immediately, before the other thread has reached the `notify_all`, + // and indicate a timeout. + let (g, res) = cond + .wait_timeout(guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000))) + .unwrap(); + assert!(!res.timed_out()); + guard = g; + } + }) +} From 3d5a40809c8322de933205a4d581059dc8adc3f3 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Fri, 31 Oct 2025 15:12:46 -0400 Subject: [PATCH 101/108] update `nonpoison::Condvar` to take guards by reference Since non-poisoning `Condvar` take non-poisoing `Mutex`es when `wait`ing, we do not need to take by ownership since a poison error cannot occur while we wait. Signed-off-by: Connor Tsui --- library/std/src/sync/barrier.rs | 2 +- library/std/src/sync/nonpoison/condvar.rs | 71 +++-- library/std/tests/sync/condvar.rs | 309 ++++++++++++++++++++-- 3 files changed, 324 insertions(+), 58 deletions(-) diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 8988126bd90c0..c2c18889dde7d 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -125,7 +125,7 @@ impl Barrier { let local_gen = lock.generation_id; lock.count += 1; if lock.count < self.num_threads { - let _guard = self.cvar.wait_while(lock, |state| local_gen == state.generation_id); + self.cvar.wait_while(&mut lock, |state| local_gen == state.generation_id); BarrierWaitResult(false) } else { lock.count = 0; diff --git a/library/std/src/sync/nonpoison/condvar.rs b/library/std/src/sync/nonpoison/condvar.rs index 994fc6816a0d0..d2b251d7c44c1 100644 --- a/library/std/src/sync/nonpoison/condvar.rs +++ b/library/std/src/sync/nonpoison/condvar.rs @@ -1,4 +1,5 @@ use crate::fmt; +use crate::ops::DerefMut; use crate::sync::WaitTimeoutResult; use crate::sync::nonpoison::{MutexGuard, mutex}; use crate::sys::sync as sys; @@ -38,7 +39,7 @@ use crate::time::{Duration, Instant}; /// let (lock, cvar) = &*pair; /// let mut started = lock.lock(); /// while !*started { -/// started = cvar.wait(started); +/// cvar.wait(&mut started); /// } /// ``` /// @@ -115,16 +116,15 @@ impl Condvar { /// let mut started = lock.lock(); /// // As long as the value inside the `Mutex` is `false`, we wait. /// while !*started { - /// started = cvar.wait(started); + /// cvar.wait(&mut started); /// } /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + pub fn wait(&self, guard: &mut MutexGuard<'_, T>) { unsafe { - let lock = mutex::guard_lock(&guard); + let lock = mutex::guard_lock(guard); self.inner.wait(lock); } - guard } /// Blocks the current thread until the provided condition becomes false. @@ -167,21 +167,17 @@ impl Condvar { /// // Wait for the thread to start up. /// let (lock, cvar) = &*pair; /// // As long as the value inside the `Mutex` is `true`, we wait. - /// let _guard = cvar.wait_while(lock.lock(), |pending| { *pending }); + /// let mut guard = lock.lock(); + /// cvar.wait_while(&mut guard, |pending| { *pending }); /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait_while<'a, T, F>( - &self, - mut guard: MutexGuard<'a, T>, - mut condition: F, - ) -> MutexGuard<'a, T> + pub fn wait_while(&self, guard: &mut MutexGuard<'_, T>, mut condition: F) where F: FnMut(&mut T) -> bool, { - while condition(&mut *guard) { - guard = self.wait(guard); + while condition(guard.deref_mut()) { + self.wait(guard); } - guard } /// Waits on this condition variable for a notification, timing out after a @@ -206,7 +202,7 @@ impl Condvar { /// The returned [`WaitTimeoutResult`] value indicates if the timeout is /// known to have elapsed. /// - /// Like [`wait`], the lock specified will be re-acquired when this function + /// Like [`wait`], the lock specified will have been re-acquired when this function /// returns, regardless of whether the timeout elapsed or not. /// /// [`wait`]: Self::wait @@ -239,9 +235,8 @@ impl Condvar { /// let mut started = lock.lock(); /// // as long as the value inside the `Mutex` is `false`, we wait /// loop { - /// let result = cvar.wait_timeout(started, Duration::from_millis(10)); + /// let result = cvar.wait_timeout(&mut started, Duration::from_millis(10)); /// // 10 milliseconds have passed, or maybe the value changed! - /// started = result.0; /// if *started == true { /// // We received the notification and the value has been updated, we can leave. /// break @@ -249,16 +244,16 @@ impl Condvar { /// } /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait_timeout<'a, T>( + pub fn wait_timeout( &self, - guard: MutexGuard<'a, T>, + guard: &mut MutexGuard<'_, T>, dur: Duration, - ) -> (MutexGuard<'a, T>, WaitTimeoutResult) { + ) -> WaitTimeoutResult { let success = unsafe { - let lock = mutex::guard_lock(&guard); + let lock = mutex::guard_lock(guard); self.inner.wait_timeout(lock, dur) }; - (guard, WaitTimeoutResult(!success)) + WaitTimeoutResult(!success) } /// Waits on this condition variable for a notification, timing out after a @@ -277,7 +272,7 @@ impl Condvar { /// The returned [`WaitTimeoutResult`] value indicates if the timeout is /// known to have elapsed without the condition being met. /// - /// Like [`wait_while`], the lock specified will be re-acquired when this + /// Like [`wait_while`], the lock specified will have been re-acquired when this /// function returns, regardless of whether the timeout elapsed or not. /// /// [`wait_while`]: Self::wait_while @@ -307,37 +302,39 @@ impl Condvar { /// /// // wait for the thread to start up /// let (lock, cvar) = &*pair; + /// let mut guard = lock.lock(); /// let result = cvar.wait_timeout_while( - /// lock.lock(), + /// &mut guard, /// Duration::from_millis(100), /// |&mut pending| pending, /// ); - /// if result.1.timed_out() { + /// if result.timed_out() { /// // timed-out without the condition ever evaluating to false. /// } - /// // access the locked mutex via result.0 + /// // access the locked mutex via guard /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait_timeout_while<'a, T, F>( + pub fn wait_timeout_while( &self, - mut guard: MutexGuard<'a, T>, + guard: &mut MutexGuard<'_, T>, dur: Duration, mut condition: F, - ) -> (MutexGuard<'a, T>, WaitTimeoutResult) + ) -> WaitTimeoutResult where F: FnMut(&mut T) -> bool, { let start = Instant::now(); - loop { - if !condition(&mut *guard) { - return (guard, WaitTimeoutResult(false)); - } + + while condition(guard.deref_mut()) { let timeout = match dur.checked_sub(start.elapsed()) { Some(timeout) => timeout, - None => return (guard, WaitTimeoutResult(true)), + None => return WaitTimeoutResult(true), }; - guard = self.wait_timeout(guard, timeout).0; + + self.wait_timeout(guard, timeout); } + + WaitTimeoutResult(false) } /// Wakes up one blocked thread on this condvar. @@ -378,7 +375,7 @@ impl Condvar { /// let mut started = lock.lock(); /// // As long as the value inside the `Mutex` is `false`, we wait. /// while !*started { - /// started = cvar.wait(started); + /// cvar.wait(&mut started); /// } /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] @@ -422,7 +419,7 @@ impl Condvar { /// let mut started = lock.lock(); /// // As long as the value inside the `Mutex` is `false`, we wait. /// while !*started { - /// started = cvar.wait(started); + /// cvar.wait(&mut started); /// } /// ``` #[unstable(feature = "nonpoison_condvar", issue = "134645")] diff --git a/library/std/tests/sync/condvar.rs b/library/std/tests/sync/condvar.rs index 74328793caeae..a52e0a00caf48 100644 --- a/library/std/tests/sync/condvar.rs +++ b/library/std/tests/sync/condvar.rs @@ -19,8 +19,8 @@ nonpoison_and_poison_unwrap_test!( #[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -fn notify_one() { - use std::sync::{Condvar, Mutex}; +fn poison_notify_one() { + use std::sync::poison::{Condvar, Mutex}; let m = Arc::new(Mutex::new(())); let m2 = m.clone(); @@ -39,8 +39,28 @@ fn notify_one() { #[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -fn notify_all() { - use std::sync::{Condvar, Mutex}; +fn nonpoison_notify_one() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); + + let mut g = m.lock(); + let _t = thread::spawn(move || { + let _g = m2.lock(); + c2.notify_one(); + }); + + c.wait(&mut g); + drop(g); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn poison_notify_all() { + use std::sync::poison::{Condvar, Mutex}; const N: usize = 10; @@ -78,8 +98,47 @@ fn notify_all() { #[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -fn test_mutex_arc_condvar() { - use std::sync::{Condvar, Mutex}; +fn nonpoison_notify_all() { + use std::sync::nonpoison::{Condvar, Mutex}; + + const N: usize = 10; + + let data = Arc::new((Mutex::new(0), Condvar::new())); + let (tx, rx) = channel(); + for _ in 0..N { + let data = data.clone(); + let tx = tx.clone(); + thread::spawn(move || { + let &(ref lock, ref cond) = &*data; + let mut cnt = lock.lock(); + *cnt += 1; + if *cnt == N { + tx.send(()).unwrap(); + } + while *cnt != 0 { + cond.wait(&mut cnt); + } + tx.send(()).unwrap(); + }); + } + drop(tx); + + let &(ref lock, ref cond) = &*data; + rx.recv().unwrap(); + let mut cnt = lock.lock(); + *cnt = 0; + cond.notify_all(); + drop(cnt); + + for _ in 0..N { + rx.recv().unwrap(); + } +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn poison_test_mutex_arc_condvar() { + use std::sync::poison::{Condvar, Mutex}; struct Packet(Arc<(Mutex, Condvar)>); @@ -113,8 +172,43 @@ fn test_mutex_arc_condvar() { #[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -fn wait_while() { - use std::sync::{Condvar, Mutex}; +fn nonpoison_test_mutex_arc_condvar() { + use std::sync::nonpoison::{Condvar, Mutex}; + + struct Packet(Arc<(Mutex, Condvar)>); + + let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + + let (tx, rx) = channel(); + + let _t = thread::spawn(move || { + // Wait until our parent has taken the lock. + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + + // Set the data to `true` and wake up our parent. + let mut guard = lock.lock(); + *guard = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut guard = lock.lock(); + // Wake up our child. + tx.send(()).unwrap(); + + // Wait until our child has set the data to `true`. + assert!(!*guard); + while !*guard { + cvar.wait(&mut guard); + } +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn poison_wait_while() { + use std::sync::poison::{Condvar, Mutex}; let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair2 = pair.clone(); @@ -136,8 +230,32 @@ fn wait_while() { #[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -fn wait_timeout_wait() { - use std::sync::{Condvar, Mutex}; +fn nonpoison_wait_while() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); + + // Inside of our lock, spawn a new thread, and then wait for it to start. + thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair2; + let mut started = lock.lock(); + *started = true; + // We notify the condvar that the value has changed. + cvar.notify_one(); + }); + + // Wait for the thread to start up. + let &(ref lock, ref cvar) = &*pair; + let mut guard = lock.lock(); + cvar.wait_while(&mut guard, |started| !*started); + assert!(*guard); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn poison_wait_timeout_wait() { + use std::sync::poison::{Condvar, Mutex}; let m = Arc::new(Mutex::new(())); let c = Arc::new(Condvar::new()); @@ -157,8 +275,29 @@ fn wait_timeout_wait() { #[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -fn wait_timeout_while_wait() { - use std::sync::{Condvar, Mutex}; +fn nonpoison_wait_timeout_wait() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + loop { + let mut g = m.lock(); + let no_timeout = c.wait_timeout(&mut g, Duration::from_millis(1)); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not timeout + if !no_timeout.timed_out() { + continue; + } + + break; + } +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn poison_wait_timeout_while_wait() { + use std::sync::poison::{Condvar, Mutex}; let m = Arc::new(Mutex::new(())); let c = Arc::new(Condvar::new()); @@ -171,8 +310,22 @@ fn wait_timeout_while_wait() { #[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -fn wait_timeout_while_instant_satisfy() { - use std::sync::{Condvar, Mutex}; +fn nonpoison_wait_timeout_while_wait() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let mut g = m.lock(); + let wait = c.wait_timeout_while(&mut g, Duration::from_millis(1), |_| true); + // no spurious wakeups. ensure it timed-out + assert!(wait.timed_out()); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn poison_wait_timeout_while_instant_satisfy() { + use std::sync::poison::{Condvar, Mutex}; let m = Arc::new(Mutex::new(())); let c = Arc::new(Condvar::new()); @@ -185,8 +338,22 @@ fn wait_timeout_while_instant_satisfy() { #[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -fn wait_timeout_while_wake() { - use std::sync::{Condvar, Mutex}; +fn nonpoison_wait_timeout_while_instant_satisfy() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let mut g = m.lock(); + let wait = c.wait_timeout_while(&mut g, Duration::from_millis(0), |_| false); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn poison_wait_timeout_while_wake() { + use std::sync::poison::{Condvar, Mutex}; let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair_copy = pair.clone(); @@ -211,8 +378,33 @@ fn wait_timeout_while_wake() { #[test] #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. -fn wait_timeout_wake() { - use std::sync::{Condvar, Mutex}; +fn nonpoison_wait_timeout_while_wake() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair_copy = pair.clone(); + + let &(ref m, ref c) = &*pair; + let mut g = m.lock(); + let _t = thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair_copy; + let mut started = lock.lock(); + thread::sleep(Duration::from_millis(1)); + *started = true; + cvar.notify_one(); + }); + + let wait = + c.wait_timeout_while(&mut g, Duration::from_millis(u64::MAX), |&mut notified| !notified); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + assert!(*g); +} + +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn poison_wait_timeout_wake() { + use std::sync::poison::{Condvar, Mutex}; let m = Arc::new(Mutex::new(())); let c = Arc::new(Condvar::new()); @@ -249,6 +441,46 @@ fn wait_timeout_wake() { } } +#[test] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. +fn nonpoison_wait_timeout_wake() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + loop { + let mut g = m.lock(); + + let c2 = c.clone(); + let m2 = m.clone(); + + let notified = Arc::new(AtomicBool::new(false)); + let notified_copy = notified.clone(); + + let t = thread::spawn(move || { + let _g = m2.lock(); + thread::sleep(Duration::from_millis(1)); + notified_copy.store(true, Ordering::Relaxed); + c2.notify_one(); + }); + + let timeout_res = c.wait_timeout(&mut g, Duration::from_millis(u64::MAX)); + assert!(!timeout_res.timed_out()); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not notified + if !notified.load(Ordering::Relaxed) { + t.join().unwrap(); + continue; + } + drop(g); + + t.join().unwrap(); + + break; + } +} + // Some platforms internally cast the timeout duration into nanoseconds. // If they fail to consider overflow during the conversion (I'm looking // at you, macOS), `wait_timeout` will return immediately and indicate a @@ -256,8 +488,8 @@ fn wait_timeout_wake() { // `std` should guard against this by clamping the timeout. // See #37440 for context. #[test] -fn timeout_nanoseconds() { - use std::sync::{Condvar, Mutex}; +fn poison_timeout_nanoseconds() { + use std::sync::poison::{Condvar, Mutex}; let sent = Mutex::new(false); let cond = Condvar::new(); @@ -293,3 +525,40 @@ fn timeout_nanoseconds() { } }) } + +#[test] +fn nonpoison_timeout_nanoseconds() { + use std::sync::nonpoison::{Condvar, Mutex}; + + let sent = Mutex::new(false); + let cond = Condvar::new(); + + thread::scope(|s| { + s.spawn(|| { + // Sleep so that the other thread has a chance to encounter the + // timeout. + thread::sleep(Duration::from_secs(2)); + sent.set(true); + cond.notify_all(); + }); + + let mut guard = sent.lock(); + // Loop until `sent` is set by the thread to guard against spurious + // wakeups. If the `wait_timeout` happens just before the signal by + // the other thread, such a spurious wakeup might prevent the + // miscalculated timeout from occurring, but this is basically just + // a smoke test anyway. + loop { + if *guard { + break; + } + + // If there is internal overflow, this call will return almost + // immediately, before the other thread has reached the `notify_all`, + // and indicate a timeout. + let res = cond + .wait_timeout(&mut guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000))); + assert!(!res.timed_out()); + } + }) +} From c1153b08ff87d09ab043bf1878cfb3f1cf55c8ec Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Fri, 31 Oct 2025 15:14:21 -0400 Subject: [PATCH 102/108] move condvar test from mutex to condvar test file Signed-off-by: Connor Tsui --- library/std/tests/sync/condvar.rs | 34 +++++++++++++++++++++++++++++++ library/std/tests/sync/mutex.rs | 34 +------------------------------ 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/library/std/tests/sync/condvar.rs b/library/std/tests/sync/condvar.rs index a52e0a00caf48..e5a7ad8f9b331 100644 --- a/library/std/tests/sync/condvar.rs +++ b/library/std/tests/sync/condvar.rs @@ -562,3 +562,37 @@ fn nonpoison_timeout_nanoseconds() { } }) } + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_arc_condvar_poison() { + use std::sync::poison::{Condvar, Mutex}; + + struct Packet(Arc<(Mutex, Condvar)>); + + let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + let (tx, rx) = channel(); + + let _t = thread::spawn(move || -> () { + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + let _g = lock.lock().unwrap(); + cvar.notify_one(); + // Parent should fail when it wakes up. + panic!(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut lock = lock.lock().unwrap(); + tx.send(()).unwrap(); + while *lock == 1 { + match cvar.wait(lock) { + Ok(l) => { + lock = l; + assert_eq!(*lock, 1); + } + Err(..) => break, + } + } +} diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index ff6aef717936f..75a6bf64607ef 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -3,7 +3,7 @@ use std::ops::FnMut; use std::panic::{self, AssertUnwindSafe}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc::channel; -use std::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; +use std::sync::{Arc, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; use std::{hint, mem, thread}; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -423,38 +423,6 @@ fn test_replace_poison() { inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); } -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_arc_condvar_poison() { - struct Packet(Arc<(Mutex, Condvar)>); - - let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); - let (tx, rx) = channel(); - - let _t = thread::spawn(move || -> () { - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; - let _g = lock.lock().unwrap(); - cvar.notify_one(); - // Parent should fail when it wakes up. - panic!(); - }); - - let &(ref lock, ref cvar) = &*packet.0; - let mut lock = lock.lock().unwrap(); - tx.send(()).unwrap(); - while *lock == 1 { - match cvar.wait(lock) { - Ok(l) => { - lock = l; - assert_eq!(*lock, 1); - } - Err(..) => break, - } - } -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_mutex_arc_poison() { From 2e01acc59e721dc220807cbf7ca4a599ff923f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 16 Jan 2025 20:56:37 +0000 Subject: [PATCH 103/108] Add tests for some cases mentioned in #135589 --- .../missing-lifetime-in-assoc-type-1.rs | 16 +++++++++++++ .../missing-lifetime-in-assoc-type-1.stderr | 15 ++++++++++++ .../missing-lifetime-in-assoc-type-2.rs | 14 +++++++++++ .../missing-lifetime-in-assoc-type-2.stderr | 24 +++++++++++++++++++ .../missing-lifetime-in-assoc-type-3.rs | 14 +++++++++++ .../missing-lifetime-in-assoc-type-3.stderr | 20 ++++++++++++++++ .../missing-lifetime-in-assoc-type-4.rs | 14 +++++++++++ .../missing-lifetime-in-assoc-type-4.stderr | 15 ++++++++++++ 8 files changed, 132 insertions(+) create mode 100644 tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs create mode 100644 tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr create mode 100644 tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs create mode 100644 tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr create mode 100644 tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.rs create mode 100644 tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr create mode 100644 tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs create mode 100644 tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs new file mode 100644 index 0000000000000..28e86635b1b79 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs @@ -0,0 +1,16 @@ +struct S; +struct T; + +impl<'a> IntoIterator for &S { + //~^ ERROR E0207 + //~| NOTE unconstrained lifetime parameter + type Item = &T; + //~^ ERROR in the trait associated type + //~| NOTE this lifetime must come from the implemented type + type IntoIter = std::collections::btree_map::Values<'a, i32, T>; + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} +fn main() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr new file mode 100644 index 0000000000000..0796f65b9b2d1 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr @@ -0,0 +1,15 @@ +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/missing-lifetime-in-assoc-type-1.rs:7:17 + | +LL | type Item = &T; + | ^ this lifetime must come from the implemented type + +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/missing-lifetime-in-assoc-type-1.rs:4:6 + | +LL | impl<'a> IntoIterator for &S { + | ^^ unconstrained lifetime parameter + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs new file mode 100644 index 0000000000000..dd720f075ac46 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs @@ -0,0 +1,14 @@ +struct S; +struct T; + +impl IntoIterator for &S { + type Item = &T; + //~^ ERROR in the trait associated type + type IntoIter = std::collections::btree_map::Values<'a, i32, T>; + //~^ ERROR use of undeclared lifetime name `'a` + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} +fn main() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr new file mode 100644 index 0000000000000..77916b651b13c --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr @@ -0,0 +1,24 @@ +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/missing-lifetime-in-assoc-type-2.rs:5:17 + | +LL | type Item = &T; + | ^ this lifetime must come from the implemented type + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/missing-lifetime-in-assoc-type-2.rs:7:57 + | +LL | type IntoIter = std::collections::btree_map::Values<'a, i32, T>; + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>; + | ++++ +help: consider introducing lifetime `'a` here + | +LL | impl<'a> IntoIterator for &S { + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0261`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.rs new file mode 100644 index 0000000000000..60d1f0f8fe571 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.rs @@ -0,0 +1,14 @@ +struct S; +struct T; + +impl IntoIterator for &S { + type Item = &T; + //~^ ERROR in the trait associated type + type IntoIter = std::collections::btree_map::Values; + //~^ ERROR missing lifetime specifier + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} +fn main() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr new file mode 100644 index 0000000000000..50012ac72d22f --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr @@ -0,0 +1,20 @@ +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/missing-lifetime-in-assoc-type-3.rs:5:17 + | +LL | type Item = &T; + | ^ this lifetime must come from the implemented type + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-in-assoc-type-3.rs:7:56 + | +LL | type IntoIter = std::collections::btree_map::Values; + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>; + | ++++ +++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs new file mode 100644 index 0000000000000..b01c59f3ee6d2 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs @@ -0,0 +1,14 @@ +struct S; +struct T; + +impl IntoIterator for &S { + type Item = &T; + //~^ ERROR in the trait associated type + type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>; + //~^ ERROR lifetime parameters or bounds on type `IntoIter` do not match the trait declaration + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} +fn main() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr new file mode 100644 index 0000000000000..f17c098ab6d04 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr @@ -0,0 +1,15 @@ +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/missing-lifetime-in-assoc-type-4.rs:5:17 + | +LL | type Item = &T; + | ^ this lifetime must come from the implemented type + +error[E0195]: lifetime parameters or bounds on type `IntoIter` do not match the trait declaration + --> $DIR/missing-lifetime-in-assoc-type-4.rs:7:18 + | +LL | type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>; + | ^^^^ lifetimes do not match type in trait + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0195`. From 8ba2950fb6ebb3f5e7cf10afb79dda6f5ed61bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 16 Jan 2025 21:02:23 +0000 Subject: [PATCH 104/108] Detect case of missing lifetime in assoc type When an associated type is missing a lifetime, point at its enclosing `impl`, whether it has or doesn't have lifetimes defined. If it does have a lifetime, suggest using it. ``` error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/missing-lifetime-in-assoc-type-1.rs:8:17 | LL | impl<'a> IntoIterator for &S { | ---- there is a named lifetime specified on the impl block you could use ... LL | type Item = &T; | ^ this lifetime must come from the implemented type | help: consider using the lifetime from the impl block | LL | type Item = &'a T; | ++ ``` ``` error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/missing-lifetime-in-assoc-type-2.rs:5:17 | LL | impl IntoIterator for &S { | - you could add a lifetime on the impl block, if the trait or the self type can have one LL | type Item = &T; | ^ this lifetime must come from the implemented type ``` --- compiler/rustc_resolve/src/late.rs | 54 +++++++++++++++++-- .../assoc-type.stderr | 2 + .../missing-lifetime-in-assoc-type-1.rs | 2 + .../missing-lifetime-in-assoc-type-1.stderr | 10 +++- .../missing-lifetime-in-assoc-type-2.stderr | 2 + .../missing-lifetime-in-assoc-type-3.stderr | 2 + .../missing-lifetime-in-assoc-type-4.rs | 2 +- .../missing-lifetime-in-assoc-type-4.stderr | 6 ++- .../ui/lifetimes/no_lending_iterators.stderr | 2 + 9 files changed, 74 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index e3051dc38eca2..4a4cb51390623 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -19,7 +19,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions, + Applicability, Diag, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions, + pluralize, }; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; @@ -1874,9 +1875,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ty: ty.span, }); } else { - self.r.dcx().emit_err(errors::AnonymousLifetimeNonGatReportError { - lifetime: lifetime.ident.span, - }); + let mut err = self.r.dcx().create_err( + errors::AnonymousLifetimeNonGatReportError { + lifetime: lifetime.ident.span, + }, + ); + self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span); + err.emit(); } } else { self.r.dcx().emit_err(errors::ElidedAnonymousLifetimeReportError { @@ -1913,6 +1918,47 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); } + fn point_at_impl_lifetimes(&mut self, err: &mut Diag<'_>, i: usize, lifetime: Span) { + let Some((rib, span)) = self.lifetime_ribs[..i] + .iter() + .rev() + .skip(1) + .filter_map(|rib| match rib.kind { + LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::ImplBlock, .. } => { + Some((rib, span)) + } + _ => None, + }) + .next() + else { + return; + }; + if !rib.bindings.is_empty() { + err.span_label( + span, + format!( + "there {} named lifetime{} specified on the impl block you could use", + if rib.bindings.len() == 1 { "is a" } else { "are" }, + pluralize!(rib.bindings.len()), + ), + ); + if rib.bindings.len() == 1 { + err.span_suggestion_verbose( + lifetime.shrink_to_hi(), + "consider using the lifetime from the impl block", + format!("{} ", rib.bindings.keys().next().unwrap()), + Applicability::MaybeIncorrect, + ); + } + } else { + err.span_label( + span, + "you could add a lifetime on the impl block, if the trait or the self type can \ + have one", + ); + } + } + #[instrument(level = "debug", skip(self))] fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) { let id = self.r.next_node_id(); diff --git a/tests/ui/impl-header-lifetime-elision/assoc-type.stderr b/tests/ui/impl-header-lifetime-elision/assoc-type.stderr index e650eeca48ad9..72c066426bd99 100644 --- a/tests/ui/impl-header-lifetime-elision/assoc-type.stderr +++ b/tests/ui/impl-header-lifetime-elision/assoc-type.stderr @@ -1,6 +1,8 @@ error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/assoc-type.rs:11:19 | +LL | impl MyTrait for &i32 { + | - you could add a lifetime on the impl block, if the trait or the self type can have one LL | type Output = &i32; | ^ this lifetime must come from the implemented type diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs index 28e86635b1b79..5953466375d4e 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs @@ -3,9 +3,11 @@ struct T; impl<'a> IntoIterator for &S { //~^ ERROR E0207 + //~| NOTE there is a named lifetime specified on the impl block you could use //~| NOTE unconstrained lifetime parameter type Item = &T; //~^ ERROR in the trait associated type + //~| HELP consider using the lifetime from the impl block //~| NOTE this lifetime must come from the implemented type type IntoIter = std::collections::btree_map::Values<'a, i32, T>; diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr index 0796f65b9b2d1..1fbde0cfb3988 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr @@ -1,8 +1,16 @@ error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type - --> $DIR/missing-lifetime-in-assoc-type-1.rs:7:17 + --> $DIR/missing-lifetime-in-assoc-type-1.rs:8:17 | +LL | impl<'a> IntoIterator for &S { + | ---- there is a named lifetime specified on the impl block you could use +... LL | type Item = &T; | ^ this lifetime must come from the implemented type + | +help: consider using the lifetime from the impl block + | +LL | type Item = &'a T; + | ++ error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> $DIR/missing-lifetime-in-assoc-type-1.rs:4:6 diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr index 77916b651b13c..8408d37a901fd 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr @@ -1,6 +1,8 @@ error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/missing-lifetime-in-assoc-type-2.rs:5:17 | +LL | impl IntoIterator for &S { + | - you could add a lifetime on the impl block, if the trait or the self type can have one LL | type Item = &T; | ^ this lifetime must come from the implemented type diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr index 50012ac72d22f..d93852aee1bd3 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr @@ -1,6 +1,8 @@ error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/missing-lifetime-in-assoc-type-3.rs:5:17 | +LL | impl IntoIterator for &S { + | - you could add a lifetime on the impl block, if the trait or the self type can have one LL | type Item = &T; | ^ this lifetime must come from the implemented type diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs index b01c59f3ee6d2..0c99e8874c354 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs @@ -5,7 +5,7 @@ impl IntoIterator for &S { type Item = &T; //~^ ERROR in the trait associated type type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>; - //~^ ERROR lifetime parameters or bounds on type `IntoIter` do not match the trait declaration + //~^ ERROR lifetime parameters or bounds on associated type `IntoIter` do not match the trait declaration fn into_iter(self) -> Self::IntoIter { todo!() diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr index f17c098ab6d04..ebe051509aad2 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.stderr @@ -1,14 +1,16 @@ error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/missing-lifetime-in-assoc-type-4.rs:5:17 | +LL | impl IntoIterator for &S { + | - you could add a lifetime on the impl block, if the trait or the self type can have one LL | type Item = &T; | ^ this lifetime must come from the implemented type -error[E0195]: lifetime parameters or bounds on type `IntoIter` do not match the trait declaration +error[E0195]: lifetime parameters or bounds on associated type `IntoIter` do not match the trait declaration --> $DIR/missing-lifetime-in-assoc-type-4.rs:7:18 | LL | type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>; - | ^^^^ lifetimes do not match type in trait + | ^^^^ lifetimes do not match associated type in trait error: aborting due to 2 previous errors diff --git a/tests/ui/lifetimes/no_lending_iterators.stderr b/tests/ui/lifetimes/no_lending_iterators.stderr index 340ef93588515..cadba149c234d 100644 --- a/tests/ui/lifetimes/no_lending_iterators.stderr +++ b/tests/ui/lifetimes/no_lending_iterators.stderr @@ -13,6 +13,8 @@ LL | impl Iterator for Data { error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/no_lending_iterators.rs:18:17 | +LL | impl Bar for usize { + | - you could add a lifetime on the impl block, if the trait or the self type can have one LL | type Item = &usize; | ^ this lifetime must come from the implemented type From 1e9e1f2f9ae414f96013568d134e2c4bdaafe62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 16 Jan 2025 21:38:14 +0000 Subject: [PATCH 105/108] On unconstrained lifetime on `impl` block, suggest using it if there's an implicit borrow in the self type ``` error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> $DIR/missing-lifetime-in-assoc-type-1.rs:4:6 | LL | impl<'a> IntoIterator for &S { | ^^ unconstrained lifetime parameter | help: consider using the named lifetime here instead of an implict lifetime | LL | impl<'a> IntoIterator for &'a S { | ++ ``` --- .../rustc_hir_analysis/src/impl_wf_check.rs | 24 +++++++++++++++- .../missing-lifetime-in-assoc-type-1.rs | 1 + .../missing-lifetime-in-assoc-type-1.stderr | 7 ++++- .../missing-lifetime-in-assoc-type-5.rs | 19 +++++++++++++ .../missing-lifetime-in-assoc-type-5.stderr | 28 +++++++++++++++++++ 5 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs create mode 100644 tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 3dede69ce1063..44867e980e478 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -12,11 +12,12 @@ use std::assert_matches::debug_assert_matches; use min_specialization::check_min_specialization; use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; use rustc_errors::codes::*; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; -use rustc_span::ErrorGuaranteed; +use rustc_span::{ErrorGuaranteed, kw}; use crate::constrained_generic_params as cgp; use crate::errors::UnconstrainedGenericParameter; @@ -150,6 +151,27 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( const_param_note2: false, }); diag.code(E0207); + for p in &impl_generics.own_params { + if p.name == kw::UnderscoreLifetime { + let span = tcx.def_span(p.def_id); + let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) else { + continue; + }; + + let (span, sugg) = if &snippet == "'_" { + (span, param.name.to_string()) + } else { + (span.shrink_to_hi(), format!("{} ", param.name)) + }; + diag.span_suggestion_verbose( + span, + "consider using the named lifetime here instead of an implict \ + lifetime", + sugg, + Applicability::MaybeIncorrect, + ); + } + } res = Err(diag.emit()); } } diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs index 5953466375d4e..1ae381ef7bc02 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs @@ -5,6 +5,7 @@ impl<'a> IntoIterator for &S { //~^ ERROR E0207 //~| NOTE there is a named lifetime specified on the impl block you could use //~| NOTE unconstrained lifetime parameter + //~| HELP consider using the named lifetime here instead of an implict lifetime type Item = &T; //~^ ERROR in the trait associated type //~| HELP consider using the lifetime from the impl block diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr index 1fbde0cfb3988..bf26ce4a63027 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr @@ -1,5 +1,5 @@ error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type - --> $DIR/missing-lifetime-in-assoc-type-1.rs:8:17 + --> $DIR/missing-lifetime-in-assoc-type-1.rs:9:17 | LL | impl<'a> IntoIterator for &S { | ---- there is a named lifetime specified on the impl block you could use @@ -17,6 +17,11 @@ error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, | LL | impl<'a> IntoIterator for &S { | ^^ unconstrained lifetime parameter + | +help: consider using the named lifetime here instead of an implict lifetime + | +LL | impl<'a> IntoIterator for &'a S { + | ++ error: aborting due to 2 previous errors diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs new file mode 100644 index 0000000000000..ff64d6b900949 --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs @@ -0,0 +1,19 @@ +struct S; +struct T; + +impl<'a> IntoIterator for &'_ S { + //~^ ERROR E0207 + //~| NOTE there is a named lifetime specified on the impl block you could use + //~| NOTE unconstrained lifetime parameter + //~| HELP consider using the named lifetime here instead of an implict lifetime + type Item = &T; + //~^ ERROR in the trait associated type + //~| HELP consider using the lifetime from the impl block + //~| NOTE this lifetime must come from the implemented type + type IntoIter = std::collections::btree_map::Values<'a, i32, T>; + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} +fn main() {} diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr new file mode 100644 index 0000000000000..7a63bd5b05eff --- /dev/null +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr @@ -0,0 +1,28 @@ +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/missing-lifetime-in-assoc-type-5.rs:9:17 + | +LL | impl<'a> IntoIterator for &'_ S { + | ---- there is a named lifetime specified on the impl block you could use +... +LL | type Item = &T; + | ^ this lifetime must come from the implemented type + | +help: consider using the lifetime from the impl block + | +LL | type Item = &'a T; + | ++ + +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/missing-lifetime-in-assoc-type-5.rs:4:6 + | +LL | impl<'a> IntoIterator for &'_ S { + | ^^ unconstrained lifetime parameter + | +help: consider using the named lifetime here instead of an implict lifetime + | +LL | impl<'a> IntoIterator for &'a S { + | ~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0207`. From 75bb675f96a261851cf3ed562b9fae31dc739dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 16 Jan 2025 22:12:35 +0000 Subject: [PATCH 106/108] Do not suggest introducing lifetime in impl assoc type ``` error[E0261]: use of undeclared lifetime name `'a` --> $DIR/missing-lifetime-in-assoc-type-2.rs:7:57 | LL | impl IntoIterator for &S { | - help: consider introducing lifetime `'a` here: `<'a>` ... LL | type IntoIter = std::collections::btree_map::Values<'a, i32, T>; | ^^ undeclared lifetime ``` ``` error[E0106]: missing lifetime specifier --> $DIR/issue-74918-missing-lifetime.rs:9:30 | LL | type Item = IteratorChunk; | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | LL ~ impl<'a, T, S: Iterator> Iterator for ChunkingIterator { LL ~ type Item = IteratorChunk<'a, T, S>; | ``` --- compiler/rustc_resolve/src/late.rs | 4 +++- compiler/rustc_resolve/src/late/diagnostics.rs | 3 +++ tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr | 4 ---- tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr | 7 +++++-- tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr | 5 +++-- .../mismatched_types/issue-74918-missing-lifetime.stderr | 5 +++-- .../ui/nll/user-annotations/region-error-ice-109072.stderr | 4 ---- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4a4cb51390623..91d0cae062a7e 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -378,6 +378,7 @@ enum LifetimeBinderKind { Function, Closure, ImplBlock, + ImplAssocType, } impl LifetimeBinderKind { @@ -388,6 +389,7 @@ impl LifetimeBinderKind { PolyTrait => "bound", WhereBound => "bound", Item | ConstItem => "item", + ImplAssocType => "associated type", ImplBlock => "impl block", Function => "function", Closure => "closure", @@ -3398,7 +3400,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &generics.params, RibKind::AssocItem, item.id, - LifetimeBinderKind::Item, + LifetimeBinderKind::ImplAssocType, generics.span, |this| { this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 9e82237694e3c..57f81ebf0d81e 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3178,6 +3178,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { { continue; } + if let LifetimeBinderKind::ImplAssocType = kind { + continue; + } if !span.can_be_used_for_suggestions() && suggest_note diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr index 8408d37a901fd..7a0246eaac8fd 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.stderr @@ -14,10 +14,6 @@ LL | type IntoIter = std::collections::btree_map::Values<'a, i32, T>; | help: consider introducing lifetime `'a` here | -LL | type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>; - | ++++ -help: consider introducing lifetime `'a` here - | LL | impl<'a> IntoIterator for &S { | ++++ diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr index d93852aee1bd3..408d5bb40664d 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-3.stderr @@ -14,8 +14,11 @@ LL | type IntoIter = std::collections::btree_map::Values; | help: consider introducing a named lifetime parameter | -LL | type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>; - | ++++ +++ +LL ~ impl<'a> IntoIterator for &S { +LL | type Item = &T; +LL | +LL ~ type IntoIter = std::collections::btree_map::Values<'a, i32, T>; + | error: aborting due to 2 previous errors diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr index 7a63bd5b05eff..9c960c67e2502 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr @@ -20,8 +20,9 @@ LL | impl<'a> IntoIterator for &'_ S { | help: consider using the named lifetime here instead of an implict lifetime | -LL | impl<'a> IntoIterator for &'a S { - | ~~ +LL - impl<'a> IntoIterator for &'_ S { +LL + impl<'a> IntoIterator for &'a S { + | error: aborting due to 2 previous errors diff --git a/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr b/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr index 5020395eb6aea..dc21c2e3cf9bf 100644 --- a/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr +++ b/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr @@ -6,8 +6,9 @@ LL | type Item = IteratorChunk; | help: consider introducing a named lifetime parameter | -LL | type Item<'a> = IteratorChunk<'a, T, S>; - | ++++ +++ +LL ~ impl<'a, T, S: Iterator> Iterator for ChunkingIterator { +LL ~ type Item = IteratorChunk<'a, T, S>; + | error: aborting due to 1 previous error diff --git a/tests/ui/nll/user-annotations/region-error-ice-109072.stderr b/tests/ui/nll/user-annotations/region-error-ice-109072.stderr index 42551b87f6234..026f5b5f80a9b 100644 --- a/tests/ui/nll/user-annotations/region-error-ice-109072.stderr +++ b/tests/ui/nll/user-annotations/region-error-ice-109072.stderr @@ -17,10 +17,6 @@ LL | type T = &'missing (); | help: consider introducing lifetime `'missing` here | -LL | type T<'missing> = &'missing (); - | ++++++++++ -help: consider introducing lifetime `'missing` here - | LL | impl<'missing> Lt<'missing> for () { | ++++++++++ From 8d5166710bf39c1218c9cdc43d7859ce4fd0ee64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 31 Oct 2025 20:46:47 +0000 Subject: [PATCH 107/108] fix typo --- compiler/rustc_hir_analysis/src/impl_wf_check.rs | 2 +- tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs | 2 +- tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr | 2 +- tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs | 2 +- tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 44867e980e478..cadbc54c34108 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -165,7 +165,7 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( }; diag.span_suggestion_verbose( span, - "consider using the named lifetime here instead of an implict \ + "consider using the named lifetime here instead of an implicit \ lifetime", sugg, Applicability::MaybeIncorrect, diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs index 1ae381ef7bc02..5401bc4ecb873 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs @@ -5,7 +5,7 @@ impl<'a> IntoIterator for &S { //~^ ERROR E0207 //~| NOTE there is a named lifetime specified on the impl block you could use //~| NOTE unconstrained lifetime parameter - //~| HELP consider using the named lifetime here instead of an implict lifetime + //~| HELP consider using the named lifetime here instead of an implicit lifetime type Item = &T; //~^ ERROR in the trait associated type //~| HELP consider using the lifetime from the impl block diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr index bf26ce4a63027..feac49eb0ff52 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr @@ -18,7 +18,7 @@ error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, LL | impl<'a> IntoIterator for &S { | ^^ unconstrained lifetime parameter | -help: consider using the named lifetime here instead of an implict lifetime +help: consider using the named lifetime here instead of an implicit lifetime | LL | impl<'a> IntoIterator for &'a S { | ++ diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs index ff64d6b900949..17cca7cc9e37b 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.rs @@ -5,7 +5,7 @@ impl<'a> IntoIterator for &'_ S { //~^ ERROR E0207 //~| NOTE there is a named lifetime specified on the impl block you could use //~| NOTE unconstrained lifetime parameter - //~| HELP consider using the named lifetime here instead of an implict lifetime + //~| HELP consider using the named lifetime here instead of an implicit lifetime type Item = &T; //~^ ERROR in the trait associated type //~| HELP consider using the lifetime from the impl block diff --git a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr index 9c960c67e2502..cb15bcb7d5049 100644 --- a/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr +++ b/tests/ui/lifetimes/missing-lifetime-in-assoc-type-5.stderr @@ -18,7 +18,7 @@ error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, LL | impl<'a> IntoIterator for &'_ S { | ^^ unconstrained lifetime parameter | -help: consider using the named lifetime here instead of an implict lifetime +help: consider using the named lifetime here instead of an implicit lifetime | LL - impl<'a> IntoIterator for &'_ S { LL + impl<'a> IntoIterator for &'a S { From 2a1595e2fea2dfa7f853f07d3595cbd543342d8e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 30 Oct 2025 16:10:53 +0100 Subject: [PATCH 108/108] Add regression test for including extern crates in import filtering --- tests/rustdoc-js/import-filter.js | 20 ++++++++++++++++++++ tests/rustdoc-js/import-filter.rs | 7 +++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/rustdoc-js/import-filter.js create mode 100644 tests/rustdoc-js/import-filter.rs diff --git a/tests/rustdoc-js/import-filter.js b/tests/rustdoc-js/import-filter.js new file mode 100644 index 0000000000000..408d0e3326179 --- /dev/null +++ b/tests/rustdoc-js/import-filter.js @@ -0,0 +1,20 @@ +// This test ensures that when filtering on `import`, `externcrate` items are also displayed. +// It also ensures that the opposite is not true. + +const EXPECTED = [ + { + 'query': 'import:st', + 'others': [ + { 'path': 'foo', 'name': 'st', 'href': '../foo/index.html#reexport.st' }, + // FIXME: `href` is wrong: + { 'path': 'foo', 'name': 'st2', 'href': '../st2/index.html' }, + ], + }, + { + 'query': 'externcrate:st', + 'others': [ + // FIXME: `href` is wrong: + { 'path': 'foo', 'name': 'st2', 'href': '../st2/index.html' }, + ], + }, +]; diff --git a/tests/rustdoc-js/import-filter.rs b/tests/rustdoc-js/import-filter.rs new file mode 100644 index 0000000000000..7d30d00f5bbf8 --- /dev/null +++ b/tests/rustdoc-js/import-filter.rs @@ -0,0 +1,7 @@ +#![crate_name = "foo"] + +pub extern crate std as st2; + +pub use crate::Bar as st; + +pub struct Bar;